From c61954653de692285ea4c345bf601522a84301e9 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 13 Oct 2022 11:57:16 +0200 Subject: [PATCH 01/61] Use Database for DataGeneration in non remote mode. --- src/AsyncSocket/AbstractEnvironment.py | 2 +- src/Database/BaseDataset.py | 252 +++++++++++++++++++++++ src/Database/BaseDatasetConfig.py | 96 +++++++++ src/Database/__init__.py | 0 src/Environment/BaseEnvironment.py | 66 ++++-- src/Environment/BaseEnvironmentConfig.py | 5 + src/Manager/DataManager.py | 155 +++++++------- src/Manager/DatabaseManager.py | 164 +++++++++++++++ src/Manager/DatasetManager.py | 2 +- src/Manager/EnvironmentManager.py | 101 +++------ src/Manager/Manager.py | 22 +- src/Manager/NetworkManager.py | 2 +- src/Manager/__init__.py | 6 +- src/Pipelines/BaseDataGenerator.py | 100 +++++---- src/Pipelines/BasePipeline.py | 121 ++++++----- src/Utils/configs.py | 33 +++ src/Utils/path.py | 79 +++++++ src/Utils/pathUtils.py | 75 ------- 18 files changed, 915 insertions(+), 366 deletions(-) create mode 100644 src/Database/BaseDataset.py create mode 100644 src/Database/BaseDatasetConfig.py create mode 100644 src/Database/__init__.py create mode 100644 src/Manager/DatabaseManager.py create mode 100644 src/Utils/configs.py create mode 100644 src/Utils/path.py delete mode 100644 src/Utils/pathUtils.py diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index c8519105..63fdba35 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -31,7 +31,7 @@ def __init__(self, self.input: ndarray = array([]) self.output: ndarray = array([]) self.loss_data: Any = None - self.compute_essential_data: bool = True + self.compute_training_data: bool = True # Dataset data variables self.sample_in: Optional[ndarray] = None diff --git a/src/Database/BaseDataset.py b/src/Database/BaseDataset.py new file mode 100644 index 00000000..4b155513 --- /dev/null +++ b/src/Database/BaseDataset.py @@ -0,0 +1,252 @@ +from typing import Dict, List, Optional, Tuple +from numpy import array, ndarray, concatenate, save, arange +from numpy.random import shuffle +from collections import namedtuple + + +class BaseDataset: + """ + | BaseDataset is a dataset class to store any data from a BaseEnvironment or from files. + | Given data is split into input data and output data. + | Saving data results in multiple partitions of input and output data. + + :param namedtuple config: Namedtuple which contains BaseDataset parameters + """ + + def __init__(self, config: namedtuple): + + self.name: str = self.__class__.__name__ + + # Data fields containers + self.data_type = ndarray + self.fields: List[str] = ['input', 'output'] + self.data: Dict[str, ndarray] = {'input': array([]), 'output': array([])} + self.shape: Dict[str, Optional[List[int]]] = {'input': None, 'output': None} + + # Indexing + self.shuffle_pattern: Optional[List[int]] = None + self.current_sample: int = 0 + + # Dataset memory + self.max_size: int = config.max_file_size + self.batch_per_field: Dict[str, int] = {field: 0 for field in ['input', 'output']} + self.__empty: bool = True + + @property + def nb_samples(self) -> int: + """ + | Property returning the current number of samples + + :return: The current number of samples in all partitions + """ + + return max([len(self.data[field]) for field in self.fields]) + + def is_empty(self) -> bool: + """ + | Check if the fields of the dataset are empty. A field is considered as non-empty if it is filled with another + sample. + + :return: The Dataset is empty or not + """ + + # The empty flag is set to False once the Dataset is considered as non-empty + if not self.__empty: + return False + # Check each registered data field + for field in self.fields: + # Dataset is considered as non-empty if a field is filled with another sample + if self.batch_per_field[field] > 1: + self.__empty = False + return False + # If all field are considered as non-empty then the Dataset is empty + return True + + def init_data_size(self, field: str, shape: List[int]) -> None: + """ + | Store the original shape of data. Reshape data containers. + + :param str field: Data field name + :param List[int] shape: Shape of the corresponding tensor + """ + + # Store the original data shape + self.shape[field] = shape + # Reshape the data container + self.data[field] = array([]).reshape((0, *shape)) + + def get_data_shape(self, field: str) -> List[int]: + """ + | Returns the data shape of field. + + :param str field: Data field name + :return: Data shape for field + """ + + return self.shape[field] + + def init_additional_field(self, field: str, shape: List[int]) -> None: + """ + | Register a new data field. + + :param str field: Name of the data field + :param List[int] shape: Data shape + """ + + # Register the data field + self.fields.append(field) + # Init the number of adds in the field + self.batch_per_field[field] = 0 + # Init the field shape + self.init_data_size(field, shape) + + def empty(self) -> None: + """ + | Empty the dataset. + """ + + # Reinit each data container + for field in self.fields: + self.data[field] = array([]) if self.shape[field] is None else array([]).reshape((0, *self.shape[field])) + # Reinit indexing variables + self.shuffle_pattern = None + self.current_sample = 0 + self.batch_per_field = {field: 0 for field in self.fields} + self.__empty = True + + def memory_size(self, field: Optional[str] = None) -> int: + """ + | Return the actual memory size of the dataset if field is None. Otherwise, return the actual memory size of the + field. + + :param Optional[str] field: Name of the data field + :return: Size in bytes of the current dataset. + """ + + # Return the total memory size + if field is None: + return sum([self.data[field].nbytes for field in self.fields]) + # Return the memory size for the specified field + return self.data[field].nbytes + + def check_data(self, field: str, data: ndarray) -> None: + """ + | Check if the data is a numpy array. + + :param str field: Values at 'input' or anything else. Define if the associated shape is correspond to input + shape or output one. + :param ndarray data: New data + """ + + if type(data) != self.data_type: + raise TypeError(f"[{self.name}] Wrong data type in field '{field}': numpy array required, got {type(data)}") + + def add(self, field: str, data: ndarray, partition_file: Optional[str] = None) -> None: + """ + | Add data to the dataset. + + :param str field: Name of the data field + :param ndarray data: New data as batch of samples + :param Optional[str] partition_file: Path to the file in which to write the data + """ + + # Check data type + self.check_data(field, data) + + # Check if field is registered + if field not in self.fields: + # Fields can be register only if Dataset is empty + if not self.is_empty(): + raise ValueError(f"[{self.name}] A new field {field} tries to be created as Dataset is non empty. This " + f"will lead to a different number of sample for each field of the dataset.") + # Add new field if not registered + self.init_additional_field(field, data[0].shape) + + # Check data size initialization + if self.shape[field] is None: + self.init_data_size(field, data[0].shape) + + # Add batched samples + self.data[field] = concatenate((self.data[field], data)) + # Save in partition + if partition_file is not None: + self.save(field, partition_file) + + # Update sample indexing in dataset + self.batch_per_field[field] += 1 + self.current_sample = max([len(self.data[f]) for f in self.fields]) + + def save(self, field: str, file: str) -> None: + """ + | Save the corresponding field of the Dataset. + + :param str field: Name of the data field + :param str file: Path to the file in which to write the data + """ + + save(file, self.data[field]) + + def set(self, field: str, data: ndarray) -> None: + """ + | Set a full field of the dataset. + + :param str field: Name of the data field + :param ndarray data: New data as batch of samples + """ + + # Check data type + self.check_data(field, data) + + # Check if field is registered + if field not in self.fields: + # Add new field if not registered + self.init_additional_field(field, data[0].shape) + + # Check data size initialization + if self.shape[field] is None: + self.init_data_size(field, data[0].shape) + + # Set the full field + self.data[field] = data + + # Update sample indexing in dataset + self.__empty = False + self.current_sample = 0 + + def get(self, field: str, idx_begin: int, idx_end: int) -> ndarray: + """ + | Get a batch of data in 'field' container. + + :param str field: Name of the data field + :param int idx_begin: Index of the first sample + :param int idx_end: Index of the last sample + :return: Batch of data from 'field' + """ + + indices = slice(idx_begin, idx_end) if self.shuffle_pattern is None else self.shuffle_pattern[idx_begin:idx_end] + return self.data[field][indices] + + def shuffle(self) -> None: + """ + | Define a random shuffle pattern. + """ + + # Nothing to shuffle if Dataset is empty + if self.is_empty(): + return + # Generate a shuffle pattern + self.shuffle_pattern = arange(self.nb_samples) + shuffle(self.shuffle_pattern) + + def __str__(self) -> str: + """ + :return: String containing information about the BaseDatasetConfig object + """ + + description = "\n" + description += f" {self.name}\n" + description += f" Max size: {self.max_size}\n" + description += f" Data fields: {self.fields}" + for field in self.fields: + description += f" {field} shape: {self.shape[field]}" + return description diff --git a/src/Database/BaseDatasetConfig.py b/src/Database/BaseDatasetConfig.py new file mode 100644 index 00000000..d1bf8bfb --- /dev/null +++ b/src/Database/BaseDatasetConfig.py @@ -0,0 +1,96 @@ +from typing import Type, Optional +from os.path import isdir, sep, join + +from DeepPhysX.Core.Database.BaseDataset import BaseDataset +from DeepPhysX.Core.Utils.configs import make_config, namedtuple + + +class BaseDatasetConfig: + + def __init__(self, + dataset_class: Type[BaseDataset] = BaseDataset, + existing_dir: Optional[str] = None, + mode: Optional[str] = None, + max_file_size: Optional[float] = None, + shuffle: bool = True, + normalize: bool = True, + recompute_normalization: bool = False): + """ + Configuration class to parameterize the Dataset and its Manager. + + :param dataset_class: BaseDataset class from which an instance will be created. + :param existing_dir: Path to an existing Dataset repository. + :param mode: Specify the Dataset mode that should be used between 'training', 'validation' and 'running'. + :param max_file_size: Maximum size (in Gb) of a single dataset file. + :param shuffle: Specify if the Dataset should be shuffled when a batch is taken. + :param normalize: If True, the data will be normalized using standard score. + :param recompute_normalization: If True, compute the normalization coefficients. + """ + + self.name: str = self.__class__.__name__ + + # Check directory variable + if existing_dir is not None: + if type(existing_dir) != str: + raise TypeError(f"[{self.name}] The given 'existing_dir'={existing_dir} must be a str.") + if not isdir(existing_dir): + raise ValueError(f"[{self.name}] The given 'existing_dir'={existing_dir} does not exist.") + if len(existing_dir.split(sep)) > 1 and existing_dir.split(sep)[-1] == 'dataset': + existing_dir = join(*existing_dir.split(sep)[:-1]) + + # Check storage variables + if mode is not None: + if type(mode) != str: + raise TypeError(f"[{self.name}] The given 'mode'={mode} must be a str.") + if mode.lower() not in (available_modes := ['training', 'validation', 'running']): + raise ValueError(f"[{self.name}] The given 'mode'={mode} must be in {available_modes}.") + if max_file_size is not None: + if type(max_file_size) not in [int, float]: + raise TypeError(f"[{self.name}] The given 'max_file_size'={max_file_size} must be a float.") + max_file_size = int(max_file_size * 1e9) if max_file_size > 0 else None + if type(shuffle) != bool: + raise TypeError(f"[{self.name}] The given 'shuffle'={shuffle} must be a bool.") + if type(normalize) != bool: + raise TypeError(f"[{self.name}] The given 'normalize'={normalize} must be a bool.") + if type(recompute_normalization) != bool: + raise TypeError(f"[{self.name}] The given 'recompute_normalization'={recompute_normalization} must be a " + f"bool.") + + # BaseDataset parameterization + self.dataset_class: Type[BaseDataset] = dataset_class + self.dataset_config: namedtuple = make_config(configuration_object=self, + configuration_name='dataset_config', + max_file_size=max_file_size) + + # DatasetManager parameterization + self.existing_dir: Optional[str] = existing_dir + self.mode: Optional[str] = mode + self.max_file_size: int = max_file_size + self.shuffle: bool = shuffle + self.normalize: bool = normalize + self.recompute_normalization: bool = recompute_normalization + + def create_dataset(self) -> BaseDataset: + """ + Create an instance of BaseDataset with given parameters. + + :return: BaseDataset instance. + """ + + dataset = self.dataset_class(config=self.dataset_config) + if not isinstance(dataset, BaseDataset): + raise TypeError(f"[{self.name}] The given 'dataset_class'={self.dataset_class} must be a BaseDataset.") + return dataset + + def __str__(self) -> str: + + description = "\n" + description += f"{self.name}\n" + description += f" Dataset Class: {self.dataset_class.__name__}\n" + description += f" Existing directory: {False if self.existing_dir is None else self.existing_dir}\n" + description += f" Mode: {self.mode}\n" + description += f" Max size: {self.dataset_config.max_size}\n" + description += f" Shuffle: {self.shuffle}\n" + description += f" Normalize: {self.normalize}\n" + description += f" Recompute normalization: {self.recompute_normalization}\n" + return description diff --git a/src/Database/__init__.py b/src/Database/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index ff3bf23f..43cacc69 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Dict, Union, Tuple +from typing import Any, Optional, Dict, Union, Tuple, List from numpy import array, ndarray from SSD.Core.Storage.Database import Database @@ -16,6 +16,7 @@ def __init__(self, number_of_instances: int = 1, as_tcp_ip_client: bool = True, environment_manager: Optional[Any] = None, + data_db: Optional[Union[Database, Tuple[str, str]]] = None, visu_db: Optional[Union[Database, Tuple[str, str]]] = None): """ BaseEnvironment is an environment class to compute simulated data for the network and its optimization process. @@ -37,8 +38,10 @@ def __init__(self, port=port) # Input and output to give to the network - self.input: ndarray = array([]) - self.output: ndarray = array([]) + self.__training_data: Dict[str, ndarray] = {} + self.__additional_data: Dict[str, ndarray] = {} + self.__first_add: List[bool] = [True, True] + # Variables to store samples from Dataset self.sample_in: Optional[ndarray] = None self.sample_out: Optional[ndarray] = None @@ -47,6 +50,17 @@ def __init__(self, # Manager if the Environment is not a TcpIpClient self.environment_manager: Any = environment_manager + # Connect the Environment to the data Database + self.database: Optional[Database] = None + if data_db is not None: + if type(data_db) == list: + pass + else: + self.database = data_db + self.database.create_fields(table_name='Training', + fields=('env_id', int)) + + # Connect the Factory to the visualization Database self.factory: Optional[VedoFactory] = None if visu_db is not None: if type(visu_db) == list: @@ -100,9 +114,17 @@ def send_parameters(self) -> Dict[Any, Any]: return {} + def init_database(self) -> None: + """ + Define the fields of the training dataset. + Required. + """ + + raise NotImplementedError + def init_visualization(self) -> None: """ - Define the visualization objects to send to he Visualizer. + Define the visualization objects to send to the Visualizer. Not mandatory. :return: Dictionary of visualization data @@ -160,19 +182,39 @@ def close(self) -> None: ########################################################################################## def set_training_data(self, - input_array: ndarray, - output_array: ndarray) -> None: + **kwargs) -> None: """ Set the training data to send to the TcpIpServer or the EnvironmentManager. - - :param input_array: Network input - :param output_array: Network expected output """ + # Check kwargs + if self.__first_add[0]: + self.__first_add[0] = False + required_fields = list(set(self.database.get_fields(table_name='Training')) - {'id', 'env_id'}) + for field in kwargs.keys(): + if field not in required_fields: + raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." + f"Required fields are {required_fields}.") + for field in required_fields: + if field not in kwargs.keys(): + raise ValueError(f"[{self.name}] The field '{field}' was not defined in training data." + f"Required fields are {required_fields}.") + # Training data is set if the Environment can compute data - if self.compute_essential_data: - self.input = input_array - self.output = output_array + if self.compute_training_data: + self.__training_data = kwargs + self.__training_data['env_id'] = self.instance_id + + def _reset_training_data(self) -> None: + self.__training_data = {} + + def _send_data(self) -> None: + line_id = self.database.add_data(table_name='Training', + data=self.__training_data) + self.database.add_data(table_name='Sync', + data={'env': line_id}) + self.database.add_data(table_name='Additional', + data=self.__additional_data) def set_loss_data(self, loss_data: Any) -> None: diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index c9da9ea5..b7595f4a 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -5,6 +5,8 @@ from subprocess import run as subprocessRun from sys import modules, executable +from SSD.Core.Storage.Database import Database + from DeepPhysX.Core.AsyncSocket.TcpIpServer import TcpIpServer from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer @@ -172,6 +174,7 @@ def start_client(self, def create_environment(self, environment_manager: Any, + data_db: Optional[Database] = None, visu_db: Optional[Any] = None) -> BaseEnvironment: """ Create an Environment that will not be a TcpIpObject. @@ -184,11 +187,13 @@ def create_environment(self, # Create instance environment = self.environment_class(environment_manager=environment_manager, as_tcp_ip_client=False, + data_db=data_db, visu_db=visu_db) # Create & Init Environment environment.recv_parameters(self.param_dict) environment.create() environment.init() + environment.init_database() environment.init_visualization() return environment diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 2e2708a2..cb69d6e1 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -3,107 +3,96 @@ from numpy import ndarray from json import load as json_load -from DeepPhysX.Core.Manager.DatasetManager import DatasetManager +from DeepPhysX.Core.Manager.DatabaseManager import DatasetManager from DeepPhysX.Core.Manager.EnvironmentManager import EnvironmentManager from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig class DataManager: def __init__(self, - dataset_config: BaseDatasetConfig, - environment_config: BaseEnvironmentConfig, - session: str, + dataset_config: Optional[BaseDatasetConfig] = None, + environment_config: Optional[BaseEnvironmentConfig] = None, manager: Optional[Any] = None, + session: str = 'sessions/default', new_session: bool = True, - training: bool = True, - offline: bool = False, - store_data: bool = True, + is_training: bool = True, + produce_data: bool = True, batch_size: int = 1): """ - DataManager deals with the generation of input / output tensors. His job is to call get_data on either the - DatasetManager or the EnvironmentManager according to the context. + DataManager deals with the generation, storage and loading of training data. + A batch is given with a call to 'get_data' on either the DatasetManager or the EnvironmentManager according to + the context. - :param dataset_config: Specialisation containing the parameters of the dataset manager - :param environment_config: Specialisation containing the parameters of the environment manager + :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param environment_config: Specialisation containing the parameters of the environment manager. + :param manager: Manager that handle the DataManager :param session: Path to the session directory. - :param manager: Manager that handle The DataManager - :param new_session: Define the creation of new directories to store data - :param training: True if this session is a network training - :param offline: True if the session is done offline - :param store_data: Format {\'in\': bool, \'out\': bool} save the tensor when bool is True - :param batch_size: Number of samples in a batch + :param bool new_session: Flag that indicates whether if the session is new + :param is_training: Flag that indicates whether if this session is training a Network. + :param produce_data: Flag that indicates whether if this session is producing data. + :param int batch_size: Number of samples in a batch """ self.name: str = self.__class__.__name__ - # Manager references + # Managers variables self.manager: Optional[Any] = manager self.dataset_manager: Optional[DatasetManager] = None self.environment_manager: Optional[EnvironmentManager] = None - # DataManager parameters - self.is_training: bool = training - self.allow_dataset_fetch: bool = True - self.data: Optional[Dict[str, ndarray]] = None - # Data normalization coefficients (default: mean = 0, standard deviation = 1) self.normalization: Dict[str, List[float]] = {'input': [0., 1.], 'output': [0., 1.]} - # If normalization flag is set to True, try to load existing coefficients - if dataset_config is not None and dataset_config.normalize: - json_file_path = None - # Existing Dataset in the current session - if os.path.exists(os.path.join(session, 'dataset')): - json_file_path = os.path.join(session, 'dataset', 'dataset.json') - # Dataset provided by config - elif dataset_config.dataset_dir is not None: - dataset_dir = dataset_config.dataset_dir - if dataset_dir[-1] != "/": - dataset_dir += "/" - if dataset_dir[-8:] != "dataset/": - dataset_dir += "dataset/" - if os.path.exists(dataset_dir): - json_file_path = os.path.join(dataset_dir, 'dataset.json') - # If Dataset exists then a json file is associated - if json_file_path is not None: - with open(json_file_path) as json_file: - json_dict = json_load(json_file) - # Get the normalization coefficients - for field in self.normalization.keys(): - if field in json_dict['normalization']: - self.normalization[field] = json_dict['normalization'][field] - - # Training - if self.is_training: - # Always create a dataset_manager for training - create_dataset = True - # Create an environment if prediction must be applied else ask DatasetManager - create_environment = False - if environment_config is not None: - create_environment = None if not environment_config.use_dataset_in_environment else True - # Prediction - else: - # Create an environment for prediction if an environment config is provided - create_environment = environment_config is not None - # Create a dataset if data will be stored from environment during prediction - create_dataset = store_data - # Create a dataset also if data should be loaded from any partition - create_dataset = create_dataset or (dataset_config is not None and dataset_config.dataset_dir is not None) - - # Create dataset if required + # # If normalization flag is set to True, try to load existing coefficients + # if dataset_config is not None and dataset_config.normalize: + # json_file_path = None + # # Existing Dataset in the current session + # if os.path.exists(os.path.join(session, 'dataset')): + # json_file_path = os.path.join(session, 'dataset', 'dataset.json') + # # Dataset provided by config + # elif dataset_config.existing_dir is not None: + # dataset_dir = dataset_config.existing_dir + # if dataset_dir[-1] != "/": + # dataset_dir += "/" + # if dataset_dir[-8:] != "dataset/": + # dataset_dir += "dataset/" + # if os.path.exists(dataset_dir): + # json_file_path = os.path.join(dataset_dir, 'dataset.json') + # # If Dataset exists then a json file is associated + # if json_file_path is not None: + # with open(json_file_path) as json_file: + # json_dict = json_load(json_file) + # # Get the normalization coefficients + # for field in self.normalization.keys(): + # if field in json_dict['normalization']: + # self.normalization[field] = json_dict['normalization'][field] + + # Create a DatasetManager if required + create_dataset = is_training or produce_data + database = None if create_dataset: - self.dataset_manager = DatasetManager(data_manager=self, dataset_config=dataset_config, - session=session, new_session=new_session, training=self.is_training, - offline=offline, store_data=store_data) - # Create environment if required - if create_environment is None: # If None then the dataset_manager exists - create_environment = self.dataset_manager.new_dataset() + self.dataset_manager = DatasetManager(dataset_config=dataset_config, + session=session, + data_manager=self, + new_session=new_session, + is_training=is_training, + produce_data=produce_data) + database = self.dataset_manager.database + + # Create an EnvironmentManager if required + create_environment = produce_data or environment_config is not None if create_environment: - self.environment_manager = EnvironmentManager(data_manager=self, environment_config=environment_config, - session=session, batch_size=batch_size, - training=self.is_training) + self.environment_manager = EnvironmentManager(environment_config=environment_config, + data_manager=self, + session=session, + data_db=database, + batch_size=batch_size) + + # DataManager variables + self.produce_data = produce_data + self.data: Optional[Dict[str, ndarray]] = None def get_manager(self) -> Any: """ @@ -117,7 +106,7 @@ def get_manager(self) -> Any: def get_data(self, epoch: int = 0, batch_size: int = 1, - animate: bool = True) -> Dict[str, ndarray]: + animate: bool = True) -> None: """ Fetch data from the EnvironmentManager or the DatasetManager according to the context. @@ -128,14 +117,11 @@ def get_data(self, """ # Training - if self.is_training: - data = None + if self.produce_data: # Get data from environment if used and if the data should be created at this epoch - if data is None and self.environment_manager is not None and \ - (epoch == 0 or self.environment_manager.always_create_data) and self.dataset_manager.new_dataset(): - self.allow_dataset_fetch = False - data = self.environment_manager.get_data(animate=animate, get_inputs=True, get_outputs=True) - self.dataset_manager.add_data(data) + if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data): + self.environment_manager.get_data(animate=animate) + # self.dataset_manager.add_data(data) # Force data from the dataset else: data = self.dataset_manager.get_data(batch_size=batch_size, get_inputs=True, get_outputs=True) @@ -174,9 +160,6 @@ def get_data(self, if self.dataset_manager is not None: self.dataset_manager.add_data(data) - self.data = data - return data - def get_prediction(self, network_input: ndarray) -> ndarray: """ diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py new file mode 100644 index 00000000..5719a398 --- /dev/null +++ b/src/Manager/DatabaseManager.py @@ -0,0 +1,164 @@ +from typing import Any, Dict, Tuple, List, Optional, Union +from os.path import isfile, isdir, abspath, join +from os import listdir, symlink, sep +from json import dump as json_dump +from json import load as json_load +from numpy import load, squeeze, ndarray, concatenate, float64 + +from SSD.Core.Storage.Database import Database + +from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDataset import BaseDataset +from DeepPhysX.Core.Utils.path import create_dir, copy_dir +from DeepPhysX.Core.Utils.jsonUtils import CustomJSONEncoder + + +class DatasetManager: + + def __init__(self, + dataset_config: Optional[BaseDatasetConfig] = None, + session: str = 'sessions/default', + data_manager: Optional[Any] = None, + new_session: bool = True, + is_training: bool = True, + produce_data: bool = True): + """ + DatasetManager handle all operations with input / output files. Allows saving and read tensors from files. + + :param dataset_config: Specialisation containing the parameters of the dataset manager + :param data_manager: DataManager that handles the DatasetManager + :param new_session: Define the creation of new directories to store data + :param is_training: True if the session is done offline + :param produce_data: True if this session is a network training + """ + + self.name: str = self.__class__.__name__ + + # Manager variables + dataset_config = BaseDatasetConfig() if dataset_config is None else dataset_config + self.data_manager: Optional[Any] = data_manager + self.dataset_dir: str = join(session, 'dataset') + self.database: Optional[Database] = None + + # Dataset parameters + self.max_file_size: int = dataset_config.max_file_size + self.shuffle: bool = dataset_config.shuffle + self.produce_data = produce_data + self.normalize: bool = dataset_config.normalize + self.recompute_normalization: bool = dataset_config.recompute_normalization + + # Dataset modes + self.modes: List[str] = ['training', 'validation', 'running'] + self.mode: str = 'training' if produce_data else 'running' + self.mode = self.mode if dataset_config.mode is None else dataset_config.mode + + # Dataset partitions + session_name = session.split(sep)[-1] + self.partition_template: Dict[str, str] = {mode: f'{session_name}_{mode}_' + '{}' for mode in self.modes} + self.partitions: Dict[str, List[str]] = {mode: [] for mode in self.modes} + self.partition_index: Dict[str, int] = {mode: 0 for mode in self.modes} + + # Dataset json file + self.json_default: Dict[str, Dict[str, Any]] = {'data_shape': {}, + 'nb_samples': {mode: [] for mode in self.modes}, + 'partitions': {mode: [] for mode in self.modes}, + 'normalization': {}} + self.json_content: Dict[str, Dict[str, Any]] = self.json_default.copy() + + # Produce training data + if produce_data: + # Produce training data in a new session + if new_session: + # Produce training data in a new session from scratch + # --> Create a new '/dataset' directory + if dataset_config.existing_dir is None: + create_dir(session_dir=session, session_name='dataset') + self.init_directory() + # Produce training data in a new session from an existing Dataset + # --> Copy the 'existing_dir/dataset' directory then load the 'session/dataset' directory + else: + copy_dir(src_dir=dataset_config.existing_dir, dest_dir=session, sub_folders='dataset') + self.load_directory() + # Produce training data in an existing session + # --> Load the 'session/dataset' directory + else: + self.load_directory() + # Load training data + else: + # Load training data in a new session + # --> Link to the 'existing_dir/dataset' directory the load the 'session/dataset' directory + if new_session: + symlink(src=join(dataset_config.existing_dir, 'dataset'), + dst=join(session, 'dataset')) + self.load_directory() + # Load training data in an existing session + # --> Load the 'session/dataset' directory + else: + self.load_directory() + + def init_directory(self): + """ + + """ + + partition_path = self.partition_template[self.mode].format(self.partition_index[self.mode]) + self.database = Database(database_dir=self.dataset_dir, + database_name=partition_path).new() + self.database.create_table(table_name='Sync', + storing_table=False, + fields=[('env', int), ('net', int)]) + self.database.create_table(table_name='Training') + self.database.create_table(table_name='Additional') + self.partition_index[self.mode] += 1 + + def load_directory(self): + + # 1. Check the directory existence to prevent bugs + if not isdir(self.dataset_dir): + raise Warning(f"[{self.name}] Impossible to load Dataset from {self.dataset_dir}.") + + # 2. Get the .json description file + json_found = False + if isfile(join(self.dataset_dir, 'dataset.json')): + json_found = True + with open(join(self.dataset_dir, 'dataset.json')) as json_file: + self.json_content = json_load(json_file) + + # 3. Load partitions for each mode + self.partitions = self.json_content['partitions'] if json_found else self.search_partitions() + + # 4. Update json file if not found + if not json_found or self.json_content == self.json_default: + self.search_partitions_info() + self.update_json(update_partitions_lists=True) + if self.recompute_normalization or ( + self.normalize and self.json_content['normalization'] == self.json_default['normalization']): + self.update_json(update_normalization=True) + + # 5. Load data from partitions + self.load_partitions() + + def search_partitions(self): + """ + + """ + + raw_partitions = {mode: [f for f in listdir(self.dataset_dir) if isfile(join(self.dataset_dir, f)) + and f.endswith('.db') and f.__contains__(mode)] for mode in self.modes} + return {mode: sorted(raw_partitions[mode]) for mode in self.modes} + + def search_partitions_info(self): + pass + + def load_partitions(self): + pass + + def update_json(self, update_shapes: bool = False, update_nb_samples: bool = False, + update_partitions_lists: bool = False, update_normalization: bool = False) -> None: + pass + + def close(self): + + if self.normalize and self.produce_data: + self.update_json(update_normalization=True) + self.database.close() diff --git a/src/Manager/DatasetManager.py b/src/Manager/DatasetManager.py index 048512a9..6860de5f 100644 --- a/src/Manager/DatasetManager.py +++ b/src/Manager/DatasetManager.py @@ -7,7 +7,7 @@ from numpy import load, squeeze, ndarray, concatenate, float64 from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig -from DeepPhysX.Core.Utils.pathUtils import create_dir +from DeepPhysX.Core.Utils.path import create_dir from DeepPhysX.Core.Utils.jsonUtils import CustomJSONEncoder diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index f8c93c5e..af07b971 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -4,6 +4,8 @@ from copy import copy from os.path import join +from SSD.Core.Storage.Database import Database + from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig, TcpIpServer, BaseEnvironment from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer @@ -11,11 +13,11 @@ class EnvironmentManager: def __init__(self, - environment_config: BaseEnvironmentConfig, - session: str, + environment_config: Optional[BaseEnvironmentConfig] = None, data_manager: Any = None, - batch_size: int = 1, - training: bool = True): + session: str = 'sessions/default', + data_db: Optional[Database] = None, + batch_size: int = 1): """ Deals with the online generation of data for both training and running of the neural networks. @@ -23,7 +25,6 @@ def __init__(self, :param session: Path to the session directory. :param data_manager: DataManager that handles the EnvironmentManager. :param batch_size: Number of samples in a batch of data. - :param training: True if this session is a network training. """ self.name: str = self.__class__.__name__ @@ -37,7 +38,6 @@ def __init__(self, self.use_dataset_in_environment: bool = environment_config.use_dataset_in_environment self.simulations_per_step: int = environment_config.simulations_per_step self.max_wrong_samples_per_step: int = environment_config.max_wrong_samples_per_step - self.train: bool = training self.dataset_batch: Optional[Dict[str, Dict[int, Any]]] = None # Create the Visualizer @@ -59,6 +59,7 @@ def __init__(self, visu_db=visu_db.get_path()) else: self.environment = environment_config.create_environment(environment_manager=self, + data_db=data_db, visu_db=visu_db) # Define get_data and dispatch methods @@ -110,87 +111,45 @@ def get_data_from_server(self, return training_data def get_data_from_environment(self, - get_inputs: bool = True, - get_outputs: bool = True, - animate: bool = True) -> Dict[str, Union[ndarray, dict]]: + animate: bool = True) -> None: """ Compute a batch of data directly from Environment. - :param get_inputs: If True, compute and return input. - :param get_outputs: If True, compute and return output. :param animate: If True, triggers an environment step. - :return: Dictionary containing all labeled data sent by the clients in their own dictionary + in and out key - corresponding to the batch. """ - # Init training data container, define production conditions - input_condition = lambda x: len(x) < self.batch_size if get_inputs else lambda _: False - output_condition = lambda x: len(x) < self.batch_size if get_outputs else lambda _: False - training_data = {'input': [], 'output': []} - # 1. Produce batch while batch size is not complete - while input_condition(training_data['input']) and output_condition(training_data['output']): - - # 1.1 Send a sample if a batch from dataset is given - if self.dataset_batch is not None: - # Extract a sample from dataset batch: input - self.environment.sample_in = self.dataset_batch['input'][0] - self.dataset_batch['input'] = self.dataset_batch['input'][1:] - # Extract a sample from dataset batch: output - self.environment.sample_out = self.dataset_batch['output'][0] - self.dataset_batch['output'] = self.dataset_batch['output'][1:] - # Extract a sample from dataset batch: additional fields - additional_fields = {} - if 'additional_fields' in self.dataset_batch: - for field in self.dataset_batch['additional_fields']: - additional_fields[field] = self.dataset_batch['additional_fields'][field][0] - self.dataset_batch['additional_fields'][field] = self.dataset_batch['additional_fields'][field][1:] - self.environment.additional_fields = additional_fields + nb_sample = 0 + while nb_sample < self.batch_size: + + # # 1.1 Send a sample if a batch from dataset is given + # if self.dataset_batch is not None: + # # Extract a sample from dataset batch: input + # self.environment.sample_in = self.dataset_batch['input'][0] + # self.dataset_batch['input'] = self.dataset_batch['input'][1:] + # # Extract a sample from dataset batch: output + # self.environment.sample_out = self.dataset_batch['output'][0] + # self.dataset_batch['output'] = self.dataset_batch['output'][1:] + # # Extract a sample from dataset batch: additional fields + # additional_fields = {} + # if 'additional_fields' in self.dataset_batch: + # for field in self.dataset_batch['additional_fields']: + # additional_fields[field] = self.dataset_batch['additional_fields'][field][0] + # self.dataset_batch['additional_fields'][field] = self.dataset_batch['additional_fields'][field][1:] + # self.environment.additional_fields = additional_fields # 1.2 Run the defined number of step if animate: for current_step in range(self.simulations_per_step): # Sub-steps do not produce data - self.environment.compute_essential_data = current_step == self.simulations_per_step - 1 + self.environment.compute_training_data = current_step == self.simulations_per_step - 1 async_run(self.environment.step()) # 1.3 Add the produced sample to the batch if the sample is validated if self.environment.check_sample(): - # Network's input - if get_inputs: - training_data['input'].append(self.environment.input) - self.environment.input = array([]) - # Network's output - if get_outputs: - training_data['output'].append(self.environment.output) - self.environment.output = array([]) - # Check if there is loss data - if self.environment.loss_data: - if 'loss' not in training_data: - training_data['loss'] = [] - training_data['loss'].append(self.environment.loss_data) - self.environment.loss_data = None - # Check if there is additional dataset fields - if self.environment.additional_fields != {}: - if 'additional_fields' not in training_data: - training_data['additional_fields'] = {} - for field in self.environment.additional_fields: - if field not in training_data['additional_fields']: - training_data['additional_fields'][field] = [] - training_data['additional_fields'][field].append(self.environment.additional_fields[field]) - self.environment.additional_fields = {} - - # 2. Convert data in ndarray - for key in training_data: - # If key does not contain a dict, convert value directly - if key != 'additional_fields': - training_data[key] = array(training_data[key]) - # If key contains a dict, convert item by item - else: - for field in training_data[key]: - training_data[key][field] = array(training_data[key][field]) - - return training_data + nb_sample += 1 + self.environment._send_data() + self.environment._reset_training_data() def dispatch_batch_to_server(self, batch: Dict[str, Union[ndarray, dict]], diff --git a/src/Manager/Manager.py b/src/Manager/Manager.py index fbbfb1a3..2a400e70 100644 --- a/src/Manager/Manager.py +++ b/src/Manager/Manager.py @@ -10,21 +10,21 @@ from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Utils.pathUtils import get_first_caller, create_dir +from DeepPhysX.Core.Utils.path import get_first_caller, create_dir class Manager: def __init__(self, - network_config: BaseNetworkConfig, - dataset_config: BaseDatasetConfig, - environment_config: BaseEnvironmentConfig, + network_config: Optional[BaseNetworkConfig] = None, + dataset_config: Optional[BaseDatasetConfig] = None, + environment_config: Optional[BaseEnvironmentConfig] = None, pipeline: Optional[Any] = None, session_dir: Optional[str] = None, session_name: str = 'DPX_default', new_session: bool = True, - training: bool = True, - store_data: bool = True, + is_training: bool = True, + produce_data: bool = True, batch_size: int = 1): """ Collection of all the specialized managers. Allows for some basic functions call. @@ -34,9 +34,11 @@ def __init__(self, :param dataset_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. :param session_name: Name of the newly created directory if session is not defined. - :param session_dir: Name of the directory in which to write all the necessary data - :param bool new_session: Define the creation of new directories to store data - :param int batch_size: Number of samples in a batch + :param session_dir: Name of the directory in which to write all the necessary data. + :param bool new_session: Flag that indicates whether if the session is new + :param is_training: Flag that indicates whether if this session is training a Network. + :param produce_data: Flag that indicates whether if this session is producing data. + :param int batch_size: Number of samples in a batch. """ self.pipeline: Optional[Any] = pipeline @@ -51,7 +53,7 @@ def __init__(self, self.session: str = osPathJoin(session_dir, session_name) # Trainer: must create a new session to avoid overwriting - if training: + if is_training: # Avoid unwanted overwritten data if new_session: self.session: str = create_dir(self.session, dir_name=session_name) diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index 15006278..52d22f82 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -5,7 +5,7 @@ from numpy import copy, array, ndarray from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Utils.pathUtils import copy_dir, create_dir +from DeepPhysX.Core.Utils.path import copy_dir, create_dir class NetworkManager: diff --git a/src/Manager/__init__.py b/src/Manager/__init__.py index e7481a52..d99a2553 100644 --- a/src/Manager/__init__.py +++ b/src/Manager/__init__.py @@ -5,6 +5,6 @@ exceptions = ['__init__.py', '__pycache__'] modules = [module for module in listdir(package) if module.endswith('.py') and module not in exceptions] __all__ = [] -for module in sorted(modules): - exec(f"from DeepPhysX.Core.Manager.{module[:-3]} import {module[:-3]}") - __all__.append(module[:-3]) +# for module in sorted(modules): +# exec(f"from DeepPhysX.Core.Manager.{module[:-3]} import {module[:-3]}") +# __all__.append(module[:-3]) diff --git a/src/Pipelines/BaseDataGenerator.py b/src/Pipelines/BaseDataGenerator.py index 949aeba4..5ff201e4 100644 --- a/src/Pipelines/BaseDataGenerator.py +++ b/src/Pipelines/BaseDataGenerator.py @@ -1,86 +1,84 @@ -import os.path -from os.path import join as osPathJoin -from os.path import basename +from os.path import join, sep, exists from sys import stdout from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline from DeepPhysX.Core.Manager.DataManager import DataManager -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Utils.progressbar import Progressbar -from DeepPhysX.Core.Utils.pathUtils import create_dir, get_first_caller +from DeepPhysX.Core.Utils.path import get_first_caller, create_dir class BaseDataGenerator(BasePipeline): - """ - | BaseDataGenerator implement a minimalist execute function that simply produce and store data without - training a neural network. - - :param BaseDatasetConfig dataset_config: Specialisation containing the parameters of the dataset manager - :param BaseEnvironmentConfig environment_config: Specialisation containing the parameters of the environment manager - :param str session_name: Name of the newly created directory if session is not defined - :param int nb_batches: Number of batches - :param int batch_size: Size of a batch - :param bool record_input: True if the input must be stored - :param bool record_output: True if the output must be stored - """ def __init__(self, dataset_config: BaseDatasetConfig, environment_config: BaseEnvironmentConfig, - session_name: str = 'default', - nb_batches: int = 0, - batch_size: int = 0, - record_input: bool = True, - record_output: bool = True): + session_dir: str = 'sessions', + session_name: str = 'data_generation', + batch_nb: int = 0, + batch_size: int = 0): + """ + BaseDataGenerator implement the main loop that only produces and stores data (no Network training). + + :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param environment_config: Specialisation containing the parameters of the environment manager. + :param session_dir: Relative path to the directory which contains sessions directories. + :param session_name: Name of the new the session directory. + :param batch_nb: Number of batches to produce. + :param batch_size: Number of samples in a single batch. + """ BasePipeline.__init__(self, dataset_config=dataset_config, environment_config=environment_config, + session_dir=session_dir, session_name=session_name, - pipeline='dataset') + pipeline='data_generation') + + # Define the session repository + root = get_first_caller() + session_dir = join(root, session_dir) - # Init session repository - dataset_dir = dataset_config.dataset_dir - if dataset_dir is not None: - if dataset_dir[-1] == "/": - dataset_dir = dataset_dir[:-1] - if dataset_dir[-8:] == "/dataset": - dataset_dir = dataset_dir[:-8] - if osPathJoin(get_first_caller(), session_name) != osPathJoin(get_first_caller(), dataset_dir): - dataset_dir = None - elif not os.path.exists(osPathJoin(get_first_caller(), dataset_dir)): - dataset_dir = None - if dataset_dir is None: - session_dir = create_dir(osPathJoin(get_first_caller(), session_name), dir_name=session_name) - session_name = (session_name if session_name is not None else basename(session_dir)).split("/")[-1] - else: - session_dir = osPathJoin(get_first_caller(), dataset_dir) - session_name = (session_name if session_name is not None else basename(session_dir)).split("/")[-1] + # Configure 'new_session' flags + # Option 1: existing_dir == None --> new_session = True + # Option 2: existing_dir == session_dir/session_name --> new_session = False + # Option 3: existing_dir != session_dir/session_name --> new_session = True + new_session = True + if dataset_config is not None and dataset_config.existing_dir is not None and \ + join(session_dir, session_name) == join(root, dataset_config.existing_dir): + new_session = False - # Create a DataManager directly - self.data_manager = DataManager(manager=self, - dataset_config=dataset_config, + # Create a new session if required + if new_session: + session_name = create_dir(session_dir=session_dir, + session_name=session_name).split(sep)[-1] + + # Create a DataManager + self.data_manager = DataManager(dataset_config=dataset_config, environment_config=environment_config, - session_name=session_name, - session_dir=session_dir, - new_session=True, - offline=True, - record_data={'input': record_input, 'output': record_output}, + session=join(session_dir, session_name), + new_session=new_session, + is_training=False, + produce_data=True, batch_size=batch_size) - self.nb_batch: int = nb_batches + self.nb_batch: int = batch_nb self.progress_bar = Progressbar(start=0, stop=self.nb_batch, c='orange', title="Data Generation") def execute(self) -> None: """ - | Run the data generation and recording process. + Launch the data generation Pipeline. """ + # Produce each batch of data for i in range(self.nb_batch): + # Produce a batch self.data_manager.get_data() + # Update progress bar stdout.write("\033[K") self.progress_bar.print(counts=i + 1) - # Close manager + + # Close DataManager self.data_manager.close() diff --git a/src/Pipelines/BasePipeline.py b/src/Pipelines/BasePipeline.py index a792bc3d..bf384b9b 100644 --- a/src/Pipelines/BasePipeline.py +++ b/src/Pipelines/BasePipeline.py @@ -1,7 +1,7 @@ -from typing import Dict, Optional, Any, List, Union +from typing import Optional, Any, List, Union from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Manager.Manager import Manager from DeepPhysX.Core.Manager.NetworkManager import NetworkManager @@ -12,60 +12,71 @@ class BasePipeline: - """ - | Base class defining Pipelines common variables. - - :param BaseNetworkConfig network_config: Specialisation containing the parameters of the network manager - :param BaseDatasetConfig dataset_config: Specialisation containing the parameters of the dataset manager - :param BaseEnvironmentConfig environment_config: Specialisation containing the parameters of the environment manager - :param str session_name: Name of the newly created directory if session is not defined - :param Optional[str] session_dir: Name of the directory in which to write all the necessary data - :param Optional[str] pipeline: Values at either 'training' or 'prediction' - """ def __init__(self, network_config: Optional[BaseNetworkConfig] = None, dataset_config: Optional[BaseDatasetConfig] = None, environment_config: Optional[BaseEnvironmentConfig] = None, + session_dir: str = 'sessions', session_name: str = 'default', - session_dir: Optional[str] = None, - pipeline: Optional[str] = None): + pipeline: str = ''): + """ + Pipelines implement the main loop that defines data flow through components (Environment, Dataset, Network...). + + :param network_config: Specialisation containing the parameters of the network manager. + :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param environment_config: Specialisation containing the parameters of the environment manager. + :param session_dir: Name of the directory in which to write all the necessary data. + :param session_name: Name of the newly created directory if session is not defined. + :param pipeline: Name of the Pipeline. + """ self.name: str = self.__class__.__name__ - # Check the arguments + # Check the configurations if network_config is not None and not isinstance(network_config, BaseNetworkConfig): - raise TypeError(f"[{self.name}] The network configuration must be a BaseNetworkConfig") - if environment_config is not None and not isinstance(environment_config, BaseEnvironmentConfig): - raise TypeError(f"[{self.name}] The environment configuration must be a BaseEnvironmentConfig") + raise TypeError(f"[{self.name}] The Network configuration must be a BaseNetworkConfig object.") if dataset_config is not None and not isinstance(dataset_config, BaseDatasetConfig): - raise TypeError(f"[{self.name}] The dataset configuration must be a BaseDatasetConfig") - if type(session_name) != str: - raise TypeError(f"[{self.name}] The network config must be a BaseNetworkConfig object.") - if session_dir is not None and type(session_dir) != str: - raise TypeError(f"[{self.name}] The session directory must be a str.") + raise TypeError(f"[{self.name}] The Dataset configuration must be a BaseDatasetConfig object.") + if environment_config is not None and not isinstance(environment_config, BaseEnvironmentConfig): + raise TypeError(f"[{self.name}] The Environment configuration must be a BaseEnvironmentConfig object.") - self.type: str = pipeline # Either training or prediction - self.debug: bool = False - self.new_session: bool = True - self.record_data: Optional[Dict[str, bool]] = None # Can be of type {'in': bool, 'out': bool} + # Check the session path variables + if type(session_dir) != str: + raise TypeError(f"[{self.name}] The given 'session_dir'={session_dir} must be a str.") + elif len(session_dir) == 0: + session_dir = 'sessions' + if type(session_name) != str: + raise TypeError(f"[{self.name}] The given 'session=name'={session_name} must be a str.") + elif len(session_name) == 0: + session_name = pipeline - # Dataset variables + # Configuration variables self.dataset_config: BaseDatasetConfig = dataset_config - # Network variables self.network_config: BaseNetworkConfig = network_config - # Simulation variables self.environment_config: BaseEnvironmentConfig = environment_config + + # Session variables + self.session_dir = session_dir + self.session_name = session_name + # Main manager self.manager: Optional[Manager] = None - def get_any_manager(self, manager_names: Union[str, List[str]]) -> Optional[Any]: + def execute(self): + """ + Launch the Pipeline. + """ + + raise NotImplemented + + def __get_any_manager(self, + manager_names: Union[str, List[str]]) -> Optional[Any]: """ - | Return the desired Manager associated with the pipeline if it exists. + Return the desired Manager associated with the pipeline if it exists. - :param Union[str, List[str]] manager_names: Name of the desired Manager or order of access to the desired - Manager - :return: Manager associated with the Pipeline + :param manager_names: Name of the desired Manager or order of access to this desired Manager. + :return: The desired Manager associated with the Pipeline. """ # If manager variable is not defined, cannot access other manager @@ -85,47 +96,47 @@ def get_any_manager(self, manager_names: Union[str, List[str]]) -> Optional[Any] return None return accessed_manager - def get_network_manager(self) -> NetworkManager: + def get_network_manager(self) -> Optional[NetworkManager]: """ - | Return the NetworkManager associated with the pipeline. + Return the NetworkManager associated with the pipeline if it exists. - :return: NetworkManager associated with the pipeline + :return: The NetworkManager associated with the pipeline. """ - return self.get_any_manager('network_manager') + return self.__get_any_manager(manager_names='network_manager') - def get_data_manager(self) -> DataManager: + def get_data_manager(self) -> Optional[DataManager]: """ - | Return the DataManager associated with the pipeline. + Return the DataManager associated with the pipeline if it exists. - :return: DataManager associated with the pipeline + :return: The DataManager associated with the pipeline. """ - return self.get_any_manager('data_manager') + return self.__get_any_manager(manager_names='data_manager') - def get_stats_manager(self) -> StatsManager: + def get_stats_manager(self) -> Optional[StatsManager]: """ - | Return the StatsManager associated with the pipeline. + Return the StatsManager associated with the pipeline if it exists. - :return: StatsManager associated with the pipeline + :return: The StatsManager associated with the pipeline. """ - return self.get_any_manager('stats_manager') + return self.__get_any_manager(manager_names='stats_manager') - def get_dataset_manager(self) -> DatasetManager: + def get_dataset_manager(self) -> Optional[DatasetManager]: """ - | Return the DatasetManager associated with the pipeline. + Return the DatasetManager associated with the pipeline if it exists. - :return: DatasetManager associated with the pipeline + :return: The DatasetManager associated with the pipeline. """ - return self.get_any_manager(['data_manager', 'dataset_manager']) + return self.__get_any_manager(manager_names=['data_manager', 'dataset_manager']) - def get_environment_manager(self) -> EnvironmentManager: + def get_environment_manager(self) -> Optional[EnvironmentManager]: """ - | Return the EnvironmentManager associated with the pipeline. + Return the EnvironmentManager associated with the pipeline if it exists. - :return: EnvironmentManager associated with the pipeline + :return: The EnvironmentManager associated with the pipeline. """ - return self.get_any_manager(['data_manager', 'environment_manager']) + return self.__get_any_manager(manager_names=['data_manager', 'environment_manager']) diff --git a/src/Utils/configs.py b/src/Utils/configs.py new file mode 100644 index 00000000..692290d7 --- /dev/null +++ b/src/Utils/configs.py @@ -0,0 +1,33 @@ +from typing import Any +from collections import namedtuple + + +def make_config(configuration_object: Any, + configuration_name: str, + **kwargs) -> namedtuple: + """ + Create a namedtuple which gathers all the parameters for any configuration object. + For a child config class, only new items are required since parent's items will be added by default. + + :param configuration_object: Instance of any Config class. + :param configuration_name: Name of the variable containing the namedtuple. + :param kwargs: Parameters to add to the namedtuple. + :return: Namedtuple which contains newly added parameters. + """ + + # Get items set as keyword arguments + fields = tuple(kwargs.keys()) + args = tuple(kwargs.values()) + + # Check if a dataset_config already exists (child class will have the parent's config by default) + if configuration_name in configuration_object.__dict__: + configuration: namedtuple = configuration_object.__getattr__(configuration_name) + # Get items set in the existing config + for key, value in configuration._asdict().items(): + # Only new items are required for children, check if the parent's items are set again anyway + if key not in fields: + fields += (key,) + args += (value,) + + # Create namedtuple with collected items + return namedtuple(configuration_name, fields)._make(args) diff --git a/src/Utils/path.py b/src/Utils/path.py new file mode 100644 index 00000000..14ede966 --- /dev/null +++ b/src/Utils/path.py @@ -0,0 +1,79 @@ +from typing import Optional +from os.path import join, isdir, abspath, normpath, dirname, basename +from os import listdir, pardir, makedirs +from inspect import getmodule, stack +from shutil import copytree + + +def get_first_caller() -> str: + """ + Return the repertory in which the main script is stored. + """ + + # Get the stack of calls + scripts_list = stack()[-1] + # Get the first one (the one launched by the user) + module = getmodule(scripts_list[0]) + # Return the path of this script + return dirname(abspath(module.__file__)) + + +def create_dir(session_dir: str, session_name: str) -> str: + """ + Create a new directory of the given name in the given directory. + If it already exists, add a unique identifier at the end of the directory name. + + :param session_dir: Path where to create the directory. + :param session_name: Name of the directory to create. + :return: Path to the newly created directory. + """ + + if isdir(join(session_dir, session_name)): + print(f"Directory conflict: you are going to overwrite {join(session_dir, session_name)}.") + # Find all the duplicated folders + session_name += '_' + copies = [folder for folder in listdir(session_dir) if isdir(join(session_dir, folder)) and + folder.find(session_name) == 0] + # Get the indices of copies + indices = [int(folder[len(session_name):]) for folder in copies] + # The new copy is the max index + 1 + max_idx = max(indices) if len(indices) > 0 else 0 + session_name += f'{max_idx + 1}' + + session = join(session_dir, session_name) + print(f"Create a new directory {session} for this session.") + makedirs(session) + return session + + +def copy_dir(src_dir: str, dest_dir: str, dest_name: Optional[str] = None, sub_folders: Optional[str] = None) -> str: + """ + Copy the source directory to the destination directory. + + :param src_dir: Source directory to copy. + :param dest_dir: Parent of the destination directory to copy. + :param dest_name: Destination directory to copy to. + :param sub_folders: If sub folders are specified, the latest is actually copied. + + :return: Path to the newly copied directory. + """ + + if dest_name is not None and isdir(join(dest_dir, dest_name)): + print(f"Directory conflict: you are going to overwrite {join(dest_dir, dest_name)}.") + # Find all the duplicated folders + dest_name += '_' + copies = [folder for folder in listdir(dest_dir) if isdir(join(dest_dir, folder)) and + folder.find(dest_name) == 0] + # Get the indices of the copies + indices = [int(folder[len(dest_name):]) for folder in copies] + # The new copy is the max index + 1 + max_id = max(indices) if len(indices) > 0 else 0 + dest_name += f'{max_id + 1}' + + dest = join(dest_dir, dest_name) if dest_name is not None else dest_dir + print(f"Copying the source directory {src_dir} to {dest} for this session.") + if sub_folders is None: + copytree(src_dir, dest) + else: + copytree(join(src_dir, sub_folders), join(dest, sub_folders)) + return dest diff --git a/src/Utils/pathUtils.py b/src/Utils/pathUtils.py deleted file mode 100644 index 4f25ceb3..00000000 --- a/src/Utils/pathUtils.py +++ /dev/null @@ -1,75 +0,0 @@ -from os.path import join as osPathJoin -from os.path import isdir, abspath, normpath, dirname, basename -from os import listdir, pardir, makedirs -from inspect import getmodule, stack -from shutil import copytree - - -def create_dir(dir_path: str, dir_name: str) -> str: - """ - Create a directory of the given name. If it already exist and specified, add a unique identifier at the end. - - :param str dir_path: Absolute directory to create - :param str dir_name: Name of the directory to check for existence of a similar directories - - :return: Name of the created directory as string - """ - if isdir(dir_path): - print(f"Directory conflict: you are going to overwrite {dir_path}.") - # Get the parent dir of training sessions - parent = abspath(osPathJoin(dir_path, pardir)) - # Find all the duplicated folder - deepest_repertory = dir_name.split('/')[-1] + '_' - copies_list = [folder for folder in listdir(parent) if - isdir(osPathJoin(parent, folder)) and - folder.__contains__(deepest_repertory) and - folder.find(deepest_repertory) == 0 and - len(folder) in [len(deepest_repertory) + i for i in range(1, 4)]] - # Get the indices of copies - indices = [int(folder[len(deepest_repertory):]) for folder in copies_list] - # The new copy is the max int + 1 - max_ind = max(indices) if len(indices) > 0 else 0 - new_name = basename(normpath(dir_path)) + f'_{max_ind + 1}/' - dir_path = osPathJoin(parent, new_name) - print(f"Create a new directory {dir_path} for this session.") - makedirs(dir_path) - return dir_path - - -def copy_dir(src_dir: str, dest_parent_dir: str, dest_dir: str) -> str: - """ - Copy source directory to destination directory at the end of destination parent directory - - :param str src_dir: Source directory to copy - :param str dest_parent_dir: Parent of the destination directory to copy - :param str dest_dir: Destination directory to copy to - - :return: destination directory that source has been copied to - """ - dest_dir = osPathJoin(dest_parent_dir, dest_dir) - if isdir(dest_dir): - print("Directory conflict: you are going to overwrite by copying in {}.".format(dest_dir)) - copies_list = [folder for folder in listdir(dest_parent_dir) if - isdir(osPathJoin(dest_parent_dir, folder)) and - folder.__contains__(dest_dir)] - new_name = dest_dir + '({})/'.format(len(copies_list)) - dest_dir = osPathJoin(dest_parent_dir, new_name) - print("Copying {} into the new directory {} for this session.".format(src_dir, dest_dir)) - else: - new_name = dest_dir + '/' - dest_dir = osPathJoin(dest_parent_dir, new_name) - copytree(src_dir, dest_dir) - return dest_dir - - -def get_first_caller() -> str: - """ - Return the repertory in which the main script is - """ - # Get the stack of called scripts - scripts_list = stack()[-1] - # Get the first one (the one launched by the user) - module = getmodule(scripts_list[0]) - # Return the path of this script - return dirname(abspath(module.__file__)) - From 5c547645ec23961a992adab03ea21f689457c705 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 14 Oct 2022 10:33:23 +0200 Subject: [PATCH 02/61] Use Database for DataGeneration in remote mode. --- src/AsyncSocket/AbstractEnvironment.py | 14 ++ src/AsyncSocket/TcpIpClient.py | 41 +----- src/AsyncSocket/TcpIpServer.py | 142 ++++++--------------- src/Environment/BaseEnvironment.py | 12 +- src/Environment/BaseEnvironmentConfig.py | 18 ++- src/Environment/launcherBaseEnvironment.py | 9 +- src/Manager/DataManager.py | 7 +- src/Manager/EnvironmentManager.py | 30 +---- src/Pipelines/BaseDataGenerator.py | 2 +- 9 files changed, 99 insertions(+), 176 deletions(-) diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 63fdba35..509ddeac 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -100,6 +100,14 @@ def init_visualization(self) -> None: pass + def init_database(self) -> None: + """ + Define the fields of the training dataset. + Required. + """ + + raise NotImplementedError + def send_parameters(self) -> dict: """ Create a dictionary of parameters to send to the manager. Automatically called after create and init. @@ -119,3 +127,9 @@ def apply_prediction(self, prediction: ndarray) -> None: """ pass + + def _send_training_data(self) -> None: + raise NotImplementedError + + def _reset_training_data(self) -> None: + raise NotImplementedError diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 6ac2e00c..35b395c8 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -76,6 +76,8 @@ async def __initialize(self) -> None: # Create the environment self.create() self.init() + if self.instance_id == 0: + self.init_database() self.init_visualization() # Send parameters @@ -147,37 +149,6 @@ async def __close(self) -> None: ########################################################################################## ########################################################################################## - async def __send_training_data(self, - loop: Optional[EventLoop] = None, - receiver: Optional[socket] = None) -> None: - """ - Send the training data to the TcpIpServer. - - :param loop: get_event_loop() return. - :param receiver: TcpIpObject receiver. - """ - - loop = get_event_loop() if loop is None else loop - receiver = self.sock if receiver is None else receiver - - # Send and reset network input - if self.input.tolist(): - await self.send_labeled_data(data_to_send=self.input, label="input", loop=loop, receiver=receiver) - self.input = array([]) - # Send network output - if self.output.tolist(): - await self.send_labeled_data(data_to_send=self.output, label="output", loop=loop, receiver=receiver) - self.output = array([]) - # Send loss data - if self.loss_data: - await self.send_labeled_data(data_to_send=self.loss_data, label='loss', loop=loop, receiver=receiver) - self.loss_data = None - # Send additional dataset fields - for key in self.additional_fields.keys(): - await self.send_labeled_data(data_to_send=self.additional_fields[key], label='dataset_' + key, - loop=loop, receiver=receiver) - self.additional_fields = {} - def send_prediction_data(self, network_input: ndarray, receiver: socket = None) -> ndarray: @@ -312,18 +283,18 @@ async def action_on_step(self, # Execute the required number of steps for step in range(self.simulations_per_step): # Compute data only on final step - self.compute_essential_data = step == self.simulations_per_step - 1 + self.compute_training_data = step == self.simulations_per_step - 1 await self.step() # If produced sample is not usable, run again - # Todo: add the max_rate here if self.sample_in is None and self.sample_out is None: while not self.check_sample(): for step in range(self.simulations_per_step): # Compute data only on final step - self.compute_essential_data = step == self.simulations_per_step - 1 + self.compute_training_data = step == self.simulations_per_step - 1 await self.step() # Sent training data to Server - await self.__send_training_data() + self._send_training_data() + self._reset_training_data() await self.send_command_done() diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index fa5a8761..e8130adb 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -136,74 +136,37 @@ async def __initialize(self, param_dict: Dict[Any, Any]) -> None: ########################################################################################## def get_batch(self, - get_inputs: bool = True, - get_outputs: bool = True, - animate: bool = True) -> Dict[str, Union[ndarray, dict]]: + animate: bool = True) -> None: """ Build a batch from clients samples. - :param get_inputs: If True, compute and return input. - :param get_outputs: If True, compute and return output. :param animate: If True, triggers an environment step. - :return: Batch (list of samples) & additional data in a dictionary. """ # Trigger communication protocol - async_run(self.__request_data_to_clients(get_inputs=get_inputs, get_outputs=get_outputs, animate=animate)) - - # Sort stored data between following fields - data_sorter = {'input': [], 'output': [], 'additional_fields': {}, 'loss': []} - list_fields = [key for key in data_sorter.keys() if type(data_sorter[key]) == list] - # Map produced samples with clients ID - self.sample_to_client_id = [] - - # Process while queue is empty or batch is full - while max([len(data_sorter[key]) for key in list_fields]) < self.batch_size and not self.data_fifo.empty(): - # Get data dict from queue - data = self.data_fifo.get() - # Network in / out / loss - for field in ['input', 'output', 'loss']: - if field in data: - data_sorter[field].append(data[field]) - # Additional fields - field = 'additional_fields' - if field in data: - for key in data[field]: - if key not in data_sorter[field].keys(): - data_sorter[field][key] = [] - data_sorter[field][key].append(data[field][key]) - # ID of client - if 'ID' in data: - self.sample_to_client_id.append(data['ID']) - return data_sorter + async_run(self.__request_data_to_clients(animate=animate)) async def __request_data_to_clients(self, - get_inputs: bool = True, - get_outputs: bool = True, animate: bool = True) -> None: """ Trigger a communication protocol for each client. Wait for all clients before to launch another communication protocol while the batch is not full. - :param get_inputs: If True, compute and return input - :param get_outputs: If True, compute and return output :param animate: If True, triggers an environment step """ - client_launched = 0 + nb_sample = 0 # Launch the communication protocol while the batch needs to be filled - while client_launched < self.batch_size: + while nb_sample < self.batch_size: # Run communicate protocol for each client and wait for the last one to finish - await gather(*[self.__communicate(client=client, client_id=client_id, get_inputs=get_inputs, - get_outputs=get_outputs, animate=animate) - for client_id, client in self.clients]) - client_launched += len(self.clients) + await gather(*[self.__communicate(client=client, + client_id=client_id, + animate=animate) for client_id, client in self.clients]) + nb_sample += len(self.clients) async def __communicate(self, client: Optional[socket] = None, client_id: Optional[int] = None, - get_inputs: bool = True, - get_outputs: bool = True, animate: bool = True) -> None: """ | Communication protocol with a client. It goes through different steps: @@ -213,47 +176,45 @@ async def __communicate(self, :param client: TcpIpObject client to communicate with. :param client_id: Index of the client. - :param get_inputs: If True, compute and return input. - :param get_outputs: If True, compute and return output. :param animate: If True, triggers an environment step. """ loop = get_event_loop() - # 1) If a sample from Dataset is given, sent it to the TcpIpClient - if self.batch_from_dataset is not None: - # Check if there is remaining samples, otherwise client is not used - if len(self.batch_from_dataset['input']) == 0 or len(self.batch_from_dataset['output']) == 0: - return - # Send the sample to the TcpIpClient - await self.send_command_sample(loop=loop, receiver=client) - # Pop the first sample of the numpy batch for network in / out - for field in ['input', 'output']: - # Tell if there is something to read - await self.send_data(data_to_send=field in self.batch_from_dataset, loop=loop, receiver=client) - if field in self.batch_from_dataset: - # Pop sample from array if there are some - sample = self.batch_from_dataset[field][0] - self.batch_from_dataset[field] = self.batch_from_dataset[field][1:] - # Keep the sample in memory - self.data_dict[client_id][field] = sample - # Send network in / out sample - await self.send_data(data_to_send=sample, loop=loop, receiver=client) - # Pop the first sample of the numpy batch for each additional dataset field - field = 'additional_fields' - # Tell TcpClient if there is additional data for this field - await self.send_data(data_to_send=field in self.batch_from_dataset, loop=loop, receiver=client) - if field in self.batch_from_dataset: - sample = {} - # Get each additional data field - for key in self.batch_from_dataset[field]: - # Pop sample from array - sample[key] = self.batch_from_dataset[field][key][0] - self.batch_from_dataset[field][key] = self.batch_from_dataset[field][key][1:] - # Keep the sample in memory - self.data_dict[client_id][field + '_' + key] = sample[key] - # Send additional in / out sample - await self.send_dict(name="additional_fields", dict_to_send=sample, loop=loop, receiver=client) + # # 1) If a sample from Dataset is given, sent it to the TcpIpClient + # if self.batch_from_dataset is not None: + # # Check if there is remaining samples, otherwise client is not used + # if len(self.batch_from_dataset['input']) == 0 or len(self.batch_from_dataset['output']) == 0: + # return + # # Send the sample to the TcpIpClient + # await self.send_command_sample(loop=loop, receiver=client) + # # Pop the first sample of the numpy batch for network in / out + # for field in ['input', 'output']: + # # Tell if there is something to read + # await self.send_data(data_to_send=field in self.batch_from_dataset, loop=loop, receiver=client) + # if field in self.batch_from_dataset: + # # Pop sample from array if there are some + # sample = self.batch_from_dataset[field][0] + # self.batch_from_dataset[field] = self.batch_from_dataset[field][1:] + # # Keep the sample in memory + # self.data_dict[client_id][field] = sample + # # Send network in / out sample + # await self.send_data(data_to_send=sample, loop=loop, receiver=client) + # # Pop the first sample of the numpy batch for each additional dataset field + # field = 'additional_fields' + # # Tell TcpClient if there is additional data for this field + # await self.send_data(data_to_send=field in self.batch_from_dataset, loop=loop, receiver=client) + # if field in self.batch_from_dataset: + # sample = {} + # # Get each additional data field + # for key in self.batch_from_dataset[field]: + # # Pop sample from array + # sample[key] = self.batch_from_dataset[field][key][0] + # self.batch_from_dataset[field][key] = self.batch_from_dataset[field][key][1:] + # # Keep the sample in memory + # self.data_dict[client_id][field + '_' + key] = sample[key] + # # Send additional in / out sample + # await self.send_dict(name="additional_fields", dict_to_send=sample, loop=loop, receiver=client) # 2) Execute n steps, the last one send data computation signal if animate: @@ -262,25 +223,6 @@ async def __communicate(self, await self.listen_while_not_done(loop=loop, sender=client, data_dict=self.data_dict, client_id=client_id) - # 3.1) Add all received in / out data to queue - data = {} - for get_data, net_field in zip([get_inputs, get_outputs], ['input', 'output']): - if get_data: - # Add network field - data[net_field] = self.data_dict[client_id][net_field] - # 3.2) Add loss data if provided - if 'loss' in self.data_dict[client_id]: - data['loss'] = self.data_dict[client_id]['loss'] - # 3.3) Add additional fields (transform key from 'dataset_{FIELD}' to '{FIELD}') - additional_fields = [key for key in self.data_dict[client_id].keys() if key.__contains__('dataset_')] - data['additional_fields'] = {} - for field in additional_fields: - data['additional_fields'][field[len('dataset_'):]] = self.data_dict[client_id][field] - # 3.4) Identify sample - data['ID'] = client_id - # 3.5) Add data to the Queue - self.data_fifo.put(data) - def set_dataset_batch(self, batch: Dict[str, Union[ndarray, Dict]]) -> None: """ diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 43cacc69..1714bbac 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -54,9 +54,11 @@ def __init__(self, self.database: Optional[Database] = None if data_db is not None: if type(data_db) == list: - pass + self.database = Database(database_dir=data_db[0], + database_name=data_db[1]).load() else: self.database = data_db + if self.instance_id == 0: self.database.create_fields(table_name='Training', fields=('env_id', int)) @@ -189,16 +191,18 @@ def set_training_data(self, # Check kwargs if self.__first_add[0]: + if self.instance_id != 0: + self.database.load() self.__first_add[0] = False required_fields = list(set(self.database.get_fields(table_name='Training')) - {'id', 'env_id'}) for field in kwargs.keys(): if field not in required_fields: raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." - f"Required fields are {required_fields}.") + f"Required fields are {required_fields}.{self.instance_id}") for field in required_fields: if field not in kwargs.keys(): raise ValueError(f"[{self.name}] The field '{field}' was not defined in training data." - f"Required fields are {required_fields}.") + f"Required fields are {required_fields}.{self.instance_id}") # Training data is set if the Environment can compute data if self.compute_training_data: @@ -208,7 +212,7 @@ def set_training_data(self, def _reset_training_data(self) -> None: self.__training_data = {} - def _send_data(self) -> None: + def _send_training_data(self) -> None: line_id = self.database.add_data(table_name='Training', data=self.__training_data) self.database.add_data(table_name='Sync', diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index b7595f4a..8b6170e5 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -105,7 +105,8 @@ def __init__(self, def create_server(self, environment_manager: Optional[Any] = None, batch_size: int = 1, - visu_db: Optional[int] = None) -> TcpIpServer: + data_db: Optional[str] = None, + visu_db: Optional[str] = None) -> TcpIpServer: """ Create a TcpIpServer and launch TcpIpClients in subprocesses. @@ -116,16 +117,19 @@ def create_server(self, """ # Create server - server = TcpIpServer(max_client_count=self.max_client_connections, batch_size=batch_size, - nb_client=self.number_of_thread, manager=environment_manager, - ip_address=self.ip_address, port=self.port) + server = TcpIpServer(ip_address=self.ip_address, + port=self.port, + nb_client=self.number_of_thread, + max_client_count=self.max_client_connections, + batch_size=batch_size, + manager=environment_manager) server_thread = Thread(target=self.start_server, args=(server,)) server_thread.start() # Create clients client_threads = [] for i in range(self.number_of_thread): - client_thread = Thread(target=self.start_client, args=(i, visu_db)) + client_thread = Thread(target=self.start_client, args=(i, data_db, visu_db)) client_threads.append(client_thread) for client in client_threads: client.start() @@ -152,7 +156,8 @@ def start_server(self, def start_client(self, idx: int = 1, - visu_db: Optional[int] = None) -> None: + data_db: Optional[str] = None, + visu_db: Optional[str] = None) -> None: """ Run a subprocess to start a TcpIpClient. @@ -170,6 +175,7 @@ def start_client(self, str(self.port), str(idx), str(self.number_of_thread), + str(data_db), str(visu_db)]) def create_environment(self, diff --git a/src/Environment/launcherBaseEnvironment.py b/src/Environment/launcherBaseEnvironment.py index 15c68b28..38fa8823 100644 --- a/src/Environment/launcherBaseEnvironment.py +++ b/src/Environment/launcherBaseEnvironment.py @@ -7,9 +7,9 @@ if __name__ == '__main__': # Check script call - if len(argv) != 8: + if len(argv) != 9: print(f"Usage: python3 {argv[0]} " - f" ") + f" ") exit(1) # Import environment_class @@ -18,9 +18,10 @@ exec(f"from {module_name} import {argv[2]} as Environment") # Create, init and run Tcp-Ip environment - visu_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] + data_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] + visu_db = None if argv[8] == 'None' else [s[1:-1] for s in argv[8][1:-1].split(', ')] client = Environment(ip_address=argv[3], port=int(argv[4]), instance_id=int(argv[5]), - number_of_instances=int(argv[6]), visu_db=visu_db) + number_of_instances=int(argv[6]), data_db=data_db, visu_db=visu_db) client.initialize() client.launch() diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index cb69d6e1..51062cf7 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -1,9 +1,7 @@ from typing import Any, Optional, Dict, List -import os.path from numpy import ndarray -from json import load as json_load -from DeepPhysX.Core.Manager.DatabaseManager import DatasetManager +from DeepPhysX.Core.Manager.DatabaseManager import DatasetManager, Database from DeepPhysX.Core.Manager.EnvironmentManager import EnvironmentManager from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig @@ -103,6 +101,9 @@ def get_manager(self) -> Any: return self.manager + def get_database(self) -> Database: + return self.dataset_manager.database + def get_data(self, epoch: int = 0, batch_size: int = 1, diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index af07b971..031c2177 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -1,5 +1,5 @@ from typing import Any, Dict, Optional, Union -from numpy import array, ndarray +from numpy import ndarray from asyncio import run as async_run from copy import copy from os.path import join @@ -56,7 +56,9 @@ def __init__(self, if environment_config.as_tcp_ip_client: self.server = environment_config.create_server(environment_manager=self, batch_size=batch_size, - visu_db=visu_db.get_path()) + data_db=data_db if data_db is None else data_db.get_path(), + visu_db=visu_db if visu_db is None else visu_db.get_path()) + self.data_manager.get_database().load() else: self.environment = environment_config.create_environment(environment_manager=self, data_db=data_db, @@ -82,33 +84,15 @@ def get_data_manager(self) -> Any: return self.data_manager def get_data_from_server(self, - get_inputs: bool = True, - get_outputs: bool = True, - animate: bool = True) -> Dict[str, Union[ndarray, dict]]: + animate: bool = True) -> None: """ Compute a batch of data from Environments requested through TcpIpServer. - :param get_inputs: If True, compute and return input. - :param get_outputs: If True, compute and return output. :param animate: If True, triggers an environment step. - :return: Dictionary containing all labeled data sent by the clients in their own dictionary + in and out key - corresponding to the batch. """ # Get data from server - batch = self.server.get_batch(get_inputs, get_outputs, animate) - # Filter input and output - training_data = {'input': array(batch['input']) if get_inputs else array([]), - 'output': array(batch['output']) if get_outputs else array([])} - # Convert each additional field - for field in batch['additional_fields']: - batch['additional_fields'][field] = array(batch['additional_fields'][field]) - training_data['additional_fields'] = batch['additional_fields'] - # Convert loss data - if 'loss' in batch and len(batch['loss']) != 0: - training_data['loss'] = array(batch['loss']) - # Return batch - return training_data + self.server.get_batch(animate) def get_data_from_environment(self, animate: bool = True) -> None: @@ -148,7 +132,7 @@ def get_data_from_environment(self, # 1.3 Add the produced sample to the batch if the sample is validated if self.environment.check_sample(): nb_sample += 1 - self.environment._send_data() + self.environment._send_training_data() self.environment._reset_training_data() def dispatch_batch_to_server(self, diff --git a/src/Pipelines/BaseDataGenerator.py b/src/Pipelines/BaseDataGenerator.py index 5ff201e4..3cc621ff 100644 --- a/src/Pipelines/BaseDataGenerator.py +++ b/src/Pipelines/BaseDataGenerator.py @@ -1,4 +1,4 @@ -from os.path import join, sep, exists +from os.path import join, sep from sys import stdout from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline From af577690d57ff448353bf53811c7bf236665af9d Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 14 Oct 2022 11:23:08 +0200 Subject: [PATCH 03/61] Make the DataGeneration mainloop modular. --- src/Pipelines/BaseDataGenerator.py | 75 +++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/src/Pipelines/BaseDataGenerator.py b/src/Pipelines/BaseDataGenerator.py index 3cc621ff..1e281f4b 100644 --- a/src/Pipelines/BaseDataGenerator.py +++ b/src/Pipelines/BaseDataGenerator.py @@ -1,3 +1,4 @@ +from typing import Optional from os.path import join, sep from sys import stdout @@ -12,14 +13,14 @@ class BaseDataGenerator(BasePipeline): def __init__(self, - dataset_config: BaseDatasetConfig, environment_config: BaseEnvironmentConfig, + dataset_config: Optional[BaseDatasetConfig] = None, session_dir: str = 'sessions', session_name: str = 'data_generation', batch_nb: int = 0, batch_size: int = 0): """ - BaseDataGenerator implement the main loop that only produces and stores data (no Network training). + BaseDataGenerator implements the main loop that only produces and stores data (no Network training). :param dataset_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. @@ -54,6 +55,11 @@ def __init__(self, session_name = create_dir(session_dir=session_dir, session_name=session_name).split(sep)[-1] + # Data generation variables + self.batch_nb: int = batch_nb + self.batch_id: int = 0 + self.progress_bar = Progressbar(start=0, stop=self.batch_id, c='orange', title="Data Generation") + # Create a DataManager self.data_manager = DataManager(dataset_config=dataset_config, environment_config=environment_config, @@ -62,23 +68,68 @@ def __init__(self, is_training=False, produce_data=True, batch_size=batch_size) - self.nb_batch: int = batch_nb - self.progress_bar = Progressbar(start=0, stop=self.nb_batch, c='orange', title="Data Generation") def execute(self) -> None: """ Launch the data generation Pipeline. + Each event is already implemented for a basic pipeline but can also be rewritten via inheritance to describe a + more complex pipeline. + """ + + self.data_generation_begin() + while self.batch_condition(): + self.batch_begin() + self.batch_produce() + self.batch_count() + self.batch_end() + self.data_generation_end() + + def data_generation_begin(self) -> None: + """ + Called once at the beginning of the data generation pipeline. + """ + + pass + + def batch_condition(self) -> bool: + """ + Check the batch number condition. + """ + + return self.batch_id < self.batch_nb + + def batch_begin(self) -> None: + """ + Called once at the beginning of a batch production. """ - # Produce each batch of data - for i in range(self.nb_batch): + pass - # Produce a batch - self.data_manager.get_data() + def batch_produce(self) -> None: + """ + Trigger the data production. + """ + + self.data_manager.get_data() + + def batch_count(self) -> None: + """ + Increment the batch counter. + """ + + self.batch_id += 1 - # Update progress bar - stdout.write("\033[K") - self.progress_bar.print(counts=i + 1) + def batch_end(self) -> None: + """ + Called once at the beginning of a batch production. + """ + + stdout.write("\033[K") + self.progress_bar.print(counts=self.batch_id + 1) + + def data_generation_end(self) -> None: + """ + Called once at the beginning of the data generation pipeline. + """ - # Close DataManager self.data_manager.close() From f216f33f3de26690a5ea9d725bb671615805b213 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 18 Oct 2022 12:09:21 +0200 Subject: [PATCH 04/61] Allow multiple partitions of SQL Database. --- src/AsyncSocket/AbstractEnvironment.py | 3 + src/AsyncSocket/TcpIpClient.py | 32 +- src/AsyncSocket/TcpIpObject.py | 616 ++++++++++++++--------- src/AsyncSocket/TcpIpServer.py | 24 + src/Database/BaseDataset.py | 252 ---------- src/Database/BaseDatasetConfig.py | 28 +- src/Environment/BaseEnvironmentConfig.py | 18 +- src/Manager/DataManager.py | 56 ++- src/Manager/DatabaseManager.py | 244 ++++++++- src/Manager/EnvironmentManager.py | 28 +- src/Manager/Manager.py | 115 ++--- src/Manager/StatsManager.py | 2 +- src/Pipelines/BaseDataGenerator.py | 4 +- src/Utils/jsonUtils.py | 2 +- 14 files changed, 760 insertions(+), 664 deletions(-) delete mode 100644 src/Database/BaseDataset.py diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 509ddeac..466c1784 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -1,6 +1,8 @@ from typing import Optional, Dict, Any from numpy import array, ndarray +from SSD.Core.Storage.Database import Database + class AbstractEnvironment: @@ -34,6 +36,7 @@ def __init__(self, self.compute_training_data: bool = True # Dataset data variables + self.database: Optional[Database] = None self.sample_in: Optional[ndarray] = None self.sample_out: Optional[ndarray] = None self.additional_fields: Dict[str, Any] = {} diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 35b395c8..3e9de830 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -1,12 +1,12 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict from socket import socket from asyncio import get_event_loop from asyncio import AbstractEventLoop as EventLoop from asyncio import run as async_run -from numpy import ndarray, array +from numpy import ndarray from DeepPhysX.Core.AsyncSocket.TcpIpObject import TcpIpObject -from DeepPhysX.Core.AsyncSocket.AbstractEnvironment import AbstractEnvironment +from DeepPhysX.Core.AsyncSocket.AbstractEnvironment import AbstractEnvironment, Database class TcpIpClient(TcpIpObject, AbstractEnvironment): @@ -211,7 +211,7 @@ async def action_on_exit(self, :param data: Dict storing data. :param client_id: ID of the TcpIpClient. - :param loop: asyncio.get_event_loop() return. + :param loop: Asyncio event loop. :param sender: TcpIpObject sender. """ @@ -228,7 +228,7 @@ async def action_on_prediction(self, :param data: Dict storing data. :param client_id: ID of the TcpIpClient. - :param loop: asyncio.get_event_loop() return. + :param loop: Asyncio event loop. :param sender: TcpIpObject sender. """ @@ -247,7 +247,7 @@ async def action_on_sample(self, :param data: Dict storing data. :param client_id: ID of the TcpIpClient. - :param loop: asyncio.get_event_loop() return. + :param loop: Asyncio event loop. :param sender: TcpIpObject sender. """ @@ -276,7 +276,7 @@ async def action_on_step(self, :param data: Dict storing data. :param client_id: ID of the TcpIpClient. - :param loop: asyncio.get_event_loop() return. + :param loop: Asyncio event loop. :param sender: TcpIpObject sender. """ @@ -298,3 +298,21 @@ async def action_on_step(self, self._send_training_data() self._reset_training_data() await self.send_command_done() + + async def action_on_change_db(self, + data: Dict[Any, Any], + client_id: int, sender: socket, + loop: EventLoop) -> None: + """ + Action to run when receiving the 'step' command. + + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. + """ + + new_database = (await self.receive_data(loop=loop, sender=sender), + await self.receive_data(loop=loop, sender=sender)) + self.database = Database(database_dir=new_database[0], + database_name=new_database[1]).load() diff --git a/src/AsyncSocket/TcpIpObject.py b/src/AsyncSocket/TcpIpObject.py index 7d2f247d..aa1994cd 100644 --- a/src/AsyncSocket/TcpIpObject.py +++ b/src/AsyncSocket/TcpIpObject.py @@ -15,16 +15,16 @@ class TcpIpObject: - """ - | TcpIpObject defines communication protocols to send and receive data and commands. - - :param str ip_address: IP address of the TcpIpObject - :param int port: Port number of the TcpIpObject - """ def __init__(self, ip_address: str = 'localhost', port: int = 10000): + """ + TcpIpObject defines communication protocols to send and receive data and commands. + + :param ip_address: IP address of the TcpIpObject. + :param port: Port number of the TcpIpObject. + """ self.name: str = self.__class__.__name__ @@ -39,7 +39,7 @@ def __init__(self, # Available commands self.command_dict: Dict[str, bytes] = {'exit': b'exit', 'step': b'step', 'done': b'done', 'finished': b'fini', 'prediction': b'pred', 'read': b'read', 'sample': b'samp', - 'visualisation': b'visu'} + 'visualisation': b'visu', 'db': b'chdb'} self.action_on_command: Dict[bytes, Any] = { self.command_dict["exit"]: self.action_on_exit, self.command_dict["step"]: self.action_on_step, @@ -49,6 +49,7 @@ def __init__(self, self.command_dict["read"]: self.action_on_read, self.command_dict["sample"]: self.action_on_sample, self.command_dict["visualisation"]: self.action_on_visualisation, + self.command_dict['db']: self.action_on_change_db } # Synchronous variables # self.send_lock = Lock() @@ -60,15 +61,16 @@ def __init__(self, ########################################################################################## ########################################################################################## - async def send_data(self, data_to_send: Convertible, loop: Optional[EventLoop] = None, + async def send_data(self, + data_to_send: Convertible, + loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: """ - | Send data through the given socket. + Send data through the given socket. - :param data_to_send: Data that will be sent on socket - :type data_to_send: Union[type(None), bytes, str, bool, int, float, List, ndarray] - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: socket receiver + :param data_to_send: Data that will be sent on socket. + :param loop: Asyncio event loop. + :param receiver: Socket receiver. """ loop = get_event_loop() if loop is None else loop @@ -79,14 +81,15 @@ async def send_data(self, data_to_send: Convertible, loop: Optional[EventLoop] = if await loop.sock_sendall(sock=receiver, data=data_as_bytes) is not None: ValueError(f"[{self.name}] Could not send all of the data for an unknown reason") - def sync_send_data(self, data_to_send: Convertible, receiver: Optional[socket] = None) -> None: + def sync_send_data(self, + data_to_send: Convertible, + receiver: Optional[socket] = None) -> None: """ - | Send data through the given socket. - | Synchronous version of 'TcpIpObject.send_data'. + Send data through the given socket. + Synchronous version of 'TcpIpObject.send_data'. - :param data_to_send: Data that will be sent on socket* - :type data_to_send: Union[type(None), bytes, str, bool, int, float, List, ndarray] - :param Optional[socket] receiver: socket receiver + :param data_to_send: Data that will be sent on socket. + :param receiver: Socket receiver. """ receiver = self.sock if receiver is None else receiver @@ -95,13 +98,15 @@ def sync_send_data(self, data_to_send: Convertible, receiver: Optional[socket] = # Send the whole message receiver.sendall(data_as_bytes) - async def receive_data(self, loop: EventLoop, sender: socket) -> Convertible: + async def receive_data(self, + loop: EventLoop, + sender: socket) -> Convertible: """ - | Receive data from a socket. + Receive data from a socket. - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: socket sender - :return: Converted data + :param loop: Asyncio event loop. + :param sender: Socket sender. + :return: Converted data. """ # Receive the number of fields to receive @@ -117,10 +122,10 @@ async def receive_data(self, loop: EventLoop, sender: socket) -> Convertible: def sync_receive_data(self) -> Convertible: """ - | Receive data from a socket. - | Synchronous version of 'TcpIpObject.receive_data'. + Receive data from a socket. + Synchronous version of 'TcpIpObject.receive_data'. - :return: Converted data + :return: Converted data. """ self.sock.setblocking(True) @@ -135,14 +140,17 @@ def sync_receive_data(self) -> Convertible: # Return the data in the expected format return self.data_converter.bytes_to_data(bytes_fields) - async def read_data(self, loop: EventLoop, sender: socket, read_size: int) -> bytes: + async def read_data(self, + loop: EventLoop, + sender: socket, + read_size: int) -> bytes: """ - | Read the data on the socket with value of buffer size as relatively small powers of 2. + Read the data on the socket with value of buffer size as relatively small powers of 2. - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: socket sender - :param int read_size: Amount of data to read on the socket - :return: Bytes field with 'read_size' length + :param loop: Asyncio event loop. + :param sender: Socket sender. + :param read_size: Amount of data to read on the socket. + :return: Bytes field with 'read_size' length. """ # Maximum read size @@ -165,13 +173,14 @@ async def read_data(self, loop: EventLoop, sender: socket, read_size: int) -> by return bytes_field - def sync_read_data(self, read_size: int) -> bytes: + def sync_read_data(self, + read_size: int) -> bytes: """ - | Read the data on the socket with value of buffer size as relatively small powers of 2. - | Synchronous version of 'TcpIpObject.read_data'. + Read the data on the socket with value of buffer size as relatively small powers of 2. + Synchronous version of 'TcpIpObject.read_data'. - :param int read_size: Amount of data to read on the socket - :return: Bytes field with 'read_size' length + :param read_size: Amount of data to read on the socket. + :return: Bytes field with 'read_size' length. """ # Maximum read sizes array @@ -199,17 +208,20 @@ def sync_read_data(self, read_size: int) -> bytes: ########################################################################################## ########################################################################################## - async def send_labeled_data(self, data_to_send: Convertible, label: str, loop: Optional[EventLoop] = None, - receiver: Optional[socket] = None, send_read_command: bool = True) -> None: + async def send_labeled_data(self, + data_to_send: Convertible, + label: str, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None, + send_read_command: bool = True) -> None: """ - | Send data with an associated label. + Send data with an associated label. - :param data_to_send: Data that will be sent on socket - :type data_to_send: Union[type(None), bytes, str, bool, int, float, List, ndarray] - :param str label: Associated label - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver - :param bool send_read_command: If True, the command 'read' is sent before sending data + :param data_to_send: Data that will be sent on socket. + :param label: Associated label. + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. + :param send_read_command: If True, the command 'read' is sent before sending data. """ loop = get_event_loop() if loop is None else loop @@ -222,18 +234,19 @@ async def send_labeled_data(self, data_to_send: Convertible, label: str, loop: O # Send data await self.send_data(data_to_send=data_to_send, loop=loop, receiver=receiver) - def sync_send_labeled_data(self, data_to_send: Convertible, label: str, receiver: Optional[socket] = None, + def sync_send_labeled_data(self, + data_to_send: Convertible, + label: str, + receiver: Optional[socket] = None, send_read_command: bool = True) -> None: """ - | Send data with an associated label. - | Synchronous version of 'TcpIpObject.send_labeled_data'. + Send data with an associated label. + Synchronous version of 'TcpIpObject.send_labeled_data'. - :param data_to_send: Data that will be sent on socket - :type data_to_send: Union[type(None), bytes, str, bool, int, float, List, ndarray] - :param str label: Associated label - :param Optional[socket] receiver: TcpIpObject receiver - :param bool send_read_command: If True, the command 'read' is sent before sending data - :return: + :param data_to_send: Data that will be sent on socket. + :param label: Associated label. + :param receiver: TcpIpObject receiver. + :param send_read_command: If True, the command 'read' is sent before sending data. """ receiver = self.sock if receiver is None else receiver @@ -245,13 +258,15 @@ def sync_send_labeled_data(self, data_to_send: Convertible, label: str, receiver # Send data self.sync_send_data(data_to_send=data_to_send, receiver=receiver) - async def receive_labeled_data(self, loop: EventLoop, sender: socket) -> Tuple[str, Convertible]: + async def receive_labeled_data(self, + loop: EventLoop, + sender: socket) -> Tuple[str, Convertible]: """ - | Receive data and an associated label. + Receive data and an associated label. - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender - :return: Label, Data + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. + :return: Label, Data. """ # Listen to sender @@ -267,10 +282,10 @@ async def receive_labeled_data(self, loop: EventLoop, sender: socket) -> Tuple[s def sync_receive_labeled_data(self) -> Tuple[str, Convertible]: """ - | Receive data and an associated label. - | Synchronous version of 'TcpIpObject.receive_labeled_data'. + Receive data and an associated label. + Synchronous version of 'TcpIpObject.receive_labeled_data'. - :return: Label, Data + :return: Label, Data. """ # Listen to sender @@ -284,15 +299,18 @@ def sync_receive_labeled_data(self) -> Tuple[str, Convertible]: data = self.sync_receive_data() return label, data - async def send_dict(self, name: str, dict_to_send: Dict[Any, Any], loop: Optional[EventLoop] = None, + async def send_dict(self, + name: str, + dict_to_send: Dict[Any, Any], + loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: """ - | Send a whole dictionary field by field as labeled data. + Send a whole dictionary field by field as labeled data. - :param str name: Name of the dictionary - :param Dict[Any, Any] dict_to_send: Dictionary to send - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param name: Name of the dictionary. + :param dict_to_send: Dictionary to send. + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ loop = get_event_loop() if loop is None else loop @@ -324,14 +342,17 @@ async def send_dict(self, name: str, dict_to_send: Dict[Any, Any], loop: Optiona await self.send_command_finished(loop=loop, receiver=receiver) await self.send_command_finished(loop=loop, receiver=receiver) - def sync_send_dict(self, name: str, dict_to_send: Dict[Any, Any], receiver: Optional[socket] = None) -> None: + def sync_send_dict(self, + name: str, + dict_to_send: Dict[Any, Any], + receiver: Optional[socket] = None) -> None: """ - | Send a whole dictionary field by field as labeled data. - | Synchronous version of 'TcpIpObject.receive_labeled_data'. + Send a whole dictionary field by field as labeled data. + Synchronous version of 'TcpIpObject.receive_labeled_data'. - :param str name: Name of the dictionary - :param Dict[Any, Any] dict_to_send: Dictionary to send - :param Optional[socket] receiver: TcpIpObject receiver + :param name: Name of the dictionary. + :param dict_to_send: Dictionary to send. + :param receiver: TcpIpObject receiver. """ receiver = self.sock if receiver is None else receiver @@ -362,14 +383,16 @@ def sync_send_dict(self, name: str, dict_to_send: Dict[Any, Any], receiver: Opti self.sync_send_command_finished(receiver=receiver) self.sync_send_command_finished(receiver=receiver) - async def __send_unnamed_dict(self, dict_to_send: Dict[Any, Any], loop: Optional[EventLoop] = None, + async def __send_unnamed_dict(self, + dict_to_send: Dict[Any, Any], + loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: """ - | Send a whole dictionary field by field as labeled data. Dictionary will be unnamed. + Send a whole dictionary field by field as labeled data. Dictionary will be unnamed. - :param dict dict_to_send: Dictionary to send - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param dict_to_send: Dictionary to send + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ loop = get_event_loop() if loop is None else loop @@ -391,13 +414,15 @@ async def __send_unnamed_dict(self, dict_to_send: Dict[Any, Any], loop: Optional # The sending is finished await self.send_command_finished(loop=loop, receiver=receiver) - def __sync_send_unnamed_dict(self, dict_to_send: Dict[Any, Any], receiver: Optional[socket] = None) -> None: + def __sync_send_unnamed_dict(self, + dict_to_send: Dict[Any, Any], + receiver: Optional[socket] = None) -> None: """ - | Send a whole dictionary field by field as labeled data. Dictionary will be unnamed. - | Synchronous version of 'TcpIpObject.receive_labeled_data'. + Send a whole dictionary field by field as labeled data. Dictionary will be unnamed. + Synchronous version of 'TcpIpObject.receive_labeled_data'. - :param Dict[Any, Any] dict_to_send: Dictionary to send - :param Optional[socket] receiver: TcpIpObject receiver + :param dict_to_send: Dictionary to send. + :param receiver: TcpIpObject receiver. """ receiver = self.sock if receiver is None else receiver @@ -420,14 +445,16 @@ def __sync_send_unnamed_dict(self, dict_to_send: Dict[Any, Any], receiver: Optio # The sending is finished self.sync_send_command_finished(receiver=receiver) - async def receive_dict(self, recv_to: Dict[Any, Any], loop: Optional[EventLoop] = None, + async def receive_dict(self, + recv_to: Dict[Any, Any], + loop: Optional[EventLoop] = None, sender: Optional[socket] = None) -> None: """ - | Receive a whole dictionary field by field as labeled data. + Receive a whole dictionary field by field as labeled data. - :param Dict[Any, Any] recv_to: Dictionary to fill with received fields - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] sender: TcpIpObject sender + :param recv_to: Dictionary to fill with received fields. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ loop = get_event_loop() if loop is None else loop @@ -445,13 +472,15 @@ async def receive_dict(self, recv_to: Dict[Any, Any], loop: Optional[EventLoop] else: recv_to[label] = param - def sync_receive_dict(self, recv_to: Dict[Any, Any], sender: Optional[socket] = None) -> None: + def sync_receive_dict(self, + recv_to: Dict[Any, Any], + sender: Optional[socket] = None) -> None: """ - | Receive a whole dictionary field by field as labeled data. - | Synchronous version of 'TcpIpObject.receive_labeled_data'. + Receive a whole dictionary field by field as labeled data. + Synchronous version of 'TcpIpObject.receive_labeled_data'. - :param Dict[Any, Any] recv_to: Dictionary to fill with received fields - :param Optional[socket] sender: TcpIpObject sender + :param recv_to: Dictionary to fill with received fields. + :param sender: TcpIpObject sender. """ sender = self.sock if sender is None else sender @@ -474,14 +503,17 @@ def sync_receive_dict(self, recv_to: Dict[Any, Any], sender: Optional[socket] = ########################################################################################## ########################################################################################## - async def __send_command(self, loop: EventLoop, receiver: socket, command: str = '') -> None: + async def __send_command(self, + loop: EventLoop, + receiver: socket, + command: str = '') -> None: """ - | Send a bytes command among the available commands. - | Do not use this one. Use the dedicated function 'send_command_{cmd}(...)'. + Send a bytes command among the available commands. + Do not use this one. Use the dedicated function 'send_command_{cmd}(...)'. - :param EventLoop loop: asyncio.get_event_loop() return - :param socket receiver: TcpIpObject receiver - :param str command: Name of the command, must be in 'self.command_dict' + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. + :param command: Name of the command, must be in 'self.command_dict'. """ # Check if the command exists @@ -492,14 +524,16 @@ async def __send_command(self, loop: EventLoop, receiver: socket, command: str = # Send command as a byte data await self.send_data(data_to_send=cmd, loop=loop, receiver=receiver) - def __sync_send_command(self, receiver: socket, command: str = '') -> None: + def __sync_send_command(self, + receiver: socket, + command: str = '') -> None: """ - | Send a bytes command among the available commands. - | Do not use this one. Use the dedicated function 'sync_send_command_{cmd}(...)'. - | Synchronous version of 'TcpIpObject.send_command'. + Send a bytes command among the available commands. + Do not use this one. Use the dedicated function 'sync_send_command_{cmd}(...)'. + Synchronous version of 'TcpIpObject.send_command'. - :param str command: name of the command to send - :param socket receiver: TcpIpObject receiver + :param command: Name of the command to send. + :param receiver: TcpIpObject receiver. """ # Check if the command exists @@ -510,203 +544,254 @@ def __sync_send_command(self, receiver: socket, command: str = '') -> None: # Send command as a byte data self.sync_send_data(data_to_send=cmd, receiver=receiver) - async def send_command_compute(self, loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: + async def send_command_compute(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: """ - | Send the 'compute' command. + Send the 'compute' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='compute') - def sync_send_command_compute(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_compute(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'compute' command. - | Synchronous version of 'TcpIpObject.send_command_compute'. + Send the 'compute' command. + Synchronous version of 'TcpIpObject.send_command_compute'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='compute') - async def send_command_done(self, loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: + async def send_command_done(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: """ - | Send the 'done' command. + Send the 'done' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='done') - def sync_send_command_done(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_done(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'done' command. - | Synchronous version of 'TcpIpObject.send_command_done'. + Send the 'done' command. + Synchronous version of 'TcpIpObject.send_command_done'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='done') - async def send_command_exit(self, loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: + async def send_command_exit(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: """ - | Send the 'exit' command. + Send the 'exit' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='exit') - def sync_send_command_exit(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_exit(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'exit' command. - | Synchronous version of 'TcpIpObject.send_command_exit'. + Send the 'exit' command. + Synchronous version of 'TcpIpObject.send_command_exit'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='exit') - async def send_command_finished(self, loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: + async def send_command_finished(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: """ - | Send the 'finished' command. + Send the 'finished' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='finished') - def sync_send_command_finished(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_finished(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'finished' command. - | Synchronous version of 'TcpIpObject.send_command_finished'. + Send the 'finished' command. + Synchronous version of 'TcpIpObject.send_command_finished'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='finished') - async def send_command_prediction(self, loop: Optional[EventLoop] = None, + async def send_command_prediction(self, + loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: """ - | Send the 'prediction' command. + Send the 'prediction' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='prediction') - def sync_send_command_prediction(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_prediction(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'prediction' command. - | Synchronous version of 'TcpIpObject.send_command_prediction'. + Send the 'prediction' command. + Synchronous version of 'TcpIpObject.send_command_prediction'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='prediction') - async def send_command_read(self, loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: + async def send_command_read(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: """ - | Send the 'read' command. + Send the 'read' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='read') - def sync_send_command_read(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_read(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'read' command. - | Synchronous version of 'TcpIpObject.send_command_read'. + Send the 'read' command. + Synchronous version of 'TcpIpObject.send_command_read'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='read') - async def send_command_sample(self, loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: + async def send_command_sample(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: """ - | Send the 'sample' command. + Send the 'sample' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='sample') - def sync_send_command_sample(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_sample(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'sample' command. - | Synchronous version of 'TcpIpObject.send_command_sample'. + Send the 'sample' command. + Synchronous version of 'TcpIpObject.send_command_sample'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='sample') - async def send_command_step(self, loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: + async def send_command_step(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: """ - | Send the 'step' command. + Send the 'step' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='step') - def sync_send_command_step(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_step(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'step' command. - | Synchronous version of 'TcpIpObject.send_command_step'. + Send the 'step' command. + Synchronous version of 'TcpIpObject.send_command_step'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='step') - async def send_command_visualisation(self, loop: Optional[EventLoop] = None, + async def send_command_visualisation(self, + loop: Optional[EventLoop] = None, receiver: Optional[socket] = None) -> None: """ - | Send the 'visualisation' command. + Send the 'visualisation' command. - :param Optional[EventLoop] loop: asyncio.get_event_loop() return - :param Optional[socket] receiver: TcpIpObject receiver + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. """ await self.__send_command(loop=loop, receiver=receiver, command='visualisation') - def sync_send_command_visualisation(self, receiver: Optional[socket] = None) -> None: + def sync_send_command_visualisation(self, + receiver: Optional[socket] = None) -> None: """ - | Send the 'visualisation' command. - | Synchronous version of 'TcpIpObject.send_command_visualisation'. + Send the 'visualisation' command. + Synchronous version of 'TcpIpObject.send_command_visualisation'. - :param Optional[socket] receiver: TcpIpObject receiver + :param receiver: TcpIpObject receiver. """ self.__sync_send_command(receiver=receiver, command='visualisation') + async def send_command_change_db(self, + loop: Optional[EventLoop] = None, + receiver: Optional[socket] = None) -> None: + """ + Send the 'change_database' command. + + :param loop: Asyncio event loop. + :param receiver: TcpIpObject receiver. + """ + + await self.__send_command(loop=loop, receiver=receiver, command='db') + + def sync_send_command_change_db(self, + receiver: Optional[socket] = None) -> None: + """ + Send the 'change_database' command. + Synchronous version of 'TcpIpObject.send_command_change_database'. + + :param receiver: TcpIpObject receiver. + """ + + self.__sync_send_command(receiver=receiver, command='db') + ########################################################################################## ########################################################################################## # Actions to perform on commands # ########################################################################################## ########################################################################################## - async def listen_while_not_done(self, loop: EventLoop, sender: socket, data_dict: Dict[Any, Any], + async def listen_while_not_done(self, + loop: EventLoop, + sender: socket, + data_dict: Dict[Any, Any], client_id: int = None) -> Dict[Any, Any]: """ - | Compute actions until 'done' command is received. + Compute actions until 'done' command is received. - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender - :param Dict[Any, Any] data_dict: Dictionary to collect data - :param int client_id: ID of a Client + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. + :param data_dict: Dictionary to collect data. + :param client_id: ID of a Client. """ # Compute actions until 'done' command is received @@ -717,74 +802,98 @@ async def listen_while_not_done(self, loop: EventLoop, sender: socket, data_dict # Return collected data return data_dict - async def action_on_compute(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_compute(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'compute' command. + Action to run when receiving the 'compute' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass - async def action_on_done(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_done(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'done' command. + Action to run when receiving the 'done' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass - async def action_on_exit(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_exit(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'exit' command. + Action to run when receiving the 'exit' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass - async def action_on_finished(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_finished(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'finished' command. + Action to run when receiving the 'finished' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass - async def action_on_prediction(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_prediction(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'prediction' command. + Action to run when receiving the 'prediction' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass - async def action_on_read(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_read(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'read' command. + Action to run when receiving the 'read' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ # Receive labeled data @@ -797,39 +906,66 @@ async def action_on_read(self, data: Dict[Any, Any], client_id: int, sender: soc else: data[client_id][label] = param - async def action_on_sample(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_sample(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'sample' command. + Action to run when receiving the 'sample' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass - async def action_on_step(self, data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: + async def action_on_step(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: """ - | Action to run when receiving the 'step' command. + Action to run when receiving the 'step' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass - async def action_on_visualisation(self, data: Dict[Any, Any], client_id: int, sender: socket, + async def action_on_visualisation(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, loop: EventLoop) -> None: """ - | Action to run when receiving the 'visualisation' command. + Action to run when receiving the 'visualisation' command. + + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. + """ + + pass + + async def action_on_change_db(self, + data: Dict[Any, Any], + client_id: int, + sender: socket, + loop: EventLoop) -> None: + """ + Action to run when receiving the 'change_database' command. - :param Dict[Any, Any] data: Dict storing data - :param int client_id: ID of the TcpIpClient - :param EventLoop loop: asyncio.get_event_loop() return - :param socket sender: TcpIpObject sender + :param data: Dict storing data. + :param client_id: ID of the TcpIpClient. + :param loop: Asyncio event loop. + :param sender: TcpIpObject sender. """ pass diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index e8130adb..07afb533 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -238,6 +238,30 @@ def set_dataset_batch(self, # Define batch from dataset self.batch_from_dataset = batch.copy() + def change_database(self, + database: str): + """ + Change the Database to connect to for each Client. + + :param database: Path to the new Database. + """ + + async_run(self.__change_database(database)) + + async def __change_database(self, + database: str): + """ + Change the Database to connect to for each Client. + + :param database: Path to the new Database. + """ + + loop = get_event_loop() + for client_id, client in self.clients: + await self.send_command_change_db(loop=loop, receiver=client) + await self.send_data(data_to_send=database[0], loop=loop, receiver=client) + await self.send_data(data_to_send=database[1], loop=loop, receiver=client) + ########################################################################################## ########################################################################################## # Server & Client shutdown # diff --git a/src/Database/BaseDataset.py b/src/Database/BaseDataset.py deleted file mode 100644 index 4b155513..00000000 --- a/src/Database/BaseDataset.py +++ /dev/null @@ -1,252 +0,0 @@ -from typing import Dict, List, Optional, Tuple -from numpy import array, ndarray, concatenate, save, arange -from numpy.random import shuffle -from collections import namedtuple - - -class BaseDataset: - """ - | BaseDataset is a dataset class to store any data from a BaseEnvironment or from files. - | Given data is split into input data and output data. - | Saving data results in multiple partitions of input and output data. - - :param namedtuple config: Namedtuple which contains BaseDataset parameters - """ - - def __init__(self, config: namedtuple): - - self.name: str = self.__class__.__name__ - - # Data fields containers - self.data_type = ndarray - self.fields: List[str] = ['input', 'output'] - self.data: Dict[str, ndarray] = {'input': array([]), 'output': array([])} - self.shape: Dict[str, Optional[List[int]]] = {'input': None, 'output': None} - - # Indexing - self.shuffle_pattern: Optional[List[int]] = None - self.current_sample: int = 0 - - # Dataset memory - self.max_size: int = config.max_file_size - self.batch_per_field: Dict[str, int] = {field: 0 for field in ['input', 'output']} - self.__empty: bool = True - - @property - def nb_samples(self) -> int: - """ - | Property returning the current number of samples - - :return: The current number of samples in all partitions - """ - - return max([len(self.data[field]) for field in self.fields]) - - def is_empty(self) -> bool: - """ - | Check if the fields of the dataset are empty. A field is considered as non-empty if it is filled with another - sample. - - :return: The Dataset is empty or not - """ - - # The empty flag is set to False once the Dataset is considered as non-empty - if not self.__empty: - return False - # Check each registered data field - for field in self.fields: - # Dataset is considered as non-empty if a field is filled with another sample - if self.batch_per_field[field] > 1: - self.__empty = False - return False - # If all field are considered as non-empty then the Dataset is empty - return True - - def init_data_size(self, field: str, shape: List[int]) -> None: - """ - | Store the original shape of data. Reshape data containers. - - :param str field: Data field name - :param List[int] shape: Shape of the corresponding tensor - """ - - # Store the original data shape - self.shape[field] = shape - # Reshape the data container - self.data[field] = array([]).reshape((0, *shape)) - - def get_data_shape(self, field: str) -> List[int]: - """ - | Returns the data shape of field. - - :param str field: Data field name - :return: Data shape for field - """ - - return self.shape[field] - - def init_additional_field(self, field: str, shape: List[int]) -> None: - """ - | Register a new data field. - - :param str field: Name of the data field - :param List[int] shape: Data shape - """ - - # Register the data field - self.fields.append(field) - # Init the number of adds in the field - self.batch_per_field[field] = 0 - # Init the field shape - self.init_data_size(field, shape) - - def empty(self) -> None: - """ - | Empty the dataset. - """ - - # Reinit each data container - for field in self.fields: - self.data[field] = array([]) if self.shape[field] is None else array([]).reshape((0, *self.shape[field])) - # Reinit indexing variables - self.shuffle_pattern = None - self.current_sample = 0 - self.batch_per_field = {field: 0 for field in self.fields} - self.__empty = True - - def memory_size(self, field: Optional[str] = None) -> int: - """ - | Return the actual memory size of the dataset if field is None. Otherwise, return the actual memory size of the - field. - - :param Optional[str] field: Name of the data field - :return: Size in bytes of the current dataset. - """ - - # Return the total memory size - if field is None: - return sum([self.data[field].nbytes for field in self.fields]) - # Return the memory size for the specified field - return self.data[field].nbytes - - def check_data(self, field: str, data: ndarray) -> None: - """ - | Check if the data is a numpy array. - - :param str field: Values at 'input' or anything else. Define if the associated shape is correspond to input - shape or output one. - :param ndarray data: New data - """ - - if type(data) != self.data_type: - raise TypeError(f"[{self.name}] Wrong data type in field '{field}': numpy array required, got {type(data)}") - - def add(self, field: str, data: ndarray, partition_file: Optional[str] = None) -> None: - """ - | Add data to the dataset. - - :param str field: Name of the data field - :param ndarray data: New data as batch of samples - :param Optional[str] partition_file: Path to the file in which to write the data - """ - - # Check data type - self.check_data(field, data) - - # Check if field is registered - if field not in self.fields: - # Fields can be register only if Dataset is empty - if not self.is_empty(): - raise ValueError(f"[{self.name}] A new field {field} tries to be created as Dataset is non empty. This " - f"will lead to a different number of sample for each field of the dataset.") - # Add new field if not registered - self.init_additional_field(field, data[0].shape) - - # Check data size initialization - if self.shape[field] is None: - self.init_data_size(field, data[0].shape) - - # Add batched samples - self.data[field] = concatenate((self.data[field], data)) - # Save in partition - if partition_file is not None: - self.save(field, partition_file) - - # Update sample indexing in dataset - self.batch_per_field[field] += 1 - self.current_sample = max([len(self.data[f]) for f in self.fields]) - - def save(self, field: str, file: str) -> None: - """ - | Save the corresponding field of the Dataset. - - :param str field: Name of the data field - :param str file: Path to the file in which to write the data - """ - - save(file, self.data[field]) - - def set(self, field: str, data: ndarray) -> None: - """ - | Set a full field of the dataset. - - :param str field: Name of the data field - :param ndarray data: New data as batch of samples - """ - - # Check data type - self.check_data(field, data) - - # Check if field is registered - if field not in self.fields: - # Add new field if not registered - self.init_additional_field(field, data[0].shape) - - # Check data size initialization - if self.shape[field] is None: - self.init_data_size(field, data[0].shape) - - # Set the full field - self.data[field] = data - - # Update sample indexing in dataset - self.__empty = False - self.current_sample = 0 - - def get(self, field: str, idx_begin: int, idx_end: int) -> ndarray: - """ - | Get a batch of data in 'field' container. - - :param str field: Name of the data field - :param int idx_begin: Index of the first sample - :param int idx_end: Index of the last sample - :return: Batch of data from 'field' - """ - - indices = slice(idx_begin, idx_end) if self.shuffle_pattern is None else self.shuffle_pattern[idx_begin:idx_end] - return self.data[field][indices] - - def shuffle(self) -> None: - """ - | Define a random shuffle pattern. - """ - - # Nothing to shuffle if Dataset is empty - if self.is_empty(): - return - # Generate a shuffle pattern - self.shuffle_pattern = arange(self.nb_samples) - shuffle(self.shuffle_pattern) - - def __str__(self) -> str: - """ - :return: String containing information about the BaseDatasetConfig object - """ - - description = "\n" - description += f" {self.name}\n" - description += f" Max size: {self.max_size}\n" - description += f" Data fields: {self.fields}" - for field in self.fields: - description += f" {field} shape: {self.shape[field]}" - return description diff --git a/src/Database/BaseDatasetConfig.py b/src/Database/BaseDatasetConfig.py index d1bf8bfb..23e90c9b 100644 --- a/src/Database/BaseDatasetConfig.py +++ b/src/Database/BaseDatasetConfig.py @@ -1,14 +1,10 @@ -from typing import Type, Optional +from typing import Optional from os.path import isdir, sep, join -from DeepPhysX.Core.Database.BaseDataset import BaseDataset -from DeepPhysX.Core.Utils.configs import make_config, namedtuple - class BaseDatasetConfig: def __init__(self, - dataset_class: Type[BaseDataset] = BaseDataset, existing_dir: Optional[str] = None, mode: Optional[str] = None, max_file_size: Optional[float] = None, @@ -18,7 +14,6 @@ def __init__(self, """ Configuration class to parameterize the Dataset and its Manager. - :param dataset_class: BaseDataset class from which an instance will be created. :param existing_dir: Path to an existing Dataset repository. :param mode: Specify the Dataset mode that should be used between 'training', 'validation' and 'running'. :param max_file_size: Maximum size (in Gb) of a single dataset file. @@ -56,12 +51,6 @@ def __init__(self, raise TypeError(f"[{self.name}] The given 'recompute_normalization'={recompute_normalization} must be a " f"bool.") - # BaseDataset parameterization - self.dataset_class: Type[BaseDataset] = dataset_class - self.dataset_config: namedtuple = make_config(configuration_object=self, - configuration_name='dataset_config', - max_file_size=max_file_size) - # DatasetManager parameterization self.existing_dir: Optional[str] = existing_dir self.mode: Optional[str] = mode @@ -70,26 +59,13 @@ def __init__(self, self.normalize: bool = normalize self.recompute_normalization: bool = recompute_normalization - def create_dataset(self) -> BaseDataset: - """ - Create an instance of BaseDataset with given parameters. - - :return: BaseDataset instance. - """ - - dataset = self.dataset_class(config=self.dataset_config) - if not isinstance(dataset, BaseDataset): - raise TypeError(f"[{self.name}] The given 'dataset_class'={self.dataset_class} must be a BaseDataset.") - return dataset - def __str__(self) -> str: description = "\n" description += f"{self.name}\n" - description += f" Dataset Class: {self.dataset_class.__name__}\n" description += f" Existing directory: {False if self.existing_dir is None else self.existing_dir}\n" description += f" Mode: {self.mode}\n" - description += f" Max size: {self.dataset_config.max_size}\n" + description += f" Max size: {self.max_file_size}\n" description += f" Shuffle: {self.shuffle}\n" description += f" Normalize: {self.normalize}\n" description += f" Recompute normalization: {self.recompute_normalization}\n" diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index 8b6170e5..e7979908 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -2,7 +2,7 @@ from os import cpu_count from os.path import join, dirname from threading import Thread -from subprocess import run as subprocessRun +from subprocess import run from sys import modules, executable from SSD.Core.Storage.Database import Database @@ -167,16 +167,9 @@ def start_client(self, script = join(dirname(modules[BaseEnvironment.__module__].__file__), 'launcherBaseEnvironment.py') # Usage: python3 script.py " - subprocessRun([executable, - script, - self.environment_file, - self.environment_class.__name__, - self.ip_address, - str(self.port), - str(idx), - str(self.number_of_thread), - str(data_db), - str(visu_db)]) + run([executable, script, self.environment_file, self.environment_class.__name__, + self.ip_address, str(self.port), str(idx), str(self.number_of_thread), + str(data_db), str(visu_db)]) def create_environment(self, environment_manager: Any, @@ -195,6 +188,9 @@ def create_environment(self, as_tcp_ip_client=False, data_db=data_db, visu_db=visu_db) + if not isinstance(environment, BaseEnvironment): + raise TypeError(f"[{self.name}] The given 'environment_class'={self.environment_class} must be a " + f"BaseEnvironment.") # Create & Init Environment environment.recv_parameters(self.param_dict) environment.create() diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 51062cf7..72afad16 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Dict, List +from typing import Any, Optional, Dict, List, Union from numpy import ndarray from DeepPhysX.Core.Manager.DatabaseManager import DatasetManager, Database @@ -15,7 +15,7 @@ def __init__(self, manager: Optional[Any] = None, session: str = 'sessions/default', new_session: bool = True, - is_training: bool = True, + pipeline: str = '', produce_data: bool = True, batch_size: int = 1): @@ -68,19 +68,19 @@ def __init__(self, # self.normalization[field] = json_dict['normalization'][field] # Create a DatasetManager if required - create_dataset = is_training or produce_data + create_dataset = pipeline in ['data_generation', 'training'] or produce_data database = None if create_dataset: self.dataset_manager = DatasetManager(dataset_config=dataset_config, session=session, data_manager=self, new_session=new_session, - is_training=is_training, + pipeline=pipeline, produce_data=produce_data) database = self.dataset_manager.database # Create an EnvironmentManager if required - create_environment = produce_data or environment_config is not None + create_environment = environment_config is not None if create_environment: self.environment_manager = EnvironmentManager(environment_config=environment_config, data_manager=self, @@ -89,8 +89,10 @@ def __init__(self, batch_size=batch_size) # DataManager variables + self.pipeline = pipeline self.produce_data = produce_data - self.data: Optional[Dict[str, ndarray]] = None + self.batch_size = batch_size + self.data_lines: Union[List[int], int] = [] def get_manager(self) -> Any: """ @@ -104,42 +106,48 @@ def get_manager(self) -> Any: def get_database(self) -> Database: return self.dataset_manager.database + def change_database(self) -> None: + # self.manager.change_database(self.dataset_manager.database) + self.environment_manager.change_database(self.dataset_manager.database) + def get_data(self, epoch: int = 0, - batch_size: int = 1, animate: bool = True) -> None: """ Fetch data from the EnvironmentManager or the DatasetManager according to the context. :param epoch: Current epoch number. - :param batch_size: Size of the desired batch. :param animate: Allow EnvironmentManager to generate a new sample. :return: Dict containing the newly computed data. """ - # Training - if self.produce_data: - # Get data from environment if used and if the data should be created at this epoch + # Data generation case + if self.pipeline == 'data_generation': + self.data_lines = self.environment_manager.get_data(animate=animate) + self.dataset_manager.add_data() + + # Training case + elif self.pipeline == 'training': + + # Get data from Environment(s) if used and if the data should be created at this epoch if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data): - self.environment_manager.get_data(animate=animate) - # self.dataset_manager.add_data(data) - # Force data from the dataset + self.data_lines = self.environment_manager.get_data(animate=animate) + self.dataset_manager.add_data() + + # Get data from Dataset else: - data = self.dataset_manager.get_data(batch_size=batch_size, get_inputs=True, get_outputs=True) + self.data_lines = self.dataset_manager.get_data(batch_size=self.batch_size) + print(self.data_lines) + # Dispatch a batch to clients if self.environment_manager is not None and self.environment_manager.use_dataset_in_environment: - new_data = self.environment_manager.dispatch_batch(batch=data) - if len(new_data['input']) != 0: - data['input'] = new_data['input'] - if len(new_data['output']) != 0: - data['output'] = new_data['output'] - if 'loss' in new_data: - data['loss'] = new_data['loss'] + self.data_lines = self.environment_manager.dispatch_batch(batch=self.data_lines) + + # Environment is no longer used elif self.environment_manager is not None: - # EnvironmentManager is no longer used self.environment_manager.close() self.environment_manager = None - # Prediction + # Prediction pipeline else: if self.dataset_manager is not None and not self.dataset_manager.new_dataset(): # Get data from dataset diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 5719a398..4136cc14 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -1,15 +1,15 @@ -from typing import Any, Dict, Tuple, List, Optional, Union -from os.path import isfile, isdir, abspath, join +from typing import Any, Dict, List, Optional +from os.path import isfile, isdir, join from os import listdir, symlink, sep from json import dump as json_dump from json import load as json_load -from numpy import load, squeeze, ndarray, concatenate, float64 +from numpy import arange, ndarray, array, abs, mean, sqrt +from numpy.random import shuffle from SSD.Core.Storage.Database import Database from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig -from DeepPhysX.Core.Database.BaseDataset import BaseDataset -from DeepPhysX.Core.Utils.path import create_dir, copy_dir +from DeepPhysX.Core.Utils.path import create_dir, copy_dir, get_first_caller from DeepPhysX.Core.Utils.jsonUtils import CustomJSONEncoder @@ -20,7 +20,7 @@ def __init__(self, session: str = 'sessions/default', data_manager: Optional[Any] = None, new_session: bool = True, - is_training: bool = True, + pipeline: str = '', produce_data: bool = True): """ DatasetManager handle all operations with input / output files. Allows saving and read tensors from files. @@ -39,6 +39,7 @@ def __init__(self, self.data_manager: Optional[Any] = data_manager self.dataset_dir: str = join(session, 'dataset') self.database: Optional[Database] = None + self.pipeline: str = pipeline # Dataset parameters self.max_file_size: int = dataset_config.max_file_size @@ -58,26 +59,36 @@ def __init__(self, self.partitions: Dict[str, List[str]] = {mode: [] for mode in self.modes} self.partition_index: Dict[str, int] = {mode: 0 for mode in self.modes} + # Dataset indexing + self.shuffle_pattern: ndarray = arange(0) + self.current_sample: int = 0 + self.first_add = True + # Dataset json file - self.json_default: Dict[str, Dict[str, Any]] = {'data_shape': {}, + self.json_default: Dict[str, Dict[str, Any]] = {'partitions': {mode: [] for mode in self.modes}, 'nb_samples': {mode: [] for mode in self.modes}, - 'partitions': {mode: [] for mode in self.modes}, + 'architecture': {}, + 'data_shape': {}, 'normalization': {}} self.json_content: Dict[str, Dict[str, Any]] = self.json_default.copy() # Produce training data + root = get_first_caller() if produce_data: # Produce training data in a new session if new_session: # Produce training data in a new session from scratch # --> Create a new '/dataset' directory if dataset_config.existing_dir is None: - create_dir(session_dir=session, session_name='dataset') - self.init_directory() + create_dir(session_dir=session, + session_name='dataset') + self.create_partition() # Produce training data in a new session from an existing Dataset # --> Copy the 'existing_dir/dataset' directory then load the 'session/dataset' directory else: - copy_dir(src_dir=dataset_config.existing_dir, dest_dir=session, sub_folders='dataset') + copy_dir(src_dir=join(root, dataset_config.existing_dir), + dest_dir=session, + sub_folders='dataset') self.load_directory() # Produce training data in an existing session # --> Load the 'session/dataset' directory @@ -88,7 +99,7 @@ def __init__(self, # Load training data in a new session # --> Link to the 'existing_dir/dataset' directory the load the 'session/dataset' directory if new_session: - symlink(src=join(dataset_config.existing_dir, 'dataset'), + symlink(src=join(root, dataset_config.existing_dir, 'dataset'), dst=join(session, 'dataset')) self.load_directory() # Load training data in an existing session @@ -96,7 +107,7 @@ def __init__(self, else: self.load_directory() - def init_directory(self): + def create_partition(self): """ """ @@ -109,9 +120,38 @@ def init_directory(self): fields=[('env', int), ('net', int)]) self.database.create_table(table_name='Training') self.database.create_table(table_name='Additional') + self.partitions[self.mode].append(partition_path) self.partition_index[self.mode] += 1 + def additional_partition(self): + + # 1. If a DB exists, get all the Fields to re-create them + fields = {} + types = {'INT': int, 'FLOAT': float, 'STR': str, 'BOOL': bool, 'NUMPY': ndarray} + if self.partition_index[self.mode] > 0: + for table_name in ['Training', 'Additional']: + fields[table_name] = [] + F = self.database.get_fields(table_name=table_name, + only_names=False) + for field in [f for f in F if f not in ['id', '_dt_']]: + fields[table_name].append((field, types[F[field].field_type])) + + # 2. Create a new Database + self.create_partition() + + # 3. Re-create the Fields if this is not the first partition + if self.partition_index[self.mode] > 1: + for table_name in fields.keys(): + self.database.create_fields(table_name=table_name, + fields=fields[table_name]) + + # 4. Tell the other components to communicate on a new DB + self.data_manager.change_database() + def load_directory(self): + """ + + """ # 1. Check the directory existence to prevent bugs if not isdir(self.dataset_dir): @@ -135,9 +175,6 @@ def load_directory(self): self.normalize and self.json_content['normalization'] == self.json_default['normalization']): self.update_json(update_normalization=True) - # 5. Load data from partitions - self.load_partitions() - def search_partitions(self): """ @@ -148,17 +185,182 @@ def search_partitions(self): return {mode: sorted(raw_partitions[mode]) for mode in self.modes} def search_partitions_info(self): - pass + """ - def load_partitions(self): - pass + """ + + # TODO: fill json - def update_json(self, update_shapes: bool = False, update_nb_samples: bool = False, - update_partitions_lists: bool = False, update_normalization: bool = False) -> None: pass + def update_json(self, + update_partitions_lists: bool = False, + update_nb_samples: bool = False, + update_architecture: bool = False, + update_shapes: bool = False, + update_normalization: bool = False) -> None: + """ + + + """ + + # Update partitions lists + if update_partitions_lists: + self.json_content['partitions'] = self.partitions + + # Update number of samples + if update_nb_samples: + if len(self.json_content['nb_samples'][self.mode]) == self.partition_index[self.mode]: + self.json_content['nb_samples'][self.mode][-1] = self.nb_samples + else: + self.json_content['nb_samples'][self.mode].append(self.nb_samples) + + # Update DB architecture + if update_architecture: + architecture = self.database.get_architecture() + for fields in architecture.values(): + for field in fields.copy(): + if field.split(' ')[0] in ['id', '_dt_']: + fields.remove(field) + architecture.pop('Sync') + self.json_content['architecture'] = architecture + + # Update data shapes + if update_shapes: + for table_name, fields in self.json_content['architecture'].items(): + data = self.database.get_line(table_name=table_name) + for field in fields: + if 'NUMPY' in field: + field_name = field.split(' ')[0] + self.json_content['data_shape'][f'{table_name}.{field_name}'] = data[field_name].shape + + # Update normalization coefficients + if update_normalization: + self.json_content['normalization'] = self.compute_normalization() + + # Overwrite json file + with open(join(self.dataset_dir, 'dataset.json'), 'w') as json_file: + json_dump(self.json_content, json_file, indent=3, cls=CustomJSONEncoder) + + @property + def nb_samples(self) -> int: + return self.database.nb_lines(table_name='Training') + + def shuffle(self): + """ + + """ + + self.shuffle_pattern = arange(self.nb_samples) + if self.shuffle: + shuffle(self.shuffle_pattern) + + def add_data(self): + """ + + """ + + # 1. Update the json file + self.update_json(update_nb_samples=True) + if self.first_add: + self.update_json(update_partitions_lists=True, update_shapes=True, update_architecture=True) + self.first_add = False + + # 2. Check the size of the partition + if self.max_file_size is not None: + if self.database.memory_size > self.max_file_size: + self.additional_partition() + self.update_json(update_partitions_lists=True, update_nb_samples=True) + + def get_data(self, + batch_size: int) -> ndarray: + """ + + """ + + # 1. Check if dataset is loaded and if the current sample is not the last + # TODO + + # 2. Update dataset index + idx = self.current_sample + self.current_sample += batch_size + + # 3. Get a batch of data + if self.shuffle: + return self.shuffle_pattern[idx:self.current_sample] + return arange(idx, self.current_sample) + def close(self): + """ + + """ if self.normalize and self.produce_data: self.update_json(update_normalization=True) self.database.close() + + def change_mode(self, mode: int) -> None: + """ + + """ + + pass + + def compute_normalization(self) -> Dict[str, List[float]]: + """ + + """ + + # Get the fields to normalize + fields = [] + for field in self.json_content['data_shape']: + table_name, field_name = field.split('.') + fields += [field_name] if table_name == 'Training' else [] + + # Init result + normalization = {field: [0., 0.] for field in fields} + + # Compute the mean for each field + means = {field: [] for field in fields} + nb_samples = [] + for partition in self.partitions['training']: + data_to_normalize = self.load_partitions_fields(partition=partition, + fields=fields) + nb_samples.append(data_to_normalize['id'][-1]) + for field in fields: + data = array(data_to_normalize[field]) + means[field].append(data.mean()) + for field in fields: + normalization[field][0] = sum([(n / sum(nb_samples)) * m + for n, m in zip(nb_samples, means[field])]) + + # Compute the standard deviation for each field + stds = {field: [] for field in fields} + for partition in self.partitions['training']: + data_to_normalize = self.load_partitions_fields(partition=partition, + fields=fields) + for field in fields: + data = array(data_to_normalize[field]) + stds[field].append(mean(abs(data - normalization[field][0]) ** 2)) + for field in fields: + normalization[field][1] = sqrt(sum([(n / sum(nb_samples)) * std + for n, std in zip(nb_samples, stds[field])])) + + return normalization + + def load_partitions_fields(self, + partition: str, + fields: List[str]): + db = Database(database_dir=self.dataset_dir, + database_name=partition).load() + return db.get_lines(table_name='Training', + fields=fields, + batched=True) + + def __str__(self): + + description = "\n" + description += f"# {self.name}\n" + description += f" Dataset Repository: {self.dataset_dir}\n" + description += f" Partitions size: {self.max_file_size * 1e-9} Go\n" + return description diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 031c2177..a89729cc 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional, Union, List from numpy import ndarray from asyncio import run as async_run from copy import copy @@ -13,8 +13,8 @@ class EnvironmentManager: def __init__(self, - environment_config: Optional[BaseEnvironmentConfig] = None, - data_manager: Any = None, + environment_config: BaseEnvironmentConfig, + data_manager: Optional[Any] = None, session: str = 'sessions/default', data_db: Optional[Database] = None, batch_size: int = 1): @@ -65,6 +65,7 @@ def __init__(self, visu_db=visu_db) # Define get_data and dispatch methods + self.change_database = self.change_database_in_server if self.server else self.change_database_in_environment self.get_data = self.get_data_from_server if self.server else self.get_data_from_environment self.dispatch_batch = self.dispatch_batch_to_server if self.server else self.dispatch_batch_to_environment @@ -74,17 +75,14 @@ def __init__(self, self.visualizer.get_database().load() self.visualizer.init_visualizer() - def get_data_manager(self) -> Any: - """ - Get the DataManager of this EnvironmentManager. - - :return: The DataManager of this EnvironmentManager. - """ + def change_database_in_server(self, database: Database): + self.server.change_database(database.get_path()) - return self.data_manager + def change_database_in_environment(self, database): + self.environment.database = database def get_data_from_server(self, - animate: bool = True) -> None: + animate: bool = True) -> List[int]: """ Compute a batch of data from Environments requested through TcpIpServer. @@ -92,10 +90,11 @@ def get_data_from_server(self, """ # Get data from server - self.server.get_batch(animate) + # TODO: return the indices of samples + return self.server.get_batch(animate) def get_data_from_environment(self, - animate: bool = True) -> None: + animate: bool = True) -> List[int]: """ Compute a batch of data directly from Environment. @@ -135,6 +134,9 @@ def get_data_from_environment(self, self.environment._send_training_data() self.environment._reset_training_data() + # TODO: return the indices of samples + return [] + def dispatch_batch_to_server(self, batch: Dict[str, Union[ndarray, dict]], animate: bool = True) -> Dict[str, Union[ndarray, dict]]: diff --git a/src/Manager/Manager.py b/src/Manager/Manager.py index 2a400e70..7747f7a9 100644 --- a/src/Manager/Manager.py +++ b/src/Manager/Manager.py @@ -1,7 +1,5 @@ -from typing import Any, Dict, Tuple, Optional -from os.path import join as osPathJoin -from os.path import isfile, basename, exists -from datetime import datetime +from typing import Dict, Tuple, Optional +from os.path import join, exists from numpy import ndarray from DeepPhysX.Core.Manager.DataManager import DataManager @@ -19,13 +17,13 @@ def __init__(self, network_config: Optional[BaseNetworkConfig] = None, dataset_config: Optional[BaseDatasetConfig] = None, environment_config: Optional[BaseEnvironmentConfig] = None, - pipeline: Optional[Any] = None, - session_dir: Optional[str] = None, - session_name: str = 'DPX_default', + session_dir: str = 'sessions', + session_name: str = 'default', new_session: bool = True, - is_training: bool = True, + pipeline: str = '', produce_data: bool = True, - batch_size: int = 1): + batch_size: int = 1, + debug_session: bool = False): """ Collection of all the specialized managers. Allows for some basic functions call. More specific behaviour have to be directly call from the corresponding manager. @@ -41,60 +39,68 @@ def __init__(self, :param int batch_size: Number of samples in a batch. """ - self.pipeline: Optional[Any] = pipeline + self.name = self.__class__.__name__ - # Constructing the session with the provided arguments - if session_name is None: - raise ValueError("[Manager] The session name cannot be set to None (will raise error).") - if session_dir is None: - # Create manager directory from the session name - self.session: str = osPathJoin(get_first_caller(), session_name) - else: - self.session: str = osPathJoin(session_dir, session_name) + # Define the session repository + root = get_first_caller() + session_dir = join(root, session_dir) - # Trainer: must create a new session to avoid overwriting - if is_training: + if pipeline in ['data_generation', 'training']: # Avoid unwanted overwritten data if new_session: - self.session: str = create_dir(self.session, dir_name=session_name) - # Prediction: work in an existing session + self.session = create_dir(session_dir=session_dir, + session_name=session_name) + else: + self.session = join(session_dir, session_name) else: + self.session = join(session_dir, session_name) if not exists(self.session): - raise ValueError("[Manager] The session directory {} does not exists.".format(self.session)) + raise ValueError(f"[{self.name}] The running session directory {self.session} does not exist.") - # Always create the NetworkMmanager - self.network_manager = NetworkManager(manager=self, - network_config=network_config, - session=self.session, - new_session=new_session, - training=training) - # Always create the DataManager for same reason - self.data_manager = DataManager(manager=self, - dataset_config=dataset_config, + # Create a DataManager + self.data_manager = DataManager(dataset_config=dataset_config, environment_config=environment_config, + manager=self, session=self.session, new_session=new_session, - training=training, - store_data=store_data, + pipeline=pipeline, + produce_data=produce_data, batch_size=batch_size) - # Create the StatsManager for training + database = self.data_manager.get_database() + self.batch_size = batch_size + + # Create a NetworkMmanager + self.network_manager = NetworkManager(network_config=network_config, + manager=self, + session=self.session, + new_session=new_session, + pipeline=pipeline, + data_db=database) + + # Create a StatsManager for training only + self.debug_session = debug_session self.stats_manager = StatsManager(manager=self, - session=self.session) if training else None + session=self.session) if pipeline == 'training' else None - def get_data(self, epoch: int = 0, batch_size: int = 1, animate: bool = True) -> None: + def change_database(self, database): + self.network_manager.change_database(database) + + def get_data(self, + epoch: int = 0, + animate: bool = True) -> None: """ - | Fetch data from the DataManager. + Fetch data from the DataManager. :param int epoch: Epoch ID - :param int batch_size: Size of a batch :param bool animate: If True allows running environment step """ - self.data_manager.get_data(epoch=epoch, batch_size=batch_size, animate=animate) + self.data_manager.get_data(epoch=epoch, + animate=animate) def optimize_network(self) -> Tuple[ndarray, Dict[str, float]]: """ - | Compute a prediction and run a back propagation with the current batch. + Compute a prediction and run a back propagation with the current batch. :return: The network prediction and the associated loss value """ @@ -110,14 +116,14 @@ def optimize_network(self) -> Tuple[ndarray, Dict[str, float]]: def save_network(self) -> None: """ - | Save network weights as a pth file + Save network parameters. """ self.network_manager.save_network() def close(self) -> None: """ - | Call all managers close procedure + Call all managers close procedure. """ if self.network_manager is not None: @@ -127,30 +133,7 @@ def close(self) -> None: if self.data_manager is not None: self.data_manager.close() - def save_info_file(self) -> None: - """ - | Called by the Trainer to save a .txt file which provides a quick description template to the user and lists - the description of all the components. - """ - - filename = osPathJoin(self.session, 'infos.txt') - date_time = datetime.now().strftime("%d/%m/%Y %H:%M:%S") - if not isfile(filename): - f = open(filename, "w+") - # Session description template for user - f.write("## DeepPhysX Training Session ##\n") - f.write(date_time + "\n\n") - f.write("Purpose of the training session:\nNetwork Input:\nNetwork Output:\nComments:\n\n") - # Listing every component descriptions - f.write("## List of Components Parameters ##\n") - f.write(str(self.pipeline)) - f.write(str(self)) - f.close() - def __str__(self) -> str: - """ - :return: A string containing valuable information about the Managers - """ manager_description = "" if self.network_manager is not None: diff --git a/src/Manager/StatsManager.py b/src/Manager/StatsManager.py index c79e7bdc..ee3c9580 100644 --- a/src/Manager/StatsManager.py +++ b/src/Manager/StatsManager.py @@ -39,7 +39,7 @@ def __init__(self, self.writer: SummaryWriter = SummaryWriter(self.log_dir) # Open Tensorboard - if not self.manager.pipeline.debug: + if not self.manager.debug_session: tb = program.TensorBoard() tb.configure(argv=[None, '--logdir', self.log_dir]) url = tb.launch() diff --git a/src/Pipelines/BaseDataGenerator.py b/src/Pipelines/BaseDataGenerator.py index 1e281f4b..5d8f3df0 100644 --- a/src/Pipelines/BaseDataGenerator.py +++ b/src/Pipelines/BaseDataGenerator.py @@ -65,7 +65,7 @@ def __init__(self, environment_config=environment_config, session=join(session_dir, session_name), new_session=new_session, - is_training=False, + pipeline='data_generation', produce_data=True, batch_size=batch_size) @@ -121,7 +121,7 @@ def batch_count(self) -> None: def batch_end(self) -> None: """ - Called once at the beginning of a batch production. + Called once at the end of a batch production. """ stdout.write("\033[K") diff --git a/src/Utils/jsonUtils.py b/src/Utils/jsonUtils.py index abc49337..bce413b1 100644 --- a/src/Utils/jsonUtils.py +++ b/src/Utils/jsonUtils.py @@ -64,7 +64,7 @@ def encode(self, o: Iterable) -> str: elif isinstance(o, dict): self.indentation_level += 1 output = [f"{self.indent_str}{json.dumps(key)}: {self.encode(value)}" for key, value in o.items()] - join_output = ",\n".join(output) + join_output = ",\n".join(output) if self.indentation_level != 1 else ",\n\n".join(output) self.indentation_level -= 1 return f"\n{self.indent_str}{'{'}\n{join_output}\n{self.indent_str}{'}'}" From 2221b3988fd293814c9cac9d43788a9e7bf3cf86 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 18 Oct 2022 18:38:04 +0200 Subject: [PATCH 05/61] Use Database for offline training. Dispatch batches of data in non remote mode. --- src/AsyncSocket/TcpIpServer.py | 14 +- src/Environment/BaseEnvironment.py | 69 ++++---- src/Manager/DataManager.py | 69 ++------ src/Manager/DatabaseManager.py | 38 ++++- src/Manager/EnvironmentManager.py | 52 +++--- src/Manager/Manager.py | 12 +- src/Manager/NetworkManager.py | 244 ++++++++++++++-------------- src/Network/BaseNetwork.py | 30 ++-- src/Network/BaseNetworkConfig.py | 149 +++++++---------- src/Network/BaseOptimization.py | 44 +++--- src/Network/DataTransformation.py | 50 +++--- src/Pipelines/BaseTrainer.py | 246 +++++++++++++++-------------- 12 files changed, 479 insertions(+), 538 deletions(-) diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index 07afb533..c8cb1cef 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional from asyncio import get_event_loop, gather from asyncio import AbstractEventLoop as EventLoop from asyncio import run as async_run @@ -216,7 +216,7 @@ async def __communicate(self, # # Send additional in / out sample # await self.send_dict(name="additional_fields", dict_to_send=sample, loop=loop, receiver=client) - # 2) Execute n steps, the last one send data computation signal + # 2. Execute n steps, the last one send data computation signal if animate: await self.send_command_step(loop=loop, receiver=client) # Receive data @@ -224,19 +224,15 @@ async def __communicate(self, client_id=client_id) def set_dataset_batch(self, - batch: Dict[str, Union[ndarray, Dict]]) -> None: + data_lines: List[int]) -> None: """ Receive a batch of data from the Dataset. Samples will be dispatched between clients. - :param batch: Batch of data. + :param data_lines: Batch of data. """ - # Check batch size - if len(batch['input']) != self.batch_size: - raise ValueError(f"[{self.name}] The size of batch from Dataset is {len(batch['input'])} while the batch " - f"size was set to {self.batch_size}.") # Define batch from dataset - self.batch_from_dataset = batch.copy() + self.batch_from_dataset = data_lines.copy() def change_database(self, database: str): diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 1714bbac..55473e71 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -1,5 +1,5 @@ from typing import Any, Optional, Dict, Union, Tuple, List -from numpy import array, ndarray +from numpy import ndarray from SSD.Core.Storage.Database import Database @@ -43,8 +43,8 @@ def __init__(self, self.__first_add: List[bool] = [True, True] # Variables to store samples from Dataset - self.sample_in: Optional[ndarray] = None - self.sample_out: Optional[ndarray] = None + self.sample_training: Optional[Dict[str, Any]] = None + self.sample_additional: Optional[Dict[str, Any]] = None # Loss data self.loss_data: Any = None # Manager if the Environment is not a TcpIpClient @@ -209,49 +209,46 @@ def set_training_data(self, self.__training_data = kwargs self.__training_data['env_id'] = self.instance_id + def set_additional_data(self, + **kwargs) -> None: + # Additional data is also set if the Environment can compute data + if self.compute_training_data: + self.__additional_data = kwargs + self.__additional_data['env_id'] = self.instance_id + def _reset_training_data(self) -> None: self.__training_data = {} + self.__additional_data = {} + self.sample_training = None + self.sample_additional = None def _send_training_data(self) -> None: line_id = self.database.add_data(table_name='Training', data=self.__training_data) - self.database.add_data(table_name='Sync', - data={'env': line_id}) self.database.add_data(table_name='Additional', data=self.__additional_data) + self.database.add_data(table_name='Sync', + data={'env': line_id}) - def set_loss_data(self, - loss_data: Any) -> None: - """ - Set the loss data to send to the TcpIpServer or the EnvironmentManager. - - :param loss_data: Optional data to compute loss. - """ - - # Training data is set if the Environment can compute data - if self.compute_essential_data: - self.loss_data = loss_data if type(loss_data) in [list, ndarray] else array([loss_data]) - - def set_additional_dataset(self, - label: str, - data: ndarray) -> None: - """ - Set additional data fields to store in the dataset. - - :param label: Name of the data field. - :param data: Data to store. - """ - - # Training data is set if the Environment can compute data - if self.compute_essential_data: - self.additional_fields[label] = data if type(data) in [list, ndarray] else array([data]) - - def reset_additional_datasets(self) -> None: - """ - Reset the additional dataset dictionaries. - """ + def _update_training_data(self, + line_id: int) -> None: + self.database.update(table_name='Training', + data=self.__training_data, + line_id=line_id) + if self.sample_additional is not None: + self.database.update(table_name='Additional', + data=self.__additional_data, + line_id=line_id) + self.database.add_data(table_name='Sync', + data={'env': line_id}) - self.additional_fields = {} + def _get_training_data(self, + line: int) -> None: + self.sample_training = self.database.get_line(table_name='Training', + line_id=line) + self.sample_additional = self.database.get_line(table_name='Additional', + line_id=line) + self.sample_additional = None if len(self.sample_additional) == 1 else self.sample_additional ########################################################################################## ########################################################################################## diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 72afad16..ec4c188e 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Dict, List, Union +from typing import Any, Optional, Dict, List from numpy import ndarray from DeepPhysX.Core.Manager.DatabaseManager import DatasetManager, Database @@ -28,8 +28,8 @@ def __init__(self, :param environment_config: Specialisation containing the parameters of the environment manager. :param manager: Manager that handle the DataManager :param session: Path to the session directory. - :param bool new_session: Flag that indicates whether if the session is new - :param is_training: Flag that indicates whether if this session is training a Network. + :param new_session: Flag that indicates whether if the session is new + :param pipeline: Flag that indicates whether if this session is training a Network. :param produce_data: Flag that indicates whether if this session is producing data. :param int batch_size: Number of samples in a batch """ @@ -41,32 +41,6 @@ def __init__(self, self.dataset_manager: Optional[DatasetManager] = None self.environment_manager: Optional[EnvironmentManager] = None - # Data normalization coefficients (default: mean = 0, standard deviation = 1) - self.normalization: Dict[str, List[float]] = {'input': [0., 1.], 'output': [0., 1.]} - # # If normalization flag is set to True, try to load existing coefficients - # if dataset_config is not None and dataset_config.normalize: - # json_file_path = None - # # Existing Dataset in the current session - # if os.path.exists(os.path.join(session, 'dataset')): - # json_file_path = os.path.join(session, 'dataset', 'dataset.json') - # # Dataset provided by config - # elif dataset_config.existing_dir is not None: - # dataset_dir = dataset_config.existing_dir - # if dataset_dir[-1] != "/": - # dataset_dir += "/" - # if dataset_dir[-8:] != "dataset/": - # dataset_dir += "dataset/" - # if os.path.exists(dataset_dir): - # json_file_path = os.path.join(dataset_dir, 'dataset.json') - # # If Dataset exists then a json file is associated - # if json_file_path is not None: - # with open(json_file_path) as json_file: - # json_dict = json_load(json_file) - # # Get the normalization coefficients - # for field in self.normalization.keys(): - # if field in json_dict['normalization']: - # self.normalization[field] = json_dict['normalization'][field] - # Create a DatasetManager if required create_dataset = pipeline in ['data_generation', 'training'] or produce_data database = None @@ -92,7 +66,7 @@ def __init__(self, self.pipeline = pipeline self.produce_data = produce_data self.batch_size = batch_size - self.data_lines: Union[List[int], int] = [] + self.data_lines: List[int] = [] def get_manager(self) -> Any: """ @@ -107,7 +81,7 @@ def get_database(self) -> Database: return self.dataset_manager.database def change_database(self) -> None: - # self.manager.change_database(self.dataset_manager.database) + self.manager.change_database(self.dataset_manager.database) self.environment_manager.change_database(self.dataset_manager.database) def get_data(self, @@ -123,24 +97,27 @@ def get_data(self, # Data generation case if self.pipeline == 'data_generation': - self.data_lines = self.environment_manager.get_data(animate=animate) + self.environment_manager.get_data(animate=animate) self.dataset_manager.add_data() # Training case elif self.pipeline == 'training': # Get data from Environment(s) if used and if the data should be created at this epoch - if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data): + # TODO + if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data)\ + and self.produce_data: self.data_lines = self.environment_manager.get_data(animate=animate) self.dataset_manager.add_data() # Get data from Dataset else: self.data_lines = self.dataset_manager.get_data(batch_size=self.batch_size) - print(self.data_lines) # Dispatch a batch to clients + # TODO if self.environment_manager is not None and self.environment_manager.use_dataset_in_environment: - self.data_lines = self.environment_manager.dispatch_batch(batch=self.data_lines) + self.environment_manager.dispatch_batch(data_lines=self.data_lines, + animate=animate) # Environment is no longer used elif self.environment_manager is not None: @@ -148,6 +125,7 @@ def get_data(self, self.environment_manager = None # Prediction pipeline + # TODO else: if self.dataset_manager is not None and not self.dataset_manager.new_dataset(): # Get data from dataset @@ -199,24 +177,9 @@ def apply_prediction(self, # Apply prediction self.environment_manager.environment.apply_prediction(prediction) - def normalize_data(self, - data: ndarray, - field: str, - reverse: bool = False) -> ndarray: - """ - Apply or unapply normalization following current standard score. - - :param data: Data to normalize. - :param field: Specify if data is an 'input' or an 'output'. - :param reverse: If False, apply normalization; if False, unapply normalization. - :return: Data with applied or misapplied normalization. - """ - - if not reverse: - # Apply normalization - return (data - self.normalization[field][0]) / self.normalization[field][1] - # Unapply normalization - return (data * self.normalization[field][1]) + self.normalization[field][0] + @property + def normalization(self) -> Dict[str, List[float]]: + return self.dataset_manager.normalization def close(self) -> None: """ diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 4136cc14..949fae10 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -50,7 +50,7 @@ def __init__(self, # Dataset modes self.modes: List[str] = ['training', 'validation', 'running'] - self.mode: str = 'training' if produce_data else 'running' + self.mode: str = 'training' if produce_data or pipeline == 'training' else 'running' self.mode = self.mode if dataset_config.mode is None else dataset_config.mode # Dataset partitions @@ -61,7 +61,7 @@ def __init__(self, # Dataset indexing self.shuffle_pattern: ndarray = arange(0) - self.current_sample: int = 0 + self.current_sample: int = 1 self.first_add = True # Dataset json file @@ -175,6 +175,23 @@ def load_directory(self): self.normalize and self.json_content['normalization'] == self.json_default['normalization']): self.update_json(update_normalization=True) + # 5. Load the Database + partition_path = self.partitions[self.mode][self.partition_index[self.mode]] + self.database = Database(database_dir=self.dataset_dir, + database_name=partition_path).load() + + def load_next_partition(self): + + # 1. Define next partition + self.partition_index[self.mode] = (self.partition_index[self.mode] + 1) % len(self.partitions[self.mode]) + partition_path = self.partitions[self.mode][self.partition_index[self.mode]] + self.database = Database(database_dir=self.dataset_dir, + database_name=partition_path).load() + + # 2. Shuffle + if self.shuffle: + self.shuffle_samples() + def search_partitions(self): """ @@ -246,7 +263,7 @@ def update_json(self, def nb_samples(self) -> int: return self.database.nb_lines(table_name='Training') - def shuffle(self): + def shuffle_samples(self): """ """ @@ -273,13 +290,15 @@ def add_data(self): self.update_json(update_partitions_lists=True, update_nb_samples=True) def get_data(self, - batch_size: int) -> ndarray: + batch_size: int) -> List[int]: """ """ # 1. Check if dataset is loaded and if the current sample is not the last - # TODO + if self.current_sample > self.nb_samples: + self.load_next_partition() + self.current_sample = 1 # 2. Update dataset index idx = self.current_sample @@ -288,7 +307,7 @@ def get_data(self, # 3. Get a batch of data if self.shuffle: return self.shuffle_pattern[idx:self.current_sample] - return arange(idx, self.current_sample) + return list(arange(idx, self.current_sample)) def close(self): """ @@ -348,6 +367,10 @@ def compute_normalization(self) -> Dict[str, List[float]]: return normalization + @property + def normalization(self) -> Dict[str, List[float]]: + return None if self.json_content['normalization'] == {} else self.json_content['normalization'] + def load_partitions_fields(self, partition: str, fields: List[str]): @@ -362,5 +385,6 @@ def __str__(self): description = "\n" description += f"# {self.name}\n" description += f" Dataset Repository: {self.dataset_dir}\n" - description += f" Partitions size: {self.max_file_size * 1e-9} Go\n" + size = f"No limits" if self.max_file_size is None else f"{self.max_file_size * 1e-9} Go" + description += f" Partitions size: {size}\n" return description diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index a89729cc..8fd65259 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -1,7 +1,5 @@ -from typing import Any, Dict, Optional, Union, List -from numpy import ndarray +from typing import Any, Optional, List from asyncio import run as async_run -from copy import copy from os.path import join from SSD.Core.Storage.Database import Database @@ -38,7 +36,7 @@ def __init__(self, self.use_dataset_in_environment: bool = environment_config.use_dataset_in_environment self.simulations_per_step: int = environment_config.simulations_per_step self.max_wrong_samples_per_step: int = environment_config.max_wrong_samples_per_step - self.dataset_batch: Optional[Dict[str, Dict[int, Any]]] = None + self.dataset_batch: Optional[List[int]] = None # Create the Visualizer self.visualizer: Optional[VedoVisualizer] = None @@ -105,21 +103,11 @@ def get_data_from_environment(self, nb_sample = 0 while nb_sample < self.batch_size: - # # 1.1 Send a sample if a batch from dataset is given - # if self.dataset_batch is not None: - # # Extract a sample from dataset batch: input - # self.environment.sample_in = self.dataset_batch['input'][0] - # self.dataset_batch['input'] = self.dataset_batch['input'][1:] - # # Extract a sample from dataset batch: output - # self.environment.sample_out = self.dataset_batch['output'][0] - # self.dataset_batch['output'] = self.dataset_batch['output'][1:] - # # Extract a sample from dataset batch: additional fields - # additional_fields = {} - # if 'additional_fields' in self.dataset_batch: - # for field in self.dataset_batch['additional_fields']: - # additional_fields[field] = self.dataset_batch['additional_fields'][field][0] - # self.dataset_batch['additional_fields'][field] = self.dataset_batch['additional_fields'][field][1:] - # self.environment.additional_fields = additional_fields + # 1.1 Send a sample if a batch from dataset is given + update_line = None + if self.dataset_batch is not None: + update_line = self.dataset_batch.pop(0) + self.environment._get_training_data(update_line) # 1.2 Run the defined number of step if animate: @@ -131,34 +119,34 @@ def get_data_from_environment(self, # 1.3 Add the produced sample to the batch if the sample is validated if self.environment.check_sample(): nb_sample += 1 - self.environment._send_training_data() + if update_line is None: + self.environment._send_training_data() + else: + self.environment._update_training_data(update_line) self.environment._reset_training_data() # TODO: return the indices of samples return [] def dispatch_batch_to_server(self, - batch: Dict[str, Union[ndarray, dict]], - animate: bool = True) -> Dict[str, Union[ndarray, dict]]: + data_lines: List[int], + animate: bool = True) -> None: """ Send samples from dataset to the Environments. Get back the training data. - :param batch: Batch of samples. + :param data_lines: Batch of samples. :param animate: If True, triggers an environment step. :return: Batch of training data. """ # Define the batch to dispatch - self.server.set_dataset_batch(batch) - # Empty the server queue - while not self.server.data_fifo.empty(): - self.server.data_fifo.get() + self.server.set_dataset_batch(data_lines) # Get data - return self.get_data(animate=animate) + self.get_data_from_server(animate=animate) def dispatch_batch_to_environment(self, - batch: Dict[str, Union[ndarray, dict]], - animate: bool = True) -> Dict[str, Union[ndarray, dict]]: + data_lines: List[int], + animate: bool = True) -> None: """ Send samples from dataset to the Environments. Get back the training data. @@ -168,9 +156,9 @@ def dispatch_batch_to_environment(self, """ # Define the batch to dispatch - self.dataset_batch = copy(batch) + self.dataset_batch = data_lines.copy() # Get data - return self.get_data(animate=animate) + self.get_data_from_environment(animate=animate) def update_visualizer(self, instance: int) -> None: diff --git a/src/Manager/Manager.py b/src/Manager/Manager.py index 7747f7a9..4e30cff5 100644 --- a/src/Manager/Manager.py +++ b/src/Manager/Manager.py @@ -105,14 +105,12 @@ def optimize_network(self) -> Tuple[ndarray, Dict[str, float]]: :return: The network prediction and the associated loss value """ - # Normalize input and output data - data = self.data_manager.data - for field in ['input', 'output']: - if field in data: - data[field] = self.data_manager.normalize_data(data[field], field) # Forward pass and optimization step - prediction, loss_dict = self.network_manager.compute_prediction_and_loss(data, optimize=True) - return prediction, loss_dict + data_pred, data_loss = self.network_manager.compute_prediction_and_loss( + data_lines=self.data_manager.data_lines, + normalization=self.data_manager.normalization, + optimize=True) + return data_pred, data_loss def save_network(self) -> None: """ diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index 52d22f82..90c2071d 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -1,8 +1,9 @@ -from typing import Any, Dict, Tuple, Optional -from os import listdir, sep -from os.path import join as osPathJoin -from os.path import isdir, isfile -from numpy import copy, array, ndarray +from typing import Any, Dict, Tuple, Optional, List +from os import listdir +from os.path import join, isdir, isfile +from numpy import copy, ndarray, array + +from SSD.Core.Storage.Database import Database from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Utils.path import copy_dir, create_dir @@ -12,175 +13,158 @@ class NetworkManager: def __init__(self, network_config: BaseNetworkConfig, - session: str, manager: Optional[Any] = None, + session: str = 'sessions/default', new_session: bool = True, - training: bool = True): + pipeline: str = '', + data_db: Optional[Database] = None): """ - Deals with all the interactions with the neural network. Predictions, saves, initialisation, loading, - back-propagation, etc... + Deals with all the interactions with the neural network: predictions, saves, initialisation, loading, + back-propagation, etc. - :param network_config: Specialisation containing the parameters of the network manager - :param manager: Manager that handle the network manager - :param new_session: Define the creation of new directories to store data - :param training: If True prediction will cause tensors gradient creation + :param network_config: Specialisation containing the parameters of the network manager. + :param manager: Manager that handle the network manager. + :param session: Path to the session directory. + :param new_session: Define the creation of new directories to store data. + :param is_training: If True prediction will cause tensors gradient creation. """ self.name: str = self.__class__.__name__ - # Check network_config type - if not isinstance(network_config, BaseNetworkConfig): - raise TypeError(f"[{self.name}] Wrong 'network_config' type: BaseNetworkConfig required, " - f"get {type(network_config)}") - # Check session type and existence - if type(session) != str: - raise TypeError(f"[{self.name}] Wrong 'session' type: str required, get {type(session)}") - if not isdir(session): - raise ValueError(f"[{self.name}] Given 'session' does not exists: {session}") - # Check new_session type - if type(new_session) != bool: - raise TypeError(f"[{self.name}] Wrong 'new_session' type: bool required, get {type(new_session)}") - # Check train type - if type(training) != bool: - raise TypeError(f"[{self.name}] Wrong 'train' type: bool required, get {type(training)}") - - # Storage management + # Managers architecture + self.manager: Any = manager + + # Storage variables + self.data_db: Database = data_db + self.batch: Optional[Any] = None self.session: str = session self.new_session: bool = new_session self.network_dir: Optional[str] = None - self.network_template_name: str = session.split(sep)[-1] + '_network_{}' - - # Network management - self.manager: Any = manager - if training and not network_config.training_stuff: - raise ValueError(f"[{self.name}] Training requires a loss and an optimizer in your NetworkConfig") - self.training: bool = training - self.save_each_epoch: bool = network_config.save_each_epoch self.saved_counter: int = 0 + self.save_each_epoch: bool = network_config.save_each_epoch - # Init network objects: Network, Optimization, DataTransformation - self.network: Any = None - self.optimization: Any = None - self.data_transformation: Any = None - self.network_config: BaseNetworkConfig = network_config - self.set_network() - - def get_manager(self) -> Any: - """ - | Return the Manager of the NetworkManager. - - :return: Manager that handles the NetworkManager - """ - - return self.manager - - def set_network(self) -> None: - """ - | Set the network to the corresponding weight from a given file. - """ - - # Init network - self.network = self.network_config.create_network() + # Init Network + self.network = network_config.create_network() self.network.set_device() - # Init optimization - self.optimization = self.network_config.create_optimization() - self.optimization.manager = self - if self.optimization.loss_class: + if pipeline == 'training' and not network_config.training_stuff: + raise ValueError(f"[{self.name}] Training requires a loss and an optimizer in your NetworkConfig") + self.is_training: bool = pipeline == 'training' + + # Init Optimization + self.optimization = network_config.create_optimization() + if self.optimization.loss_class is not None: self.optimization.set_loss() # Init DataTransformation - self.data_transformation = self.network_config.create_data_transformation() + self.data_transformation = network_config.create_data_transformation() - # Training - if self.training: - # Configure as training + # Training configuration + if self.is_training: self.network.set_train() self.optimization.set_optimizer(self.network) # Setting network directory - if self.new_session and self.network_config.network_dir and isdir(self.network_config.network_dir): - self.network_dir = self.network_config.network_dir - self.network_dir = copy_dir(self.network_dir, self.session, dest_dir='network') - self.load_network() + if new_session and network_config.network_dir is not None and isdir(network_config.network_dir): + self.network_dir = copy_dir(src_dir=network_config.network_dir, + dest_dir=session, + sub_folders='network') + self.load_network(which_network=network_config.which_network) else: - self.network_dir = osPathJoin(self.session, 'network/') - self.network_dir = create_dir(self.network_dir, dir_name='network') + self.network_dir = create_dir(session_dir=session, session_name='network') - # Prediction + # Prediction configuration else: - # Configure as prediction self.network.set_eval() - # Need an existing network - self.network_dir = osPathJoin(self.session, 'network/') - # Load parameters - self.load_network() + self.network_dir = join(session, 'network/') + self.load_network(which_network=network_config.which_network) + + def change_database(self, database): + self.data_db = database - def load_network(self) -> None: + def load_network(self, + which_network: int = -1) -> None: """ - | Load an existing set of parameters to the network. + Load an existing set of parameters to the network. """ # Get eventual epoch saved networks - networks_list = [osPathJoin(self.network_dir, f) for f in listdir(self.network_dir) if - isfile(osPathJoin(self.network_dir, f)) and f.__contains__('_network_.')] + networks_list = [join(self.network_dir, f) for f in listdir(self.network_dir) if + isfile(join(self.network_dir, f)) and f.__contains__('network_')] networks_list = sorted(networks_list) # Add the final saved network - last_saved_network = [osPathJoin(self.network_dir, f) for f in listdir(self.network_dir) if - isfile(osPathJoin(self.network_dir, f)) and f.__contains__('network.')] + last_saved_network = [join(self.network_dir, f) for f in listdir(self.network_dir) if + isfile(join(self.network_dir, f)) and f.__contains__('network.')] networks_list = networks_list + last_saved_network - which_network = self.network_config.which_network if len(networks_list) == 0: - print(f"[{self.name}]: There is no network in {self.network_dir}. Shutting down.") - quit(0) + raise FileNotFoundError(f"[{self.name}]: There is no network in {self.network_dir}.") elif len(networks_list) == 1: which_network = 0 - elif len(networks_list) > 1 and which_network is None: - print(f"[{self.name}] There is more than one network in this directory, loading the most trained by " - f"default. If you want to load another network please use the 'which_network' variable.") - which_network = -1 - elif which_network > len(networks_list) > 1: - print(f"[{self.name}] The selected network doesn't exist (index is too big), loading the most trained " + elif which_network > len(networks_list): + print(f"[{self.name}] The network 'network_{self.saved_counter} doesn't exist, loading the most trained " f"by default.") which_network = -1 print(f"[{self.name}]: Loading network from {networks_list[which_network]}.") self.network.load_parameters(networks_list[which_network]) - def compute_prediction_and_loss(self, batch: Dict[str, ndarray], - optimize: bool) -> Tuple[ndarray, Dict[str, float]]: + def compute_prediction_and_loss(self, + optimize: bool, + data_lines: List[int], + normalization: Optional[Dict[str, List[float]]] = None) -> Tuple[ndarray, Dict[str, float]]: """ - | Make a prediction with the data passed as argument, optimize or not the network + Make a prediction with the data passed as argument, optimize or not the network - :param Dict[str, ndarray] batch: Format {'input': numpy.ndarray, 'output': numpy.ndarray}. - Contains the input value and ground truth to compare against - :param bool optimize: If true run a back propagation + :param batch_indices: Indices of the line of the Database that correspond to the current bach + :param optimize: If true run a back propagation :return: The prediction and the associated loss value """ - # Getting data from the data manager - data_in = self.network.transform_from_numpy(batch['input'], grad=optimize) - data_gt = self.network.transform_from_numpy(batch['output'], grad=optimize) - loss_data = self.network.transform_from_numpy(batch['loss'], grad=False) if 'loss' in batch.keys() else None + # Define in and out batches + batches = {} + normalization = {} if normalization is None else normalization + for side, fields in zip(['net', 'opt'], [self.network.net_fields, self.network.opt_fields]): + + # Get the batch from the Database + batch = self.data_db.get_lines(table_name='Training', + fields=fields, + lines_id=data_lines, + batched=True) + del batch['id'] + + # Apply normalization and convert to tensor + for field in batch.keys(): + batch[field] = array(batch[field]) + if field in normalization: + batch[field] = self.normalize_data(data=batch[field], + normalization=normalization[field]) + batch[field] = self.network.numpy_to_tensor(data=batch[field], + grad=optimize) + batches[side] = batch + data_net, data_opt = batches.values() # Compute prediction - data_in = self.data_transformation.transform_before_prediction(data_in) - data_out = self.network.predict(data_in) + data_net = self.data_transformation.transform_before_prediction(data_net) + data_pred = self.network.predict(data_net) # Compute loss - data_out, data_gt = self.data_transformation.transform_before_loss(data_out, data_gt) - loss_dict = self.optimization.compute_loss(data_out.reshape(data_gt.shape), data_gt, loss_data) + data_pred, data_opt = self.data_transformation.transform_before_loss(data_pred, data_opt) + data_loss = self.optimization.compute_loss(data_pred, data_opt) + # Optimizing network if training if optimize: self.optimization.optimize() + # Transform prediction to be compatible with environment - data_out = self.data_transformation.transform_before_apply(data_out) - prediction = self.network.transform_to_numpy(data_out) - return prediction, loss_dict + data_pred = self.data_transformation.transform_before_apply(data_pred) + for field in data_pred: + data_pred[field] = self.network.tensor_to_numpy(data_pred[field]) + return data_pred, data_loss - def compute_online_prediction(self, network_input: ndarray) -> ndarray: + def compute_online_prediction(self, + network_input: ndarray) -> ndarray: """ - | Make a prediction with the data passed as argument. + Make a prediction with the data passed as argument. - :param ndarray network_input: Input of the network= + :param network_input: Input of the network :return: The prediction """ @@ -195,6 +179,26 @@ def compute_online_prediction(self, network_input: ndarray) -> ndarray: pred = self.network.transform_to_numpy(pred) return pred.reshape(-1) + @classmethod + def normalize_data(cls, + data: ndarray, + normalization: List[float], + reverse: bool = False) -> ndarray: + """ + Apply or unapply normalization following current standard score. + + :param data: Data to normalize. + :param field: Specify if data is an 'input' or an 'output'. + :param reverse: If False, apply normalization; if False, unapply normalization. + :return: Data with applied or misapplied normalization. + """ + + if reverse: + # Unapply normalization + return (data * normalization[1]) + normalization[0] + # Apply normalization + return (data - normalization[0]) / normalization[1] + def save_network(self, last_save: bool = False) -> None: """ | Save the network with the corresponding suffix, so they do not erase the last save. @@ -217,18 +221,14 @@ def save_network(self, last_save: bool = False) -> None: def close(self) -> None: """ - | Closing procedure. + Closing procedure. """ - if self.training: + if self.is_training: self.save_network(last_save=True) del self.network - del self.network_config def __str__(self) -> str: - """ - :return: String containing information about the BaseNetwork object - """ description = "\n" description += f"# {self.__class__.__name__}\n" diff --git a/src/Network/BaseNetwork.py b/src/Network/BaseNetwork.py index e61139fb..4a6d8881 100644 --- a/src/Network/BaseNetwork.py +++ b/src/Network/BaseNetwork.py @@ -4,27 +4,33 @@ class BaseNetwork: - """ - | BaseNetwork is a network class to compute predictions from input data according to actual state. - :param namedtuple config: namedtuple containing BaseNetwork parameters - """ + def __init__(self, + config: namedtuple): + """ + BaseNetwork is a network class to compute predictions from input data according to actual state. - def __init__(self, config: namedtuple): + :param namedtuple config: namedtuple containing BaseNetwork parameters + """ # Config self.device = None self.config = config - def predict(self, input_data: Any) -> Any: + # Data fields + self.net_fields = ['input'] + self.opt_fields = ['ground_truth'] + self.pred_fields = ['prediction'] + + def predict(self, data_net: Dict[str, Any]) -> Dict[str, Any]: """ - | Same as forward + Compute a forward pass of the network. - :param Any input_data: Input tensor - :return: Network prediction + :param data_net: Data used by the Network. + :return: Data produced by the Network. """ - return self.forward(input_data) + return {'prediction': self.forward(data_net['input'])} def forward(self, input_data: Any) -> Any: """ @@ -93,7 +99,7 @@ def nb_parameters(self) -> int: raise NotImplementedError - def transform_from_numpy(self, data: ndarray, grad: bool = True) -> Any: + def numpy_to_tensor(self, data: ndarray, grad: bool = True) -> Any: """ | Transform and cast data from numpy to the desired tensor type. @@ -104,7 +110,7 @@ def transform_from_numpy(self, data: ndarray, grad: bool = True) -> Any: return data.astype(self.config.data_type) - def transform_to_numpy(self, data: Any) -> ndarray: + def tensor_to_numpy(self, data: Any) -> ndarray: """ | Transform and cast data from tensor type to numpy. diff --git a/src/Network/BaseNetworkConfig.py b/src/Network/BaseNetworkConfig.py index 24a943b2..69f93caa 100644 --- a/src/Network/BaseNetworkConfig.py +++ b/src/Network/BaseNetworkConfig.py @@ -1,38 +1,14 @@ from typing import Any, Optional, Type -from collections import namedtuple from os.path import isdir from numpy import typeDict from DeepPhysX.Core.Network.BaseNetwork import BaseNetwork from DeepPhysX.Core.Network.BaseOptimization import BaseOptimization from DeepPhysX.Core.Network.DataTransformation import DataTransformation - -NetworkType = BaseNetwork -OptimizationType = BaseOptimization -DataTransformationType = DataTransformation +from DeepPhysX.Core.Utils.configs import make_config, namedtuple class BaseNetworkConfig: - """ - | BaseNetworkConfig is a configuration class to parameterize and create BaseNetwork, BaseOptimization and - DataTransformation for the NetworkManager. - - :param Type[BaseNetwork] network_class: BaseNetwork class from which an instance will be created - :param Type[BaseOptimization] optimization_class: BaseOptimization class from which an instance will be created - :param Type[DataTransformation] data_transformation_class: DataTransformation class from which an instance will - be created - :param Optional[str] network_dir: Name of an existing network repository - :param str network_name: Name of the network - :param str network_type: Type of the network - :param int which_network: If several networks in network_dir, load the specified one - :param bool save_each_epoch: If True, network state will be saved at each epoch end; if False, network state - will be saved at the end of the training - :param str data_type: Type of the training data - :param Optional[float] lr: Learning rate - :param bool require_training_stuff: If specified, loss and optimizer class can be not necessary for training - :param Optional[Any] loss: Loss class - :param Optional[Any] optimizer: Network's parameters optimizer class - """ def __init__(self, network_class: Type[BaseNetwork] = BaseNetwork, @@ -41,13 +17,34 @@ def __init__(self, network_dir: Optional[str] = None, network_name: str = 'Network', network_type: str = 'BaseNetwork', - which_network: int = 0, + which_network: int = -1, save_each_epoch: bool = False, data_type: str = 'float32', lr: Optional[float] = None, require_training_stuff: bool = True, loss: Optional[Any] = None, optimizer: Optional[Any] = None): + """ + BaseNetworkConfig is a configuration class to parameterize and create BaseNetwork, BaseOptimization and + DataTransformation for the NetworkManager. + + :param network_class: BaseNetwork class from which an instance will be created. + :param optimization_class: BaseOptimization class from which an instance will be created. + :param data_transformation_class: DataTransformation class from which an instance will be created. + :param network_dir: Name of an existing network repository. + :param network_name: Name of the network. + :param network_type: Type of the network. + :param which_network: If several networks in network_dir, load the specified one. + :param save_each_epoch: If True, network state will be saved at each epoch end; if False, network state + will be saved at the end of the training. + :param data_type: Type of the training data. + :param lr: Learning rate. + :param require_training_stuff: If specified, loss and optimizer class can be not necessary for training. + :param loss: Loss class. + :param optimizer: Network's parameters optimizer class. + """ + + self.name = self.__class__.__name__ # Check network_dir type and existence if network_dir is not None: @@ -68,8 +65,6 @@ def __init__(self, if type(which_network) != int: raise TypeError( f"[{self.__class__.__name__}] Wrong 'which_network' type: int required, get {type(which_network)}") - if which_network < 0: - raise ValueError(f"[{self.__class__.__name__}] Given 'which_network' value is negative") # Check save_each_epoch type if type(save_each_epoch) != bool: raise TypeError( @@ -81,110 +76,74 @@ def __init__(self, # BaseNetwork parameterization self.network_class: Type[BaseNetwork] = network_class - self.network_config: namedtuple = self.make_config(config_name='network_config', - network_name=network_name, - network_type=network_type, - data_type=data_type) + self.network_config: namedtuple = make_config(configuration_object=self, + configuration_name='network_config', + network_name=network_name, + network_type=network_type, + data_type=data_type) # BaseOptimization parameterization self.optimization_class: Type[BaseOptimization] = optimization_class - self.optimization_config: namedtuple = self.make_config(config_name='optimization_config', - loss=loss, - lr=lr, - optimizer=optimizer) + self.optimization_config: namedtuple = make_config(configuration_object=self, + configuration_name='optimization_config', + loss=loss, + lr=lr, + optimizer=optimizer) self.training_stuff: bool = (loss is not None) and (optimizer is not None) or (not require_training_stuff) # NetworkManager parameterization self.data_transformation_class: Type[DataTransformation] = data_transformation_class - self.data_transformation_config: namedtuple = self.make_config(config_name='data_transformation_config') + self.data_transformation_config: namedtuple = make_config(configuration_object=self, + configuration_name='data_transformation_config') + + # NetworkManager parameterization self.network_dir: str = network_dir self.which_network: int = which_network self.save_each_epoch: bool = save_each_epoch and self.training_stuff - def make_config(self, config_name: str, **kwargs) -> namedtuple: - """ - | Create a namedtuple which gathers all the parameters for an Object configuration (Network or Optimization). - | For a child config class, only new items are required since parent's items will be added by default. - - :param str config_name: Name of the configuration to fill - :param kwargs: Items to add to the Object configuration - :return: Namedtuple which contains Object parameters + def create_network(self) -> BaseNetwork: """ - - # Get items set as keyword arguments - fields = tuple(kwargs.keys()) - args = tuple(kwargs.values()) - # Check if a config already exists with the same name (child class will have the parent's config by default) - if config_name in self.__dict__: - config = self.__getattribute__(config_name) - for key, value in config._asdict().items(): - # Only new items are required for children, check if the parent's items are set again anyway - if key not in fields: - fields += (key,) - args += (value,) - # Create namedtuple with collected items - return namedtuple(config_name, fields)._make(args) - - def create_network(self) -> NetworkType: - """ - | Create an instance of network_class with given parameters. + Create an instance of network_class with given parameters. :return: BaseNetwork object from network_class and its parameters. """ - try: - network = self.network_class(config=self.network_config) - except: - raise ValueError( - f"[{self.__class__.__name__}] Given 'network_class' cannot be created in {self.__class__.__name__}") + # Create instance + network = self.network_class(config=self.network_config) if not isinstance(network, BaseNetwork): - raise TypeError( - f"[{self.__class__.__name__}] Wrong 'network_class' type: BaseNetwork required, get " - f"{self.network_class}") + raise TypeError(f"[{self.name}] The given 'network_class'={self.network_class} must be a BaseNetwork.") return network - def create_optimization(self) -> OptimizationType: + def create_optimization(self) -> BaseOptimization: """ - | Create an instance of optimization_class with given parameters. + Create an instance of optimization_class with given parameters. :return: BaseOptimization object from optimization_class and its parameters. """ - try: - optimization = self.optimization_class(config=self.optimization_config) - except: - raise ValueError( - f"[{self.__class__.__name__}] Given 'optimization_class' got an unexpected keyword argument 'config'") + # Create instance + optimization = self.optimization_class(config=self.optimization_config) if not isinstance(optimization, BaseOptimization): - raise TypeError(f"[{self.__class__.__name__}] Wrong 'optimization_class' type: BaseOptimization required, " - f"get {self.optimization_class}") + raise TypeError(f"[{self.name}] The given 'optimization_class'={self.optimization_class} must be a " + f"BaseOptimization.") return optimization - def create_data_transformation(self) -> DataTransformationType: + def create_data_transformation(self) -> DataTransformation: """ - | Create an instance of data_transformation_class with given parameters. + Create an instance of data_transformation_class with given parameters. :return: DataTransformation object from data_transformation_class and its parameters. """ - try: - data_transformation = self.data_transformation_class(config=self.data_transformation_config) - except: - raise ValueError( - f"[{self.__class__.__name__}] Given 'data_transformation_class' got an unexpected keyword argument " - f"'config'") + # Create instance + data_transformation = self.data_transformation_class(config=self.data_transformation_config) if not isinstance(data_transformation, DataTransformation): - raise TypeError( - f"[{self.__class__.__name__}] Wrong 'data_transformation_class' type: DataTransformation required, " - f"get {self.data_transformation_class}") + raise TypeError(f"[{self.name}] The given 'data_transformation_class'={self.data_transformation_class} " + f"must be a DataTransformation.") return data_transformation def __str__(self) -> str: - """ - :return: String containing information about the BaseDatasetConfig object - """ - # Todo: fields in Configs are the set in Managers or objects, then remove __str__ method description = "\n" description += f"{self.__class__.__name__}\n" description += f" Network class: {self.network_class.__name__}\n" diff --git a/src/Network/BaseOptimization.py b/src/Network/BaseOptimization.py index 1c75d66c..2419d866 100644 --- a/src/Network/BaseOptimization.py +++ b/src/Network/BaseOptimization.py @@ -5,14 +5,14 @@ class BaseOptimization: - """ - | BaseOptimization is dedicated to network optimization: compute loss between prediction and target, update - network parameters. - - :param namedtuple config: Namedtuple containing BaseOptimization parameters - """ def __init__(self, config: namedtuple): + """ + BaseOptimization is dedicated to network optimization: compute loss between prediction and target, update + network parameters. + + :param config: Namedtuple containing BaseOptimization parameters + """ self.manager: Any = None @@ -28,28 +28,31 @@ def __init__(self, config: namedtuple): def set_loss(self) -> None: """ - | Initialize the loss function. + Initialize the loss function. """ raise NotImplementedError - def compute_loss(self, prediction: Any, ground_truth: Any, data: Dict[str, Any]) -> Dict[str, float]: + def compute_loss(self, + data_pred: Dict[str, Any], + data_opt: Dict[str, Any]) -> Dict[str, Any]: """ - | Compute loss from prediction / ground truth. + Compute loss from prediction / ground truth. - :param Any prediction: Tensor produced by the forward pass of the Network - :param Any ground_truth: Ground truth tensor to be compared with prediction - :param Dict[str, Any] data: Additional data sent as dict to compute loss value - :return: Loss value + :param data_pred: Tensor produced by the forward pass of the Network. + :param data_opt: Ground truth tensor to be compared with prediction. + :return: Loss value. """ + # WARNING: self.optimization.compute_loss(data_pred.reshape(data_gt.shape), data_gt, loss_data) raise NotImplementedError - def transform_loss(self, data: Dict[str, Any]) -> Dict[str, float]: + def transform_loss(self, + data_opt: Dict[str, Any]) -> Dict[str, float]: """ - | Apply a transformation on the loss value using the potential additional data. + Apply a transformation on the loss value using the potential additional data. - :param Dict[str, Any] data: Additional data sent as dict to compute loss value + :param data_opt: Additional data sent as dict to compute loss value :return: Transformed loss value """ @@ -57,24 +60,21 @@ def transform_loss(self, data: Dict[str, Any]) -> Dict[str, float]: def set_optimizer(self, net: BaseNetwork) -> None: """ - | Define an optimization process. + Define an optimization process. - :param BaseNetwork net: Network whose parameters will be optimized. + :param net: Network whose parameters will be optimized. """ raise NotImplementedError def optimize(self) -> None: """ - | Run an optimization step. + Run an optimization step. """ raise NotImplementedError def __str__(self) -> str: - """ - :return: String containing information about the BaseOptimization object - """ description = "\n" description += f" {self.__class__.__name__}\n" diff --git a/src/Network/DataTransformation.py b/src/Network/DataTransformation.py index 8e3d7fbb..cb758fdc 100644 --- a/src/Network/DataTransformation.py +++ b/src/Network/DataTransformation.py @@ -1,15 +1,17 @@ -from typing import Callable, Any, Optional, Tuple +from typing import Callable, Any, Optional, Tuple, Dict from collections import namedtuple +DTYPE = Dict[str, Any] -class DataTransformation: - """ - | DataTransformation is dedicated to data operations before and after network predictions. - :param namedtuple config: Namedtuple containing the parameters of the network manager - """ +class DataTransformation: def __init__(self, config: namedtuple): + """ + DataTransformation is dedicated to data operations before and after network predictions. + + :param namedtuple config: Namedtuple containing the parameters of the network manager. + """ self.name = self.__class__.__name__ @@ -27,36 +29,40 @@ def inner(self, *args): return inner - def transform_before_prediction(self, data_in: Any) -> Any: + def transform_before_prediction(self, + data_net: DTYPE) -> DTYPE: """ - | Apply data operations before network's prediction. + Apply data operations before network's prediction. - :param Any data_in: Input data - :return: Transformed input data + :param data_net: Data used by the Network. + :return: Transformed data_net. """ - return data_in + return data_net - def transform_before_loss(self, data_out: Any, data_gt: Optional[Any] = None) -> Tuple[Any, Optional[Any]]: + def transform_before_loss(self, + data_pred: DTYPE, + data_opt: Optional[DTYPE] = None) -> Tuple[DTYPE, Optional[DTYPE]]: """ - | Apply data operations between network's prediction and loss computation. + Apply data operations between network's prediction and loss computation. - :param Any data_out: Prediction data - :param Optional[Any] data_gt: Ground truth data - :return: Transformed prediction data, transformed ground truth data + :param data_pred: Data produced by the Network. + :param data_opt: Data used by the Optimizer. + :return: Transformed data_pred, data_opt. """ - return data_out, data_gt + return data_pred, data_opt - def transform_before_apply(self, data_out: Any) -> Any: + def transform_before_apply(self, + data_pred: DTYPE) -> DTYPE: """ - | Apply data operations between loss computation and prediction apply in environment. + Apply data operations between loss computation and prediction apply in environment. - :param Any data_out: Prediction data - :return: Transformed prediction data + :param data_pred: Data produced by the Network. + :return: Transformed data_pred. """ - return data_out + return data_pred def __str__(self) -> str: """ diff --git a/src/Pipelines/BaseTrainer.py b/src/Pipelines/BaseTrainer.py index 2c98addd..124b1c77 100644 --- a/src/Pipelines/BaseTrainer.py +++ b/src/Pipelines/BaseTrainer.py @@ -1,96 +1,96 @@ from typing import Optional from sys import stdout +from os.path import join, isfile +from datetime import datetime from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline from DeepPhysX.Core.Manager.Manager import Manager from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Utils.progressbar import Progressbar class BaseTrainer(BasePipeline): - """ - | BaseTrainer is a pipeline defining the training process of an artificial neural network. - | It provides a highly tunable learning process that can be used with any machine learning library. - - :param BaseNetworkConfig network_config: Specialisation containing the parameters of the network manager - :param BaseDatasetConfig dataset_config: Specialisation containing the parameters of the dataset manager - :param Optional[BaseEnvironmentConfig] environment_config: Specialisation containing the parameters of the - environment manager - :param str session_name: Name of the newly created directory if session is not defined - :param Optional[str] session_dir: Name of the directory in which to write all the necessary data - :param bool new_session: Define the creation of new directories to store data - :param int nb_epochs: Number of epochs - :param int nb_batches: Number of batches - :param int batch_size: Size of a batch - :param bool debug: If True, main training features will not be launched - """ def __init__(self, network_config: BaseNetworkConfig, dataset_config: BaseDatasetConfig, environment_config: Optional[BaseEnvironmentConfig] = None, - session_name: str = 'default', - session_dir: Optional[str] = None, - new_session: bool = True, - nb_epochs: int = 0, - nb_batches: int = 0, + session_dir: str = 'sessions', + session_name: str = 'training', + epoch_nb: int = 0, + batch_nb: int = 0, batch_size: int = 0, - debug: bool = False): + new_session: bool = True, + debug_session: bool = False): + """ + BaseTrainer implements the main loop that defines the training process of an artificial neural network. + Training can be launched with several data sources (from a Dataset, from an Environment, from combined sources). + It provides a highly tunable learning process that can be used with any machine learning library. - if environment_config is None and dataset_config.dataset_dir is None: - print("BaseTrainer: You have to give me a dataset source (existing dataset directory or simulation to " - "create data on the fly") - quit(0) + :param network_config: Specialisation containing the parameters of the network manager. + :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param environment_config: Specialisation containing the parameters of the environment manager. + :param session_dir: Relative path to the directory which contains sessions directories. + :param session_name: Name of the new the session directory. + :param epoch_nb: Number of epochs to run. + :param batch_nb: Number of batches to use. + :param batch_size: Number of samples in a single batch. + :param new_session: Define the creation of new directories to store data. + :param debug_session: If True, main training features will not be launched. + """ BasePipeline.__init__(self, network_config=network_config, dataset_config=dataset_config, environment_config=environment_config, - session_name=session_name, session_dir=session_dir, + session_name=session_name, pipeline='training') # Training variables - self.nb_epochs = nb_epochs - self.id_epoch = 0 - self.nb_batches = nb_batches + self.epoch_nb = epoch_nb + self.epoch_id = 0 + self.batch_nb = batch_nb self.batch_size = batch_size - self.id_batch = 0 - self.nb_samples = nb_batches * batch_size * nb_epochs + self.batch_id = 0 + self.nb_samples = batch_nb * batch_size * epoch_nb self.loss_dict = None + self.debug = debug_session - # Tell if data is recording while predicting (output is recorded only if input too) - self.record_data = {'input': True, 'output': True} + # Configure 'produce_data' flag + if environment_config is not None and dataset_config.existing_dir is None: + raise ValueError(f"[{self.name}] No data source provided.") + produce_data = dataset_config.existing_dir is None - self.debug = debug + # Progressbar if not self.debug: self.progress_counter = 0 - self.digits = ['{' + f':0{len(str(self.nb_epochs))}d' + '}', - '{' + f':0{len(str(self.nb_batches))}d' + '}'] - id_epoch, nb_epoch = self.digits[0].format(0), self.digits[0].format(self.nb_epochs) - id_batch, nb_batch = self.digits[1].format(0), self.digits[1].format(self.nb_batches) - self.progress_bar = Progressbar(start=0, stop=self.nb_batches * self.nb_epochs, c='orange', - title=f'Epoch n°{id_epoch}/{nb_epoch} - Batch n°{id_batch}/{nb_batch} ') - - self.manager = Manager(pipeline=self, - network_config=self.network_config, - dataset_config=dataset_config, + self.digits = ['{' + f':0{len(str(self.epoch_nb))}d' + '}', + '{' + f':0{len(str(self.batch_nb))}d' + '}'] + epoch_id, epoch_nb = self.digits[0].format(0), self.digits[0].format(self.epoch_nb) + batch_id, batch_nb = self.digits[1].format(0), self.digits[1].format(self.batch_nb) + self.progress_bar = Progressbar(start=0, stop=self.batch_nb * self.epoch_nb, c='orange', + title=f'Epoch n°{epoch_id}/{epoch_nb} - Batch n°{batch_id}/{batch_nb}') + + self.manager = Manager(network_config=self.network_config, + dataset_config=self.dataset_config, environment_config=self.environment_config, - session_name=session_name, session_dir=session_dir, + session_name=session_name, new_session=new_session, - batch_size=batch_size) - - self.manager.save_info_file() + pipeline='training', + produce_data=produce_data, + batch_size=batch_size, + debug_session=debug_session) + self.save_info_file(self.manager.session) def execute(self) -> None: """ - | Main function of the training process \"execute\" call the functions associated with the learning process. - | Each of the called functions are already implemented so one can start a basic training. - | Each of the called function can also be rewritten via inheritance to provide more specific / complex training - process. + Launch the training Pipeline. + Each event is already implemented for a basic pipeline but can also be rewritten via inheritance to describe a + more complex pipeline. """ self.train_begin() @@ -106,128 +106,132 @@ def execute(self) -> None: self.save_network() self.train_end() - def optimize(self) -> None: - """ - | Pulls data from the manager and run a prediction and optimizer step. - """ - - self.manager.get_data(self.id_epoch, self.batch_size) - _, self.loss_dict = self.manager.optimize_network() - - def save_network(self) -> None: - """ - | Registers the network weights and biases in the corresponding directory (session_name/network or - session/network) - """ - - self.manager.save_network() - def train_begin(self) -> None: """ - | Called once at the very beginning of the training process. - | Allows the user to run some pre-computations. + Called once at the beginning of the training pipeline. """ pass - def train_end(self) -> None: + def epoch_condition(self) -> bool: """ - | Called once at the very end of the training process. - | Allows the user to run some post-computations. + Check the epoch number condition. """ - self.manager.close() + return self.epoch_id < self.epoch_nb def epoch_begin(self) -> None: """ - | Called one at the start of each epoch. - | Allows the user to run some pre-epoch computations. + Called one at the beginning of each epoch. """ - self.id_batch = 0 + self.batch_id = 0 - def epoch_end(self) -> None: + def batch_condition(self) -> bool: """ - | Called one at the end of each epoch. - | Allows the user to run some post-epoch computations. + Check the batch number condition. """ - self.manager.stats_manager.add_train_epoch_loss(self.loss_dict['loss'], self.id_epoch) + return self.batch_id < self.batch_nb - def epoch_condition(self) -> bool: + def batch_begin(self) -> None: """ - | Condition that characterize the end of the training process. - - :return: False if the training needs to stop. + Called one at the beginning of a batch production. """ - return self.id_epoch < self.nb_epochs + if not self.debug: + stdout.write("\033[K") + self.progress_counter += 1 + id_epoch, nb_epoch = self.digits[0].format(self.epoch_id + 1), self.digits[0].format(self.epoch_nb) + id_batch, nb_batch = self.digits[1].format(self.batch_id + 1), self.digits[1].format(self.batch_nb) + self.progress_bar.title = f'Epoch n°{id_epoch}/{nb_epoch} - Batch n°{id_batch}/{nb_batch} ' + self.progress_bar.print(counts=self.progress_counter) - def epoch_count(self) -> None: + def optimize(self) -> None: """ - | Allows user for custom update of epochs count. + Pulls data, run a prediction and an optimizer step. """ - self.id_epoch += 1 + self.manager.get_data(self.epoch_id) + _, self.loss_dict = self.manager.optimize_network() - def batch_begin(self) -> None: + def batch_count(self) -> None: """ - | Called one at the start of each batch. - | Allows the user to run some pre-batch computations. + Increment the batch counter. """ - if not self.debug: - stdout.write("\033[K") - self.progress_counter += 1 - id_epoch, nb_epoch = self.digits[0].format(self.id_epoch + 1), self.digits[0].format(self.nb_epochs) - id_batch, nb_batch = self.digits[1].format(self.id_batch + 1), self.digits[1].format(self.nb_batches) - self.progress_bar.title = f'Epoch n°{id_epoch}/{nb_epoch} - Batch n°{id_batch}/{nb_batch} ' - self.progress_bar.print(counts=self.progress_counter) + self.batch_id += 1 def batch_end(self) -> None: """ - | Called one at the start of each batch. - | Allows the user to run some post-batch computations. + Called one at the end of a batch production. """ self.manager.stats_manager.add_train_batch_loss(self.loss_dict['loss'], - self.id_epoch * self.nb_batches + self.id_batch) + self.epoch_id * self.batch_nb + self.batch_id) for key in self.loss_dict.keys(): if key != 'loss': self.manager.stats_manager.add_custom_scalar(tag=key, value=self.loss_dict[key], - count=self.id_epoch * self.nb_batches + self.id_batch) + count=self.epoch_id * self.batch_nb + self.batch_id) - def batch_condition(self) -> bool: + def epoch_count(self) -> None: """ - | Condition that characterize the end of the epoch. - - :return: False if the epoch needs to stop. + Increment the epoch counter. """ - return self.id_batch < self.nb_batches + self.epoch_id += 1 - def batch_count(self): + def epoch_end(self) -> None: """ - | Allows user for custom update of batches count. - - :return: + Called one at the end of each epoch. """ - self.id_batch += 1 + self.manager.stats_manager.add_train_epoch_loss(self.loss_dict['loss'], self.epoch_id) - def __str__(self) -> str: + def save_network(self) -> None: + """ + Store the network parameters in the corresponding directory. + """ + + self.manager.save_network() + + def train_end(self) -> None: """ - :return: str Contains training information about the training process + Called once at the end of the training pipeline. """ + self.manager.close() + + def save_info_file(self, + directory: str) -> None: + """ + Save a .txt file that provides a template for user notes and the description of all the components. + """ + + filename = join(directory, 'info.txt') + date_time = datetime.now().strftime('%d/%m/%Y %H:%M:%S') + if not isfile(filename): + f = open(filename, "w+") + # Session description template for user + f.write("## DeepPhysX Training Session ##\n") + f.write(date_time + "\n\n") + f.write("Personal notes on the training session:\nNetwork Input:\nNetwork Output:\nComments:\n\n") + # Listing every component descriptions + f.write("## List of Components Parameters ##\n") + f.write(str(self)) + f.write(str(self.manager)) + f.close() + + def __str__(self) -> str: + description = "\n" description += f"# {self.__class__.__name__}\n" description += f" Session directory: {self.manager.session}\n" - description += f" Number of epochs: {self.nb_epochs}\n" - description += f" Number of batches per epoch: {self.nb_batches}\n" + description += f" Number of epochs: {self.epoch_nb}\n" + description += f" Number of batches per epoch: {self.batch_nb}\n" description += f" Number of samples per batch: {self.batch_size}\n" - description += f" Number of samples per epoch: {self.nb_batches * self.batch_size}\n" - description += f" Total: Number of batches : {self.nb_batches * self.nb_epochs}\n" + description += f" Number of samples per epoch: {self.batch_nb * self.batch_size}\n" + description += f" Total: Number of batches : {self.batch_nb * self.epoch_nb}\n" description += f" Number of samples : {self.nb_samples}\n" return description From be654f46067b3689a39a4d0b7ad77c02d38662b8 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 19 Oct 2022 11:39:03 +0200 Subject: [PATCH 06/61] Dispatch batches of data in remote mode. --- src/AsyncSocket/AbstractEnvironment.py | 21 ++++++++---- src/AsyncSocket/TcpIpClient.py | 32 ++++++------------ src/AsyncSocket/TcpIpServer.py | 46 ++++++-------------------- src/Environment/BaseEnvironment.py | 26 +++++++++------ src/Manager/DataManager.py | 5 ++- 5 files changed, 52 insertions(+), 78 deletions(-) diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 466c1784..f2c9687f 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -1,5 +1,5 @@ from typing import Optional, Dict, Any -from numpy import array, ndarray +from numpy import ndarray from SSD.Core.Storage.Database import Database @@ -30,16 +30,15 @@ def __init__(self, self.number_of_instances: int = number_of_instances # Training data variables - self.input: ndarray = array([]) - self.output: ndarray = array([]) - self.loss_data: Any = None + self.__training_data: Dict[str, ndarray] = {} + self.__additional_data: Dict[str, ndarray] = {} self.compute_training_data: bool = True # Dataset data variables self.database: Optional[Database] = None - self.sample_in: Optional[ndarray] = None - self.sample_out: Optional[ndarray] = None - self.additional_fields: Dict[str, Any] = {} + self.update_line: Optional[int] = None + self.sample_training: Optional[Dict[str, Any]] = None + self.sample_additional: Optional[Dict[str, Any]] = None def create(self) -> None: """ @@ -136,3 +135,11 @@ def _send_training_data(self) -> None: def _reset_training_data(self) -> None: raise NotImplementedError + + def _update_training_data(self, + line_id: int) -> None: + raise NotImplementedError + + def _get_training_data(self, + line: int) -> None: + raise NotImplementedError diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 3e9de830..a082d564 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -251,20 +251,8 @@ async def action_on_sample(self, :param sender: TcpIpObject sender. """ - # Receive input sample - if await self.receive_data(loop=loop, sender=sender): - self.sample_in = await self.receive_data(loop=loop, sender=sender) - # Receive output sample - if await self.receive_data(loop=loop, sender=sender): - self.sample_out = await self.receive_data(loop=loop, sender=sender) - - additional_fields = {} - # Receive additional input sample if there are any - if await self.receive_data(loop=loop, sender=sender): - await self.receive_dict(recv_to=additional_fields, loop=loop, sender=sender) - - # Set the samples from Dataset - self.additional_fields = additional_fields.get('additional_fields', {}) + dataset_batch = await self.receive_data(loop=loop, sender=sender) + self._get_training_data(dataset_batch) async def action_on_step(self, data: ndarray, @@ -287,15 +275,17 @@ async def action_on_step(self, await self.step() # If produced sample is not usable, run again - if self.sample_in is None and self.sample_out is None: - while not self.check_sample(): - for step in range(self.simulations_per_step): - # Compute data only on final step - self.compute_training_data = step == self.simulations_per_step - 1 - await self.step() + while not self.check_sample(): + for step in range(self.simulations_per_step): + # Compute data only on final step + self.compute_training_data = step == self.simulations_per_step - 1 + await self.step() # Sent training data to Server - self._send_training_data() + if self.update_line is None: + self._send_training_data() + else: + self._update_training_data(self.update_line) self._reset_training_data() await self.send_command_done() diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index c8cb1cef..ce9ebcd8 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -3,7 +3,6 @@ from asyncio import AbstractEventLoop as EventLoop from asyncio import run as async_run from socket import socket -from numpy import ndarray from queue import SimpleQueue from DeepPhysX.Core.AsyncSocket.TcpIpObject import TcpIpObject @@ -49,7 +48,7 @@ def __init__(self, self.data_fifo: SimpleQueue = SimpleQueue() self.data_dict: Dict[Any, Any] = {} self.sample_to_client_id: List[int] = [] - self.batch_from_dataset: Optional[Dict[str, ndarray]] = None + self.batch_from_dataset: Optional[List[int]] = None self.first_time: bool = True # Reference to EnvironmentManager @@ -181,40 +180,15 @@ async def __communicate(self, loop = get_event_loop() - # # 1) If a sample from Dataset is given, sent it to the TcpIpClient - # if self.batch_from_dataset is not None: - # # Check if there is remaining samples, otherwise client is not used - # if len(self.batch_from_dataset['input']) == 0 or len(self.batch_from_dataset['output']) == 0: - # return - # # Send the sample to the TcpIpClient - # await self.send_command_sample(loop=loop, receiver=client) - # # Pop the first sample of the numpy batch for network in / out - # for field in ['input', 'output']: - # # Tell if there is something to read - # await self.send_data(data_to_send=field in self.batch_from_dataset, loop=loop, receiver=client) - # if field in self.batch_from_dataset: - # # Pop sample from array if there are some - # sample = self.batch_from_dataset[field][0] - # self.batch_from_dataset[field] = self.batch_from_dataset[field][1:] - # # Keep the sample in memory - # self.data_dict[client_id][field] = sample - # # Send network in / out sample - # await self.send_data(data_to_send=sample, loop=loop, receiver=client) - # # Pop the first sample of the numpy batch for each additional dataset field - # field = 'additional_fields' - # # Tell TcpClient if there is additional data for this field - # await self.send_data(data_to_send=field in self.batch_from_dataset, loop=loop, receiver=client) - # if field in self.batch_from_dataset: - # sample = {} - # # Get each additional data field - # for key in self.batch_from_dataset[field]: - # # Pop sample from array - # sample[key] = self.batch_from_dataset[field][key][0] - # self.batch_from_dataset[field][key] = self.batch_from_dataset[field][key][1:] - # # Keep the sample in memory - # self.data_dict[client_id][field + '_' + key] = sample[key] - # # Send additional in / out sample - # await self.send_dict(name="additional_fields", dict_to_send=sample, loop=loop, receiver=client) + # 1. Send a sample to the Client if a batch from the Dataset is given + if self.batch_from_dataset is not None: + # Check if there is remaining samples, otherwise the Client is not used + if len(self.batch_from_dataset) == 0: + return + # Send the sample to the Client + await self.send_command_sample(loop=loop, receiver=client) + line = int(self.batch_from_dataset.pop(0)) + await self.send_data(data_to_send=line, loop=loop, receiver=client) # 2. Execute n steps, the last one send data computation signal if animate: diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 55473e71..498d161d 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -37,16 +37,18 @@ def __init__(self, ip_address=ip_address, port=port) - # Input and output to give to the network + # Training data variables self.__training_data: Dict[str, ndarray] = {} self.__additional_data: Dict[str, ndarray] = {} - self.__first_add: List[bool] = [True, True] + self.compute_training_data: bool = True - # Variables to store samples from Dataset + # Dataset data variables + self.database: Optional[Database] = None + self.update_line: Optional[int] = None self.sample_training: Optional[Dict[str, Any]] = None self.sample_additional: Optional[Dict[str, Any]] = None - # Loss data - self.loss_data: Any = None + self.__first_add: List[bool] = [True, True] + # Manager if the Environment is not a TcpIpClient self.environment_manager: Any = environment_manager @@ -216,12 +218,6 @@ def set_additional_data(self, self.__additional_data = kwargs self.__additional_data['env_id'] = self.instance_id - def _reset_training_data(self) -> None: - self.__training_data = {} - self.__additional_data = {} - self.sample_training = None - self.sample_additional = None - def _send_training_data(self) -> None: line_id = self.database.add_data(table_name='Training', data=self.__training_data) @@ -230,6 +226,13 @@ def _send_training_data(self) -> None: self.database.add_data(table_name='Sync', data={'env': line_id}) + def _reset_training_data(self) -> None: + self.__training_data = {} + self.__additional_data = {} + self.sample_training = None + self.sample_additional = None + self.update_line = None + def _update_training_data(self, line_id: int) -> None: self.database.update(table_name='Training', @@ -244,6 +247,7 @@ def _update_training_data(self, def _get_training_data(self, line: int) -> None: + self.update_line = line self.sample_training = self.database.get_line(table_name='Training', line_id=line) self.sample_additional = self.database.get_line(table_name='Additional', diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index ec4c188e..a7b60bb0 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -114,11 +114,10 @@ def get_data(self, else: self.data_lines = self.dataset_manager.get_data(batch_size=self.batch_size) # Dispatch a batch to clients - # TODO - if self.environment_manager is not None and self.environment_manager.use_dataset_in_environment: + if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data)\ + and self.environment_manager.use_dataset_in_environment: self.environment_manager.dispatch_batch(data_lines=self.data_lines, animate=animate) - # Environment is no longer used elif self.environment_manager is not None: self.environment_manager.close() From 63349fac0e4ed76b119a04f8938c96b86a6f12c7 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 19 Oct 2022 15:52:52 +0200 Subject: [PATCH 07/61] Use Database for online training in local mode. --- src/Environment/BaseEnvironment.py | 3 +- src/Manager/DataManager.py | 2 +- src/Manager/DatabaseManager.py | 73 ++++++++++++++++++++++++++---- src/Manager/EnvironmentManager.py | 9 ++-- src/Pipelines/BaseTrainer.py | 2 +- 5 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 498d161d..b46c4130 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -218,13 +218,14 @@ def set_additional_data(self, self.__additional_data = kwargs self.__additional_data['env_id'] = self.instance_id - def _send_training_data(self) -> None: + def _send_training_data(self) -> int: line_id = self.database.add_data(table_name='Training', data=self.__training_data) self.database.add_data(table_name='Additional', data=self.__additional_data) self.database.add_data(table_name='Sync', data={'env': line_id}) + return line_id def _reset_training_data(self) -> None: self.__training_data = {} diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index a7b60bb0..1b27a468 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -108,7 +108,7 @@ def get_data(self, if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data)\ and self.produce_data: self.data_lines = self.environment_manager.get_data(animate=animate) - self.dataset_manager.add_data() + self.dataset_manager.add_data(self.data_lines) # Get data from Dataset else: diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 949fae10..977447dd 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -46,6 +46,7 @@ def __init__(self, self.shuffle: bool = dataset_config.shuffle self.produce_data = produce_data self.normalize: bool = dataset_config.normalize + self.total_nb_sample: int = 0 self.recompute_normalization: bool = dataset_config.recompute_normalization # Dataset modes @@ -58,6 +59,7 @@ def __init__(self, self.partition_template: Dict[str, str] = {mode: f'{session_name}_{mode}_' + '{}' for mode in self.modes} self.partitions: Dict[str, List[str]] = {mode: [] for mode in self.modes} self.partition_index: Dict[str, int] = {mode: 0 for mode in self.modes} + self.current_partition: str = '' # Dataset indexing self.shuffle_pattern: ndarray = arange(0) @@ -112,15 +114,15 @@ def create_partition(self): """ - partition_path = self.partition_template[self.mode].format(self.partition_index[self.mode]) + self.current_partition = self.partition_template[self.mode].format(self.partition_index[self.mode]) self.database = Database(database_dir=self.dataset_dir, - database_name=partition_path).new() + database_name=self.current_partition).new() self.database.create_table(table_name='Sync', storing_table=False, fields=[('env', int), ('net', int)]) self.database.create_table(table_name='Training') self.database.create_table(table_name='Additional') - self.partitions[self.mode].append(partition_path) + self.partitions[self.mode].append(self.current_partition) self.partition_index[self.mode] += 1 def additional_partition(self): @@ -176,17 +178,21 @@ def load_directory(self): self.update_json(update_normalization=True) # 5. Load the Database - partition_path = self.partitions[self.mode][self.partition_index[self.mode]] + self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] self.database = Database(database_dir=self.dataset_dir, - database_name=partition_path).load() + database_name=self.current_partition).load() + + # 6. Shuffle Database indices + if self.shuffle: + self.shuffle_samples() def load_next_partition(self): # 1. Define next partition self.partition_index[self.mode] = (self.partition_index[self.mode] + 1) % len(self.partitions[self.mode]) - partition_path = self.partitions[self.mode][self.partition_index[self.mode]] + self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] self.database = Database(database_dir=self.dataset_dir, - database_name=partition_path).load() + database_name=self.current_partition).load() # 2. Shuffle if self.shuffle: @@ -272,7 +278,8 @@ def shuffle_samples(self): if self.shuffle: shuffle(self.shuffle_pattern) - def add_data(self): + def add_data(self, + data_lines: Optional[List[int]] = None): """ """ @@ -282,6 +289,9 @@ def add_data(self): if self.first_add: self.update_json(update_partitions_lists=True, update_shapes=True, update_architecture=True) self.first_add = False + if self.normalize and self.mode == 'training' and self.pipeline == 'training' and data_lines is not None: + self.json_content['normalization'] = self.update_normalization(data_lines=data_lines) + self.update_json() # 2. Check the size of the partition if self.max_file_size is not None: @@ -289,6 +299,9 @@ def add_data(self): self.additional_partition() self.update_json(update_partitions_lists=True, update_nb_samples=True) + # 3. Update sample counter + self.current_sample = self.nb_samples + 1 + def get_data(self, batch_size: int) -> List[int]: """ @@ -306,7 +319,7 @@ def get_data(self, # 3. Get a batch of data if self.shuffle: - return self.shuffle_pattern[idx:self.current_sample] + return list(self.shuffle_pattern[idx:self.current_sample]) return list(arange(idx, self.current_sample)) def close(self): @@ -314,7 +327,7 @@ def close(self): """ - if self.normalize and self.produce_data: + if self.normalize and self.pipeline == 'data_generation': self.update_json(update_normalization=True) self.database.close() @@ -367,6 +380,46 @@ def compute_normalization(self) -> Dict[str, List[float]]: return normalization + def update_normalization(self, + data_lines: List[int]) -> Dict[str, List[float]]: + + previous_normalization = self.normalization + previous_nb_samples = self.total_nb_sample + self.total_nb_sample += len(data_lines) + + # First update + if previous_normalization is None: + return self.compute_normalization() + new_normalization = previous_normalization.copy() + + # Compute the mean for each field + fields = list(previous_normalization.keys()) + data_to_normalize = self.database.get_lines(table_name='Training', + fields=fields, + lines_id=data_lines, + batched=True) + for field in fields: + data = array(data_to_normalize[field]) + m = (previous_nb_samples / self.total_nb_sample) * previous_normalization[field][0] + \ + (len(data_lines) / self.total_nb_sample) * data.mean() + new_normalization[field][0] = m + + # Compute standard deviation for each field + stds = {field: [] for field in fields} + nb_samples = [] + for partition in self.partitions['training']: + data_to_normalize = self.load_partitions_fields(partition=partition, + fields=fields) + nb_samples.append(data_to_normalize['id'][-1]) + for field in fields: + data = array(data_to_normalize[field]) + stds[field].append(mean(abs(data - new_normalization[field][0]) ** 2)) + for field in fields: + new_normalization[field][1] = sqrt(sum([(n / sum(nb_samples)) * std + for n, std in zip(nb_samples, stds[field])])) + + return new_normalization + @property def normalization(self) -> Dict[str, List[float]]: return None if self.json_content['normalization'] == {} else self.json_content['normalization'] diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 8fd65259..d8a3ccf2 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -101,6 +101,7 @@ def get_data_from_environment(self, # 1. Produce batch while batch size is not complete nb_sample = 0 + dataset_lines = [] while nb_sample < self.batch_size: # 1.1 Send a sample if a batch from dataset is given @@ -120,13 +121,14 @@ def get_data_from_environment(self, if self.environment.check_sample(): nb_sample += 1 if update_line is None: - self.environment._send_training_data() + new_line = self.environment._send_training_data() + dataset_lines.append(new_line) else: self.environment._update_training_data(update_line) + dataset_lines.append(update_line) self.environment._reset_training_data() - # TODO: return the indices of samples - return [] + return dataset_lines def dispatch_batch_to_server(self, data_lines: List[int], @@ -194,6 +196,5 @@ def __str__(self) -> str: # description += f" Record wrong samples: {self.record_wrong_samples}\n" description += f" Number of threads: {self.number_of_thread}\n" # description += f" Managed objects: Environment: {self.environment.env_name}\n" - # Todo: manage the print log of each Environment since they can have different parameters # description += str(self.environment) return description diff --git a/src/Pipelines/BaseTrainer.py b/src/Pipelines/BaseTrainer.py index 124b1c77..15cf6041 100644 --- a/src/Pipelines/BaseTrainer.py +++ b/src/Pipelines/BaseTrainer.py @@ -60,7 +60,7 @@ def __init__(self, self.debug = debug_session # Configure 'produce_data' flag - if environment_config is not None and dataset_config.existing_dir is None: + if environment_config is None and dataset_config.existing_dir is None: raise ValueError(f"[{self.name}] No data source provided.") produce_data = dataset_config.existing_dir is None From 84fc59a8753f67f2ee7e139d4ff25d8b864e1fd9 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 19 Oct 2022 18:16:46 +0200 Subject: [PATCH 08/61] Use Database for online training in remote mode. Request prediction with Database in local mode. --- src/AsyncSocket/TcpIpClient.py | 4 +- src/AsyncSocket/TcpIpServer.py | 7 ++- src/Environment/BaseEnvironment.py | 45 +++++++++++++------ src/Environment/BaseEnvironmentConfig.py | 2 +- src/Manager/DataManager.py | 18 +++++--- src/Manager/DatabaseManager.py | 3 +- src/Manager/EnvironmentManager.py | 1 - src/Manager/Manager.py | 1 + src/Manager/NetworkManager.py | 55 +++++++++++++++++++----- 9 files changed, 102 insertions(+), 34 deletions(-) diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index a082d564..a63bd8c9 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -283,11 +283,13 @@ async def action_on_step(self, # Sent training data to Server if self.update_line is None: - self._send_training_data() + line = self._send_training_data() else: self._update_training_data(self.update_line) + line = self.update_line self._reset_training_data() await self.send_command_done() + await self.send_data(data_to_send=line, loop=loop, receiver=sender) async def action_on_change_db(self, data: Dict[Any, Any], diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index ce9ebcd8..1b392fd3 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -50,6 +50,7 @@ def __init__(self, self.sample_to_client_id: List[int] = [] self.batch_from_dataset: Optional[List[int]] = None self.first_time: bool = True + self.data_lines: List[int] = [] # Reference to EnvironmentManager self.environment_manager: Optional[Any] = manager @@ -135,7 +136,7 @@ async def __initialize(self, param_dict: Dict[Any, Any]) -> None: ########################################################################################## def get_batch(self, - animate: bool = True) -> None: + animate: bool = True) -> List[int]: """ Build a batch from clients samples. @@ -144,6 +145,7 @@ def get_batch(self, # Trigger communication protocol async_run(self.__request_data_to_clients(animate=animate)) + return self.data_lines async def __request_data_to_clients(self, animate: bool = True) -> None: @@ -155,6 +157,7 @@ async def __request_data_to_clients(self, """ nb_sample = 0 + self.data_lines = [] # Launch the communication protocol while the batch needs to be filled while nb_sample < self.batch_size: # Run communicate protocol for each client and wait for the last one to finish @@ -196,6 +199,8 @@ async def __communicate(self, # Receive data await self.listen_while_not_done(loop=loop, sender=client, data_dict=self.data_dict, client_id=client_id) + line = await self.receive_data(loop=loop, sender=client) + self.data_lines.append(line) def set_dataset_batch(self, data_lines: List[int]) -> None: diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index b46c4130..dfe42ef6 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Dict, Union, Tuple, List +from typing import Any, Optional, Dict, Union, Tuple, List, Type from numpy import ndarray from SSD.Core.Storage.Database import Database @@ -63,6 +63,8 @@ def __init__(self, if self.instance_id == 0: self.database.create_fields(table_name='Training', fields=('env_id', int)) + self.database.create_fields(table_name='Additional', + fields=('env_id', int)) # Connect the Factory to the visualization Database self.factory: Optional[VedoFactory] = None @@ -185,6 +187,14 @@ def close(self) -> None: ########################################################################################## ########################################################################################## + def define_training_fields(self, + fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + self.database.create_fields(table_name='Training', fields=fields) + + def define_additional_fields(self, + fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + self.database.create_fields(table_name='Additional', fields=fields) + def set_training_data(self, **kwargs) -> None: """ @@ -200,11 +210,11 @@ def set_training_data(self, for field in kwargs.keys(): if field not in required_fields: raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." - f"Required fields are {required_fields}.{self.instance_id}") + f"Required fields are {required_fields}.") for field in required_fields: if field not in kwargs.keys(): raise ValueError(f"[{self.name}] The field '{field}' was not defined in training data." - f"Required fields are {required_fields}.{self.instance_id}") + f"Required fields are {required_fields}.") # Training data is set if the Environment can compute data if self.compute_training_data: @@ -262,7 +272,7 @@ def _get_training_data(self, ########################################################################################## def get_prediction(self, - input_array: ndarray) -> ndarray: + **kwargs) -> ndarray: """ Request a prediction from Network. @@ -270,6 +280,17 @@ def get_prediction(self, :return: Network prediction. """ + # Check kwargs + if self.__first_add[1]: + if self.instance_id != 0: + self.database.load() + self.__first_add[1] = False + required_fields = list(set(self.database.get_fields(table_name='Prediction')) - {'id'}) + for field in kwargs.keys(): + if field not in required_fields: + raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." + f"Required fields are {required_fields}.") + # If Environment is a TcpIpClient, send request to the Server if self.as_tcp_ip_client: return TcpIpClient.request_get_prediction(self, input_array=input_array) @@ -277,15 +298,15 @@ def get_prediction(self, # Otherwise, check the hierarchy of managers if self.environment_manager.data_manager is None: raise ValueError("Cannot request prediction if DataManager does not exist") - elif self.environment_manager.data_manager.manager is None: - raise ValueError("Cannot request prediction if Manager does not exist") - elif not hasattr(self.environment_manager.data_manager.manager, 'network_manager'): - raise AttributeError("Cannot request prediction if NetworkManager does not exist. If using a data " - "generation pipeline, please disable get_prediction requests.") - elif self.environment_manager.data_manager.manager.network_manager is None: - raise ValueError("Cannot request prediction if NetworkManager does not exist") # Get a prediction - return self.environment_manager.data_manager.get_prediction(network_input=input_array[None, ]) + self.database.update(table_name='Prediction', + data=kwargs, + line_id=self.instance_id) + self.environment_manager.data_manager.get_prediction(self.instance_id) + data_pred = self.database.get_line(table_name='Prediction', + line_id=self.instance_id) + del data_pred['id'] + return data_pred def update_visualisation(self) -> None: """ diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index e7979908..11e5991a 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -129,7 +129,7 @@ def create_server(self, # Create clients client_threads = [] for i in range(self.number_of_thread): - client_thread = Thread(target=self.start_client, args=(i, data_db, visu_db)) + client_thread = Thread(target=self.start_client, args=(i + 1, data_db, visu_db)) client_threads.append(client_thread) for client in client_threads: client.start() diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 1b27a468..fd78d19d 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -84,6 +84,12 @@ def change_database(self) -> None: self.manager.change_database(self.dataset_manager.database) self.environment_manager.change_database(self.dataset_manager.database) + @property + def nb_environment(self): + if self.environment_manager is None: + return None + return 1 if self.environment_manager.server is None else self.environment_manager.number_of_thread + def get_data(self, epoch: int = 0, animate: bool = True) -> None: @@ -147,20 +153,18 @@ def get_data(self, self.dataset_manager.add_data(data) def get_prediction(self, - network_input: ndarray) -> ndarray: + instance_id: int) -> None: """ Get a Network prediction from an input array. Normalization is applied on input and prediction. - :param network_input: Input array of the Network. :return: Network prediction. """ - # Apply normalization - network_input = self.normalize_data(network_input, 'input') # Get a prediction - prediction = self.manager.network_manager.compute_online_prediction(network_input=network_input) - # Unapply normalization on prediction - return self.normalize_data(prediction, 'output', reverse=True) + if self.manager is None: + raise ValueError("Cannot request prediction if Manager (and then NetworkManager) does not exist.") + self.manager.network_manager.compute_online_prediction(instance_id=instance_id, + normalization=self.normalization) def apply_prediction(self, prediction: ndarray) -> None: diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 977447dd..58718802 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -122,6 +122,7 @@ def create_partition(self): fields=[('env', int), ('net', int)]) self.database.create_table(table_name='Training') self.database.create_table(table_name='Additional') + self.database.create_table(table_name='Prediction') self.partitions[self.mode].append(self.current_partition) self.partition_index[self.mode] += 1 @@ -131,7 +132,7 @@ def additional_partition(self): fields = {} types = {'INT': int, 'FLOAT': float, 'STR': str, 'BOOL': bool, 'NUMPY': ndarray} if self.partition_index[self.mode] > 0: - for table_name in ['Training', 'Additional']: + for table_name in ['Training', 'Additional', 'Prediction']: fields[table_name] = [] F = self.database.get_fields(table_name=table_name, only_names=False) diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index d8a3ccf2..bef46176 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -88,7 +88,6 @@ def get_data_from_server(self, """ # Get data from server - # TODO: return the indices of samples return self.server.get_batch(animate) def get_data_from_environment(self, diff --git a/src/Manager/Manager.py b/src/Manager/Manager.py index 4e30cff5..c30ec50a 100644 --- a/src/Manager/Manager.py +++ b/src/Manager/Manager.py @@ -76,6 +76,7 @@ def __init__(self, new_session=new_session, pipeline=pipeline, data_db=database) + self.network_manager.link_clients(self.data_manager.nb_environment) # Create a StatsManager for training only self.debug_session = debug_session diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index 90c2071d..289084f9 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -1,7 +1,7 @@ from typing import Any, Dict, Tuple, Optional, List from os import listdir from os.path import join, isdir, isfile -from numpy import copy, ndarray, array +from numpy import ndarray, array from SSD.Core.Storage.Database import Database @@ -80,6 +80,14 @@ def __init__(self, def change_database(self, database): self.data_db = database + def link_clients(self, + nb_clients: Optional[int] = None): + if nb_clients is not None: + fields = [(field_name, ndarray) for field_name in self.network.net_fields + self.network.pred_fields] + self.data_db.create_fields(table_name='Prediction', fields=fields) + for _ in range(nb_clients): + self.data_db.add_data(table_name='Prediction', data={}) + def load_network(self, which_network: int = -1) -> None: """ @@ -157,10 +165,15 @@ def compute_prediction_and_loss(self, data_pred = self.data_transformation.transform_before_apply(data_pred) for field in data_pred: data_pred[field] = self.network.tensor_to_numpy(data_pred[field]) + if field in normalization.keys(): + data_pred[field] = self.normalize_data(data=data_pred[field], + normalization=normalization[field], + reverse=True) return data_pred, data_loss def compute_online_prediction(self, - network_input: ndarray) -> ndarray: + instance_id: int, + normalization: Optional[Dict[str, List[float]]] = None) -> None: """ Make a prediction with the data passed as argument. @@ -168,16 +181,38 @@ def compute_online_prediction(self, :return: The prediction """ - # Getting data from the data manager - data_in = self.network.transform_from_numpy(copy(network_input), grad=False) + # Get Network data + normalization = {} if normalization is None else normalization + sample = self.data_db.get_line(table_name='Prediction', + fields=self.network.net_fields, + line_id=instance_id) + del sample['id'] + + # Apply normalization and convert to tensor + for field in sample.keys(): + sample[field] = array(sample[field]) + if field in normalization.keys(): + sample[field] = self.normalize_data(data=sample[field], + normalization=normalization[field]) + sample[field] = self.network.numpy_to_tensor(data=sample[field]) # Compute prediction - data_in = self.data_transformation.transform_before_prediction(data_in) - pred = self.network.predict(data_in) - pred, _ = self.data_transformation.transform_before_loss(pred) - pred = self.data_transformation.transform_before_apply(pred) - pred = self.network.transform_to_numpy(pred) - return pred.reshape(-1) + data_net = self.data_transformation.transform_before_prediction(sample) + data_pred = self.network.predict(data_net) + data_pred, _ = self.data_transformation.transform_before_loss(data_pred) + data_pred = self.data_transformation.transform_before_apply(data_pred) + + # Return the prediction + for field in data_pred.keys(): + data_pred[field] = self.network.tensor_to_numpy(data=data_pred[field]) + if field in normalization.keys(): + data_pred[field] = self.normalize_data(data=data_pred[field], + normalization=normalization[field], + reverse=True) + data_pred[field].reshape(-1) + self.data_db.update(table_name='Prediction', + data=data_pred, + line_id=instance_id) @classmethod def normalize_data(cls, From 8b49dc11c123808f459b5ada25154f2875739700 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 19 Oct 2022 18:43:09 +0200 Subject: [PATCH 09/61] Request prediction with Database in remote mode. --- src/AsyncSocket/TcpIpClient.py | 17 +++++++++++++---- src/AsyncSocket/TcpIpServer.py | 19 ++----------------- src/Environment/BaseEnvironment.py | 8 ++++---- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index a63bd8c9..36f35480 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -76,7 +76,7 @@ async def __initialize(self) -> None: # Create the environment self.create() self.init() - if self.instance_id == 0: + if self.instance_id == 1: self.init_database() self.init_visualization() @@ -175,8 +175,8 @@ def send_prediction_data(self, ########################################################################################## ########################################################################################## - def request_get_prediction(self, - input_array: ndarray) -> ndarray: + def get_prediction(self, + **kwargs) -> Dict[str, ndarray]: """ Request a prediction from Network. @@ -184,7 +184,16 @@ def request_get_prediction(self, :return: Prediction of the Network. """ - return self.send_prediction_data(network_input=input_array) + # Get a prediction + self.database.update(table_name='Prediction', + data=kwargs, + line_id=self.instance_id) + self.sync_send_command_prediction() + _ = self.sync_receive_data() + data_pred = self.database.get_line(table_name='Prediction', + line_id=self.instance_id) + del data_pred['id'] + return data_pred def request_update_visualization(self) -> None: """ diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index 1b392fd3..e9cb04eb 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -300,25 +300,10 @@ async def action_on_prediction(self, :param sender: TcpIpObject sender. """ - # Receive network input - label, network_input = await self.receive_labeled_data(loop=loop, sender=sender) - - # Check that manager hierarchy is well-defined if self.environment_manager.data_manager is None: raise ValueError("Cannot request prediction if DataManager does not exist") - elif self.environment_manager.data_manager.manager is None: - raise ValueError("Cannot request prediction if Manager does not exist") - elif not hasattr(self.environment_manager.data_manager.manager, 'network_manager'): - raise AttributeError("Cannot request prediction if NetworkManager does not exist. If using a data " - "generation pipeline, please disable get_prediction requests.") - elif self.environment_manager.data_manager.manager.network_manager is None: - raise ValueError("Cannot request prediction if NetworkManager does not exist") - - # Get the prediction from NetworkPrediction - prediction = self.environment_manager.data_manager.get_prediction(network_input=network_input[None, ]) - # Send back the prediction to the Client - await self.send_labeled_data(data_to_send=prediction, label="prediction", receiver=sender, - send_read_command=False) + self.environment_manager.data_manager.get_prediction(client_id) + await self.send_data(data_to_send=True, receiver=sender) async def action_on_visualisation(self, data: Dict[Any, Any], diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index dfe42ef6..074b9a67 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -60,7 +60,7 @@ def __init__(self, database_name=data_db[1]).load() else: self.database = data_db - if self.instance_id == 0: + if self.instance_id == 1: self.database.create_fields(table_name='Training', fields=('env_id', int)) self.database.create_fields(table_name='Additional', @@ -163,7 +163,7 @@ def check_sample(self) -> bool: return True def apply_prediction(self, - prediction: ndarray) -> None: + prediction: Dict[str, ndarray]) -> None: """ Apply network prediction in environment. Not mandatory. @@ -272,7 +272,7 @@ def _get_training_data(self, ########################################################################################## def get_prediction(self, - **kwargs) -> ndarray: + **kwargs) -> Dict[str, ndarray]: """ Request a prediction from Network. @@ -293,7 +293,7 @@ def get_prediction(self, # If Environment is a TcpIpClient, send request to the Server if self.as_tcp_ip_client: - return TcpIpClient.request_get_prediction(self, input_array=input_array) + return TcpIpClient.get_prediction(self, **kwargs) # Otherwise, check the hierarchy of managers if self.environment_manager.data_manager is None: From 5f4a68941dbe8fcff79a65bd9ac63e5d089dd7d7 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 20 Oct 2022 14:39:04 +0200 Subject: [PATCH 10/61] Dispatch samples from Database in online training. --- src/Environment/BaseEnvironment.py | 9 +++++---- src/Manager/DataManager.py | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 074b9a67..04481b81 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -246,10 +246,11 @@ def _reset_training_data(self) -> None: def _update_training_data(self, line_id: int) -> None: - self.database.update(table_name='Training', - data=self.__training_data, - line_id=line_id) - if self.sample_additional is not None: + if self.__training_data != {}: + self.database.update(table_name='Training', + data=self.__training_data, + line_id=line_id) + if self.__additional_data != {}: self.database.update(table_name='Additional', data=self.__additional_data, line_id=line_id) diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index fd78d19d..248fd441 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -110,7 +110,6 @@ def get_data(self, elif self.pipeline == 'training': # Get data from Environment(s) if used and if the data should be created at this epoch - # TODO if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data)\ and self.produce_data: self.data_lines = self.environment_manager.get_data(animate=animate) @@ -120,8 +119,8 @@ def get_data(self, else: self.data_lines = self.dataset_manager.get_data(batch_size=self.batch_size) # Dispatch a batch to clients - if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data)\ - and self.environment_manager.use_dataset_in_environment: + if self.environment_manager is not None and (epoch == 0 or + self.environment_manager.use_dataset_in_environment): self.environment_manager.dispatch_batch(data_lines=self.data_lines, animate=animate) # Environment is no longer used From df7feb00b2ebde6ec39796f5508db6b5cfdecae8 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 20 Oct 2022 17:08:55 +0200 Subject: [PATCH 11/61] Remove parameters exchange with TCP-IP, replace with a dedicated Database. --- src/AsyncSocket/AbstractEnvironment.py | 98 ++++---------- src/AsyncSocket/TcpIpClient.py | 18 +-- src/AsyncSocket/TcpIpServer.py | 31 +---- ...DatasetConfig.py => BaseDatabaseConfig.py} | 8 +- src/Environment/BaseEnvironment.py | 122 ++++++++--------- src/Environment/BaseEnvironmentConfig.py | 125 ++++++++---------- src/Environment/launcherBaseEnvironment.py | 12 +- src/Manager/DataManager.py | 72 +++++----- src/Manager/DatabaseManager.py | 52 ++++---- src/Manager/EnvironmentManager.py | 20 +-- src/Manager/Manager.py | 10 +- src/Manager/NetworkManager.py | 6 +- ...DataGenerator.py => BaseDataGeneration.py} | 18 +-- src/Pipelines/BasePipeline.py | 24 ++-- .../{BaseRunner.py => BasePrediction.py} | 68 +++++----- .../{BaseTrainer.py => BaseTraining.py} | 18 +-- 16 files changed, 300 insertions(+), 402 deletions(-) rename src/Database/{BaseDatasetConfig.py => BaseDatabaseConfig.py} (95%) rename src/Pipelines/{BaseDataGenerator.py => BaseDataGeneration.py} (86%) rename src/Pipelines/{BaseRunner.py => BasePrediction.py} (65%) rename src/Pipelines/{BaseTrainer.py => BaseTraining.py} (93%) diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index f2c9687f..57495df1 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -40,95 +40,47 @@ def __init__(self, self.sample_training: Optional[Dict[str, Any]] = None self.sample_additional: Optional[Dict[str, Any]] = None - def create(self) -> None: - """ - Create the Environment. Automatically called when Environment is launched. - Must be implemented by user. - """ + ########################################################################################## + ########################################################################################## + # Initializing Environment # + ########################################################################################## + ########################################################################################## + def create(self) -> None: raise NotImplementedError def init(self) -> None: - """ - Initialize the Environment. Automatically called when Environment is launched. - Not mandatory. - """ - pass - def close(self) -> None: - """ - Close the Environment. Automatically called when Environment is shut down. - Not mandatory. - """ + def init_database(self) -> None: + raise NotImplementedError + def init_visualization(self) -> None: pass - async def step(self) -> None: - """ - Compute the number of steps in the Environment specified by simulations_per_step in EnvironmentConfig. - Must be implemented by user. - """ + ########################################################################################## + ########################################################################################## + # Environment behavior # + ########################################################################################## + ########################################################################################## + async def step(self) -> None: raise NotImplementedError def check_sample(self) -> bool: - """ - Check if the current produced sample is usable. - Not mandatory. - - :return: Current sample can be used or not - """ - return True - def recv_parameters(self, - param_dict: Dict[str, Any]) -> None: - """ - Receive simulation parameters set in Config from the Server. Automatically called before create and init. - - :param param_dict: Dictionary of parameters. - """ - + def apply_prediction(self, prediction: Dict[str, ndarray]) -> None: pass - def init_visualization(self) -> None: - """ - Define the visualization objects to send to the Visualizer. Automatically called after create and init. - Not mandatory. - - :return: Initial visualization data dictionary. - """ - + def close(self) -> None: pass - def init_database(self) -> None: - """ - Define the fields of the training dataset. - Required. - """ - - raise NotImplementedError - - def send_parameters(self) -> dict: - """ - Create a dictionary of parameters to send to the manager. Automatically called after create and init. - Not mandatory. - - :return: Dictionary of parameters. - """ - - return {} - - def apply_prediction(self, prediction: ndarray) -> None: - """ - Apply network prediction in environment. - Not mandatory. - - :param prediction: Prediction data. - """ - - pass + ########################################################################################## + ########################################################################################## + # Defining a sample # + ########################################################################################## + ########################################################################################## def _send_training_data(self) -> None: raise NotImplementedError @@ -136,10 +88,8 @@ def _send_training_data(self) -> None: def _reset_training_data(self) -> None: raise NotImplementedError - def _update_training_data(self, - line_id: int) -> None: + def _update_training_data(self, line_id: int) -> None: raise NotImplementedError - def _get_training_data(self, - line: int) -> None: + def _get_training_data(self, line: int) -> None: raise NotImplementedError diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 36f35480..bc7d8fc1 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -53,37 +53,27 @@ def __init__(self, def initialize(self) -> None: """ - Run __initialize method with asyncio. + Receive parameters from the server to create environment. """ async_run(self.__initialize()) async def __initialize(self) -> None: """ - Receive parameters from the server to create environment, send parameters to the server in exchange. + Receive parameters from the server to create environment. """ loop = get_event_loop() + # Receive number of sub-steps self.simulations_per_step = await self.receive_data(loop=loop, sender=self.sock) - # Receive parameters - recv_param_dict = {} - await self.receive_dict(recv_to=recv_param_dict, sender=self.sock, loop=loop) - # Use received parameters - if 'parameters' in recv_param_dict: - self.recv_parameters(recv_param_dict['parameters']) # Create the environment self.create() self.init() - if self.instance_id == 1: - self.init_database() + self.init_database() self.init_visualization() - # Send parameters - param_dict = self.send_parameters() - await self.send_dict(name="parameters", dict_to_send=param_dict, loop=loop, receiver=self.sock) - # Initialization done await self.send_command_done(loop=loop, receiver=self.sock) diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index e9cb04eb..e3d17700 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -63,7 +63,7 @@ def __init__(self, def connect(self) -> None: """ - Run __connect method with asyncio. + Accept connections from clients. """ print(f"[{self.name}] Waiting for clients...") @@ -90,43 +90,26 @@ async def __connect(self) -> None: ########################################################################################## ########################################################################################## - def initialize(self, - param_dict: Dict[Any, Any]) -> Dict[Any, Any]: + def initialize(self) -> None: """ - Run __initialize method with asyncio. Manage parameters exchange. - - :param param_dict: Dictionary of parameters to send to the client's environment. - :return: Dictionary of parameters for each environment to send the manager. + Send parameters to the clients to create their environments. """ print(f"[{self.name}] Initializing clients...") - async_run(self.__initialize(param_dict)) - # Return param dict - param_dict = {} - for client_id in self.data_dict: - if 'parameters' in self.data_dict[client_id]: - param_dict[client_id] = self.data_dict[client_id]['parameters'] - return param_dict + async_run(self.__initialize()) - async def __initialize(self, param_dict: Dict[Any, Any]) -> None: + async def __initialize(self) -> None: """ - Send parameters to the clients to create their environments, receive parameters from clients in exchange. - - :param param_dict: Dictionary of parameters to send to the client's environment. + Send parameters to the clients to create their environments. """ loop = get_event_loop() - # Empty dictionaries for received parameters from clients - self.data_dict = {client_ID: {} for client_ID in range(len(self.clients))} + # Initialisation process for each client for client_id, client in self.clients: # Send number of sub-steps nb_steps = self.environment_manager.simulations_per_step if self.environment_manager else 1 await self.send_data(data_to_send=nb_steps, loop=loop, receiver=client) - # Send parameters to client - await self.send_dict(name="parameters", dict_to_send=param_dict, loop=loop, receiver=client) - # Receive visualization data and parameters - await self.listen_while_not_done(loop=loop, sender=client, data_dict=self.data_dict, client_id=client_id) print(f"[{self.name}] Client n°{client_id} initialisation done") ########################################################################################## diff --git a/src/Database/BaseDatasetConfig.py b/src/Database/BaseDatabaseConfig.py similarity index 95% rename from src/Database/BaseDatasetConfig.py rename to src/Database/BaseDatabaseConfig.py index 23e90c9b..187585b1 100644 --- a/src/Database/BaseDatasetConfig.py +++ b/src/Database/BaseDatabaseConfig.py @@ -2,14 +2,14 @@ from os.path import isdir, sep, join -class BaseDatasetConfig: +class BaseDatabaseConfig: def __init__(self, existing_dir: Optional[str] = None, mode: Optional[str] = None, max_file_size: Optional[float] = None, - shuffle: bool = True, - normalize: bool = True, + shuffle: bool = False, + normalize: bool = False, recompute_normalization: bool = False): """ Configuration class to parameterize the Dataset and its Manager. @@ -51,7 +51,7 @@ def __init__(self, raise TypeError(f"[{self.name}] The given 'recompute_normalization'={recompute_normalization} must be a " f"bool.") - # DatasetManager parameterization + # DatabaseManager parameterization self.existing_dir: Optional[str] = existing_dir self.mode: Optional[str] = mode self.max_file_size: int = max_file_size diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 04481b81..4f311e0e 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -1,5 +1,6 @@ from typing import Any, Optional, Dict, Union, Tuple, List, Type from numpy import ndarray +from os.path import isfile, join from SSD.Core.Storage.Database import Database @@ -16,8 +17,8 @@ def __init__(self, number_of_instances: int = 1, as_tcp_ip_client: bool = True, environment_manager: Optional[Any] = None, - data_db: Optional[Union[Database, Tuple[str, str]]] = None, - visu_db: Optional[Union[Database, Tuple[str, str]]] = None): + training_db: Optional[Union[Database, Tuple[str, str]]] = None, + visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): """ BaseEnvironment is an environment class to compute simulated data for the network and its optimization process. @@ -27,7 +28,7 @@ def __init__(self, :param number_of_instances: Number of simultaneously launched instances. :param as_tcp_ip_client: Environment is owned by a TcpIpClient if True, by an EnvironmentManager if False. :param environment_manager: EnvironmentManager that handles the Environment if 'as_tcp_ip_client' is False. - :param visu_db: The path to the visualization Database or the visualization Database object to connect to. + :param visualization_db: The path to the visualization Database or the visualization Database object to connect to. """ TcpIpClient.__init__(self, @@ -54,12 +55,12 @@ def __init__(self, # Connect the Environment to the data Database self.database: Optional[Database] = None - if data_db is not None: - if type(data_db) == list: - self.database = Database(database_dir=data_db[0], - database_name=data_db[1]).load() + if training_db is not None: + if type(training_db) == list: + self.database = Database(database_dir=training_db[0], + database_name=training_db[1]).load() else: - self.database = data_db + self.database = training_db if self.instance_id == 1: self.database.create_fields(table_name='Training', fields=('env_id', int)) @@ -68,13 +69,13 @@ def __init__(self, # Connect the Factory to the visualization Database self.factory: Optional[VedoFactory] = None - if visu_db is not None: - if type(visu_db) == list: - self.factory = VedoFactory(database_path=visu_db, + if visualization_db is not None: + if type(visualization_db) == list: + self.factory = VedoFactory(database_path=visualization_db, idx_instance=instance_id, remote=True) else: - self.factory = VedoFactory(database=visu_db, + self.factory = VedoFactory(database=visualization_db, idx_instance=instance_id) ########################################################################################## @@ -83,20 +84,9 @@ def __init__(self, ########################################################################################## ########################################################################################## - def recv_parameters(self, - param_dict: Dict[Any, Any]) -> None: - """ - Exploit received parameters before scene creation. - Not mandatory. - - :param param_dict: Dictionary of parameters. - """ - - pass - def create(self) -> None: """ - Create the Environment. + Create the Environment. Automatically called when Environment is launched. Must be implemented by user. """ @@ -104,36 +94,24 @@ def create(self) -> None: def init(self) -> None: """ - Initialize the Environment. + Initialize the Environment. Automatically called when Environment is launched. Not mandatory. """ pass - def send_parameters(self) -> Dict[Any, Any]: - """ - Create a dictionary of parameters to send to the manager. - Not mandatory. - - :return: Dictionary of parameters - """ - - return {} - def init_database(self) -> None: """ - Define the fields of the training dataset. - Required. + Define the fields of the training dataset. Automatically called when Environment is launched. + Must be implemented by user. """ raise NotImplementedError def init_visualization(self) -> None: """ - Define the visualization objects to send to the Visualizer. + Define the visualization objects to send to the Visualizer. Automatically called when Environment is launched. Not mandatory. - - :return: Dictionary of visualization data """ pass @@ -162,8 +140,7 @@ def check_sample(self) -> bool: return True - def apply_prediction(self, - prediction: Dict[str, ndarray]) -> None: + def apply_prediction(self, prediction: Dict[str, ndarray]) -> None: """ Apply network prediction in environment. Not mandatory. @@ -175,7 +152,7 @@ def apply_prediction(self, def close(self) -> None: """ - Close the Environment. + Close the Environment. Automatically called when Environment is shut down. Not mandatory. """ @@ -187,16 +164,48 @@ def close(self) -> None: ########################################################################################## ########################################################################################## - def define_training_fields(self, - fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - self.database.create_fields(table_name='Training', fields=fields) + def save_parameters(self, **kwargs) -> None: + """ + Save a set of parameters in the Database. + """ - def define_additional_fields(self, - fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - self.database.create_fields(table_name='Additional', fields=fields) + database_dir = self.database.get_path()[0] + if isfile(join(database_dir, 'environment_parameters.db')): + database = Database(database_dir=database_dir, + database_name='environment_parameters').load() + else: + database = Database(database_dir=database_dir, + database_name='environment_parameters').new() + fields = [(field, type(value)) for field, value in kwargs.items()] + database.create_table(table_name=f'Environment_{self.instance_id}', fields=fields) + database.add_data(table_name=f'Environment_{self.instance_id}', data=kwargs) + database.close() + + def load_parameters(self) -> Dict[str, Any]: + """ + Load a set of parameters from the Database. + """ - def set_training_data(self, - **kwargs) -> None: + database_dir = self.database.get_path()[0] + if isfile(join(database_dir, 'environment_parameters.db')): + database = Database(database_dir=database_dir, + database_name='environment_parameters').load() + parameters = database.get_line(table_name=f'Environment_{self.instance_id}') + del parameters['id'] + return parameters + return {} + + def define_training_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + self.database.load() + if len(self.database.get_fields(table_name='Training')) == 2: + self.database.create_fields(table_name='Training', fields=fields) + + def define_additional_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + self.database.load() + if len(self.database.get_fields(table_name='Additional')) == 2: + self.database.create_fields(table_name='Additional', fields=fields) + + def set_training_data(self, **kwargs) -> None: """ Set the training data to send to the TcpIpServer or the EnvironmentManager. """ @@ -233,8 +242,6 @@ def _send_training_data(self) -> int: data=self.__training_data) self.database.add_data(table_name='Additional', data=self.__additional_data) - self.database.add_data(table_name='Sync', - data={'env': line_id}) return line_id def _reset_training_data(self) -> None: @@ -254,8 +261,6 @@ def _update_training_data(self, self.database.update(table_name='Additional', data=self.__additional_data, line_id=line_id) - self.database.add_data(table_name='Sync', - data={'env': line_id}) def _get_training_data(self, line: int) -> None: @@ -272,12 +277,10 @@ def _get_training_data(self, ########################################################################################## ########################################################################################## - def get_prediction(self, - **kwargs) -> Dict[str, ndarray]: + def get_prediction(self, **kwargs) -> Dict[str, ndarray]: """ Request a prediction from Network. - :param input_array: Network input. :return: Network prediction. """ @@ -320,9 +323,6 @@ def update_visualisation(self) -> None: self.factory.render() def __str__(self) -> str: - """ - :return: String containing information about the BaseEnvironment object - """ description = "\n" description += f" {self.name}\n" diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index 11e5991a..2e9a0ebe 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, Type +from typing import Any, Optional, Type from os import cpu_count from os.path import join, dirname from threading import Thread @@ -16,45 +16,34 @@ class BaseEnvironmentConfig: def __init__(self, environment_class: Type[BaseEnvironment], - visualizer: Optional[Type[VedoVisualizer]] = None, - simulations_per_step: int = 1, - max_wrong_samples_per_step: int = 10, - always_create_data: bool = False, - record_wrong_samples: bool = False, - screenshot_sample_rate: int = 0, - use_dataset_in_environment: bool = False, - param_dict: Optional[Dict[Any, Any]] = None, as_tcp_ip_client: bool = True, number_of_thread: int = 1, - max_client_connection: int = 1000, - environment_file: Optional[str] = None, ip_address: str = 'localhost', - port: int = 10000): + port: int = 10000, + simulations_per_step: int = 1, + max_wrong_samples_per_step: int = 10, + load_samples: bool = False, + only_first_epoch: bool = True, + visualizer: Optional[Type[VedoVisualizer]] = None, + record_wrong_samples: bool = False): """ BaseEnvironmentConfig is a configuration class to parameterize and create a BaseEnvironment for the EnvironmentManager. :param environment_class: Class from which an instance will be created. - :param visualizer: Class of the Visualizer to use. - :param simulations_per_step: Number of iterations to compute in the Environment at each time step. - :param max_wrong_samples_per_step: Maximum number of wrong samples to produce in a step. - :param always_create_data: If True, data will always be created from environment. If False, data will be - created from the environment during the first epoch and then re-used from the - Dataset. - :param record_wrong_samples: If True, wrong samples are recorded through Visualizer. - :param screenshot_sample_rate: A screenshot of the viewer will be done every x sample. - :param use_dataset_in_environment: If True, the dataset will always be used in the environment. - :param param_dict: Dictionary containing specific environment parameters. :param as_tcp_ip_client: Environment is owned by a TcpIpClient if True, by an EnvironmentManager if False. :param number_of_thread: Number of thread to run. - :param max_client_connection: Maximum number of handled instances. - :param environment_file: Path of the file containing the Environment class. :param ip_address: IP address of the TcpIpObject. :param port: Port number of the TcpIpObject. + :param simulations_per_step: Number of iterations to compute in the Environment at each time step. + :param max_wrong_samples_per_step: Maximum number of wrong samples to produce in a step. + :param load_samples: If True, the dataset will always be used in the environment. + :param only_first_epoch: If True, data will always be created from environment. If False, data will be created + from the environment during the first epoch and then re-used from the Dataset. + :param visualizer: Class of the Visualizer to use. + :param record_wrong_samples: If True, wrong samples are recorded through Visualizer. """ - if param_dict is None: - param_dict = {} self.name: str = self.__class__.__name__ # Check simulations_per_step type and value @@ -69,50 +58,49 @@ def __init__(self, f"{type(max_wrong_samples_per_step)}") if max_wrong_samples_per_step < 1: raise ValueError(f"[{self.name}] Given max_wrong_simulations_per_step value is negative or null") - # Check always_create_data type - if type(always_create_data) != bool: + # Check only_first_epoch type + if type(only_first_epoch) != bool: raise TypeError(f"[{self.name}] Wrong always_create_data type: bool required, get " - f"{type(always_create_data)}") + f"{type(only_first_epoch)}") if type(number_of_thread) != int: raise TypeError(f"[{self.name}] The number_of_thread number must be a positive integer.") if number_of_thread < 0: raise ValueError(f"[{self.name}] The number_of_thread number must be a positive integer.") - # TcpIpClients parameterization + # TcpIpClients variables self.environment_class: Type[BaseEnvironment] = environment_class - self.environment_file: str = environment_file if environment_file is not None else modules[ - self.environment_class.__module__].__file__ - self.param_dict: Optional[Dict[Any, Any]] = param_dict + self.environment_file: str = modules[self.environment_class.__module__].__file__ self.as_tcp_ip_client: bool = as_tcp_ip_client - # EnvironmentManager parameterization - self.received_parameters: Dict[Any, Any] = {} - self.always_create_data: bool = always_create_data - self.record_wrong_samples: bool = record_wrong_samples - self.screenshot_sample_rate: int = screenshot_sample_rate - self.use_dataset_in_environment: bool = use_dataset_in_environment - self.simulations_per_step: int = simulations_per_step - self.max_wrong_samples_per_step: int = max_wrong_samples_per_step - self.visualizer: Optional[Type[VedoVisualizer]] = visualizer - - # TcpIpServer parameterization + # TcpIpServer variables + self.number_of_thread: int = min(max(number_of_thread, 1), cpu_count()) # Assert nb is between 1 and cpu_count self.ip_address: str = ip_address self.port: int = port self.server_is_ready: bool = False - self.number_of_thread: int = min(max(number_of_thread, 1), cpu_count()) # Assert nb is between 1 and cpu_count - self.max_client_connections = max_client_connection + self.max_client_connections: int = 100 + + # EnvironmentManager variables + self.simulations_per_step: int = simulations_per_step + self.max_wrong_samples_per_step: int = max_wrong_samples_per_step + self.load_samples: bool = load_samples + self.only_first_epoch: bool = only_first_epoch + + # Visualizer variables + self.visualizer: Optional[Type[VedoVisualizer]] = visualizer + self.record_wrong_samples: bool = record_wrong_samples def create_server(self, environment_manager: Optional[Any] = None, batch_size: int = 1, - data_db: Optional[str] = None, - visu_db: Optional[str] = None) -> TcpIpServer: + training_db: Optional[str] = None, + visualization_db: Optional[str] = None) -> TcpIpServer: """ Create a TcpIpServer and launch TcpIpClients in subprocesses. :param environment_manager: EnvironmentManager. :param batch_size: Number of sample in a batch. - :param visu_db: The path to the visualization Database to connect to. + :param training_db: Path of the training Database to connect to. + :param visualization_db: Path to the visualization Database to connect to. :return: TcpIpServer object. """ @@ -129,12 +117,12 @@ def create_server(self, # Create clients client_threads = [] for i in range(self.number_of_thread): - client_thread = Thread(target=self.start_client, args=(i + 1, data_db, visu_db)) + client_thread = Thread(target=self.start_client, args=(i + 1, training_db, visualization_db)) client_threads.append(client_thread) for client in client_threads: client.start() - # Return server to manager when ready + # Return server to manager when it is ready while not self.server_is_ready: pass return server @@ -147,52 +135,49 @@ def start_server(self, :param server: TcpIpServer. """ - # Allow clients connections server.connect() - # Send and receive parameters with clients - self.received_parameters = server.initialize(self.param_dict) - # Server is ready - self.server_is_ready: bool = True + server.initialize() + self.server_is_ready = True def start_client(self, idx: int = 1, - data_db: Optional[str] = None, - visu_db: Optional[str] = None) -> None: + training_db: Optional[str] = None, + visualization_db: Optional[str] = None) -> None: """ Run a subprocess to start a TcpIpClient. :param idx: Index of client. - :param visu_db: The path to the visualization Database to connect to. + :param training_db: Path of the training Database to connect to. + :param visualization_db: Path to the visualization Database to connect to. """ script = join(dirname(modules[BaseEnvironment.__module__].__file__), 'launcherBaseEnvironment.py') - # Usage: python3 script.py " run([executable, script, self.environment_file, self.environment_class.__name__, self.ip_address, str(self.port), str(idx), str(self.number_of_thread), - str(data_db), str(visu_db)]) + str(training_db), str(visualization_db)]) def create_environment(self, environment_manager: Any, - data_db: Optional[Database] = None, - visu_db: Optional[Any] = None) -> BaseEnvironment: + training_db: Optional[Database] = None, + visualization_db: Optional[Any] = None) -> BaseEnvironment: """ Create an Environment that will not be a TcpIpObject. :param environment_manager: EnvironmentManager that handles the Environment. - :param visu_db: The visualisation Database to connect to. + :param training_db: Path of the training Database to connect to. + :param visualization_db: Path to the visualization Database to connect to. :return: Environment object. """ # Create instance environment = self.environment_class(environment_manager=environment_manager, as_tcp_ip_client=False, - data_db=data_db, - visu_db=visu_db) + training_db=training_db, + visualization_db=visualization_db) if not isinstance(environment, BaseEnvironment): raise TypeError(f"[{self.name}] The given 'environment_class'={self.environment_class} must be a " f"BaseEnvironment.") # Create & Init Environment - environment.recv_parameters(self.param_dict) environment.create() environment.init() environment.init_database() @@ -200,15 +185,11 @@ def create_environment(self, return environment def __str__(self) -> str: - """ - :return: String containing information about the BaseEnvironmentConfig object - """ - # Todo: fields in Configs are the set in Managers or objects, remove __str__ method description = "\n" description += f"{self.name}\n" description += f" Environment class: {self.environment_class.__name__}\n" description += f" Simulations per step: {self.simulations_per_step}\n" description += f" Max wrong samples per step: {self.max_wrong_samples_per_step}\n" - description += f" Always create data: {self.always_create_data}\n" + description += f" Always create data: {self.only_first_epoch}\n" return description diff --git a/src/Environment/launcherBaseEnvironment.py b/src/Environment/launcherBaseEnvironment.py index 38fa8823..b2ed4a3d 100644 --- a/src/Environment/launcherBaseEnvironment.py +++ b/src/Environment/launcherBaseEnvironment.py @@ -18,10 +18,14 @@ exec(f"from {module_name} import {argv[2]} as Environment") # Create, init and run Tcp-Ip environment - data_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] - visu_db = None if argv[8] == 'None' else [s[1:-1] for s in argv[8][1:-1].split(', ')] - client = Environment(ip_address=argv[3], port=int(argv[4]), instance_id=int(argv[5]), - number_of_instances=int(argv[6]), data_db=data_db, visu_db=visu_db) + training_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] + visualization_db = None if argv[8] == 'None' else [s[1:-1] for s in argv[8][1:-1].split(', ')] + client = Environment(ip_address=argv[3], + port=int(argv[4]), + instance_id=int(argv[5]), + number_of_instances=int(argv[6]), + training_db=training_db, + visualization_db=visualization_db) client.initialize() client.launch() diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 248fd441..3807edff 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -1,16 +1,16 @@ from typing import Any, Optional, Dict, List from numpy import ndarray -from DeepPhysX.Core.Manager.DatabaseManager import DatasetManager, Database +from DeepPhysX.Core.Manager.DatabaseManager import DatabaseManager, Database from DeepPhysX.Core.Manager.EnvironmentManager import EnvironmentManager from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig class DataManager: def __init__(self, - dataset_config: Optional[BaseDatasetConfig] = None, + database_config: Optional[BaseDatabaseConfig] = None, environment_config: Optional[BaseEnvironmentConfig] = None, manager: Optional[Any] = None, session: str = 'sessions/default', @@ -21,10 +21,10 @@ def __init__(self, """ DataManager deals with the generation, storage and loading of training data. - A batch is given with a call to 'get_data' on either the DatasetManager or the EnvironmentManager according to + A batch is given with a call to 'get_data' on either the DatabaseManager or the EnvironmentManager according to the context. - :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param database_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. :param manager: Manager that handle the DataManager :param session: Path to the session directory. @@ -38,28 +38,24 @@ def __init__(self, # Managers variables self.manager: Optional[Any] = manager - self.dataset_manager: Optional[DatasetManager] = None + self.database_manager: Optional[DatabaseManager] = None self.environment_manager: Optional[EnvironmentManager] = None - # Create a DatasetManager if required - create_dataset = pipeline in ['data_generation', 'training'] or produce_data - database = None - if create_dataset: - self.dataset_manager = DatasetManager(dataset_config=dataset_config, - session=session, - data_manager=self, - new_session=new_session, - pipeline=pipeline, - produce_data=produce_data) - database = self.dataset_manager.database + # Create a DatabaseManager + self.database_manager = DatabaseManager(database_config=database_config, + session=session, + data_manager=self, + new_session=new_session, + pipeline=pipeline, + produce_data=produce_data) + training_db = self.database_manager.database # Create an EnvironmentManager if required - create_environment = environment_config is not None - if create_environment: + if environment_config is not None: self.environment_manager = EnvironmentManager(environment_config=environment_config, data_manager=self, session=session, - data_db=database, + training_db=training_db, batch_size=batch_size) # DataManager variables @@ -78,11 +74,11 @@ def get_manager(self) -> Any: return self.manager def get_database(self) -> Database: - return self.dataset_manager.database + return self.database_manager.database def change_database(self) -> None: - self.manager.change_database(self.dataset_manager.database) - self.environment_manager.change_database(self.dataset_manager.database) + self.manager.change_database(self.database_manager.database) + self.environment_manager.change_database(self.database_manager.database) @property def nb_environment(self): @@ -94,7 +90,7 @@ def get_data(self, epoch: int = 0, animate: bool = True) -> None: """ - Fetch data from the EnvironmentManager or the DatasetManager according to the context. + Fetch data from the EnvironmentManager or the DatabaseManager according to the context. :param epoch: Current epoch number. :param animate: Allow EnvironmentManager to generate a new sample. @@ -104,23 +100,23 @@ def get_data(self, # Data generation case if self.pipeline == 'data_generation': self.environment_manager.get_data(animate=animate) - self.dataset_manager.add_data() + self.database_manager.add_data() # Training case elif self.pipeline == 'training': # Get data from Environment(s) if used and if the data should be created at this epoch - if self.environment_manager is not None and (epoch == 0 or self.environment_manager.always_create_data)\ + if self.environment_manager is not None and (epoch == 0 or self.environment_manager.only_first_epoch) \ and self.produce_data: self.data_lines = self.environment_manager.get_data(animate=animate) - self.dataset_manager.add_data(self.data_lines) + self.database_manager.add_data(self.data_lines) # Get data from Dataset else: - self.data_lines = self.dataset_manager.get_data(batch_size=self.batch_size) + self.data_lines = self.database_manager.get_data(batch_size=self.batch_size) # Dispatch a batch to clients if self.environment_manager is not None and (epoch == 0 or - self.environment_manager.use_dataset_in_environment): + self.environment_manager.load_samples): self.environment_manager.dispatch_batch(data_lines=self.data_lines, animate=animate) # Environment is no longer used @@ -131,9 +127,9 @@ def get_data(self, # Prediction pipeline # TODO else: - if self.dataset_manager is not None and not self.dataset_manager.new_dataset(): + if self.database_manager is not None and not self.database_manager.new_dataset(): # Get data from dataset - data = self.dataset_manager.get_data(batch_size=1, get_inputs=True, get_outputs=True) + data = self.database_manager.get_data(batch_size=1, get_inputs=True, get_outputs=True) if self.environment_manager is not None: new_data = self.environment_manager.dispatch_batch(batch=data, animate=animate) else: @@ -148,8 +144,8 @@ def get_data(self, # Get data from environment data = self.environment_manager.get_data(animate=animate, get_inputs=True, get_outputs=True) # Record data - if self.dataset_manager is not None: - self.dataset_manager.add_data(data) + if self.database_manager is not None: + self.database_manager.add_data(data) def get_prediction(self, instance_id: int) -> None: @@ -181,7 +177,7 @@ def apply_prediction(self, @property def normalization(self) -> Dict[str, List[float]]: - return self.dataset_manager.normalization + return self.database_manager.normalization def close(self) -> None: """ @@ -190,8 +186,8 @@ def close(self) -> None: if self.environment_manager is not None: self.environment_manager.close() - if self.dataset_manager is not None: - self.dataset_manager.close() + if self.database_manager is not None: + self.database_manager.close() def __str__(self) -> str: """ @@ -201,6 +197,6 @@ def __str__(self) -> str: data_manager_str = "" if self.environment_manager: data_manager_str += str(self.environment_manager) - if self.dataset_manager: - data_manager_str += str(self.dataset_manager) + if self.database_manager: + data_manager_str += str(self.database_manager) return data_manager_str diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 58718802..687f64eb 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -8,25 +8,25 @@ from SSD.Core.Storage.Database import Database -from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Utils.path import create_dir, copy_dir, get_first_caller from DeepPhysX.Core.Utils.jsonUtils import CustomJSONEncoder -class DatasetManager: +class DatabaseManager: def __init__(self, - dataset_config: Optional[BaseDatasetConfig] = None, + database_config: Optional[BaseDatabaseConfig] = None, session: str = 'sessions/default', data_manager: Optional[Any] = None, new_session: bool = True, pipeline: str = '', produce_data: bool = True): """ - DatasetManager handle all operations with input / output files. Allows saving and read tensors from files. + DatabaseManager handle all operations with input / output files. Allows saving and read tensors from files. - :param dataset_config: Specialisation containing the parameters of the dataset manager - :param data_manager: DataManager that handles the DatasetManager + :param database_config: Specialisation containing the parameters of the dataset manager + :param data_manager: DataManager that handles the DatabaseManager :param new_session: Define the creation of new directories to store data :param is_training: True if the session is done offline :param produce_data: True if this session is a network training @@ -35,24 +35,24 @@ def __init__(self, self.name: str = self.__class__.__name__ # Manager variables - dataset_config = BaseDatasetConfig() if dataset_config is None else dataset_config + database_config = BaseDatabaseConfig() if database_config is None else database_config self.data_manager: Optional[Any] = data_manager self.dataset_dir: str = join(session, 'dataset') self.database: Optional[Database] = None self.pipeline: str = pipeline # Dataset parameters - self.max_file_size: int = dataset_config.max_file_size - self.shuffle: bool = dataset_config.shuffle + self.max_file_size: int = database_config.max_file_size + self.shuffle: bool = database_config.shuffle self.produce_data = produce_data - self.normalize: bool = dataset_config.normalize + self.normalize: bool = database_config.normalize self.total_nb_sample: int = 0 - self.recompute_normalization: bool = dataset_config.recompute_normalization + self.recompute_normalization: bool = database_config.recompute_normalization # Dataset modes self.modes: List[str] = ['training', 'validation', 'running'] self.mode: str = 'training' if produce_data or pipeline == 'training' else 'running' - self.mode = self.mode if dataset_config.mode is None else dataset_config.mode + self.mode = self.mode if database_config.mode is None else database_config.mode # Dataset partitions session_name = session.split(sep)[-1] @@ -81,14 +81,14 @@ def __init__(self, if new_session: # Produce training data in a new session from scratch # --> Create a new '/dataset' directory - if dataset_config.existing_dir is None: + if database_config.existing_dir is None: create_dir(session_dir=session, session_name='dataset') self.create_partition() # Produce training data in a new session from an existing Dataset # --> Copy the 'existing_dir/dataset' directory then load the 'session/dataset' directory else: - copy_dir(src_dir=join(root, dataset_config.existing_dir), + copy_dir(src_dir=join(root, database_config.existing_dir), dest_dir=session, sub_folders='dataset') self.load_directory() @@ -101,7 +101,7 @@ def __init__(self, # Load training data in a new session # --> Link to the 'existing_dir/dataset' directory the load the 'session/dataset' directory if new_session: - symlink(src=join(root, dataset_config.existing_dir, 'dataset'), + symlink(src=join(root, database_config.existing_dir, 'dataset'), dst=join(session, 'dataset')) self.load_directory() # Load training data in an existing session @@ -117,12 +117,8 @@ def create_partition(self): self.current_partition = self.partition_template[self.mode].format(self.partition_index[self.mode]) self.database = Database(database_dir=self.dataset_dir, database_name=self.current_partition).new() - self.database.create_table(table_name='Sync', - storing_table=False, - fields=[('env', int), ('net', int)]) self.database.create_table(table_name='Training') self.database.create_table(table_name='Additional') - self.database.create_table(table_name='Prediction') self.partitions[self.mode].append(self.current_partition) self.partition_index[self.mode] += 1 @@ -132,12 +128,14 @@ def additional_partition(self): fields = {} types = {'INT': int, 'FLOAT': float, 'STR': str, 'BOOL': bool, 'NUMPY': ndarray} if self.partition_index[self.mode] > 0: - for table_name in ['Training', 'Additional', 'Prediction']: + for table_name in self.database.get_tables(): fields[table_name] = [] F = self.database.get_fields(table_name=table_name, only_names=False) for field in [f for f in F if f not in ['id', '_dt_']]: fields[table_name].append((field, types[F[field].field_type])) + if 'Prediction' in fields.keys(): + self.database.remove_table(table_name='Prediction') # 2. Create a new Database self.create_partition() @@ -242,21 +240,23 @@ def update_json(self, # Update DB architecture if update_architecture: architecture = self.database.get_architecture() + if 'Prediction' in architecture.keys(): + del architecture['Prediction'] for fields in architecture.values(): for field in fields.copy(): if field.split(' ')[0] in ['id', '_dt_']: fields.remove(field) - architecture.pop('Sync') self.json_content['architecture'] = architecture # Update data shapes if update_shapes: for table_name, fields in self.json_content['architecture'].items(): - data = self.database.get_line(table_name=table_name) - for field in fields: - if 'NUMPY' in field: - field_name = field.split(' ')[0] - self.json_content['data_shape'][f'{table_name}.{field_name}'] = data[field_name].shape + if self.database.nb_lines(table_name=table_name) > 0: + data = self.database.get_line(table_name=table_name) + for field in fields: + if 'NUMPY' in field: + field_name = field.split(' ')[0] + self.json_content['data_shape'][f'{table_name}.{field_name}'] = data[field_name].shape # Update normalization coefficients if update_normalization: diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index bef46176..f815d68d 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -14,7 +14,7 @@ def __init__(self, environment_config: BaseEnvironmentConfig, data_manager: Optional[Any] = None, session: str = 'sessions/default', - data_db: Optional[Database] = None, + training_db: Optional[Database] = None, batch_size: int = 1): """ Deals with the online generation of data for both training and running of the neural networks. @@ -32,20 +32,20 @@ def __init__(self, # Data producing parameters self.batch_size: int = batch_size - self.always_create_data: bool = environment_config.always_create_data - self.use_dataset_in_environment: bool = environment_config.use_dataset_in_environment + self.only_first_epoch: bool = environment_config.only_first_epoch + self.load_samples: bool = environment_config.load_samples self.simulations_per_step: int = environment_config.simulations_per_step self.max_wrong_samples_per_step: int = environment_config.max_wrong_samples_per_step self.dataset_batch: Optional[List[int]] = None # Create the Visualizer self.visualizer: Optional[VedoVisualizer] = None - visu_db = None + visualization_db = None if environment_config.visualizer is not None: self.visualizer = environment_config.visualizer(database_dir=join(session, 'dataset'), database_name='Visualization', remote=environment_config.as_tcp_ip_client) - visu_db = self.visualizer.get_database() + visualization_db = self.visualizer.get_database() # Create a single Environment or a TcpIpServer self.number_of_thread: int = environment_config.number_of_thread @@ -54,13 +54,13 @@ def __init__(self, if environment_config.as_tcp_ip_client: self.server = environment_config.create_server(environment_manager=self, batch_size=batch_size, - data_db=data_db if data_db is None else data_db.get_path(), - visu_db=visu_db if visu_db is None else visu_db.get_path()) + training_db=training_db if training_db is None else training_db.get_path(), + visualization_db=visualization_db if visualization_db is None else visualization_db.get_path()) self.data_manager.get_database().load() else: self.environment = environment_config.create_environment(environment_manager=self, - data_db=data_db, - visu_db=visu_db) + training_db=training_db, + visualization_db=visualization_db) # Define get_data and dispatch methods self.change_database = self.change_database_in_server if self.server else self.change_database_in_environment @@ -191,7 +191,7 @@ def __str__(self) -> str: description = "\n" description += f"# {self.name}\n" - description += f" Always create data: {self.always_create_data}\n" + description += f" Always create data: {self.only_first_epoch}\n" # description += f" Record wrong samples: {self.record_wrong_samples}\n" description += f" Number of threads: {self.number_of_thread}\n" # description += f" Managed objects: Environment: {self.environment.env_name}\n" diff --git a/src/Manager/Manager.py b/src/Manager/Manager.py index c30ec50a..5ff6fed6 100644 --- a/src/Manager/Manager.py +++ b/src/Manager/Manager.py @@ -6,7 +6,7 @@ from DeepPhysX.Core.Manager.NetworkManager import NetworkManager from DeepPhysX.Core.Manager.StatsManager import StatsManager from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Utils.path import get_first_caller, create_dir @@ -15,12 +15,12 @@ class Manager: def __init__(self, network_config: Optional[BaseNetworkConfig] = None, - dataset_config: Optional[BaseDatasetConfig] = None, + database_config: Optional[BaseDatabaseConfig] = None, environment_config: Optional[BaseEnvironmentConfig] = None, session_dir: str = 'sessions', session_name: str = 'default', - new_session: bool = True, pipeline: str = '', + new_session: bool = True, produce_data: bool = True, batch_size: int = 1, debug_session: bool = False): @@ -29,7 +29,7 @@ def __init__(self, More specific behaviour have to be directly call from the corresponding manager. :param network_config: Specialisation containing the parameters of the network manager. - :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param database_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. :param session_name: Name of the newly created directory if session is not defined. :param session_dir: Name of the directory in which to write all the necessary data. @@ -58,7 +58,7 @@ def __init__(self, raise ValueError(f"[{self.name}] The running session directory {self.session} does not exist.") # Create a DataManager - self.data_manager = DataManager(dataset_config=dataset_config, + self.data_manager = DataManager(database_config=database_config, environment_config=environment_config, manager=self, session=self.session, diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index 289084f9..c9e04322 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -84,7 +84,7 @@ def link_clients(self, nb_clients: Optional[int] = None): if nb_clients is not None: fields = [(field_name, ndarray) for field_name in self.network.net_fields + self.network.pred_fields] - self.data_db.create_fields(table_name='Prediction', fields=fields) + self.data_db.create_table(table_name='Prediction', fields=fields) for _ in range(nb_clients): self.data_db.add_data(table_name='Prediction', data={}) @@ -243,8 +243,8 @@ def save_network(self, last_save: bool = False) -> None: # Final session saving if last_save: - path = self.network_dir + "network" - print(f"[{self.name}] Saving final network at {path}.") + path = join(self.network_dir, 'network') + print(f"[{self.name}] Saving final network at {self.network_dir}.") self.network.save_parameters(path) # Intermediate states saving diff --git a/src/Pipelines/BaseDataGenerator.py b/src/Pipelines/BaseDataGeneration.py similarity index 86% rename from src/Pipelines/BaseDataGenerator.py rename to src/Pipelines/BaseDataGeneration.py index 5d8f3df0..6a0b1dfc 100644 --- a/src/Pipelines/BaseDataGenerator.py +++ b/src/Pipelines/BaseDataGeneration.py @@ -4,25 +4,25 @@ from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline from DeepPhysX.Core.Manager.DataManager import DataManager -from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Utils.progressbar import Progressbar from DeepPhysX.Core.Utils.path import get_first_caller, create_dir -class BaseDataGenerator(BasePipeline): +class BaseDataGeneration(BasePipeline): def __init__(self, environment_config: BaseEnvironmentConfig, - dataset_config: Optional[BaseDatasetConfig] = None, + database_config: Optional[BaseDatabaseConfig] = None, session_dir: str = 'sessions', session_name: str = 'data_generation', batch_nb: int = 0, batch_size: int = 0): """ - BaseDataGenerator implements the main loop that only produces and stores data (no Network training). + BaseDataGeneration implements the main loop that only produces and stores data (no Network training). - :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param database_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. :param session_dir: Relative path to the directory which contains sessions directories. :param session_name: Name of the new the session directory. @@ -31,7 +31,7 @@ def __init__(self, """ BasePipeline.__init__(self, - dataset_config=dataset_config, + database_config=database_config, environment_config=environment_config, session_dir=session_dir, session_name=session_name, @@ -46,8 +46,8 @@ def __init__(self, # Option 2: existing_dir == session_dir/session_name --> new_session = False # Option 3: existing_dir != session_dir/session_name --> new_session = True new_session = True - if dataset_config is not None and dataset_config.existing_dir is not None and \ - join(session_dir, session_name) == join(root, dataset_config.existing_dir): + if database_config is not None and database_config.existing_dir is not None and \ + join(session_dir, session_name) == join(root, database_config.existing_dir): new_session = False # Create a new session if required @@ -61,7 +61,7 @@ def __init__(self, self.progress_bar = Progressbar(start=0, stop=self.batch_id, c='orange', title="Data Generation") # Create a DataManager - self.data_manager = DataManager(dataset_config=dataset_config, + self.data_manager = DataManager(database_config=database_config, environment_config=environment_config, session=join(session_dir, session_name), new_session=new_session, diff --git a/src/Pipelines/BasePipeline.py b/src/Pipelines/BasePipeline.py index bf384b9b..4d935479 100644 --- a/src/Pipelines/BasePipeline.py +++ b/src/Pipelines/BasePipeline.py @@ -1,13 +1,13 @@ from typing import Optional, Any, List, Union from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Manager.Manager import Manager -from DeepPhysX.Core.Manager.NetworkManager import NetworkManager from DeepPhysX.Core.Manager.DataManager import DataManager from DeepPhysX.Core.Manager.StatsManager import StatsManager -from DeepPhysX.Core.Manager.DatasetManager import DatasetManager +from DeepPhysX.Core.Manager.NetworkManager import NetworkManager +from DeepPhysX.Core.Manager.DatabaseManager import DatabaseManager from DeepPhysX.Core.Manager.EnvironmentManager import EnvironmentManager @@ -15,7 +15,7 @@ class BasePipeline: def __init__(self, network_config: Optional[BaseNetworkConfig] = None, - dataset_config: Optional[BaseDatasetConfig] = None, + database_config: Optional[BaseDatabaseConfig] = None, environment_config: Optional[BaseEnvironmentConfig] = None, session_dir: str = 'sessions', session_name: str = 'default', @@ -24,7 +24,7 @@ def __init__(self, Pipelines implement the main loop that defines data flow through components (Environment, Dataset, Network...). :param network_config: Specialisation containing the parameters of the network manager. - :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param database_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. :param session_dir: Name of the directory in which to write all the necessary data. :param session_name: Name of the newly created directory if session is not defined. @@ -36,8 +36,8 @@ def __init__(self, # Check the configurations if network_config is not None and not isinstance(network_config, BaseNetworkConfig): raise TypeError(f"[{self.name}] The Network configuration must be a BaseNetworkConfig object.") - if dataset_config is not None and not isinstance(dataset_config, BaseDatasetConfig): - raise TypeError(f"[{self.name}] The Dataset configuration must be a BaseDatasetConfig object.") + if database_config is not None and not isinstance(database_config, BaseDatabaseConfig): + raise TypeError(f"[{self.name}] The Dataset configuration must be a BaseDatabaseConfig object.") if environment_config is not None and not isinstance(environment_config, BaseEnvironmentConfig): raise TypeError(f"[{self.name}] The Environment configuration must be a BaseEnvironmentConfig object.") @@ -52,7 +52,7 @@ def __init__(self, session_name = pipeline # Configuration variables - self.dataset_config: BaseDatasetConfig = dataset_config + self.database_config: BaseDatabaseConfig = database_config self.network_config: BaseNetworkConfig = network_config self.environment_config: BaseEnvironmentConfig = environment_config @@ -123,14 +123,14 @@ def get_stats_manager(self) -> Optional[StatsManager]: return self.__get_any_manager(manager_names='stats_manager') - def get_dataset_manager(self) -> Optional[DatasetManager]: + def get_dataset_manager(self) -> Optional[DatabaseManager]: """ - Return the DatasetManager associated with the pipeline if it exists. + Return the DatabaseManager associated with the pipeline if it exists. - :return: The DatasetManager associated with the pipeline. + :return: The DatabaseManager associated with the pipeline. """ - return self.__get_any_manager(manager_names=['data_manager', 'dataset_manager']) + return self.__get_any_manager(manager_names=['data_manager', 'database_manager']) def get_environment_manager(self) -> Optional[EnvironmentManager]: """ diff --git a/src/Pipelines/BaseRunner.py b/src/Pipelines/BasePrediction.py similarity index 65% rename from src/Pipelines/BaseRunner.py rename to src/Pipelines/BasePrediction.py index 12771724..f774d949 100644 --- a/src/Pipelines/BaseRunner.py +++ b/src/Pipelines/BasePrediction.py @@ -2,66 +2,60 @@ from numpy import ndarray from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline from DeepPhysX.Core.Manager.Manager import Manager -class BaseRunner(BasePipeline): - """ - | BaseRunner is a pipeline defining the running process of an artificial neural network. - | It provides a highly tunable learning process that can be used with any machine learning library. - - :param BaseNetworkConfig network_config: Specialisation containing the parameters of the network manager - :param BaseEnvironmentConfig environment_config: Specialisation containing the parameters of the environment manager - :param Optional[BaseDatasetConfig] dataset_config: Specialisation containing the parameters of the dataset manager - :param str session_name: Name of the newly created directory if session is not defined - :param Optional[str] session_dir: Name of the directory in which to write all the necessary data - :param int nb_steps: Number of simulation step to play - :param bool record_inputs: Save or not the input in a numpy file - :param bool record_outputs: Save or not the output in a numpy file - """ +class BasePrediction(BasePipeline): def __init__(self, network_config: BaseNetworkConfig, environment_config: Optional[BaseEnvironmentConfig] = None, - dataset_config: Optional[BaseDatasetConfig] = None, - session_name: str = 'default', - session_dir: Optional[str] = None, - nb_steps: int = 0, - record_inputs: bool = False, - record_outputs: bool = False): + database_config: Optional[BaseDatabaseConfig] = None, + session_dir: str = 'session', + session_name: str = 'training', + nb_steps: int = -1, + record: bool = False): + """ + BasePrediction is a pipeline defining the running process of an artificial neural network. + It provides a highly tunable learning process that can be used with any machine learning library. + + :param network_config: Specialisation containing the parameters of the network manager. + :param environment_config: Specialisation containing the parameters of the environment manager. + :param database_config: Specialisation containing the parameters of the dataset manager. + :param session_name: Name of the newly created directory if session is not defined. + :param session_dir: Name of the directory in which to write all the necessary data. + :param nb_steps: Number of simulation step to play. + :param record: Save or not the prediction data. + """ BasePipeline.__init__(self, network_config=network_config, - dataset_config=dataset_config, + database_config=database_config, environment_config=environment_config, - session_name=session_name, session_dir=session_dir, + session_name=session_name, pipeline='prediction') - self.name = self.__class__.__name__ - - if type(nb_steps) != int or nb_steps < 0: - raise TypeError("[BaseRunner] The number of steps must be a positive int") - + # Prediction variables self.nb_samples = nb_steps self.idx_step = 0 - # Tell if data is recording while predicting (output is recorded only if input too) - self.record_data = {'input': False, 'output': False} - if dataset_config is not None: - self.record_data = {'input': record_inputs, 'output': record_outputs and record_inputs} + # Tell if data is recording while predicting + self.record_data = record self.is_environment = environment_config is not None - self.manager = Manager(pipeline=self, - network_config=self.network_config, - dataset_config=dataset_config, + self.manager = Manager(network_config=self.network_config, + database_config=database_config, environment_config=self.environment_config, - session_name=session_name, session_dir=session_dir, - new_session=True) + session_name=session_name, + new_session=False, + pipeline='prediction', + produce_data=environment_config is not None and record, + batch_size=1) def execute(self) -> None: """ diff --git a/src/Pipelines/BaseTrainer.py b/src/Pipelines/BaseTraining.py similarity index 93% rename from src/Pipelines/BaseTrainer.py rename to src/Pipelines/BaseTraining.py index 15cf6041..521bdad4 100644 --- a/src/Pipelines/BaseTrainer.py +++ b/src/Pipelines/BaseTraining.py @@ -6,16 +6,16 @@ from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline from DeepPhysX.Core.Manager.Manager import Manager from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Database.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Utils.progressbar import Progressbar -class BaseTrainer(BasePipeline): +class BaseTraining(BasePipeline): def __init__(self, network_config: BaseNetworkConfig, - dataset_config: BaseDatasetConfig, + database_config: BaseDatabaseConfig, environment_config: Optional[BaseEnvironmentConfig] = None, session_dir: str = 'sessions', session_name: str = 'training', @@ -25,12 +25,12 @@ def __init__(self, new_session: bool = True, debug_session: bool = False): """ - BaseTrainer implements the main loop that defines the training process of an artificial neural network. + BaseTraining implements the main loop that defines the training process of an artificial neural network. Training can be launched with several data sources (from a Dataset, from an Environment, from combined sources). It provides a highly tunable learning process that can be used with any machine learning library. :param network_config: Specialisation containing the parameters of the network manager. - :param dataset_config: Specialisation containing the parameters of the dataset manager. + :param database_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. :param session_dir: Relative path to the directory which contains sessions directories. :param session_name: Name of the new the session directory. @@ -43,7 +43,7 @@ def __init__(self, BasePipeline.__init__(self, network_config=network_config, - dataset_config=dataset_config, + database_config=database_config, environment_config=environment_config, session_dir=session_dir, session_name=session_name, @@ -60,9 +60,9 @@ def __init__(self, self.debug = debug_session # Configure 'produce_data' flag - if environment_config is None and dataset_config.existing_dir is None: + if environment_config is None and database_config.existing_dir is None: raise ValueError(f"[{self.name}] No data source provided.") - produce_data = dataset_config.existing_dir is None + produce_data = database_config.existing_dir is None # Progressbar if not self.debug: @@ -75,7 +75,7 @@ def __init__(self, title=f'Epoch n°{epoch_id}/{epoch_nb} - Batch n°{batch_id}/{batch_nb}') self.manager = Manager(network_config=self.network_config, - dataset_config=self.dataset_config, + database_config=self.database_config, environment_config=self.environment_config, session_dir=session_dir, session_name=session_name, From c91d32337a60f0773fb8781c0285150f3f3f6074 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 20 Oct 2022 17:47:14 +0200 Subject: [PATCH 12/61] Possibility to add data to an existing Dataset. --- src/Database/BaseDatabaseConfig.py | 2 +- src/Manager/DatabaseManager.py | 92 +++++++++++++++++++----------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/Database/BaseDatabaseConfig.py b/src/Database/BaseDatabaseConfig.py index 187585b1..804846a1 100644 --- a/src/Database/BaseDatabaseConfig.py +++ b/src/Database/BaseDatabaseConfig.py @@ -37,7 +37,7 @@ def __init__(self, if mode is not None: if type(mode) != str: raise TypeError(f"[{self.name}] The given 'mode'={mode} must be a str.") - if mode.lower() not in (available_modes := ['training', 'validation', 'running']): + if mode.lower() not in (available_modes := ['training', 'validation', 'prediction']): raise ValueError(f"[{self.name}] The given 'mode'={mode} must be in {available_modes}.") if max_file_size is not None: if type(max_file_size) not in [int, float]: diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 687f64eb..37694bc1 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -40,6 +40,7 @@ def __init__(self, self.dataset_dir: str = join(session, 'dataset') self.database: Optional[Database] = None self.pipeline: str = pipeline + root = get_first_caller() # Dataset parameters self.max_file_size: int = database_config.max_file_size @@ -50,8 +51,8 @@ def __init__(self, self.recompute_normalization: bool = database_config.recompute_normalization # Dataset modes - self.modes: List[str] = ['training', 'validation', 'running'] - self.mode: str = 'training' if produce_data or pipeline == 'training' else 'running' + self.modes: List[str] = ['training', 'validation', 'prediction'] + self.mode: str = 'training' if produce_data or pipeline == 'training' else 'prediction' self.mode = self.mode if database_config.mode is None else database_config.mode # Dataset partitions @@ -74,40 +75,58 @@ def __init__(self, 'normalization': {}} self.json_content: Dict[str, Dict[str, Any]] = self.json_default.copy() - # Produce training data - root = get_first_caller() - if produce_data: - # Produce training data in a new session + # DataGeneration case + if self.pipeline == 'data_generation': + # Generate data in a new session if new_session: - # Produce training data in a new session from scratch - # --> Create a new '/dataset' directory + # Generate data from scratch --> create a new directory if database_config.existing_dir is None: - create_dir(session_dir=session, - session_name='dataset') + create_dir(session_dir=session, session_name='dataset') self.create_partition() - # Produce training data in a new session from an existing Dataset - # --> Copy the 'existing_dir/dataset' directory then load the 'session/dataset' directory + # Complete a Database in a new session else: - copy_dir(src_dir=join(root, database_config.existing_dir), - dest_dir=session, + copy_dir(src_dir=join(root, database_config.existing_dir), dest_dir=session, sub_folders='dataset') - self.load_directory() - # Produce training data in an existing session - # --> Load the 'session/dataset' directory + self.load_directory(last_partition=True) + # Complete a Database in the same session else: - self.load_directory() - # Load training data + self.load_directory(last_partition=True) + + else: - # Load training data in a new session - # --> Link to the 'existing_dir/dataset' directory the load the 'session/dataset' directory - if new_session: - symlink(src=join(root, database_config.existing_dir, 'dataset'), - dst=join(session, 'dataset')) - self.load_directory() - # Load training data in an existing session - # --> Load the 'session/dataset' directory + # Produce training data + if produce_data: + # Produce training data in a new session + if new_session: + # Produce training data in a new session from scratch + # --> Create a new '/dataset' directory + if database_config.existing_dir is None: + create_dir(session_dir=session, + session_name='dataset') + self.create_partition() + # Produce training data in a new session from an existing Dataset + # --> Copy the 'existing_dir/dataset' directory then load the 'session/dataset' directory + else: + copy_dir(src_dir=join(root, database_config.existing_dir), + dest_dir=session, + sub_folders='dataset') + self.load_directory() + # Produce training data in an existing session + # --> Load the 'session/dataset' directory + else: + self.load_directory() + # Load training data else: - self.load_directory() + # Load training data in a new session + # --> Link to the 'existing_dir/dataset' directory the load the 'session/dataset' directory + if new_session: + symlink(src=join(root, database_config.existing_dir, 'dataset'), + dst=join(session, 'dataset')) + self.load_directory() + # Load training data in an existing session + # --> Load the 'session/dataset' directory + else: + self.load_directory() def create_partition(self): """ @@ -120,7 +139,6 @@ def create_partition(self): self.database.create_table(table_name='Training') self.database.create_table(table_name='Additional') self.partitions[self.mode].append(self.current_partition) - self.partition_index[self.mode] += 1 def additional_partition(self): @@ -138,6 +156,7 @@ def additional_partition(self): self.database.remove_table(table_name='Prediction') # 2. Create a new Database + self.partition_index[self.mode] += 1 self.create_partition() # 3. Re-create the Fields if this is not the first partition @@ -149,7 +168,8 @@ def additional_partition(self): # 4. Tell the other components to communicate on a new DB self.data_manager.change_database() - def load_directory(self): + def load_directory(self, + last_partition: bool): """ """ @@ -177,9 +197,13 @@ def load_directory(self): self.update_json(update_normalization=True) # 5. Load the Database - self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] - self.database = Database(database_dir=self.dataset_dir, - database_name=self.current_partition).load() + if len(self.partitions[self.mode]) == 0: + self.create_partition() + else: + self.partition_index[self.mode] = len(self.partitions[self.mode]) - 1 if last_partition else 0 + self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] + self.database = Database(database_dir=self.dataset_dir, + database_name=self.current_partition).load() # 6. Shuffle Database indices if self.shuffle: @@ -232,7 +256,7 @@ def update_json(self, # Update number of samples if update_nb_samples: - if len(self.json_content['nb_samples'][self.mode]) == self.partition_index[self.mode]: + if len(self.json_content['nb_samples'][self.mode]) == self.partition_index[self.mode] + 1: self.json_content['nb_samples'][self.mode][-1] = self.nb_samples else: self.json_content['nb_samples'][self.mode].append(self.nb_samples) From 28b69e219b83561d84a7d2c610c120c31ff13d78 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 20 Oct 2022 17:54:23 +0200 Subject: [PATCH 13/61] Fix DatabaseManager behavior. --- src/Manager/DatabaseManager.py | 43 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 37694bc1..82581c09 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -83,50 +83,45 @@ def __init__(self, if database_config.existing_dir is None: create_dir(session_dir=session, session_name='dataset') self.create_partition() - # Complete a Database in a new session + # Complete a Database in a new session --> copy and load the existing directory else: copy_dir(src_dir=join(root, database_config.existing_dir), dest_dir=session, sub_folders='dataset') self.load_directory(last_partition=True) - # Complete a Database in the same session + # Complete a Database in the same session --> load the directory else: self.load_directory(last_partition=True) - + # Training and prediction cases else: - # Produce training data + + # Generate data if produce_data: - # Produce training data in a new session + # Generate data in a new session if new_session: - # Produce training data in a new session from scratch - # --> Create a new '/dataset' directory + # Generate data from scratch --> create a new directory if database_config.existing_dir is None: - create_dir(session_dir=session, - session_name='dataset') + create_dir(session_dir=session, session_name='dataset') self.create_partition() - # Produce training data in a new session from an existing Dataset - # --> Copy the 'existing_dir/dataset' directory then load the 'session/dataset' directory + # Complete a Database in a new session --> copy and load the existing directory else: - copy_dir(src_dir=join(root, database_config.existing_dir), - dest_dir=session, + copy_dir(src_dir=join(root, database_config.existing_dir), dest_dir=session, sub_folders='dataset') - self.load_directory() - # Produce training data in an existing session - # --> Load the 'session/dataset' directory + self.load_directory(last_partition=False) + # Complete a Database in the same directory --> load the directory else: - self.load_directory() - # Load training data + self.load_directory(last_partition=False) + + # Load data else: - # Load training data in a new session - # --> Link to the 'existing_dir/dataset' directory the load the 'session/dataset' directory + # Load data in a new session --> link and load the existing directory if new_session: symlink(src=join(root, database_config.existing_dir, 'dataset'), dst=join(session, 'dataset')) - self.load_directory() - # Load training data in an existing session - # --> Load the 'session/dataset' directory + self.load_directory(last_partition=False) + # Load data in the same session --> load the directory else: - self.load_directory() + self.load_directory(last_partition=False) def create_partition(self): """ From a87a65cf59ec68ee376c564e3afeb5ca5554d768 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 24 Oct 2022 18:02:40 +0200 Subject: [PATCH 14/61] Fix shuffle in online training. --- src/Manager/DataManager.py | 5 ++--- src/Manager/DatabaseManager.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 3807edff..1bcbcfe4 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -106,7 +106,7 @@ def get_data(self, elif self.pipeline == 'training': # Get data from Environment(s) if used and if the data should be created at this epoch - if self.environment_manager is not None and (epoch == 0 or self.environment_manager.only_first_epoch) \ + if self.environment_manager is not None and (epoch == 0 or not self.environment_manager.only_first_epoch) \ and self.produce_data: self.data_lines = self.environment_manager.get_data(animate=animate) self.database_manager.add_data(self.data_lines) @@ -115,8 +115,7 @@ def get_data(self, else: self.data_lines = self.database_manager.get_data(batch_size=self.batch_size) # Dispatch a batch to clients - if self.environment_manager is not None and (epoch == 0 or - self.environment_manager.load_samples): + if self.environment_manager is not None and (epoch == 0 or self.environment_manager.load_samples): self.environment_manager.dispatch_batch(data_lines=self.data_lines, animate=animate) # Environment is no longer used diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 82581c09..fb46504c 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -294,7 +294,7 @@ def shuffle_samples(self): """ - self.shuffle_pattern = arange(self.nb_samples) + self.shuffle_pattern = arange(1, self.nb_samples + 1) if self.shuffle: shuffle(self.shuffle_pattern) From 5585aefc5fdcc63c432a1a73ea87208fac1462d4 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 24 Oct 2022 19:19:26 +0200 Subject: [PATCH 15/61] Use Database for Prediction pipeline. --- src/Environment/BaseEnvironment.py | 12 +++++ src/Manager/DataManager.py | 46 +++++------------ src/Manager/DatabaseManager.py | 38 ++++++++++---- src/Manager/EnvironmentManager.py | 15 ++++-- src/Pipelines/BasePrediction.py | 80 ++++++++++-------------------- 5 files changed, 92 insertions(+), 99 deletions(-) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 4f311e0e..ab94350d 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -312,6 +312,18 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: del data_pred['id'] return data_pred + def _get_prediction(self): + """ + + """ + + training_data = self.__training_data.copy() + required_fields = self.database.get_fields(table_name='Prediction') + for field in self.__training_data.keys(): + if field not in required_fields: + del training_data[field] + self.apply_prediction(self.get_prediction(**training_data)) + def update_visualisation(self) -> None: """ Triggers the Visualizer update. diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 1bcbcfe4..f1706345 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -88,7 +88,8 @@ def nb_environment(self): def get_data(self, epoch: int = 0, - animate: bool = True) -> None: + animate: bool = True, + request_prediction: bool = False) -> None: """ Fetch data from the EnvironmentManager or the DatabaseManager according to the context. @@ -124,27 +125,20 @@ def get_data(self, self.environment_manager = None # Prediction pipeline - # TODO else: - if self.database_manager is not None and not self.database_manager.new_dataset(): - # Get data from dataset - data = self.database_manager.get_data(batch_size=1, get_inputs=True, get_outputs=True) - if self.environment_manager is not None: - new_data = self.environment_manager.dispatch_batch(batch=data, animate=animate) - else: - new_data = data - if len(new_data['input']) != 0: - data['input'] = new_data['input'] - if len(new_data['output']) != 0: - data['output'] = new_data['output'] - if 'loss' in new_data: - data['loss'] = new_data['loss'] + + # Get data from Dataset + if self.produce_data: + self.data_lines = self.database_manager.get_data(batch_size=1) + self.environment_manager.dispatch_batch(data_lines=self.data_lines, + animate=animate, + request_prediction=True) + # Get data from Environment else: - # Get data from environment - data = self.environment_manager.get_data(animate=animate, get_inputs=True, get_outputs=True) - # Record data + self.data_lines = self.environment_manager.get_data(animate=animate, + request_prediction=True) if self.database_manager is not None: - self.database_manager.add_data(data) + self.database_manager.add_data(self.data_lines) def get_prediction(self, instance_id: int) -> None: @@ -160,20 +154,6 @@ def get_prediction(self, self.manager.network_manager.compute_online_prediction(instance_id=instance_id, normalization=self.normalization) - def apply_prediction(self, - prediction: ndarray) -> None: - """ - Apply the Network prediction in the Environment. - - :param prediction: Prediction of the Network to apply. - """ - - if self.environment_manager is not None: - # Unapply normalization on prediction - prediction = self.normalize_data(prediction, 'output', reverse=True) - # Apply prediction - self.environment_manager.environment.apply_prediction(prediction) - @property def normalization(self) -> Dict[str, List[float]]: return self.database_manager.normalization diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index fb46504c..c059fd5c 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional from os.path import isfile, isdir, join -from os import listdir, symlink, sep +from os import listdir, symlink, sep, remove from json import dump as json_dump from json import load as json_load from numpy import arange, ndarray, array, abs, mean, sqrt @@ -92,8 +92,8 @@ def __init__(self, else: self.load_directory(last_partition=True) - # Training and prediction cases - else: + # Training case + elif self.pipeline == 'training': # Generate data if produce_data: @@ -123,6 +123,16 @@ def __init__(self, else: self.load_directory(last_partition=False) + # Prediction case + else: + + # Generate data + if produce_data: + self.load_directory(last_partition=True) + + else: + self.load_directory(load_partition=False) + def create_partition(self): """ @@ -164,7 +174,8 @@ def additional_partition(self): self.data_manager.change_database() def load_directory(self, - last_partition: bool): + last_partition: bool = True, + load_partition: bool = True): """ """ @@ -192,13 +203,19 @@ def load_directory(self, self.update_json(update_normalization=True) # 5. Load the Database - if len(self.partitions[self.mode]) == 0: - self.create_partition() + if load_partition: + if len(self.partitions[self.mode]) == 0: + self.create_partition() + else: + self.partition_index[self.mode] = len(self.partitions[self.mode]) - 1 if last_partition else 0 + self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] + self.database = Database(database_dir=self.dataset_dir, + database_name=self.current_partition).load() else: - self.partition_index[self.mode] = len(self.partitions[self.mode]) - 1 if last_partition else 0 - self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] self.database = Database(database_dir=self.dataset_dir, - database_name=self.current_partition).load() + database_name='temp').new() + self.database.create_table(table_name='Training') + self.database.create_table(table_name='Additional') # 6. Shuffle Database indices if self.shuffle: @@ -350,6 +367,9 @@ def close(self): if self.normalize and self.pipeline == 'data_generation': self.update_json(update_normalization=True) self.database.close() + if self.pipeline == 'prediction' and not self.produce_data: + path = self.database.get_path() + remove(join(path[0], f'{path[1]}.db')) def change_mode(self, mode: int) -> None: """ diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index f815d68d..a7f4deaf 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -91,7 +91,8 @@ def get_data_from_server(self, return self.server.get_batch(animate) def get_data_from_environment(self, - animate: bool = True) -> List[int]: + animate: bool = True, + request_prediction: bool = False) -> List[int]: """ Compute a batch of data directly from Environment. @@ -125,6 +126,8 @@ def get_data_from_environment(self, else: self.environment._update_training_data(update_line) dataset_lines.append(update_line) + if request_prediction: + self.environment._get_prediction() self.environment._reset_training_data() return dataset_lines @@ -147,7 +150,8 @@ def dispatch_batch_to_server(self, def dispatch_batch_to_environment(self, data_lines: List[int], - animate: bool = True) -> None: + animate: bool = True, + request_prediction: bool = False) -> None: """ Send samples from dataset to the Environments. Get back the training data. @@ -159,7 +163,12 @@ def dispatch_batch_to_environment(self, # Define the batch to dispatch self.dataset_batch = data_lines.copy() # Get data - self.get_data_from_environment(animate=animate) + self.get_data_from_environment(animate=animate, + request_prediction=request_prediction) + + def request_prediction(self): + + self.environment._get_prediction() def update_visualizer(self, instance: int) -> None: diff --git a/src/Pipelines/BasePrediction.py b/src/Pipelines/BasePrediction.py index f774d949..66721cbd 100644 --- a/src/Pipelines/BasePrediction.py +++ b/src/Pipelines/BasePrediction.py @@ -1,5 +1,4 @@ from typing import Optional -from numpy import ndarray from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig @@ -12,7 +11,7 @@ class BasePrediction(BasePipeline): def __init__(self, network_config: BaseNetworkConfig, - environment_config: Optional[BaseEnvironmentConfig] = None, + environment_config: BaseEnvironmentConfig, database_config: Optional[BaseDatabaseConfig] = None, session_dir: str = 'session', session_name: str = 'training', @@ -45,7 +44,6 @@ def __init__(self, # Tell if data is recording while predicting self.record_data = record - self.is_environment = environment_config is not None self.manager = Manager(network_config=self.network_config, database_config=database_config, @@ -54,57 +52,33 @@ def __init__(self, session_name=session_name, new_session=False, pipeline='prediction', - produce_data=environment_config is not None and record, + produce_data=record, batch_size=1) def execute(self) -> None: """ - | Main function of the running process "execute" call the functions associated with the learning process. - | Each of the called functions are already implemented so one can start a basic run session. - | Each of the called function can also be rewritten via inheritance to provide more specific / complex running - process. + Launch the prediction Pipeline. + Each event is already implemented for a basic pipeline but can also be rewritten via inheritance to describe a + more complex pipeline. """ - self.run_begin() - while self.running_condition(): + self.prediction_begin() + while self.prediction_condition(): self.sample_begin() - self.sample_end(self.predict()) + self.predict() + self.sample_end() self.run_end() - def predict(self, animate: bool = True) -> ndarray: + def prediction_begin(self) -> None: """ - | Pull the data from the manager and return the prediction - - :param bool animate: True if getData fetch from the environment - :return: Prediction from the Network - """ - - self.manager.get_data(animate=animate) - data = self.manager.data_manager.data['input'] - data = self.manager.data_manager.normalize_data(data, 'input') - return self.manager.network_manager.compute_online_prediction(data) - - def run_begin(self) -> None: - """ - | Called once at the very beginning of the Run process. - | Allows the user to run some pre-computations. + Called once at the beginning of the prediction pipeline. """ pass - def run_end(self) -> None: - """ - | Called once at the very end of the Run process. - | Allows the user to run some post-computations. + def prediction_condition(self) -> bool: """ - - pass - - def running_condition(self) -> bool: - """ - | Condition that characterize the end of the running process. - - :return: False if the training needs to stop. + Condition that characterize the end of the prediction pipeline. """ running = self.idx_step < self.nb_samples if self.nb_samples > 0 else True @@ -113,39 +87,37 @@ def running_condition(self) -> bool: def sample_begin(self) -> None: """ - | Called one at the start of each step. - | Allows the user to run some pre-step computations. + Called one at the beginning of each sample. """ pass - def sample_end(self, prediction: ndarray) -> None: + def predict(self) -> None: + """ + Pull the data from the manager and return the prediction. """ - | Called one at the end of each step. - | Allows the user to run some post-step computations. - :param ndarray prediction: Prediction of the Network. + self.manager.get_data(animate=True) + + def sample_end(self) -> None: + """ + Called one at the end of each sample. """ - if self.is_environment: - self.manager.data_manager.apply_prediction(prediction) + pass - def close(self) -> None: + def run_end(self) -> None: """ - | End the running process and close all the managers + Called once at the end of the prediction pipeline. """ self.manager.close() def __str__(self) -> str: - """ - :return: str Contains running information about the running process - """ description = "" description += f"Running statistics :\n" description += f"Number of simulation step: {self.nb_samples}\n" - description += f"Record inputs : {self.record_data[0]}\n" - description += f"Record outputs : {self.record_data[1]}\n" + description += f"Record data : {self.record_data}\n" return description From d6df347954cb169d803384a56c4ac26e4e67fe42 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 25 Oct 2022 18:16:13 +0200 Subject: [PATCH 16/61] New definition of partitions. Create DB handlers for Managers. Adapt DataGeneration pipeline. --- src/AsyncSocket/AbstractEnvironment.py | 38 ++-- src/AsyncSocket/BytesConverter.py | 35 ++-- src/AsyncSocket/TcpIpClient.py | 105 +++++------ src/AsyncSocket/TcpIpObject.py | 3 - src/Database/DatabaseHandler.py | 90 ++++++++++ src/Environment/BaseEnvironment.py | 133 +++++++------- src/Environment/BaseEnvironmentConfig.py | 13 +- src/Environment/launcherBaseEnvironment.py | 8 +- src/Manager/DataManager.py | 13 +- src/Manager/DatabaseManager.py | 192 ++++++++++++--------- src/Manager/DatasetManager.py | 2 +- src/Manager/EnvironmentManager.py | 24 ++- src/Pipelines/BaseDataGeneration.py | 26 ++- src/Pipelines/BasePipeline.py | 3 + 14 files changed, 389 insertions(+), 296 deletions(-) create mode 100644 src/Database/DatabaseHandler.py diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 57495df1..c1d9d0ef 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -1,4 +1,4 @@ -from typing import Optional, Dict, Any +from typing import Optional, Dict, Any, Union, Tuple from numpy import ndarray from SSD.Core.Storage.Database import Database @@ -9,33 +9,35 @@ class AbstractEnvironment: def __init__(self, as_tcp_ip_client: bool = True, instance_id: int = 0, - number_of_instances: int = 1): + instance_nb: int = 1, + visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): """ AbstractEnvironment sets the Environment API for TcpIpClient. - Do not use AbstractEnvironment to implement a personal Environment, use BaseEnvironment instead. + Do not use AbstractEnvironment to implement an Environment, use BaseEnvironment instead. - :param instance_id: ID of the instance. - :param number_of_instances: Number of simultaneously launched instances. - :param as_tcp_ip_client: Environment is a TcpIpObject if True, is owned by an EnvironmentManager if False. + :param as_tcp_ip_client: If True, the Environment is a TcpIpObject, else it is owned by an EnvironmentManager. + :param instance_id: Index of this instance. + :param instance_nb: Number of simultaneously launched instances. """ self.name: str = self.__class__.__name__ + f" n°{instance_id}" # TcpIpClient variables - if instance_id > number_of_instances: + if instance_id > instance_nb: raise ValueError(f"[{self.name}] Instance ID ({instance_id}) is bigger than max instances " - f"({number_of_instances}).") + f"({instance_nb}).") self.as_tcp_ip_client: bool = as_tcp_ip_client self.instance_id: int = instance_id - self.number_of_instances: int = number_of_instances + self.instance_nb: int = instance_nb + + # Manager of the Environment + self.environment_manager: Any = None + self.tcp_ip_client: Any = None # Training data variables - self.__training_data: Dict[str, ndarray] = {} - self.__additional_data: Dict[str, ndarray] = {} self.compute_training_data: bool = True # Dataset data variables - self.database: Optional[Database] = None self.update_line: Optional[int] = None self.sample_training: Optional[Dict[str, Any]] = None self.sample_additional: Optional[Dict[str, Any]] = None @@ -50,13 +52,13 @@ def create(self) -> None: raise NotImplementedError def init(self) -> None: - pass + raise NotImplementedError def init_database(self) -> None: raise NotImplementedError def init_visualization(self) -> None: - pass + raise NotImplementedError ########################################################################################## ########################################################################################## @@ -68,13 +70,13 @@ async def step(self) -> None: raise NotImplementedError def check_sample(self) -> bool: - return True + raise NotImplementedError def apply_prediction(self, prediction: Dict[str, ndarray]) -> None: - pass + raise NotImplementedError def close(self) -> None: - pass + raise NotImplementedError ########################################################################################## ########################################################################################## @@ -91,5 +93,5 @@ def _reset_training_data(self) -> None: def _update_training_data(self, line_id: int) -> None: raise NotImplementedError - def _get_training_data(self, line: int) -> None: + def _get_training_data(self, line_id: int) -> None: raise NotImplementedError diff --git a/src/AsyncSocket/BytesConverter.py b/src/AsyncSocket/BytesConverter.py index 4992472c..37f63361 100644 --- a/src/AsyncSocket/BytesConverter.py +++ b/src/AsyncSocket/BytesConverter.py @@ -6,12 +6,12 @@ class BytesConverter: - """ - | Convert usual types to bytes and vice versa. - | Available types: None, bytes, str, bool, int, float, list, ndarray. - """ def __init__(self): + """ + Convert usual types to bytes and vice versa. + Available types: None, bytes, str, bool, int, float, list, ndarray. + """ # Data to bytes conversions self.__data_to_bytes_conversion: Dict[type, Callable[[Convertible], bytes]] = { @@ -42,17 +42,17 @@ def __init__(self): self.size_from_bytes: Callable[[bytes], int] = lambda b: self.__bytes_to_data_conversion[int.__name__](b) self.int_size: int = calcsize("i") - def data_to_bytes(self, data: Convertible, as_list: bool = False) -> Union[bytes, List[bytes]]: + def data_to_bytes(self, + data: Convertible, + as_list: bool = False) -> Union[bytes, List[bytes]]: """ - | Convert data to bytes. - | Available types: None, bytes, str, bool, signed int, float, list, ndarray. + Convert data to bytes. + Available types: None, bytes, str, bool, signed int, float, list, ndarray. :param data: Data to convert. - :type data: Union[type(None), bytes, str, bool, int, float, List, ndarray] - :param bool as_list: (For tests only, False by default) - If False, the whole bytes message is returned. - If True, the return will be a list of bytes fields. - :return: Concatenated bytes fields (Number of fields, Size of fields, Type, Data, Args) + :param as_list: (For tests only, False by default) If False, the whole bytes message is returned. If True, the + return will be a list of bytes fields. + :return: Concatenated bytes fields (Number of fields, Size of fields, Type, Data, Args). """ # Convert the type of 'data' from str to bytes @@ -91,13 +91,14 @@ def data_to_bytes(self, data: Convertible, as_list: bool = False) -> Union[bytes bytes_message += f return bytes_message - def bytes_to_data(self, bytes_fields: List[bytes]) -> Convertible: + def bytes_to_data(self, + bytes_fields: List[bytes]) -> Convertible: """ - | Recover data from bytes fields. - | Available types: None, bytes, str, bool, signed int, float, list, ndarray. + Recover data from bytes fields. + Available types: None, bytes, str, bool, signed int, float, list, ndarray. - :param List[bytes] bytes_fields: Bytes fields (Type, Data, Args) - :return: Converted data + :param bytes_fields: Bytes fields (Type, Data, Args). + :return: Converted data. """ # Recover the data type diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index bc7d8fc1..3c19e8a2 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any, Dict, Type, Optional, Union, Tuple from socket import socket from asyncio import get_event_loop from asyncio import AbstractEventLoop as EventLoop @@ -9,39 +9,41 @@ from DeepPhysX.Core.AsyncSocket.AbstractEnvironment import AbstractEnvironment, Database -class TcpIpClient(TcpIpObject, AbstractEnvironment): +class TcpIpClient(TcpIpObject): def __init__(self, + environment: Type[AbstractEnvironment], ip_address: str = 'localhost', port: int = 10000, - as_tcp_ip_client: bool = True, instance_id: int = 0, - number_of_instances: int = 1,): + instance_nb: int = 1, + training_db: Optional[Union[Database, Tuple[str, str]]] = None, + visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): """ TcpIpClient is both a TcpIpObject which communicate with a TcpIpServer and an AbstractEnvironment to compute simulated data. :param ip_address: IP address of the TcpIpObject. :param port: Port number of the TcpIpObject. - :param as_tcp_ip_client: Environment is a TcpIpObject if True, is owned by an EnvironmentManager if False. - :param instance_id: ID of the instance. - :param number_of_instances: Number of simultaneously launched instances. + :param instance_id: Index of this instance. + :param instance_nb: Number of simultaneously launched instances. """ - AbstractEnvironment.__init__(self, - as_tcp_ip_client=as_tcp_ip_client, - instance_id=instance_id, - number_of_instances=number_of_instances) + TcpIpObject.__init__(self, + ip_address=ip_address, + port=port) + + self.environment = environment(as_tcp_ip_client=True, + instance_id=instance_id, + instance_nb=instance_nb, + visualization_db=visualization_db) + self.environment.tcp_ip_client = self # Bind to client address - if self.as_tcp_ip_client: - TcpIpObject.__init__(self, - ip_address=ip_address, - port=port) - self.sock.connect((ip_address, port)) - # Send ID - self.sync_send_labeled_data(data_to_send=instance_id, label="instance_ID", receiver=self.sock, - send_read_command=False) + self.sock.connect((ip_address, port)) + # Send ID + self.sync_send_labeled_data(data_to_send=instance_id, label="instance_ID", receiver=self.sock, + send_read_command=False) # Flag to trigger client's shutdown self.close_client: bool = False @@ -69,10 +71,10 @@ async def __initialize(self) -> None: self.simulations_per_step = await self.receive_data(loop=loop, sender=self.sock) # Create the environment - self.create() - self.init() - self.init_database() - self.init_visualization() + self.environment.create() + self.environment.init() + self.environment.init_database() + self.environment.init_visualization() # Initialization done await self.send_command_done(loop=loop, receiver=self.sock) @@ -85,7 +87,7 @@ async def __initialize(self) -> None: def launch(self) -> None: """ - Run __launch method with asyncio. + Trigger the main communication protocol with the server. """ async_run(self.__launch()) @@ -124,7 +126,7 @@ async def __close(self) -> None: # Close environment try: - self.close() + self.environment.close() except NotImplementedError: pass # Confirm exit command to the server @@ -133,32 +135,6 @@ async def __close(self) -> None: # Close socket self.sock.close() - ########################################################################################## - ########################################################################################## - # Data sending to Server # - ########################################################################################## - ########################################################################################## - - def send_prediction_data(self, - network_input: ndarray, - receiver: socket = None) -> ndarray: - """ - Request a prediction from the Environment. - - :param network_input: Data to send under the label 'input'. - :param receiver: TcpIpObject receiver. - :return: Prediction of the Network. - """ - - receiver = self.sock if receiver is None else receiver - # Send prediction command - self.sync_send_command_prediction() - # Send the network input - self.sync_send_labeled_data(data_to_send=network_input, label='input', receiver=receiver) - # Receive the network prediction - _, pred = self.sync_receive_labeled_data() - return pred - ########################################################################################## ########################################################################################## # Available requests to Server # @@ -170,18 +146,17 @@ def get_prediction(self, """ Request a prediction from Network. - :param input_array: Network input. :return: Prediction of the Network. """ # Get a prediction self.database.update(table_name='Prediction', data=kwargs, - line_id=self.instance_id) + line_id=self.environment.instance_id) self.sync_send_command_prediction() _ = self.sync_receive_data() data_pred = self.database.get_line(table_name='Prediction', - line_id=self.instance_id) + line_id=self.environment.instance_id) del data_pred['id'] return data_pred @@ -191,7 +166,7 @@ def request_update_visualization(self) -> None: """ self.sync_send_command_visualisation() - self.sync_send_labeled_data(data_to_send=self.instance_id, + self.sync_send_labeled_data(data_to_send=self.environment.instance_id, label='instance') ########################################################################################## @@ -234,7 +209,7 @@ async def action_on_prediction(self, # Receive prediction prediction = await self.receive_data(loop=loop, sender=sender) # Apply the prediction in Environment - self.apply_prediction(prediction) + self.environment.apply_prediction(prediction) async def action_on_sample(self, data: ndarray, @@ -251,7 +226,7 @@ async def action_on_sample(self, """ dataset_batch = await self.receive_data(loop=loop, sender=sender) - self._get_training_data(dataset_batch) + self.environment._get_training_data(dataset_batch) async def action_on_step(self, data: ndarray, @@ -271,22 +246,22 @@ async def action_on_step(self, for step in range(self.simulations_per_step): # Compute data only on final step self.compute_training_data = step == self.simulations_per_step - 1 - await self.step() + await self.environment.step() # If produced sample is not usable, run again - while not self.check_sample(): + while not self.environment.check_sample(): for step in range(self.simulations_per_step): # Compute data only on final step self.compute_training_data = step == self.simulations_per_step - 1 - await self.step() + await self.environment.step() # Sent training data to Server - if self.update_line is None: - line = self._send_training_data() + if self.environment.update_line is None: + line = self.environment._send_training_data() else: - self._update_training_data(self.update_line) - line = self.update_line - self._reset_training_data() + self.environment._update_training_data(self.environment.update_line) + line = self.environment.update_line + self.environment._reset_training_data() await self.send_command_done() await self.send_data(data_to_send=line, loop=loop, receiver=sender) diff --git a/src/AsyncSocket/TcpIpObject.py b/src/AsyncSocket/TcpIpObject.py index aa1994cd..32bf1b7a 100644 --- a/src/AsyncSocket/TcpIpObject.py +++ b/src/AsyncSocket/TcpIpObject.py @@ -51,9 +51,6 @@ def __init__(self, self.command_dict["visualisation"]: self.action_on_visualisation, self.command_dict['db']: self.action_on_change_db } - # Synchronous variables - # self.send_lock = Lock() - # self.receive_lock = Lock() ########################################################################################## ########################################################################################## diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py new file mode 100644 index 00000000..32c984b5 --- /dev/null +++ b/src/Database/DatabaseHandler.py @@ -0,0 +1,90 @@ +from typing import Union, List, Dict, Any, Callable, Optional, Type, Tuple +from numpy import ndarray + +from SSD.Core.Storage.Database import Database + + +class DatabaseHandler: + + def __init__(self, + remote: bool = False, + on_init_handler: Optional[Callable] = None): + + self.remote = remote + self.__partitions: List[Database] = [] + self.__on_init_handler = self.__default_handler if on_init_handler is None else on_init_handler + + def __default_handler(self): + pass + + ### + # Manage partitions + ### + + def init(self, partitions: List[Database]) -> None: + self.__partitions = partitions.copy() + self.__on_init_handler() + + def init_remote(self, partitions: List[List[str]]) -> None: + self.__partitions = [Database(database_dir=partition[0], + database_name=partition[1]).load() for partition in partitions] + self.__on_init_handler() + + def update_list_partitions(self, + partition: Database) -> None: + self.__partitions.append(partition) + + def get_database_dir(self): + return self.__partitions[0].get_path()[0] + + def load(self): + for db in self.__partitions: + db.load() + + ### + # Database Architecture + ### + + def create_fields(self, + table_name: str, + fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + if len(self.__partitions) == 1: + self.__partitions[0].create_fields(table_name=table_name, + fields=fields) + + def define_fields(self, + table_name: str, + fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + if len(self.__partitions) == 1: + self.__partitions[0].load() + if len(self.__partitions[0].get_fields(table_name=table_name)) == 2: + self.__partitions[0].create_fields(table_name=table_name, + fields=fields) + + def get_fields(self, + table_name: str) -> List[str]: + return self.__partitions[0].get_fields(table_name=table_name) + + ### + # Data Read / Write + ### + + def get_line(self, + table_name: str, + line_id: List[int]) -> Dict[str, ndarray]: + return self.__partitions[line_id[0]].get_line(table_name=table_name, + line_id=line_id[1]) + + def add_data(self, + table_name: str, + data: Dict[str, Any]) -> int: + return self.__partitions[-1].add_data(table_name=table_name, + data=data) + + def update(self, + table_name: str, + data: Dict[str, Any], + line_id: int) -> None: + self.__partitions[-1].update(table_name=table_name, + data=data, + line_id=line_id) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index ab94350d..e7f6c3aa 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -5,67 +5,44 @@ from SSD.Core.Storage.Database import Database from DeepPhysX.Core.Visualization.VedoFactory import VedoFactory -from DeepPhysX.Core.AsyncSocket.TcpIpClient import TcpIpClient +from DeepPhysX.Core.AsyncSocket.AbstractEnvironment import AbstractEnvironment +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler -class BaseEnvironment(TcpIpClient): +class BaseEnvironment(AbstractEnvironment): def __init__(self, - ip_address: str = 'localhost', - port: int = 10000, - instance_id: int = 0, - number_of_instances: int = 1, as_tcp_ip_client: bool = True, - environment_manager: Optional[Any] = None, - training_db: Optional[Union[Database, Tuple[str, str]]] = None, + instance_id: int = 0, + instance_nb: int = 1, visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): """ BaseEnvironment is an environment class to compute simulated data for the network and its optimization process. - :param ip_address: IP address of the TcpIpObject. - :param port: Port number of the TcpIpObject. :param instance_id: ID of the instance. - :param number_of_instances: Number of simultaneously launched instances. - :param as_tcp_ip_client: Environment is owned by a TcpIpClient if True, by an EnvironmentManager if False. - :param environment_manager: EnvironmentManager that handles the Environment if 'as_tcp_ip_client' is False. + :param instance_nb: Number of simultaneously launched instances. + :param as_tcp_ip_client: Environment is a TcpIpObject if True, is owned by an EnvironmentManager if False. :param visualization_db: The path to the visualization Database or the visualization Database object to connect to. """ - TcpIpClient.__init__(self, - instance_id=instance_id, - number_of_instances=number_of_instances, - as_tcp_ip_client=as_tcp_ip_client, - ip_address=ip_address, - port=port) + AbstractEnvironment.__init__(self, + as_tcp_ip_client=as_tcp_ip_client, + instance_id=instance_id, + instance_nb=instance_nb) # Training data variables - self.__training_data: Dict[str, ndarray] = {} - self.__additional_data: Dict[str, ndarray] = {} + self.__data_training: Dict[str, ndarray] = {} + self.__data_additional: Dict[str, ndarray] = {} self.compute_training_data: bool = True # Dataset data variables - self.database: Optional[Database] = None self.update_line: Optional[int] = None self.sample_training: Optional[Dict[str, Any]] = None self.sample_additional: Optional[Dict[str, Any]] = None self.__first_add: List[bool] = [True, True] - # Manager if the Environment is not a TcpIpClient - self.environment_manager: Any = environment_manager - # Connect the Environment to the data Database - self.database: Optional[Database] = None - if training_db is not None: - if type(training_db) == list: - self.database = Database(database_dir=training_db[0], - database_name=training_db[1]).load() - else: - self.database = training_db - if self.instance_id == 1: - self.database.create_fields(table_name='Training', - fields=('env_id', int)) - self.database.create_fields(table_name='Additional', - fields=('env_id', int)) + self.__database_handler = DatabaseHandler(on_init_handler=self.__database_handler_init) # Connect the Factory to the visualization Database self.factory: Optional[VedoFactory] = None @@ -84,6 +61,16 @@ def __init__(self, ########################################################################################## ########################################################################################## + def __database_handler_init(self): + if self.instance_id == 1: + self.__database_handler.create_fields(table_name='Training', + fields=('env_id', int)) + self.__database_handler.create_fields(table_name='Additional', + fields=('env_id', int)) + + def get_database_handler(self) -> DatabaseHandler: + return self.__database_handler + def create(self) -> None: """ Create the Environment. Automatically called when Environment is launched. @@ -169,7 +156,7 @@ def save_parameters(self, **kwargs) -> None: Save a set of parameters in the Database. """ - database_dir = self.database.get_path()[0] + database_dir = self.__database_handler.get_database_dir() if isfile(join(database_dir, 'environment_parameters.db')): database = Database(database_dir=database_dir, database_name='environment_parameters').load() @@ -186,7 +173,7 @@ def load_parameters(self) -> Dict[str, Any]: Load a set of parameters from the Database. """ - database_dir = self.database.get_path()[0] + database_dir = self.__database_handler.get_database_dir() if isfile(join(database_dir, 'environment_parameters.db')): database = Database(database_dir=database_dir, database_name='environment_parameters').load() @@ -196,14 +183,12 @@ def load_parameters(self) -> Dict[str, Any]: return {} def define_training_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - self.database.load() - if len(self.database.get_fields(table_name='Training')) == 2: - self.database.create_fields(table_name='Training', fields=fields) + self.__database_handler.define_fields(table_name='Training', + fields=fields) def define_additional_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - self.database.load() - if len(self.database.get_fields(table_name='Additional')) == 2: - self.database.create_fields(table_name='Additional', fields=fields) + self.__database_handler.define_fields(table_name='Additional', + fields=fields) def set_training_data(self, **kwargs) -> None: """ @@ -213,9 +198,9 @@ def set_training_data(self, **kwargs) -> None: # Check kwargs if self.__first_add[0]: if self.instance_id != 0: - self.database.load() + self.__database_handler.load() self.__first_add[0] = False - required_fields = list(set(self.database.get_fields(table_name='Training')) - {'id', 'env_id'}) + required_fields = list(set(self.__database_handler.get_fields(table_name='Training')) - {'id', 'env_id'}) for field in kwargs.keys(): if field not in required_fields: raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." @@ -227,21 +212,21 @@ def set_training_data(self, **kwargs) -> None: # Training data is set if the Environment can compute data if self.compute_training_data: - self.__training_data = kwargs - self.__training_data['env_id'] = self.instance_id + self.__data_training = kwargs + self.__data_training['env_id'] = self.instance_id def set_additional_data(self, **kwargs) -> None: # Additional data is also set if the Environment can compute data if self.compute_training_data: - self.__additional_data = kwargs - self.__additional_data['env_id'] = self.instance_id + self.__data_additional = kwargs + self.__data_additional['env_id'] = self.instance_id def _send_training_data(self) -> int: - line_id = self.database.add_data(table_name='Training', - data=self.__training_data) - self.database.add_data(table_name='Additional', - data=self.__additional_data) + line_id = self.__database_handler.add_data(table_name='Training', + data=self.__data_training) + self.__database_handler.add_data(table_name='Additional', + data=self.__data_additional) return line_id def _reset_training_data(self) -> None: @@ -254,21 +239,21 @@ def _reset_training_data(self) -> None: def _update_training_data(self, line_id: int) -> None: if self.__training_data != {}: - self.database.update(table_name='Training', - data=self.__training_data, - line_id=line_id) + self.__database_handler.update(table_name='Training', + data=self.__training_data, + line_id=line_id) if self.__additional_data != {}: - self.database.update(table_name='Additional', - data=self.__additional_data, - line_id=line_id) + self.__database_handler.update(table_name='Additional', + data=self.__additional_data, + line_id=line_id) def _get_training_data(self, - line: int) -> None: + line: List[int]) -> None: self.update_line = line - self.sample_training = self.database.get_line(table_name='Training', - line_id=line) - self.sample_additional = self.database.get_line(table_name='Additional', - line_id=line) + self.sample_training = self.__database_handler.get_line(table_name='Training', + line_id=line) + self.sample_additional = self.__database_handler.get_line(table_name='Additional', + line_id=line) self.sample_additional = None if len(self.sample_additional) == 1 else self.sample_additional ########################################################################################## @@ -287,9 +272,9 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: # Check kwargs if self.__first_add[1]: if self.instance_id != 0: - self.database.load() + self.__database_handler.load() self.__first_add[1] = False - required_fields = list(set(self.database.get_fields(table_name='Prediction')) - {'id'}) + required_fields = list(set(self.__database_handler.get_fields(table_name='Prediction')) - {'id'}) for field in kwargs.keys(): if field not in required_fields: raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." @@ -303,12 +288,12 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: if self.environment_manager.data_manager is None: raise ValueError("Cannot request prediction if DataManager does not exist") # Get a prediction - self.database.update(table_name='Prediction', - data=kwargs, - line_id=self.instance_id) + self.__database_handler.update(table_name='Prediction', + data=kwargs, + line_id=self.instance_id) self.environment_manager.data_manager.get_prediction(self.instance_id) - data_pred = self.database.get_line(table_name='Prediction', - line_id=self.instance_id) + data_pred = self.__database_handler.get_line(table_name='Prediction', + line_id=[0, self.instance_id]) del data_pred['id'] return data_pred @@ -318,7 +303,7 @@ def _get_prediction(self): """ training_data = self.__training_data.copy() - required_fields = self.database.get_fields(table_name='Prediction') + required_fields = self.__database_handler.get_fields(table_name='Prediction') for field in self.__training_data.keys(): if field not in required_fields: del training_data[field] diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index 2e9a0ebe..8447a36e 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -157,31 +157,20 @@ def start_client(self, str(training_db), str(visualization_db)]) def create_environment(self, - environment_manager: Any, - training_db: Optional[Database] = None, visualization_db: Optional[Any] = None) -> BaseEnvironment: """ Create an Environment that will not be a TcpIpObject. - :param environment_manager: EnvironmentManager that handles the Environment. - :param training_db: Path of the training Database to connect to. :param visualization_db: Path to the visualization Database to connect to. :return: Environment object. """ # Create instance - environment = self.environment_class(environment_manager=environment_manager, - as_tcp_ip_client=False, - training_db=training_db, + environment = self.environment_class(as_tcp_ip_client=False, visualization_db=visualization_db) if not isinstance(environment, BaseEnvironment): raise TypeError(f"[{self.name}] The given 'environment_class'={self.environment_class} must be a " f"BaseEnvironment.") - # Create & Init Environment - environment.create() - environment.init() - environment.init_database() - environment.init_visualization() return environment def __str__(self) -> str: diff --git a/src/Environment/launcherBaseEnvironment.py b/src/Environment/launcherBaseEnvironment.py index b2ed4a3d..f60112de 100644 --- a/src/Environment/launcherBaseEnvironment.py +++ b/src/Environment/launcherBaseEnvironment.py @@ -3,6 +3,7 @@ from sys import argv, path from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment as Environment +from DeepPhysX.Core.AsyncSocket.TcpIpClient import TcpIpClient if __name__ == '__main__': @@ -20,12 +21,11 @@ # Create, init and run Tcp-Ip environment training_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] visualization_db = None if argv[8] == 'None' else [s[1:-1] for s in argv[8][1:-1].split(', ')] - client = Environment(ip_address=argv[3], + client = TcpIpClient(environment=Environment, + ip_address=argv[3], port=int(argv[4]), instance_id=int(argv[5]), - number_of_instances=int(argv[6]), - training_db=training_db, - visualization_db=visualization_db) + instance_nb=int(argv[6])) client.initialize() client.launch() diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index f1706345..e24ed1ff 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -40,6 +40,7 @@ def __init__(self, self.manager: Optional[Any] = manager self.database_manager: Optional[DatabaseManager] = None self.environment_manager: Optional[EnvironmentManager] = None + self.connected_managers: List[Any] = [] # Create a DatabaseManager self.database_manager = DatabaseManager(database_config=database_config, @@ -48,14 +49,12 @@ def __init__(self, new_session=new_session, pipeline=pipeline, produce_data=produce_data) - training_db = self.database_manager.database # Create an EnvironmentManager if required if environment_config is not None: self.environment_manager = EnvironmentManager(environment_config=environment_config, data_manager=self, session=session, - training_db=training_db, batch_size=batch_size) # DataManager variables @@ -73,12 +72,18 @@ def get_manager(self) -> Any: return self.manager + def connect_handler(self, handler): + self.database_manager.connect_handler(handler) + def get_database(self) -> Database: return self.database_manager.database + def connect_manager(self, manager: Any): + self.connected_managers.append(manager) + def change_database(self) -> None: - self.manager.change_database(self.database_manager.database) - self.environment_manager.change_database(self.database_manager.database) + for manager in self.connected_managers: + manager.change_database(self.database_manager.database) @property def nb_environment(self): diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index c059fd5c..7b97bb4f 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -9,6 +9,7 @@ from SSD.Core.Storage.Database import Database from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler from DeepPhysX.Core.Utils.path import create_dir, copy_dir, get_first_caller from DeepPhysX.Core.Utils.jsonUtils import CustomJSONEncoder @@ -28,19 +29,18 @@ def __init__(self, :param database_config: Specialisation containing the parameters of the dataset manager :param data_manager: DataManager that handles the DatabaseManager :param new_session: Define the creation of new directories to store data - :param is_training: True if the session is done offline :param produce_data: True if this session is a network training """ self.name: str = self.__class__.__name__ # Manager variables - database_config = BaseDatabaseConfig() if database_config is None else database_config - self.data_manager: Optional[Any] = data_manager - self.dataset_dir: str = join(session, 'dataset') - self.database: Optional[Database] = None self.pipeline: str = pipeline + self.data_manager: Optional[Any] = data_manager + self.database_dir: str = join(session, 'dataset') + self.database_handlers: List[DatabaseHandler] = [] root = get_first_caller() + database_config = BaseDatabaseConfig() if database_config is None else database_config # Dataset parameters self.max_file_size: int = database_config.max_file_size @@ -52,15 +52,20 @@ def __init__(self, # Dataset modes self.modes: List[str] = ['training', 'validation', 'prediction'] - self.mode: str = 'training' if produce_data or pipeline == 'training' else 'prediction' - self.mode = self.mode if database_config.mode is None else database_config.mode + if pipeline == 'data_generation': + self.mode: str = 'training' if database_config.mode is None else database_config.mode + elif pipeline == 'training': + self.mode: str = 'training' + else: + self.mode: str = 'prediction' if database_config.mode is None else database_config.mode + self.mode = 'prediction' if produce_data else self.mode # Dataset partitions session_name = session.split(sep)[-1] self.partition_template: Dict[str, str] = {mode: f'{session_name}_{mode}_' + '{}' for mode in self.modes} - self.partitions: Dict[str, List[str]] = {mode: [] for mode in self.modes} self.partition_index: Dict[str, int] = {mode: 0 for mode in self.modes} - self.current_partition: str = '' + self.partition_names: Dict[str, List[str]] = {mode: [] for mode in self.modes} + self.partitions: Dict[str, List[Database]] = {mode: [] for mode in self.modes} # Dataset indexing self.shuffle_pattern: ndarray = arange(0) @@ -133,45 +138,13 @@ def __init__(self, else: self.load_directory(load_partition=False) - def create_partition(self): - """ - - """ - - self.current_partition = self.partition_template[self.mode].format(self.partition_index[self.mode]) - self.database = Database(database_dir=self.dataset_dir, - database_name=self.current_partition).new() - self.database.create_table(table_name='Training') - self.database.create_table(table_name='Additional') - self.partitions[self.mode].append(self.current_partition) - - def additional_partition(self): - - # 1. If a DB exists, get all the Fields to re-create them - fields = {} - types = {'INT': int, 'FLOAT': float, 'STR': str, 'BOOL': bool, 'NUMPY': ndarray} - if self.partition_index[self.mode] > 0: - for table_name in self.database.get_tables(): - fields[table_name] = [] - F = self.database.get_fields(table_name=table_name, - only_names=False) - for field in [f for f in F if f not in ['id', '_dt_']]: - fields[table_name].append((field, types[F[field].field_type])) - if 'Prediction' in fields.keys(): - self.database.remove_table(table_name='Prediction') - - # 2. Create a new Database - self.partition_index[self.mode] += 1 - self.create_partition() - - # 3. Re-create the Fields if this is not the first partition - if self.partition_index[self.mode] > 1: - for table_name in fields.keys(): - self.database.create_fields(table_name=table_name, - fields=fields[table_name]) - - # 4. Tell the other components to communicate on a new DB - self.data_manager.change_database() + def connect_handler(self, + handler: DatabaseHandler) -> None: + if handler.remote: + handler.init_remote(self.get_partition_names()) + else: + handler.init(self.get_partition_objects()) + self.database_handlers.append(handler) def load_directory(self, last_partition: bool = True, @@ -181,14 +154,14 @@ def load_directory(self, """ # 1. Check the directory existence to prevent bugs - if not isdir(self.dataset_dir): - raise Warning(f"[{self.name}] Impossible to load Dataset from {self.dataset_dir}.") + if not isdir(self.database_dir): + raise Warning(f"[{self.name}] Impossible to load Dataset from {self.database_dir}.") # 2. Get the .json description file json_found = False - if isfile(join(self.dataset_dir, 'dataset.json')): + if isfile(join(self.database_dir, 'dataset.json')): json_found = True - with open(join(self.dataset_dir, 'dataset.json')) as json_file: + with open(join(self.database_dir, 'dataset.json')) as json_file: self.json_content = json_load(json_file) # 3. Load partitions for each mode @@ -209,10 +182,10 @@ def load_directory(self, else: self.partition_index[self.mode] = len(self.partitions[self.mode]) - 1 if last_partition else 0 self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] - self.database = Database(database_dir=self.dataset_dir, + self.database = Database(database_dir=self.database_dir, database_name=self.current_partition).load() else: - self.database = Database(database_dir=self.dataset_dir, + self.database = Database(database_dir=self.database_dir, database_name='temp').new() self.database.create_table(table_name='Training') self.database.create_table(table_name='Additional') @@ -221,12 +194,55 @@ def load_directory(self, if self.shuffle: self.shuffle_samples() + def create_partition(self): + """ + + """ + + # 1. Define the partition name + partition_name = self.partition_template[self.mode].format(self.partition_index[self.mode]) + self.partition_names[self.mode].append(partition_name) + + # 2. Create the Database partition + db = Database(database_dir=self.database_dir, + database_name=partition_name).new() + db.create_table(table_name='Training') + db.create_table(table_name='Additional') + self.partitions[self.mode].append(db) + + # 3. If the partition is an additional one, create all fields + if self.partition_index[self.mode] > 0: + # Get fields + fields = {} + types = {'INT': int, 'FLOAT': float, 'STR': str, 'BOOL': bool, 'NUMPY': ndarray} + for table_name in self.partitions[self.mode][0].get_tables(): + fields[table_name] = [] + F = self.partitions[self.mode][0].get_fields(table_name=table_name, + only_names=False) + for field in [f for f in F if f not in ['id', '_dt_']]: + fields[table_name].append((field, types[F[field].field_type])) + # Re-create them + for table_name in fields.keys(): + self.partitions[self.mode][-1].create_fields(table_name=table_name, + fields=fields[table_name]) + + # 4. Update the partitions in handlers + for handler in self.database_handlers: + handler.update_list_partitions(self.partitions[self.mode][-1]) + self.partition_index[self.mode] += 1 + + def get_partition_objects(self) -> List[Database]: + return self.partitions[self.mode] + + def get_partition_names(self) -> List[List[str]]: + return [db.get_path() for db in self.partitions[self.mode]] + def load_next_partition(self): # 1. Define next partition self.partition_index[self.mode] = (self.partition_index[self.mode] + 1) % len(self.partitions[self.mode]) self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] - self.database = Database(database_dir=self.dataset_dir, + self.database = Database(database_dir=self.database_dir, database_name=self.current_partition).load() # 2. Shuffle @@ -238,7 +254,7 @@ def search_partitions(self): """ - raw_partitions = {mode: [f for f in listdir(self.dataset_dir) if isfile(join(self.dataset_dir, f)) + raw_partitions = {mode: [f for f in listdir(self.database_dir) if isfile(join(self.database_dir, f)) and f.endswith('.db') and f.__contains__(mode)] for mode in self.modes} return {mode: sorted(raw_partitions[mode]) for mode in self.modes} @@ -251,6 +267,18 @@ def search_partitions_info(self): pass + def remove_empty_partitions(self): + + for mode in self.modes: + if len(self.partitions[mode]) > 0 and self.partitions[mode][-1].nb_lines(table_name='Training') == 0: + database = self.partitions[mode].pop(-1) + path = database.get_path() + remove(join(path[0], f'{path[1]}.db')) + self.partition_names[mode].pop(-1) + self.partition_index[mode] -= 1 + self.json_content['nb_samples'][mode].pop(-1) + self.update_json(update_partitions_lists=True) + def update_json(self, update_partitions_lists: bool = False, update_nb_samples: bool = False, @@ -262,20 +290,22 @@ def update_json(self, """ + database = self.partitions[self.mode][-1] + # Update partitions lists if update_partitions_lists: - self.json_content['partitions'] = self.partitions + self.json_content['partitions'] = self.partition_names # Update number of samples if update_nb_samples: - if len(self.json_content['nb_samples'][self.mode]) == self.partition_index[self.mode] + 1: + if len(self.json_content['nb_samples'][self.mode]) == self.partition_index[self.mode]: self.json_content['nb_samples'][self.mode][-1] = self.nb_samples else: self.json_content['nb_samples'][self.mode].append(self.nb_samples) # Update DB architecture if update_architecture: - architecture = self.database.get_architecture() + architecture = database.get_architecture() if 'Prediction' in architecture.keys(): del architecture['Prediction'] for fields in architecture.values(): @@ -287,8 +317,8 @@ def update_json(self, # Update data shapes if update_shapes: for table_name, fields in self.json_content['architecture'].items(): - if self.database.nb_lines(table_name=table_name) > 0: - data = self.database.get_line(table_name=table_name) + if database.nb_lines(table_name=table_name) > 0: + data = database.get_line(table_name=table_name) for field in fields: if 'NUMPY' in field: field_name = field.split(' ')[0] @@ -299,12 +329,12 @@ def update_json(self, self.json_content['normalization'] = self.compute_normalization() # Overwrite json file - with open(join(self.dataset_dir, 'dataset.json'), 'w') as json_file: + with open(join(self.database_dir, 'dataset.json'), 'w') as json_file: json_dump(self.json_content, json_file, indent=3, cls=CustomJSONEncoder) @property def nb_samples(self) -> int: - return self.database.nb_lines(table_name='Training') + return self.partitions[self.mode][-1].nb_lines(table_name='Training') def shuffle_samples(self): """ @@ -332,8 +362,8 @@ def add_data(self, # 2. Check the size of the partition if self.max_file_size is not None: - if self.database.memory_size > self.max_file_size: - self.additional_partition() + if self.partitions[self.mode][-1].memory_size > self.max_file_size: + self.create_partition() self.update_json(update_partitions_lists=True, update_nb_samples=True) # 3. Update sample counter @@ -364,9 +394,18 @@ def close(self): """ + # Check non-empty last partition + self.remove_empty_partitions() + if self.normalize and self.pipeline == 'data_generation': self.update_json(update_normalization=True) - self.database.close() + + # Cose databases + for mode in self.modes: + for database in self.partitions[mode]: + database.close() + + # Remove prediction pipeline DB if self.pipeline == 'prediction' and not self.produce_data: path = self.database.get_path() remove(join(path[0], f'{path[1]}.db')) @@ -434,10 +473,10 @@ def update_normalization(self, # Compute the mean for each field fields = list(previous_normalization.keys()) - data_to_normalize = self.database.get_lines(table_name='Training', - fields=fields, - lines_id=data_lines, - batched=True) + data_to_normalize = self.partitions[self.mode][-1].get_lines(table_name='Training', + fields=fields, + lines_id=data_lines, + batched=True) for field in fields: data = array(data_to_normalize[field]) m = (previous_nb_samples / self.total_nb_sample) * previous_normalization[field][0] + \ @@ -465,19 +504,18 @@ def normalization(self) -> Dict[str, List[float]]: return None if self.json_content['normalization'] == {} else self.json_content['normalization'] def load_partitions_fields(self, - partition: str, + partition: Database, fields: List[str]): - db = Database(database_dir=self.dataset_dir, - database_name=partition).load() - return db.get_lines(table_name='Training', - fields=fields, - batched=True) + partition.load() + return partition.get_lines(table_name='Training', + fields=fields, + batched=True) def __str__(self): description = "\n" description += f"# {self.name}\n" - description += f" Dataset Repository: {self.dataset_dir}\n" + description += f" Dataset Repository: {self.database_dir}\n" size = f"No limits" if self.max_file_size is None else f"{self.max_file_size * 1e-9} Go" description += f" Partitions size: {size}\n" return description diff --git a/src/Manager/DatasetManager.py b/src/Manager/DatasetManager.py index 6860de5f..02257d64 100644 --- a/src/Manager/DatasetManager.py +++ b/src/Manager/DatasetManager.py @@ -258,7 +258,7 @@ def get_data(self, get_inputs: bool, get_outputs: bool, batch_size: int = 1, if data['input'].shape[0] != data['output'].shape[0]: raise ValueError(f"[{self.name}] Size of loaded batch mismatch for input and output " f"(in: {data['input'].shape} / out: {data['output'].shape}") - if 'additional_data' in data.keys(): + if '__data_additional' in data.keys(): for field in data['additional_fields'].keys(): if data['additional_fields'][field].shape[0] != data['input'].shape[0]: raise ValueError(f"[{self.name}] Size of loaded batch mismatch for additional field {field} " diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index a7f4deaf..2d170320 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -14,7 +14,6 @@ def __init__(self, environment_config: BaseEnvironmentConfig, data_manager: Optional[Any] = None, session: str = 'sessions/default', - training_db: Optional[Database] = None, batch_size: int = 1): """ Deals with the online generation of data for both training and running of the neural networks. @@ -54,15 +53,22 @@ def __init__(self, if environment_config.as_tcp_ip_client: self.server = environment_config.create_server(environment_manager=self, batch_size=batch_size, - training_db=training_db if training_db is None else training_db.get_path(), - visualization_db=visualization_db if visualization_db is None else visualization_db.get_path()) + visualization_db=None if visualization_db is None else visualization_db.get_path()) self.data_manager.get_database().load() else: - self.environment = environment_config.create_environment(environment_manager=self, - training_db=training_db, - visualization_db=visualization_db) + self.environment = environment_config.create_environment(visualization_db=visualization_db) + self.environment.environment_manager = self + self.data_manager.connect_handler(self.environment.get_database_handler()) + + # Create & Init Environment + self.environment.create() + self.environment.init() + self.environment.init_database() + self.environment.init_visualization() + # Define get_data and dispatch methods + self.get_database_handler = self.get_server_database_handler if self.server else self.get_environment_database_handler self.change_database = self.change_database_in_server if self.server else self.change_database_in_environment self.get_data = self.get_data_from_server if self.server else self.get_data_from_environment self.dispatch_batch = self.dispatch_batch_to_server if self.server else self.dispatch_batch_to_environment @@ -73,6 +79,12 @@ def __init__(self, self.visualizer.get_database().load() self.visualizer.init_visualizer() + def get_server_database_handler(self): + return self.server.get_database_handler() + + def get_environment_database_handler(self): + return self.environment.get_database_handler() + def change_database_in_server(self, database: Database): self.server.change_database(database.get_path()) diff --git a/src/Pipelines/BaseDataGeneration.py b/src/Pipelines/BaseDataGeneration.py index 6a0b1dfc..d18bc85c 100644 --- a/src/Pipelines/BaseDataGeneration.py +++ b/src/Pipelines/BaseDataGeneration.py @@ -1,5 +1,5 @@ from typing import Optional -from os.path import join, sep +from os.path import join, sep, exists from sys import stdout from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline @@ -17,6 +17,7 @@ def __init__(self, database_config: Optional[BaseDatabaseConfig] = None, session_dir: str = 'sessions', session_name: str = 'data_generation', + new_session: bool = True, batch_nb: int = 0, batch_size: int = 0): """ @@ -26,6 +27,7 @@ def __init__(self, :param environment_config: Specialisation containing the parameters of the environment manager. :param session_dir: Relative path to the directory which contains sessions directories. :param session_name: Name of the new the session directory. + :param new_session: If True, the session will be run in a new repository. :param batch_nb: Number of batches to produce. :param batch_size: Number of samples in a single batch. """ @@ -35,31 +37,20 @@ def __init__(self, environment_config=environment_config, session_dir=session_dir, session_name=session_name, + new_session=new_session, pipeline='data_generation') # Define the session repository root = get_first_caller() session_dir = join(root, session_dir) - # Configure 'new_session' flags - # Option 1: existing_dir == None --> new_session = True - # Option 2: existing_dir == session_dir/session_name --> new_session = False - # Option 3: existing_dir != session_dir/session_name --> new_session = True - new_session = True - if database_config is not None and database_config.existing_dir is not None and \ - join(session_dir, session_name) == join(root, database_config.existing_dir): - new_session = False - # Create a new session if required + if not new_session: + new_session = not exists(session_dir) if new_session: session_name = create_dir(session_dir=session_dir, session_name=session_name).split(sep)[-1] - # Data generation variables - self.batch_nb: int = batch_nb - self.batch_id: int = 0 - self.progress_bar = Progressbar(start=0, stop=self.batch_id, c='orange', title="Data Generation") - # Create a DataManager self.data_manager = DataManager(database_config=database_config, environment_config=environment_config, @@ -69,6 +60,11 @@ def __init__(self, produce_data=True, batch_size=batch_size) + # Data generation variables + self.batch_nb: int = batch_nb + self.batch_id: int = 0 + self.progress_bar = Progressbar(start=0, stop=self.batch_id, c='orange', title="Data Generation") + def execute(self) -> None: """ Launch the data generation Pipeline. diff --git a/src/Pipelines/BasePipeline.py b/src/Pipelines/BasePipeline.py index 4d935479..f62637b0 100644 --- a/src/Pipelines/BasePipeline.py +++ b/src/Pipelines/BasePipeline.py @@ -19,6 +19,7 @@ def __init__(self, environment_config: Optional[BaseEnvironmentConfig] = None, session_dir: str = 'sessions', session_name: str = 'default', + new_session: bool = True, pipeline: str = ''): """ Pipelines implement the main loop that defines data flow through components (Environment, Dataset, Network...). @@ -28,6 +29,7 @@ def __init__(self, :param environment_config: Specialisation containing the parameters of the environment manager. :param session_dir: Name of the directory in which to write all the necessary data. :param session_name: Name of the newly created directory if session is not defined. + :param new_session: If True, the session will be run in a new repository. :param pipeline: Name of the Pipeline. """ @@ -59,6 +61,7 @@ def __init__(self, # Session variables self.session_dir = session_dir self.session_name = session_name + self.new_session = new_session # Main manager self.manager: Optional[Manager] = None From b248ff05ad706c222b4f402f637594b851b23c61 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 26 Oct 2022 13:30:53 +0200 Subject: [PATCH 17/61] Fix all DataGeneration use cases. --- src/Manager/DatabaseManager.py | 148 ++++++++++++++++++---------- src/Pipelines/BaseDataGeneration.py | 2 +- 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 7b97bb4f..14b18bb4 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional from os.path import isfile, isdir, join -from os import listdir, symlink, sep, remove +from os import listdir, symlink, sep, remove, rename from json import dump as json_dump from json import load as json_load from numpy import arange, ndarray, array, abs, mean, sqrt @@ -92,10 +92,10 @@ def __init__(self, else: copy_dir(src_dir=join(root, database_config.existing_dir), dest_dir=session, sub_folders='dataset') - self.load_directory(last_partition=True) + self.load_directory(rename_partitions=True) # Complete a Database in the same session --> load the directory else: - self.load_directory(last_partition=True) + self.load_directory() # Training case elif self.pipeline == 'training': @@ -112,10 +112,10 @@ def __init__(self, else: copy_dir(src_dir=join(root, database_config.existing_dir), dest_dir=session, sub_folders='dataset') - self.load_directory(last_partition=False) + self.load_directory() # Complete a Database in the same directory --> load the directory else: - self.load_directory(last_partition=False) + self.load_directory() # Load data else: @@ -123,20 +123,20 @@ def __init__(self, if new_session: symlink(src=join(root, database_config.existing_dir, 'dataset'), dst=join(session, 'dataset')) - self.load_directory(last_partition=False) + self.load_directory() # Load data in the same session --> load the directory else: - self.load_directory(last_partition=False) + self.load_directory() # Prediction case else: # Generate data if produce_data: - self.load_directory(last_partition=True) + self.load_directory() else: - self.load_directory(load_partition=False) + self.load_directory(load_partitions=False) def connect_handler(self, handler: DatabaseHandler) -> None: @@ -147,8 +147,8 @@ def connect_handler(self, self.database_handlers.append(handler) def load_directory(self, - last_partition: bool = True, - load_partition: bool = True): + load_partitions: bool = True, + rename_partitions: bool = False): """ """ @@ -164,26 +164,36 @@ def load_directory(self, with open(join(self.database_dir, 'dataset.json')) as json_file: self.json_content = json_load(json_file) - # 3. Load partitions for each mode - self.partitions = self.json_content['partitions'] if json_found else self.search_partitions() - - # 4. Update json file if not found + # 3. Update json file if not found if not json_found or self.json_content == self.json_default: self.search_partitions_info() - self.update_json(update_partitions_lists=True) + self.update_json() if self.recompute_normalization or ( self.normalize and self.json_content['normalization'] == self.json_default['normalization']): self.update_json(update_normalization=True) - # 5. Load the Database - if load_partition: - if len(self.partitions[self.mode]) == 0: + # 4. Load partitions for each mode + self.partition_names = self.json_content['partitions'] + self.partition_index = {mode: len(self.partition_names[mode]) for mode in self.modes} + if rename_partitions: + for mode in self.modes: + current_name = self.partition_template[mode].split(f'_{mode}_')[0] + for i, name in enumerate(self.partition_names[mode]): + if name.split(f'_{mode}_')[0] != current_name: + self.partition_names[mode][i] = current_name + f'_{mode}_{i}' + rename(src=join(self.database_dir, f'{name}.db'), + dst=join(self.database_dir, f'{self.partition_names[mode][i]}.db')) + + # 5. Load the partitions + if load_partitions: + for mode in self.modes: + for name in self.partition_names[mode]: + db = Database(database_dir=self.database_dir, + database_name=name).load() + self.partitions[mode].append(db) + if len(self.partitions[self.mode]) == 0 or self.partitions[self.mode][-1].memory_size > self.max_file_size: self.create_partition() - else: - self.partition_index[self.mode] = len(self.partitions[self.mode]) - 1 if last_partition else 0 - self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] - self.database = Database(database_dir=self.database_dir, - database_name=self.current_partition).load() + else: self.database = Database(database_dir=self.database_dir, database_name='temp').new() @@ -191,7 +201,7 @@ def load_directory(self, self.database.create_table(table_name='Additional') # 6. Shuffle Database indices - if self.shuffle: + if self.shuffle and load_partitions: self.shuffle_samples() def create_partition(self): @@ -230,6 +240,7 @@ def create_partition(self): for handler in self.database_handlers: handler.update_list_partitions(self.partitions[self.mode][-1]) self.partition_index[self.mode] += 1 + self.update_json(update_partitions_lists=True, update_nb_samples=True) def get_partition_objects(self) -> List[Database]: return self.partitions[self.mode] @@ -249,23 +260,70 @@ def load_next_partition(self): if self.shuffle: self.shuffle_samples() - def search_partitions(self): + def search_partitions_info(self): """ """ + # 1. Get all the partitions raw_partitions = {mode: [f for f in listdir(self.database_dir) if isfile(join(self.database_dir, f)) and f.endswith('.db') and f.__contains__(mode)] for mode in self.modes} - return {mode: sorted(raw_partitions[mode]) for mode in self.modes} - - def search_partitions_info(self): - """ + raw_partitions = {mode: [f.split('.')[0] for f in raw_partitions[mode]] for mode in self.modes} + self.json_content['partitions'] = {mode: sorted(raw_partitions[mode]) for mode in self.modes} - """ - - # TODO: fill json - - pass + # 2. Get the number of samples + for mode in self.modes: + for name in self.json_content['partitions'][mode]: + db = Database(database_dir=self.database_dir, + database_name=name).load() + self.json_content['nb_samples'][mode].append(db.nb_lines(table_name='Training')) + db.close() + + # 3. Get the Database architecture + self.json_content['architecture'] = self.get_database_architecture() + self.first_add = False + + # 4. Get the data shapes + self.json_content['data_shape'] = self.get_data_shapes() + + def get_database_architecture(self): + if len(self.json_content['partitions']['training']) != 0: + db = Database(database_dir=self.database_dir, + database_name=self.json_content['partitions']['training'][0]).load() + elif len(self.json_content['partitions']['validation']) != 0: + db = Database(database_dir=self.database_dir, + database_name=self.json_content['partitions']['validation'][0]).load() + else: + return {} + architecture = db.get_architecture() + if 'Prediction' in architecture.keys(): + del architecture['Prediction'] + for fields in architecture.values(): + for field in fields.copy(): + if field.split(' ')[0] in ['id', '_dt_']: + fields.remove(field) + db.close() + return architecture + + def get_data_shapes(self): + if len(self.json_content['partitions']['training']) != 0: + db = Database(database_dir=self.database_dir, + database_name=self.json_content['partitions']['training'][0]).load() + elif len(self.json_content['partitions']['validation']) != 0: + db = Database(database_dir=self.database_dir, + database_name=self.json_content['partitions']['validation'][0]).load() + else: + return {} + shapes = {} + for table_name, fields in self.json_content['architecture'].items(): + if db.nb_lines(table_name=table_name) > 0: + data = db.get_line(table_name=table_name) + for field in fields: + if 'NUMPY' in field: + field_name = field.split(' ')[0] + shapes[f'{table_name}.{field_name}'] = data[field_name].shape + db.close() + return shapes def remove_empty_partitions(self): @@ -290,8 +348,6 @@ def update_json(self, """ - database = self.partitions[self.mode][-1] - # Update partitions lists if update_partitions_lists: self.json_content['partitions'] = self.partition_names @@ -305,24 +361,11 @@ def update_json(self, # Update DB architecture if update_architecture: - architecture = database.get_architecture() - if 'Prediction' in architecture.keys(): - del architecture['Prediction'] - for fields in architecture.values(): - for field in fields.copy(): - if field.split(' ')[0] in ['id', '_dt_']: - fields.remove(field) - self.json_content['architecture'] = architecture + self.json_content['architecture'] = self.get_database_architecture() # Update data shapes if update_shapes: - for table_name, fields in self.json_content['architecture'].items(): - if database.nb_lines(table_name=table_name) > 0: - data = database.get_line(table_name=table_name) - for field in fields: - if 'NUMPY' in field: - field_name = field.split(' ')[0] - self.json_content['data_shape'][f'{table_name}.{field_name}'] = data[field_name].shape + self.json_content['data_shape'] = self.get_data_shapes() # Update normalization coefficients if update_normalization: @@ -364,7 +407,6 @@ def add_data(self, if self.max_file_size is not None: if self.partitions[self.mode][-1].memory_size > self.max_file_size: self.create_partition() - self.update_json(update_partitions_lists=True, update_nb_samples=True) # 3. Update sample counter self.current_sample = self.nb_samples + 1 diff --git a/src/Pipelines/BaseDataGeneration.py b/src/Pipelines/BaseDataGeneration.py index d18bc85c..7a612e44 100644 --- a/src/Pipelines/BaseDataGeneration.py +++ b/src/Pipelines/BaseDataGeneration.py @@ -46,7 +46,7 @@ def __init__(self, # Create a new session if required if not new_session: - new_session = not exists(session_dir) + new_session = not exists(join(session_dir, session_name)) if new_session: session_name = create_dir(session_dir=session_dir, session_name=session_name).split(sep)[-1] From 9fd83d991c47f22778255015dc5e92f7ea774282 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 26 Oct 2022 17:46:38 +0200 Subject: [PATCH 18/61] Adapt offline Training in remote mode for DB handlers. --- src/Database/DatabaseHandler.py | 122 ++++++++++++++++++------- src/Environment/BaseEnvironment.py | 22 ++--- src/Manager/DataManager.py | 46 ++++------ src/Manager/DatabaseManager.py | 134 ++++++++++++++++------------ src/Manager/EnvironmentManager.py | 5 +- src/Manager/NetworkManager.py | 35 +++----- src/Manager/StatsManager.py | 35 +++----- src/Pipelines/BaseDataGeneration.py | 4 +- src/Pipelines/BasePipeline.py | 1 + src/Pipelines/BaseTraining.py | 117 +++++++++++++++--------- 10 files changed, 299 insertions(+), 222 deletions(-) diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py index 32c984b5..bd362fde 100644 --- a/src/Database/DatabaseHandler.py +++ b/src/Database/DatabaseHandler.py @@ -1,5 +1,6 @@ from typing import Union, List, Dict, Any, Callable, Optional, Type, Tuple -from numpy import ndarray +from numpy import array, where +from itertools import chain from SSD.Core.Storage.Database import Database @@ -11,7 +12,8 @@ def __init__(self, on_init_handler: Optional[Callable] = None): self.remote = remote - self.__partitions: List[Database] = [] + self.__storing_partitions: List[Database] = [] + self.__exchange_db: Optional[Database] = None self.__on_init_handler = self.__default_handler if on_init_handler is None else on_init_handler def __default_handler(self): @@ -21,25 +23,36 @@ def __default_handler(self): # Manage partitions ### - def init(self, partitions: List[Database]) -> None: - self.__partitions = partitions.copy() + def init(self, + storing_partitions: List[Database], + exchange_db: Database) -> None: + self.__storing_partitions = storing_partitions.copy() + self.__exchange_db = exchange_db self.__on_init_handler() - def init_remote(self, partitions: List[List[str]]) -> None: - self.__partitions = [Database(database_dir=partition[0], - database_name=partition[1]).load() for partition in partitions] + def init_remote(self, + storing_partitions: List[List[str]], + exchange_db: List[str]) -> None: + self.__storing_partitions = [Database(database_dir=partition[0], + database_name=partition[1]).load() for partition in storing_partitions] + self.__exchange_db = Database(database_dir=exchange_db[0], + database_name=exchange_db[1]).load() self.__on_init_handler() def update_list_partitions(self, partition: Database) -> None: - self.__partitions.append(partition) + self.__storing_partitions.append(partition) def get_database_dir(self): - return self.__partitions[0].get_path()[0] + return self.__storing_partitions[0].get_path()[0] def load(self): - for db in self.__partitions: + for db in self.__storing_partitions: db.load() + self.__exchange_db.load() + + def get_partitions(self): + return self.__storing_partitions ### # Database Architecture @@ -48,43 +61,84 @@ def load(self): def create_fields(self, table_name: str, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - if len(self.__partitions) == 1: - self.__partitions[0].create_fields(table_name=table_name, - fields=fields) - def define_fields(self, - table_name: str, - fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - if len(self.__partitions) == 1: - self.__partitions[0].load() - if len(self.__partitions[0].get_fields(table_name=table_name)) == 2: - self.__partitions[0].create_fields(table_name=table_name, - fields=fields) + if table_name == 'Exchange': + self.__exchange_db.load() + if len(self.__exchange_db.get_fields(table_name=table_name)) == 1: + self.__exchange_db.create_fields(table_name=table_name, + fields=fields) + else: + if len(self.__storing_partitions) == 1: + self.__storing_partitions[0].load() + if len(self.__storing_partitions[0].get_fields(table_name=table_name)) <= 2: + self.__storing_partitions[0].create_fields(table_name=table_name, + fields=fields) def get_fields(self, table_name: str) -> List[str]: - return self.__partitions[0].get_fields(table_name=table_name) + + database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[0] + return database.get_fields(table_name=table_name) ### # Data Read / Write ### - def get_line(self, - table_name: str, - line_id: List[int]) -> Dict[str, ndarray]: - return self.__partitions[line_id[0]].get_line(table_name=table_name, - line_id=line_id[1]) - def add_data(self, table_name: str, data: Dict[str, Any]) -> int: - return self.__partitions[-1].add_data(table_name=table_name, - data=data) + + database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[-1] + return database.add_data(table_name=table_name, data=data) def update(self, table_name: str, data: Dict[str, Any], - line_id: int) -> None: - self.__partitions[-1].update(table_name=table_name, - data=data, - line_id=line_id) + line_id: Union[int, List[int]]) -> None: + + database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[line_id[0]] + line_id = line_id[1] if type(line_id) == list else line_id + database.update(table_name=table_name, data=data, line_id=line_id) + + def get_line(self, + table_name: str, + line_id: Union[int, List[int]], + fields: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]: + + database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[line_id[0]] + line_id = line_id[1] if type(line_id) == list else line_id + return database.get_line(table_name=table_name, + line_id=line_id, + fields=fields) + + def get_lines(self, + table_name: str, + lines_id: List[List[int]], + fields: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]: + + # Transform list of lines to batch of lines per partition + batch_indices = array(lines_id) + partition_batch_indices = [] + for i in range(len(self.__storing_partitions)): + partition_indices = where(batch_indices[:, 0] == i)[0] + if len(partition_indices) > 0: + partition_batch_indices.append([i, batch_indices[partition_indices, 1].tolist()]) + + # Get lines of data + partition_batches = [] + for partition_indices in partition_batch_indices: + data = self.__storing_partitions[partition_indices[0]].get_lines(table_name=table_name, + lines_id=partition_indices[1], + fields=fields, + batched=True) + del data['id'] + partition_batches.append(data) + + # Merge batches + if len(partition_batches) == 1: + return partition_batches[0] + else: + return dict(zip(partition_batches[0].keys(), + [list(chain.from_iterable([partition_batches[i][key] + for i in range(len(partition_batches))])) + for key in partition_batches[0].keys()])) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index e7f6c3aa..2c23b512 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -183,11 +183,11 @@ def load_parameters(self) -> Dict[str, Any]: return {} def define_training_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - self.__database_handler.define_fields(table_name='Training', + self.__database_handler.create_fields(table_name='Training', fields=fields) def define_additional_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: - self.__database_handler.define_fields(table_name='Additional', + self.__database_handler.create_fields(table_name='Additional', fields=fields) def set_training_data(self, **kwargs) -> None: @@ -230,21 +230,21 @@ def _send_training_data(self) -> int: return line_id def _reset_training_data(self) -> None: - self.__training_data = {} - self.__additional_data = {} + self.__data_training = {} + self.__data_additional = {} self.sample_training = None self.sample_additional = None self.update_line = None def _update_training_data(self, - line_id: int) -> None: - if self.__training_data != {}: + line_id: List[int]) -> None: + if self.__data_training != {}: self.__database_handler.update(table_name='Training', - data=self.__training_data, + data=self.__data_training, line_id=line_id) - if self.__additional_data != {}: + if self.__data_additional != {}: self.__database_handler.update(table_name='Additional', - data=self.__additional_data, + data=self.__data_additional, line_id=line_id) def _get_training_data(self, @@ -302,9 +302,9 @@ def _get_prediction(self): """ - training_data = self.__training_data.copy() + training_data = self.__data_training.copy() required_fields = self.__database_handler.get_fields(table_name='Prediction') - for field in self.__training_data.keys(): + for field in self.__data_training.keys(): if field not in required_fields: del training_data[field] self.apply_prediction(self.get_prediction(**training_data)) diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index e24ed1ff..edae03a1 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -1,5 +1,4 @@ from typing import Any, Optional, Dict, List -from numpy import ndarray from DeepPhysX.Core.Manager.DatabaseManager import DatabaseManager, Database from DeepPhysX.Core.Manager.EnvironmentManager import EnvironmentManager @@ -10,12 +9,11 @@ class DataManager: def __init__(self, + pipeline: Any, database_config: Optional[BaseDatabaseConfig] = None, environment_config: Optional[BaseEnvironmentConfig] = None, - manager: Optional[Any] = None, session: str = 'sessions/default', new_session: bool = True, - pipeline: str = '', produce_data: bool = True, batch_size: int = 1): @@ -24,30 +22,29 @@ def __init__(self, A batch is given with a call to 'get_data' on either the DatabaseManager or the EnvironmentManager according to the context. + :param pipeline: Pipeline that handle the DataManager. :param database_config: Specialisation containing the parameters of the dataset manager. :param environment_config: Specialisation containing the parameters of the environment manager. - :param manager: Manager that handle the DataManager :param session: Path to the session directory. - :param new_session: Flag that indicates whether if the session is new - :param pipeline: Flag that indicates whether if this session is training a Network. + :param new_session: Flag that indicates whether if the session is new. :param produce_data: Flag that indicates whether if this session is producing data. - :param int batch_size: Number of samples in a batch + :param int batch_size: Number of samples in a batch. """ self.name: str = self.__class__.__name__ # Managers variables - self.manager: Optional[Any] = manager + self.pipeline: Optional[Any] = pipeline self.database_manager: Optional[DatabaseManager] = None self.environment_manager: Optional[EnvironmentManager] = None self.connected_managers: List[Any] = [] # Create a DatabaseManager self.database_manager = DatabaseManager(database_config=database_config, - session=session, data_manager=self, + pipeline=pipeline.type, + session=session, new_session=new_session, - pipeline=pipeline, produce_data=produce_data) # Create an EnvironmentManager if required @@ -58,19 +55,9 @@ def __init__(self, batch_size=batch_size) # DataManager variables - self.pipeline = pipeline self.produce_data = produce_data self.batch_size = batch_size - self.data_lines: List[int] = [] - - def get_manager(self) -> Any: - """ - Return the Manager of this DataManager. - - :return: The Manager of this DataManager. - """ - - return self.manager + self.data_lines: List[List[int]] = [] def connect_handler(self, handler): self.database_manager.connect_handler(handler) @@ -104,16 +91,16 @@ def get_data(self, """ # Data generation case - if self.pipeline == 'data_generation': + if self.pipeline.type == 'data_generation': self.environment_manager.get_data(animate=animate) self.database_manager.add_data() # Training case - elif self.pipeline == 'training': + elif self.pipeline.type == 'training': # Get data from Environment(s) if used and if the data should be created at this epoch - if self.environment_manager is not None and (epoch == 0 or not self.environment_manager.only_first_epoch) \ - and self.produce_data: + if self.environment_manager is not None and self.produce_data and \ + (epoch == 0 or not self.environment_manager.only_first_epoch): self.data_lines = self.environment_manager.get_data(animate=animate) self.database_manager.add_data(self.data_lines) @@ -121,7 +108,8 @@ def get_data(self, else: self.data_lines = self.database_manager.get_data(batch_size=self.batch_size) # Dispatch a batch to clients - if self.environment_manager is not None and (epoch == 0 or self.environment_manager.load_samples): + if self.environment_manager is not None and self.environment_manager.load_samples and \ + (epoch == 0 or not self.environment_manager.only_first_epoch): self.environment_manager.dispatch_batch(data_lines=self.data_lines, animate=animate) # Environment is no longer used @@ -154,10 +142,10 @@ def get_prediction(self, """ # Get a prediction - if self.manager is None: + if self.pipeline is None: raise ValueError("Cannot request prediction if Manager (and then NetworkManager) does not exist.") - self.manager.network_manager.compute_online_prediction(instance_id=instance_id, - normalization=self.normalization) + self.pipeline.network_manager.compute_online_prediction(instance_id=instance_id, + normalization=self.normalization) @property def normalization(self) -> Dict[str, List[float]]: diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 14b18bb4..b31a4805 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -3,7 +3,7 @@ from os import listdir, symlink, sep, remove, rename from json import dump as json_dump from json import load as json_load -from numpy import arange, ndarray, array, abs, mean, sqrt +from numpy import arange, ndarray, array, abs, mean, sqrt, empty, concatenate from numpy.random import shuffle from SSD.Core.Storage.Database import Database @@ -18,10 +18,10 @@ class DatabaseManager: def __init__(self, database_config: Optional[BaseDatabaseConfig] = None, - session: str = 'sessions/default', data_manager: Optional[Any] = None, - new_session: bool = True, pipeline: str = '', + session: str = 'sessions/default', + new_session: bool = True, produce_data: bool = True): """ DatabaseManager handle all operations with input / output files. Allows saving and read tensors from files. @@ -68,8 +68,8 @@ def __init__(self, self.partitions: Dict[str, List[Database]] = {mode: [] for mode in self.modes} # Dataset indexing - self.shuffle_pattern: ndarray = arange(0) - self.current_sample: int = 1 + self.sample_indices: ndarray = empty((0, 2), dtype=int) + self.sample_id: int = 0 self.first_add = True # Dataset json file @@ -82,6 +82,7 @@ def __init__(self, # DataGeneration case if self.pipeline == 'data_generation': + # Generate data in a new session if new_session: # Generate data from scratch --> create a new directory @@ -138,13 +139,14 @@ def __init__(self, else: self.load_directory(load_partitions=False) - def connect_handler(self, - handler: DatabaseHandler) -> None: - if handler.remote: - handler.init_remote(self.get_partition_names()) - else: - handler.init(self.get_partition_objects()) - self.database_handlers.append(handler) + # Finally create an exchange database + self.exchange = Database(database_dir=self.database_dir, + database_name='Exchange').new() + self.exchange.create_table(table_name='Exchange') + + ######################### + # Partitions Management # + ######################### def load_directory(self, load_partitions: bool = True, @@ -191,7 +193,9 @@ def load_directory(self, db = Database(database_dir=self.database_dir, database_name=name).load() self.partitions[mode].append(db) - if len(self.partitions[self.mode]) == 0 or self.partitions[self.mode][-1].memory_size > self.max_file_size: + if len(self.partitions[self.mode]) == 0: + self.create_partition() + elif self.max_file_size is not None and self.partitions[self.mode][-1].memory_size > self.max_file_size: self.create_partition() else: @@ -200,9 +204,8 @@ def load_directory(self, self.database.create_table(table_name='Training') self.database.create_table(table_name='Additional') - # 6. Shuffle Database indices - if self.shuffle and load_partitions: - self.shuffle_samples() + # 6. Index partitions + self.index_samples() def create_partition(self): """ @@ -248,17 +251,21 @@ def get_partition_objects(self) -> List[Database]: def get_partition_names(self) -> List[List[str]]: return [db.get_path() for db in self.partitions[self.mode]] - def load_next_partition(self): + def remove_empty_partitions(self): - # 1. Define next partition - self.partition_index[self.mode] = (self.partition_index[self.mode] + 1) % len(self.partitions[self.mode]) - self.current_partition = self.partitions[self.mode][self.partition_index[self.mode]] - self.database = Database(database_dir=self.database_dir, - database_name=self.current_partition).load() + for mode in self.modes: + if len(self.partitions[mode]) > 0 and self.partitions[mode][-1].nb_lines(table_name='Training') == 0: + database = self.partitions[mode].pop(-1) + path = database.get_path() + remove(join(path[0], f'{path[1]}.db')) + self.partition_names[mode].pop(-1) + self.partition_index[mode] -= 1 + self.json_content['nb_samples'][mode].pop(-1) + self.update_json(update_partitions_lists=True) - # 2. Shuffle - if self.shuffle: - self.shuffle_samples() + ################## + # JSON info file # + ################## def search_partitions_info(self): """ @@ -325,18 +332,6 @@ def get_data_shapes(self): db.close() return shapes - def remove_empty_partitions(self): - - for mode in self.modes: - if len(self.partitions[mode]) > 0 and self.partitions[mode][-1].nb_lines(table_name='Training') == 0: - database = self.partitions[mode].pop(-1) - path = database.get_path() - remove(join(path[0], f'{path[1]}.db')) - self.partition_names[mode].pop(-1) - self.partition_index[mode] -= 1 - self.json_content['nb_samples'][mode].pop(-1) - self.update_json(update_partitions_lists=True) - def update_json(self, update_partitions_lists: bool = False, update_nb_samples: bool = False, @@ -375,18 +370,34 @@ def update_json(self, with open(join(self.database_dir, 'dataset.json'), 'w') as json_file: json_dump(self.json_content, json_file, indent=3, cls=CustomJSONEncoder) - @property - def nb_samples(self) -> int: - return self.partitions[self.mode][-1].nb_lines(table_name='Training') + ######################### + # Database read / write # + ######################### - def shuffle_samples(self): - """ + def connect_handler(self, + handler: DatabaseHandler) -> None: + if handler.remote: + handler.init_remote(storing_partitions=self.get_partition_names(), + exchange_db=self.exchange.get_path()) + else: + handler.init(storing_partitions=self.get_partition_objects(), + exchange_db=self.exchange) + self.database_handlers.append(handler) - """ + def index_samples(self): - self.shuffle_pattern = arange(1, self.nb_samples + 1) + for i, nb_sample in enumerate(self.json_content['nb_samples'][self.mode]): + partition_indices = empty((nb_sample, 2), dtype=int) + partition_indices[:, 0] = i + partition_indices[:, 1] = arange(1, nb_sample + 1) + self.sample_indices = concatenate((self.sample_indices, partition_indices)) + self.sample_id = 0 if self.shuffle: - shuffle(self.shuffle_pattern) + shuffle(self.sample_indices) + + @property + def nb_samples(self) -> int: + return self.partitions[self.mode][-1].nb_lines(table_name='Training') def add_data(self, data_lines: Optional[List[int]] = None): @@ -408,28 +419,31 @@ def add_data(self, if self.partitions[self.mode][-1].memory_size > self.max_file_size: self.create_partition() - # 3. Update sample counter - self.current_sample = self.nb_samples + 1 - def get_data(self, - batch_size: int) -> List[int]: + batch_size: int) -> List[List[int]]: """ """ # 1. Check if dataset is loaded and if the current sample is not the last - if self.current_sample > self.nb_samples: - self.load_next_partition() - self.current_sample = 1 + if self.sample_id >= len(self.sample_indices): + self.index_samples() # 2. Update dataset index - idx = self.current_sample - self.current_sample += batch_size + idx = self.sample_id + self.sample_id += batch_size # 3. Get a batch of data - if self.shuffle: - return list(self.shuffle_pattern[idx:self.current_sample]) - return list(arange(idx, self.current_sample)) + lines = self.sample_indices[idx:self.sample_id].tolist() + + # 4. Ensure the batch has th good size + if len(lines) < batch_size: + lines += self.get_data(batch_size=batch_size - len(lines)) + return lines + + ############ + # Behavior # + ############ def close(self): """ @@ -459,6 +473,10 @@ def change_mode(self, mode: int) -> None: pass + ################# + # Normalization # + ################# + def compute_normalization(self) -> Dict[str, List[float]]: """ @@ -543,7 +561,7 @@ def update_normalization(self, @property def normalization(self) -> Dict[str, List[float]]: - return None if self.json_content['normalization'] == {} else self.json_content['normalization'] + return None if self.json_content['normalization'] == {} or not self.normalize else self.json_content['normalization'] def load_partitions_fields(self, partition: Database, diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 2d170320..21cd4c7d 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -35,7 +35,7 @@ def __init__(self, self.load_samples: bool = environment_config.load_samples self.simulations_per_step: int = environment_config.simulations_per_step self.max_wrong_samples_per_step: int = environment_config.max_wrong_samples_per_step - self.dataset_batch: Optional[List[int]] = None + self.dataset_batch: Optional[List[List[int]]] = None # Create the Visualizer self.visualizer: Optional[VedoVisualizer] = None @@ -53,7 +53,8 @@ def __init__(self, if environment_config.as_tcp_ip_client: self.server = environment_config.create_server(environment_manager=self, batch_size=batch_size, - visualization_db=None if visualization_db is None else visualization_db.get_path()) + visualization_db=None if visualization_db is None else + visualization_db.get_path()) self.data_manager.get_database().load() else: self.environment = environment_config.create_environment(visualization_db=visualization_db) diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index c9e04322..a75172c7 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -3,8 +3,7 @@ from os.path import join, isdir, isfile from numpy import ndarray, array -from SSD.Core.Storage.Database import Database - +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Utils.path import copy_dir, create_dir @@ -13,29 +12,23 @@ class NetworkManager: def __init__(self, network_config: BaseNetworkConfig, - manager: Optional[Any] = None, - session: str = 'sessions/default', - new_session: bool = True, pipeline: str = '', - data_db: Optional[Database] = None): + session: str = 'sessions/default', + new_session: bool = True): """ Deals with all the interactions with the neural network: predictions, saves, initialisation, loading, back-propagation, etc. :param network_config: Specialisation containing the parameters of the network manager. - :param manager: Manager that handle the network manager. + :param pipeline: Type of the Pipeline. :param session: Path to the session directory. :param new_session: Define the creation of new directories to store data. - :param is_training: If True prediction will cause tensors gradient creation. """ self.name: str = self.__class__.__name__ - # Managers architecture - self.manager: Any = manager - # Storage variables - self.data_db: Database = data_db + self.database_handler: DatabaseHandler = DatabaseHandler() self.batch: Optional[Any] = None self.session: str = session self.new_session: bool = new_session @@ -77,16 +70,16 @@ def __init__(self, self.network_dir = join(session, 'network/') self.load_network(which_network=network_config.which_network) - def change_database(self, database): - self.data_db = database + def get_database_handler(self): + return self.database_handler def link_clients(self, nb_clients: Optional[int] = None): if nb_clients is not None: fields = [(field_name, ndarray) for field_name in self.network.net_fields + self.network.pred_fields] - self.data_db.create_table(table_name='Prediction', fields=fields) + self.database_handler.create_fields(table_name='Exchange', fields=fields) for _ in range(nb_clients): - self.data_db.add_data(table_name='Prediction', data={}) + self.database_handler.add_data(table_name='Exchange', data={}) def load_network(self, which_network: int = -1) -> None: @@ -115,7 +108,7 @@ def load_network(self, def compute_prediction_and_loss(self, optimize: bool, - data_lines: List[int], + data_lines: List[List[int]], normalization: Optional[Dict[str, List[float]]] = None) -> Tuple[ndarray, Dict[str, float]]: """ Make a prediction with the data passed as argument, optimize or not the network @@ -132,11 +125,9 @@ def compute_prediction_and_loss(self, for side, fields in zip(['net', 'opt'], [self.network.net_fields, self.network.opt_fields]): # Get the batch from the Database - batch = self.data_db.get_lines(table_name='Training', - fields=fields, - lines_id=data_lines, - batched=True) - del batch['id'] + batch = self.database_handler.get_lines(table_name='Training', + fields=fields, + lines_id=data_lines) # Apply normalization and convert to tensor for field in batch.keys(): diff --git a/src/Manager/StatsManager.py b/src/Manager/StatsManager.py index ee3c9580..c119c89c 100644 --- a/src/Manager/StatsManager.py +++ b/src/Manager/StatsManager.py @@ -17,33 +17,29 @@ def generate_default_material(): class StatsManager: - """ - | Record all given values using the tensorboard framework. Open a tab in the navigator to inspect these values - during the training. - - :param str log_dir: Path of the created directory - :param Manager manager: Manager that handles the StatsManager - :param bool keep_losses: If True Allow saving loss to .csv file - """ def __init__(self, session: str, - manager: Any = None, keep_losses: bool = False): + """ + Record all given values using the tensorboard framework. Open a tab in the navigator to inspect these values + during the training. + + :param str log_dir: Path of the created directory + :param bool keep_losses: If True Allow saving loss to .csv file + """ self.name: str = self.__class__.__name__ # Init writer - self.manager = manager self.log_dir: str = join(session, 'stats/') self.writer: SummaryWriter = SummaryWriter(self.log_dir) # Open Tensorboard - if not self.manager.debug_session: - tb = program.TensorBoard() - tb.configure(argv=[None, '--logdir', self.log_dir]) - url = tb.launch() - w_open(url) + tb = program.TensorBoard() + tb.configure(argv=[None, '--logdir', self.log_dir]) + url = tb.launch() + w_open(url) # Values self.mean: ndarray = full(4, inf) # Contains in the 1st dimension the mean, and 2nd the variance of the mean @@ -51,15 +47,6 @@ def __init__(self, self.keep_losses: bool = keep_losses self.tag_dict: Dict[str, int] = {} - def get_manager(self) -> Any: - """ - | Return the Manager of the StatsManager. - - :return: Manager that handles the StatsManager - """ - - return self.manager - def add_train_batch_loss(self, value: float, count: int) -> None: """ | Add batch loss to tensorboard framework. Also compute mean and variance. diff --git a/src/Pipelines/BaseDataGeneration.py b/src/Pipelines/BaseDataGeneration.py index 7a612e44..25342895 100644 --- a/src/Pipelines/BaseDataGeneration.py +++ b/src/Pipelines/BaseDataGeneration.py @@ -52,11 +52,11 @@ def __init__(self, session_name=session_name).split(sep)[-1] # Create a DataManager - self.data_manager = DataManager(database_config=database_config, + self.data_manager = DataManager(pipeline=self, + database_config=database_config, environment_config=environment_config, session=join(session_dir, session_name), new_session=new_session, - pipeline='data_generation', produce_data=True, batch_size=batch_size) diff --git a/src/Pipelines/BasePipeline.py b/src/Pipelines/BasePipeline.py index f62637b0..9f61ef1f 100644 --- a/src/Pipelines/BasePipeline.py +++ b/src/Pipelines/BasePipeline.py @@ -62,6 +62,7 @@ def __init__(self, self.session_dir = session_dir self.session_name = session_name self.new_session = new_session + self.type = pipeline # Main manager self.manager: Optional[Manager] = None diff --git a/src/Pipelines/BaseTraining.py b/src/Pipelines/BaseTraining.py index 521bdad4..03f84a85 100644 --- a/src/Pipelines/BaseTraining.py +++ b/src/Pipelines/BaseTraining.py @@ -1,14 +1,17 @@ from typing import Optional from sys import stdout -from os.path import join, isfile +from os.path import join, isfile, exists, sep from datetime import datetime from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline -from DeepPhysX.Core.Manager.Manager import Manager +from DeepPhysX.Core.Manager.DataManager import DataManager +from DeepPhysX.Core.Manager.NetworkManager import NetworkManager +from DeepPhysX.Core.Manager.StatsManager import StatsManager from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Utils.progressbar import Progressbar +from DeepPhysX.Core.Utils.path import get_first_caller, create_dir class BaseTraining(BasePipeline): @@ -19,11 +22,11 @@ def __init__(self, environment_config: Optional[BaseEnvironmentConfig] = None, session_dir: str = 'sessions', session_name: str = 'training', + new_session: bool = True, epoch_nb: int = 0, batch_nb: int = 0, batch_size: int = 0, - new_session: bool = True, - debug_session: bool = False): + debug: bool = False): """ BaseTraining implements the main loop that defines the training process of an artificial neural network. Training can be launched with several data sources (from a Dataset, from an Environment, from combined sources). @@ -34,11 +37,11 @@ def __init__(self, :param environment_config: Specialisation containing the parameters of the environment manager. :param session_dir: Relative path to the directory which contains sessions directories. :param session_name: Name of the new the session directory. + :param new_session: Define the creation of new directories to store data. :param epoch_nb: Number of epochs to run. :param batch_nb: Number of batches to use. :param batch_size: Number of samples in a single batch. - :param new_session: Define the creation of new directories to store data. - :param debug_session: If True, main training features will not be launched. + :param debug: If True, main training features will not be launched. """ BasePipeline.__init__(self, @@ -47,8 +50,47 @@ def __init__(self, environment_config=environment_config, session_dir=session_dir, session_name=session_name, + new_session=new_session, pipeline='training') + # Define the session repository + root = get_first_caller() + session_dir = join(root, session_dir) + + # Create a new session if required + if not new_session: + new_session = not exists(join(session_dir, session_name)) + if new_session: + session_name = create_dir(session_dir=session_dir, + session_name=session_name).split(sep)[-1] + self.session = join(session_dir, session_name) + + # Configure 'produce_data' flag + if environment_config is None and database_config.existing_dir is None: + raise ValueError(f"[{self.name}] No data source provided.") + produce_data = database_config.existing_dir is None + + # Create a DataManager + self.data_manager = DataManager(pipeline=self, + database_config=database_config, + environment_config=environment_config, + session=self.session, + new_session=new_session, + produce_data=produce_data, + batch_size=batch_size) + self.batch_size = batch_size + + # Create a NetworkMmanager + self.network_manager = NetworkManager(network_config=network_config, + pipeline=self.type, + session=self.session, + new_session=new_session) + self.data_manager.connect_handler(self.network_manager.get_database_handler()) + self.network_manager.link_clients(self.data_manager.nb_environment) + + # Create a StatsManager + self.stats_manager = StatsManager(session=self.session) if not debug else None + # Training variables self.epoch_nb = epoch_nb self.epoch_id = 0 @@ -57,12 +99,7 @@ def __init__(self, self.batch_id = 0 self.nb_samples = batch_nb * batch_size * epoch_nb self.loss_dict = None - self.debug = debug_session - - # Configure 'produce_data' flag - if environment_config is None and database_config.existing_dir is None: - raise ValueError(f"[{self.name}] No data source provided.") - produce_data = database_config.existing_dir is None + self.debug = debug # Progressbar if not self.debug: @@ -74,17 +111,7 @@ def __init__(self, self.progress_bar = Progressbar(start=0, stop=self.batch_nb * self.epoch_nb, c='orange', title=f'Epoch n°{epoch_id}/{epoch_nb} - Batch n°{batch_id}/{batch_nb}') - self.manager = Manager(network_config=self.network_config, - database_config=self.database_config, - environment_config=self.environment_config, - session_dir=session_dir, - session_name=session_name, - new_session=new_session, - pipeline='training', - produce_data=produce_data, - batch_size=batch_size, - debug_session=debug_session) - self.save_info_file(self.manager.session) + self.save_info_file() def execute(self) -> None: """ @@ -152,8 +179,11 @@ def optimize(self) -> None: Pulls data, run a prediction and an optimizer step. """ - self.manager.get_data(self.epoch_id) - _, self.loss_dict = self.manager.optimize_network() + self.data_manager.get_data(epoch=self.epoch_id, + animate=True) + _, self.loss_dict = self.network_manager.compute_prediction_and_loss(data_lines=self.data_manager.data_lines, + normalization=self.data_manager.normalization, + optimize=True) def batch_count(self) -> None: """ @@ -167,13 +197,14 @@ def batch_end(self) -> None: Called one at the end of a batch production. """ - self.manager.stats_manager.add_train_batch_loss(self.loss_dict['loss'], - self.epoch_id * self.batch_nb + self.batch_id) - for key in self.loss_dict.keys(): - if key != 'loss': - self.manager.stats_manager.add_custom_scalar(tag=key, - value=self.loss_dict[key], - count=self.epoch_id * self.batch_nb + self.batch_id) + if self.stats_manager is not None: + self.stats_manager.add_train_batch_loss(self.loss_dict['loss'], + self.epoch_id * self.batch_nb + self.batch_id) + for key in self.loss_dict.keys(): + if key != 'loss': + self.stats_manager.add_custom_scalar(tag=key, + value=self.loss_dict[key], + count=self.epoch_id * self.batch_nb + self.batch_id) def epoch_count(self) -> None: """ @@ -187,29 +218,32 @@ def epoch_end(self) -> None: Called one at the end of each epoch. """ - self.manager.stats_manager.add_train_epoch_loss(self.loss_dict['loss'], self.epoch_id) + if self.stats_manager is not None: + self.stats_manager.add_train_epoch_loss(self.loss_dict['loss'], self.epoch_id) def save_network(self) -> None: """ Store the network parameters in the corresponding directory. """ - self.manager.save_network() + self.network_manager.save_network() def train_end(self) -> None: """ Called once at the end of the training pipeline. """ - self.manager.close() + self.data_manager.close() + self.network_manager.close() + if self.stats_manager is not None: + self.stats_manager.close() - def save_info_file(self, - directory: str) -> None: + def save_info_file(self) -> None: """ Save a .txt file that provides a template for user notes and the description of all the components. """ - filename = join(directory, 'info.txt') + filename = join(self.session, 'info.txt') date_time = datetime.now().strftime('%d/%m/%Y %H:%M:%S') if not isfile(filename): f = open(filename, "w+") @@ -220,14 +254,17 @@ def save_info_file(self, # Listing every component descriptions f.write("## List of Components Parameters ##\n") f.write(str(self)) - f.write(str(self.manager)) + f.write(str(self.network_manager)) + f.write(str(self.data_manager)) + if self.stats_manager is not None: + f.write(str(self.stats_manager)) f.close() def __str__(self) -> str: description = "\n" description += f"# {self.__class__.__name__}\n" - description += f" Session directory: {self.manager.session}\n" + description += f" Session directory: {self.session}\n" description += f" Number of epochs: {self.epoch_nb}\n" description += f" Number of batches per epoch: {self.batch_nb}\n" description += f" Number of samples per batch: {self.batch_size}\n" From 19d9d15d6e2cbd460c0fd1359200a7aed55cd826 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 10:54:21 +0200 Subject: [PATCH 19/61] Adapt online Training pipeline in local mode for DB Handlers. --- src/Database/DatabaseHandler.py | 9 ++++++--- src/Environment/BaseEnvironmentConfig.py | 4 ++-- src/Manager/DataManager.py | 19 ++++++++++--------- src/Manager/DatabaseManager.py | 1 + src/Manager/EnvironmentManager.py | 3 ++- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py index bd362fde..7eae31f8 100644 --- a/src/Database/DatabaseHandler.py +++ b/src/Database/DatabaseHandler.py @@ -86,10 +86,13 @@ def get_fields(self, def add_data(self, table_name: str, - data: Dict[str, Any]) -> int: + data: Dict[str, Any]) -> Union[int, List[int]]: - database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[-1] - return database.add_data(table_name=table_name, data=data) + if table_name == 'Exchange': + return self.__exchange_db.add_data(table_name=table_name, data=data) + else: + return [len(self.__storing_partitions) - 1, + self.__storing_partitions[-1].add_data(table_name=table_name, data=data)] def update(self, table_name: str, diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index 8447a36e..825583c8 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -5,8 +5,6 @@ from subprocess import run from sys import modules, executable -from SSD.Core.Storage.Database import Database - from DeepPhysX.Core.AsyncSocket.TcpIpServer import TcpIpServer from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer @@ -24,6 +22,7 @@ def __init__(self, max_wrong_samples_per_step: int = 10, load_samples: bool = False, only_first_epoch: bool = True, + always_produce: bool = False, visualizer: Optional[Type[VedoVisualizer]] = None, record_wrong_samples: bool = False): """ @@ -84,6 +83,7 @@ def __init__(self, self.max_wrong_samples_per_step: int = max_wrong_samples_per_step self.load_samples: bool = load_samples self.only_first_epoch: bool = only_first_epoch + self.always_produce: bool = always_produce # Visualizer variables self.visualizer: Optional[Type[VedoVisualizer]] = visualizer diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index edae03a1..0cb80f65 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -100,7 +100,7 @@ def get_data(self, # Get data from Environment(s) if used and if the data should be created at this epoch if self.environment_manager is not None and self.produce_data and \ - (epoch == 0 or not self.environment_manager.only_first_epoch): + (epoch == 0 or self.environment_manager.always_produce): self.data_lines = self.environment_manager.get_data(animate=animate) self.database_manager.add_data(self.data_lines) @@ -108,14 +108,15 @@ def get_data(self, else: self.data_lines = self.database_manager.get_data(batch_size=self.batch_size) # Dispatch a batch to clients - if self.environment_manager is not None and self.environment_manager.load_samples and \ - (epoch == 0 or not self.environment_manager.only_first_epoch): - self.environment_manager.dispatch_batch(data_lines=self.data_lines, - animate=animate) - # Environment is no longer used - elif self.environment_manager is not None: - self.environment_manager.close() - self.environment_manager = None + if self.environment_manager is not None: + if self.environment_manager.load_samples and \ + (epoch == 0 or not self.environment_manager.only_first_epoch): + self.environment_manager.dispatch_batch(data_lines=self.data_lines, + animate=animate) + # Environment is no longer used + else: + self.environment_manager.close() + self.environment_manager = None # Prediction pipeline else: diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index b31a4805..32e80dc8 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -460,6 +460,7 @@ def close(self): for mode in self.modes: for database in self.partitions[mode]: database.close() + self.exchange.close(erase_file=True) # Remove prediction pipeline DB if self.pipeline == 'prediction' and not self.produce_data: diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 21cd4c7d..7241ae1f 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -33,6 +33,7 @@ def __init__(self, self.batch_size: int = batch_size self.only_first_epoch: bool = environment_config.only_first_epoch self.load_samples: bool = environment_config.load_samples + self.always_produce: bool = environment_config.always_produce self.simulations_per_step: int = environment_config.simulations_per_step self.max_wrong_samples_per_step: int = environment_config.max_wrong_samples_per_step self.dataset_batch: Optional[List[List[int]]] = None @@ -105,7 +106,7 @@ def get_data_from_server(self, def get_data_from_environment(self, animate: bool = True, - request_prediction: bool = False) -> List[int]: + request_prediction: bool = False) -> List[List[int]]: """ Compute a batch of data directly from Environment. From 8db542477f306f80fc89c9720baf23e932c6f653 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 12:01:34 +0200 Subject: [PATCH 20/61] Adapt Prediction pipeline in local mode for DB Handlers. --- src/Environment/BaseEnvironment.py | 10 ++--- src/Manager/DataManager.py | 14 ++++--- src/Manager/DatabaseManager.py | 39 +++++------------- src/Manager/EnvironmentManager.py | 22 ++++++---- src/Manager/NetworkManager.py | 13 +++--- src/Pipelines/BasePrediction.py | 66 +++++++++++++++++++----------- src/Pipelines/BaseTraining.py | 10 +---- 7 files changed, 86 insertions(+), 88 deletions(-) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 2c23b512..1bc31289 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -274,7 +274,7 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: if self.instance_id != 0: self.__database_handler.load() self.__first_add[1] = False - required_fields = list(set(self.__database_handler.get_fields(table_name='Prediction')) - {'id'}) + required_fields = list(set(self.__database_handler.get_fields(table_name='Exchange')) - {'id'}) for field in kwargs.keys(): if field not in required_fields: raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." @@ -288,12 +288,12 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: if self.environment_manager.data_manager is None: raise ValueError("Cannot request prediction if DataManager does not exist") # Get a prediction - self.__database_handler.update(table_name='Prediction', + self.__database_handler.update(table_name='Exchange', data=kwargs, line_id=self.instance_id) self.environment_manager.data_manager.get_prediction(self.instance_id) - data_pred = self.__database_handler.get_line(table_name='Prediction', - line_id=[0, self.instance_id]) + data_pred = self.__database_handler.get_line(table_name='Exchange', + line_id=self.instance_id) del data_pred['id'] return data_pred @@ -303,7 +303,7 @@ def _get_prediction(self): """ training_data = self.__data_training.copy() - required_fields = self.__database_handler.get_fields(table_name='Prediction') + required_fields = self.__database_handler.get_fields(table_name='Exchange') for field in self.__data_training.keys(): if field not in required_fields: del training_data[field] diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 0cb80f65..be29997a 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -51,6 +51,7 @@ def __init__(self, if environment_config is not None: self.environment_manager = EnvironmentManager(environment_config=environment_config, data_manager=self, + pipeline=pipeline.type, session=session, batch_size=batch_size) @@ -80,8 +81,7 @@ def nb_environment(self): def get_data(self, epoch: int = 0, - animate: bool = True, - request_prediction: bool = False) -> None: + animate: bool = True) -> None: """ Fetch data from the EnvironmentManager or the DatabaseManager according to the context. @@ -122,16 +122,18 @@ def get_data(self, else: # Get data from Dataset - if self.produce_data: + if self.environment_manager.load_samples: self.data_lines = self.database_manager.get_data(batch_size=1) self.environment_manager.dispatch_batch(data_lines=self.data_lines, animate=animate, - request_prediction=True) + request_prediction=True, + save_data=self.produce_data) # Get data from Environment else: self.data_lines = self.environment_manager.get_data(animate=animate, - request_prediction=True) - if self.database_manager is not None: + request_prediction=True, + save_data=self.produce_data) + if self.produce_data: self.database_manager.add_data(self.data_lines) def get_prediction(self, diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 32e80dc8..d0e3a8d6 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -131,13 +131,7 @@ def __init__(self, # Prediction case else: - - # Generate data - if produce_data: - self.load_directory() - - else: - self.load_directory(load_partitions=False) + self.load_directory() # Finally create an exchange database self.exchange = Database(database_dir=self.database_dir, @@ -149,7 +143,6 @@ def __init__(self, ######################### def load_directory(self, - load_partitions: bool = True, rename_partitions: bool = False): """ @@ -187,22 +180,15 @@ def load_directory(self, dst=join(self.database_dir, f'{self.partition_names[mode][i]}.db')) # 5. Load the partitions - if load_partitions: - for mode in self.modes: - for name in self.partition_names[mode]: - db = Database(database_dir=self.database_dir, - database_name=name).load() - self.partitions[mode].append(db) - if len(self.partitions[self.mode]) == 0: - self.create_partition() - elif self.max_file_size is not None and self.partitions[self.mode][-1].memory_size > self.max_file_size: - self.create_partition() - - else: - self.database = Database(database_dir=self.database_dir, - database_name='temp').new() - self.database.create_table(table_name='Training') - self.database.create_table(table_name='Additional') + for mode in self.modes: + for name in self.partition_names[mode]: + db = Database(database_dir=self.database_dir, + database_name=name).load() + self.partitions[mode].append(db) + if len(self.partitions[self.mode]) == 0: + self.create_partition() + elif self.max_file_size is not None and self.partitions[self.mode][-1].memory_size > self.max_file_size: + self.create_partition() # 6. Index partitions self.index_samples() @@ -462,11 +448,6 @@ def close(self): database.close() self.exchange.close(erase_file=True) - # Remove prediction pipeline DB - if self.pipeline == 'prediction' and not self.produce_data: - path = self.database.get_path() - remove(join(path[0], f'{path[1]}.db')) - def change_mode(self, mode: int) -> None: """ diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 7241ae1f..b4d33661 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -13,6 +13,7 @@ class EnvironmentManager: def __init__(self, environment_config: BaseEnvironmentConfig, data_manager: Optional[Any] = None, + pipeline: str = '', session: str = 'sessions/default', batch_size: int = 1): """ @@ -48,10 +49,11 @@ def __init__(self, visualization_db = self.visualizer.get_database() # Create a single Environment or a TcpIpServer - self.number_of_thread: int = environment_config.number_of_thread + force_local = pipeline == 'prediction' + self.number_of_thread: int = 1 if force_local else environment_config.number_of_thread self.server: Optional[TcpIpServer] = None self.environment: Optional[BaseEnvironment] = None - if environment_config.as_tcp_ip_client: + if environment_config.as_tcp_ip_client and not force_local: self.server = environment_config.create_server(environment_manager=self, batch_size=batch_size, visualization_db=None if visualization_db is None else @@ -106,6 +108,7 @@ def get_data_from_server(self, def get_data_from_environment(self, animate: bool = True, + save_data: bool = True, request_prediction: bool = False) -> List[List[int]]: """ Compute a batch of data directly from Environment. @@ -134,14 +137,15 @@ def get_data_from_environment(self, # 1.3 Add the produced sample to the batch if the sample is validated if self.environment.check_sample(): nb_sample += 1 - if update_line is None: - new_line = self.environment._send_training_data() - dataset_lines.append(new_line) - else: - self.environment._update_training_data(update_line) - dataset_lines.append(update_line) if request_prediction: self.environment._get_prediction() + if save_data: + if update_line is None: + new_line = self.environment._send_training_data() + dataset_lines.append(new_line) + else: + self.environment._update_training_data(update_line) + dataset_lines.append(update_line) self.environment._reset_training_data() return dataset_lines @@ -165,6 +169,7 @@ def dispatch_batch_to_server(self, def dispatch_batch_to_environment(self, data_lines: List[int], animate: bool = True, + save_data: bool = True, request_prediction: bool = False) -> None: """ Send samples from dataset to the Environments. Get back the training data. @@ -178,6 +183,7 @@ def dispatch_batch_to_environment(self, self.dataset_batch = data_lines.copy() # Get data self.get_data_from_environment(animate=animate, + save_data=save_data, request_prediction=request_prediction) def request_prediction(self): diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index a75172c7..093a92a0 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -174,10 +174,9 @@ def compute_online_prediction(self, # Get Network data normalization = {} if normalization is None else normalization - sample = self.data_db.get_line(table_name='Prediction', - fields=self.network.net_fields, - line_id=instance_id) - del sample['id'] + sample = self.database_handler.get_line(table_name='Exchange', + fields=self.network.net_fields, + line_id=instance_id) # Apply normalization and convert to tensor for field in sample.keys(): @@ -201,9 +200,9 @@ def compute_online_prediction(self, normalization=normalization[field], reverse=True) data_pred[field].reshape(-1) - self.data_db.update(table_name='Prediction', - data=data_pred, - line_id=instance_id) + self.database_handler.update(table_name='Exchange', + data=data_pred, + line_id=instance_id) @classmethod def normalize_data(cls, diff --git a/src/Pipelines/BasePrediction.py b/src/Pipelines/BasePrediction.py index 66721cbd..de5c3778 100644 --- a/src/Pipelines/BasePrediction.py +++ b/src/Pipelines/BasePrediction.py @@ -1,10 +1,13 @@ from typing import Optional +from os.path import join, exists +from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline +from DeepPhysX.Core.Manager.DataManager import DataManager +from DeepPhysX.Core.Manager.NetworkManager import NetworkManager from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline -from DeepPhysX.Core.Manager.Manager import Manager +from DeepPhysX.Core.Utils.path import get_first_caller class BasePrediction(BasePipeline): @@ -15,7 +18,7 @@ def __init__(self, database_config: Optional[BaseDatabaseConfig] = None, session_dir: str = 'session', session_name: str = 'training', - nb_steps: int = -1, + step_nb: int = -1, record: bool = False): """ BasePrediction is a pipeline defining the running process of an artificial neural network. @@ -26,7 +29,7 @@ def __init__(self, :param database_config: Specialisation containing the parameters of the dataset manager. :param session_name: Name of the newly created directory if session is not defined. :param session_dir: Name of the directory in which to write all the necessary data. - :param nb_steps: Number of simulation step to play. + :param step_nb: Number of simulation step to play. :param record: Save or not the prediction data. """ @@ -36,24 +39,36 @@ def __init__(self, environment_config=environment_config, session_dir=session_dir, session_name=session_name, + new_session=False, pipeline='prediction') + # Define the session repository + root = get_first_caller() + session_dir = join(root, session_dir) + if not exists(join(session_dir, session_name)): + raise ValueError(f"[{self.name}] The following directory does not exist: {join(session_dir, session_name)}") + self.session = join(session_dir, session_name) + + # Create a DataManager + self.data_manager = DataManager(pipeline=self, + database_config=database_config, + environment_config=environment_config, + session=self.session, + new_session=False, + produce_data=record, + batch_size=1) + + # Create a NetworkManager + self.network_manager = NetworkManager(network_config=network_config, + pipeline=self.type, + session=self.session, + new_session=False) + self.data_manager.connect_handler(self.network_manager.get_database_handler()) + self.network_manager.link_clients(self.data_manager.nb_environment) + # Prediction variables - self.nb_samples = nb_steps - self.idx_step = 0 - - # Tell if data is recording while predicting - self.record_data = record - - self.manager = Manager(network_config=self.network_config, - database_config=database_config, - environment_config=self.environment_config, - session_dir=session_dir, - session_name=session_name, - new_session=False, - pipeline='prediction', - produce_data=record, - batch_size=1) + self.step_nb = step_nb + self.step_id = 0 def execute(self) -> None: """ @@ -81,8 +96,8 @@ def prediction_condition(self) -> bool: Condition that characterize the end of the prediction pipeline. """ - running = self.idx_step < self.nb_samples if self.nb_samples > 0 else True - self.idx_step += 1 + running = self.step_id < self.step_nb if self.step_nb > 0 else True + self.step_id += 1 return running def sample_begin(self) -> None: @@ -97,7 +112,8 @@ def predict(self) -> None: Pull the data from the manager and return the prediction. """ - self.manager.get_data(animate=True) + self.data_manager.get_data(epoch=0, + animate=True) def sample_end(self) -> None: """ @@ -111,13 +127,13 @@ def run_end(self) -> None: Called once at the end of the prediction pipeline. """ - self.manager.close() + self.data_manager.close() + self.network_manager.close() def __str__(self) -> str: description = "" description += f"Running statistics :\n" - description += f"Number of simulation step: {self.nb_samples}\n" - description += f"Record data : {self.record_data}\n" + description += f"Number of simulation step: {self.step_nb}\n" return description diff --git a/src/Pipelines/BaseTraining.py b/src/Pipelines/BaseTraining.py index 03f84a85..9578a6ef 100644 --- a/src/Pipelines/BaseTraining.py +++ b/src/Pipelines/BaseTraining.py @@ -80,7 +80,7 @@ def __init__(self, batch_size=batch_size) self.batch_size = batch_size - # Create a NetworkMmanager + # Create a NetworkManager self.network_manager = NetworkManager(network_config=network_config, pipeline=self.type, session=self.session, @@ -130,7 +130,6 @@ def execute(self) -> None: self.batch_end() self.epoch_count() self.epoch_end() - self.save_network() self.train_end() def train_begin(self) -> None: @@ -220,12 +219,6 @@ def epoch_end(self) -> None: if self.stats_manager is not None: self.stats_manager.add_train_epoch_loss(self.loss_dict['loss'], self.epoch_id) - - def save_network(self) -> None: - """ - Store the network parameters in the corresponding directory. - """ - self.network_manager.save_network() def train_end(self) -> None: @@ -233,6 +226,7 @@ def train_end(self) -> None: Called once at the end of the training pipeline. """ + self.network_manager.save_network(last_save=True) self.data_manager.close() self.network_manager.close() if self.stats_manager is not None: From 4e4554bde969c3ec6239a7d229f7fd75d88478ac Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 13:02:41 +0200 Subject: [PATCH 21/61] Remove unused Manages. --- src/Manager/DatasetManager.py | 749 ---------------------------------- src/Manager/Manager.py | 144 ------- 2 files changed, 893 deletions(-) delete mode 100644 src/Manager/DatasetManager.py delete mode 100644 src/Manager/Manager.py diff --git a/src/Manager/DatasetManager.py b/src/Manager/DatasetManager.py deleted file mode 100644 index 02257d64..00000000 --- a/src/Manager/DatasetManager.py +++ /dev/null @@ -1,749 +0,0 @@ -from typing import Any, Dict, Tuple, List, Optional, Union -from os.path import join as osPathJoin -from os.path import isfile, isdir, abspath -from os import listdir, symlink, sep -from json import dump as json_dump -from json import load as json_load -from numpy import load, squeeze, ndarray, concatenate, float64 - -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig -from DeepPhysX.Core.Utils.path import create_dir -from DeepPhysX.Core.Utils.jsonUtils import CustomJSONEncoder - - -class DatasetManager: - - def __init__(self, - dataset_config: BaseDatasetConfig, - session: str, - data_manager: Optional[Any] = None, - new_session: bool = True, - training: bool = True, - offline: bool = False, - store_data: bool = True): - """ - | DatasetManager handle all operations with input / output files. Allows saving and read tensors from files. - - :param dataset_config: Specialisation containing the parameters of the dataset manager - :param data_manager: DataManager that handles the DatasetManager - :param new_session: Define the creation of new directories to store data - :param training: True if this session is a network training - :param offline: True if the session is done offline - :param store_data: Format {\'in\': bool, \'out\': bool} save the tensor when bool is True - """ - - self.name: str = self.__class__.__name__ - self.data_manager: Optional[Any] = data_manager - - # Checking arguments - if dataset_config is not None and not isinstance(dataset_config, BaseDatasetConfig): - raise TypeError(f"[{self.name}] The dataset config must be a BaseDatasetConfig object.") - if type(session) != str: - raise TypeError(f"[{self.name}] The session name must be a str.") - elif not isdir(session): - raise ValueError(f"[{self.name}] Given 'session' does not exists: {session}") - if type(new_session) != bool: - raise TypeError(f"[{self.name}] The 'new_network' argument must be a boolean.") - if type(training) != bool: - raise TypeError(f"[{self.name}] The 'train' argument must be a boolean.") - if type(store_data) != bool: - raise TypeError(f"[{self.name}] The 'store_data' argument must be a boolean.") - - # Create the Dataset object (default if no config specified) - dataset_config = BaseDatasetConfig() if dataset_config is None else dataset_config - self.dataset = dataset_config.create_dataset() - - # Dataset parameters - self.max_size: int = self.dataset.max_size - self.shuffle_dataset: bool = dataset_config.shuffle_dataset - self.record_data: bool = store_data - self.first_add: bool = True - self.__writing: bool = False - self.normalize: bool = dataset_config.normalize - self.normalization_security = dataset_config.recompute_normalization - self.offline = offline - - # Dataset modes - self.modes: Dict[str, int] = {'Training': 0, 'Validation': 1, 'Running': 2} - self.mode: int = self.modes['Training'] if training else self.modes['Running'] - self.mode = self.mode if dataset_config.use_mode is None else self.modes[dataset_config.use_mode] - self.last_loaded_dataset_mode: int = self.mode - - # Dataset partitions - session_name = session.split(sep)[-1] - self.partitions_templates: Tuple[str, str, str] = (session_name + '_training_{}_{}.npy', - session_name + '_validation_{}_{}.npy', - session_name + '_running_{}_{}.npy') - self.fields: List[str] = ['input', 'output'] - self.list_partitions: Dict[str, Optional[List[List[ndarray]]]] = { - 'input': [[], [], []] if self.record_data else None, - 'output': [[], [], []] if self.record_data else None} - self.idx_partitions: List[int] = [0, 0, 0] - self.current_partition_path: Dict[str, Optional[str]] = {'input': None, 'output': None} - - # Dataset loading with multiple partitions variables - self.mul_part_list_path: Optional[List[Dict[str, str]]] = None - self.mul_part_slices: Optional[List[List[int]]] = None - self.mul_part_idx: Optional[int] = None - - # Dataset Json file - self.json_filename: str = 'dataset.json' - self.json_empty: Dict[str, Dict[str, Union[List[int], Dict[Any, Any]]]] = {'data_shape': {}, - 'nb_samples': {mode: [] for mode in - self.modes}, - 'partitions': {mode: {} for mode in - self.modes}, - 'normalization': {}} - self.json_dict: Dict[str, Dict[str, Union[List[int], Dict[Any, Any]]]] = self.json_empty.copy() - self.json_found: bool = False - - # Dataset repository - self.session: str = session - dataset_dir: str = dataset_config.dataset_dir - self.new_session: bool = new_session - self.__new_dataset: bool = False - - # Training - if training: - # New training session - if new_session: - # New training session with new dataset - if dataset_dir is None: - self.dataset_dir: str = create_dir(dir_path=osPathJoin(self.session, 'dataset/'), - dir_name='dataset') - self.__new_dataset = True - # New training session with existing dataset - else: - if dataset_dir[-1] != "/": - dataset_dir += "/" - if dataset_dir[-8:] != "dataset/": - dataset_dir += "dataset/" - self.dataset_dir = dataset_dir - if abspath(self.dataset_dir) != osPathJoin(self.session, 'dataset'): - symlink(abspath(self.dataset_dir), osPathJoin(self.session, 'dataset')) - self.load_directory() - # Special case: adding data in existing Dataset with DataGeneration - else: - self.load_directory(load_data=False) - self.__new_dataset = True - # Existing training session - else: - self.dataset_dir = osPathJoin(self.session, 'dataset/') - self.load_directory() - # Prediction - else: - # Saving running data - if dataset_dir is None: - self.dataset_dir = osPathJoin(self.session, 'dataset/') - self.__new_dataset = True - # self.create_running_partitions() - self.load_directory(load_data=False) - # Loading partitions - else: - if dataset_dir[-1] != "/": - dataset_dir += "/" - if dataset_dir[-8:] != "dataset/": - dataset_dir += "dataset/" - self.dataset_dir = dataset_dir - self.load_directory() - - def get_data_manager(self) -> Any: - """ - | Return the Manager of the DataManager. - - :return: DataManager that handle The DatasetManager - """ - - return self.data_manager - - def add_data(self, data: Dict[str, Union[ndarray, Dict[str, ndarray]]]) -> None: - """ - | Push the data in the dataset. If max size is reached generate a new partition and write into it. - - :param data: Format {'input':numpy.ndarray, 'output':numpy.ndarray} contain in 'input' input tensors and - in 'output' output tensors - :type data: Dict[str, Union[ndarray, Dict[str, ndarray]]] - """ - - # 1. If first add, create first partitions - if self.first_add: - self.__writing = True - if 'additional_fields' in data: - self.register_new_fields(list(data['additional_fields'].keys())) - self.create_partitions() - - # 2. Add network data - for field in ['input', 'output']: - if self.record_data: - self.dataset.add(field, data[field], self.current_partition_path[field]) - - # 3. Add additional data - # 3.1 If there is additional data, convert field names then add each field - if 'additional_fields' in data.keys(): - # Check all registered are in additional data - for field in self.fields[2:]: - if field not in data['additional_fields']: - raise ValueError(f"[{self.name}] No data received for the additional field {field}.") - # Add each field to the dataset - for field in data['additional_fields']: - self.dataset.add(field, data['additional_fields'][field], self.current_partition_path[field]) - # 3.2 If there is no additional data but registered additional data - elif 'additional_fields' not in data.keys() and len(self.fields) > 2: - raise ValueError(f"[{self.name}] No data received for the additional fields {self.fields[:2]}") - - # 4. Update json file - self.update_json(update_nb_samples=True) - if self.first_add: - self.update_json(update_partitions_lists=True, update_shapes=True) - self.first_add = False - if self.normalize and not self.offline and self.mode == 0: - self.update_json(update_normalization=True) - - # 5. Check the size of the dataset - if self.dataset.memory_size() > self.max_size: - self.save_data() - self.create_partitions() - self.update_json(update_partitions_lists=True, update_nb_samples=True) - self.dataset.empty() - - def get_data(self, get_inputs: bool, get_outputs: bool, batch_size: int = 1, - batched: bool = True) -> Dict[str, ndarray]: - """ - | Fetch tensors from the dataset or reload partitions if dataset is empty or specified. - - :param bool get_inputs: If True fill the data['input'] field - :param bool get_outputs: If True fill the data['output'] field - :param int batch_size: Size of a batch - :param bool batched: Add an empty dimension before [4,100] -> [0,4,100] - - :return: Dict of format {'input':numpy.ndarray, 'output':numpy.ndarray} filled with desired data - """ - - # Do not allow overwriting partitions - self.__writing = False - - # 1. Check if a dataset is loaded and if the current sample is not the last - if self.current_partition_path['input'] is None or self.dataset.current_sample >= self.dataset.nb_samples: - # if not force_partition_reload: - # return None - self.load_partitions() - if self.shuffle_dataset: - self.dataset.shuffle() - self.dataset.current_sample = 0 - - # 2. Update dataset indices with batch size - idx = self.dataset.current_sample - self.dataset.current_sample += batch_size - - # 3. Get a batch of each data field - data = {} - fields = self.fields[2:] - fields += ['input'] if get_inputs else [] - fields += ['output'] if get_outputs else [] - for field in fields: - # Network input and output fields - if field in ['input', 'output']: - data[field] = self.dataset.get(field, idx, idx + batch_size) - if not batched: - data[field] = squeeze(data[field], axis=0) - # Additional data fields - else: - if 'additional_fields' not in data.keys(): - data['additional_fields'] = {} - data['additional_fields'][field] = self.dataset.get(field, idx, idx + batch_size) - if not batched: - data['additional_fields'][field] = squeeze(data['additional_fields'][field], axis=0) - - # 4. Ensure each field received the same batch size - if data['input'].shape[0] != data['output'].shape[0]: - raise ValueError(f"[{self.name}] Size of loaded batch mismatch for input and output " - f"(in: {data['input'].shape} / out: {data['output'].shape}") - if '__data_additional' in data.keys(): - for field in data['additional_fields'].keys(): - if data['additional_fields'][field].shape[0] != data['input'].shape[0]: - raise ValueError(f"[{self.name}] Size of loaded batch mismatch for additional field {field} " - f"(net: {data['input'].shape} / {field}: {data['additional_fields'][field].shape}") - - # 5. Ensure the batch has the good size, otherwise load new data to complete it - if data['input'].shape[0] < batch_size: - # Load next samples from the dataset - self.load_partitions() - if self.shuffle_dataset: - self.dataset.shuffle() - self.dataset.current_sample = 0 - # Get the remaining samples - missing_samples = batch_size - data['input'].shape[0] - missing_data = self.get_data(get_inputs=get_inputs, get_outputs=get_outputs, batch_size=missing_samples) - # Merge fields - for field in ['input', 'output']: - data[field] = concatenate((data[field], missing_data[field])) - if 'additional_fields' in data.keys(): - for field in data['additional_fields'].keys(): - data['additional_fields'][field] = concatenate((data['additional_fields'][field], - missing_data['additional_fields'][field])) - return data - - def register_new_fields(self, new_fields: List[str]) -> None: - """ - | Add new data fields in the dataset. - - :param List[str] new_fields: Name of the new fields split in either 'IN' side or 'OUT' side of the dataset - """ - - for field in new_fields: - self.register_new_field(field) - - def register_new_field(self, new_field: str) -> None: - """ - | Add a new data field in the dataset. - - :param str new_field: Name of the new field. - """ - - if new_field not in self.fields or self.list_partitions[new_field] is None: - self.fields.append(new_field) - self.list_partitions[new_field] = [[], [], []] - - def create_partitions(self) -> None: - """ - | Create a new partition for current mode and for each registered fields. - """ - - print(f"[{self.name}] New partitions added for each field with max size ~{float(self.max_size) / 1e9}Gb.") - for field in self.fields: - if self.record_data: - # Fill partition name template - if field in ['input', 'output']: - name = 'IN' if field == 'input' else 'OUT' - else: - name = 'ADD_' + field - partition_path = self.partitions_templates[self.mode].format(name, self.idx_partitions[self.mode]) - # Make it current partition - self.list_partitions[field][self.mode].append(partition_path) - self.current_partition_path[field] = self.dataset_dir + partition_path - # Index partitions - self.idx_partitions[self.mode] += 1 - - def create_running_partitions(self) -> None: - """ - | Run specific function. Handle partitions creation when not training. - """ - - # 1. Load the directory without loading data - self.load_directory(load_data=False) - - # 2. Find out how many partitions exists for running mode - mode = list(self.modes.keys())[self.mode] - partitions_dict = self.json_dict['partitions'][mode] - nb_running_partitions = max([len(partitions_dict[field]) for field in partitions_dict.keys()]) - - # 3. Create a new partition partitions - self.idx_partitions[self.mode] = nb_running_partitions - self.create_partitions() - - def load_directory(self, load_data: bool = True) -> None: - """ - | Load the desired directory. Try to find partition list and upload it. - | No data loading here. - """ - - # 1. Check the directory exists - if not isdir(self.dataset_dir): - raise Warning(f"[{self.name}] Loading directory: The given path is not an existing directory") - if load_data: - print(f"[{self.name}] Loading directory: Read dataset from {self.dataset_dir}") - - # 2. Look for the json info file - if isfile(osPathJoin(self.dataset_dir, self.json_filename)): - self.json_found = True - with open(osPathJoin(self.dataset_dir, self.json_filename)) as json_file: - self.json_dict = json_load(json_file) - - # 3. Load partitions for each mode - for mode in self.modes: - # 3.1. Get sorted partitions - partitions_dict = self.json_dict['partitions'][mode] if self.json_found else self.search_partitions(mode) - # 3.2. Register additional fields - for field in partitions_dict: - self.register_new_field(field) - # 3.3. Register each partition - for field in partitions_dict: - self.list_partitions[field][self.modes[mode]] = partitions_dict[field] - # 3.4. Check that the number of partitions is the same for each field - number_of_partitions = len(self.list_partitions[self.fields[0]][self.modes[mode]]) - for field in self.fields: - if len(self.list_partitions[field][self.modes[mode]]) != number_of_partitions: - raise ValueError(f"[{self.name}] The number of partitions is different for {field} with " - f"{len(self.list_partitions[field][self.modes[mode]])} partitions found.") - - # 4. Update Json file if not found - if not self.json_found or self.empty_json_fields(): - self.search_partitions_info() - self.update_json(update_partitions_lists=True) - if self.normalization_security or (self.normalize and self.json_dict['normalization'] == self.json_empty['normalization']): - self.update_json(update_normalization=True) - - # 4. Load data from partitions - self.idx_partitions = [len(partitions_list) for partitions_list in self.list_partitions['input']] - if load_data: - self.load_partitions(force_reload=True) - - def search_partitions(self, mode: str) -> Dict[str, List[str]]: - """ - | If loading a directory without JSON info file, search for existing partitions manually. - - :param str mode: Mode of the partitions to find. - :return: Dictionary with found partitions for specified mode - """ - - # 1. Get all the partitions for the mode - partitions_dict = {} - partitions_list = [f for f in listdir(self.dataset_dir) if isfile(osPathJoin(self.dataset_dir, f)) - and f.endswith('.npy') and f.__contains__(mode.lower())] - - # 2. Sort partitions by side (IN, OUT, ADD) and by name - in_partitions = sorted([file for file in partitions_list if file.__contains__('_IN_')]) - out_partitions = sorted([file for file in partitions_list if file.__contains__('_OUT_')]) - add_partitions = sorted([file for file in partitions_list if file.__contains__('_ADD_')]) - - # 3. Sort partitions by data field - for side, partitions, field_name in zip(['IN', 'OUT', 'ADD'], [in_partitions, out_partitions, add_partitions], - ['input', 'output', '']): - for partition in partitions: - # Extract information from the filenames - split_name = partition.split('_') - clues = split_name[split_name.index(side):] - # Partition name for network data: {NAME_OF_SESSION}_SIDE_IDX.npy - if len(clues) == 2: - if field_name not in partitions_dict.keys(): - partitions_dict[field_name] = [] - partitions_dict[field_name].append(partition) - # Partition name for additional data: {NAME_OF_SESSION}_SIDE_{NAME_OF_FIELD}_IDX.npy - else: - field_name = '_'.join(clues[:-1]) - if field_name not in partitions_dict.keys(): - partitions_dict[field_name] = [] - partitions_dict[field_name].append(partition) - - return partitions_dict - - def search_partitions_info(self) -> None: - """ - | If loading a directory without JSON info file. - """ - - # 1. Get the shape of each partition - partition_shapes = [{field: [] for field in self.fields} for _ in self.modes] - for mode in self.modes: - for field in self.fields: - for partition in [self.dataset_dir + path for path in self.list_partitions[field][self.modes[mode]]]: - partition_data = load(partition) - partition_shapes[self.modes[mode]][field].append(partition_data.shape) - del partition_data - - # 2. Get the number of samples per partition for each mode - for mode in self.modes: - number_of_samples = {} - for field in self.fields: - number_of_samples[field] = [shape[0] for shape in partition_shapes[self.modes[mode]][field]] - # Number of samples through partitions must be the same along fields - if number_of_samples[field] != list(number_of_samples.values())[0]: - raise ValueError(f"[{self.name}] The number of sample in each partition is not consistent:\n" - f"{number_of_samples}") - # Store the number of samples per partition for the mode - self.json_dict['nb_samples'][mode] = list(number_of_samples.values())[0] - - # 3. Get the data shape for each field - data_shape = {field: [] for field in self.fields} - for mode in self.modes: - for field in self.fields: - for i, shape in enumerate(partition_shapes[self.modes[mode]][field]): - if len(data_shape[field]) == 0: - data_shape[field] = shape[1:] - # Data shape must be the same along partitions and mode - if shape[1:] != data_shape[field]: - raise ValueError(f"[{self.name}] Two different data sizes found for mode {mode}, field {field}," - f" partition n°{i}: {data_shape[field]} vs {shape[1:]}") - # Store the data shapes - self.json_dict['data_shape'] = data_shape - - def load_partitions(self, force_reload: bool = False) -> None: - """ - | Load data from partitions. - - :param bool force_reload: If True, force partitions reload - """ - - # 1. If there is only one partition for the current mode for input field at least, don't need to reload it - if self.last_loaded_dataset_mode == self.mode and self.idx_partitions[self.mode] == 1 and not force_reload: - if self.shuffle_dataset: - self.dataset.shuffle() - return - - # 2. Check partitions existence for the current mode - if self.idx_partitions[self.mode] == 0: - raise ValueError(f"[{self.name}] No partitions to read for {list(self.modes.keys())[self.mode]} mode.") - - # 3. Load new data in dataset - self.dataset.empty() - # Training mode with mixed dataset: read multiple partitions per field - if self.mode == self.modes['Training'] and self.idx_partitions[self.modes['Running']] > 0: - if self.mul_part_idx is None: - self.load_multiple_partitions([self.modes['Training'], self.modes['Running']]) - self.read_multiple_partitions() - return - # Training mode without mixed dataset or other modes: check the number of partitions per field to read - if self.idx_partitions[self.mode] == 1: - self.read_last_partitions() - else: - if self.mul_part_idx is None: - self.load_multiple_partitions([self.mode]) - self.read_multiple_partitions() - - def read_last_partitions(self) -> None: - """ - | Load the last loaded partitions for each data field. - """ - - for field in self.fields: - self.current_partition_path[field] = self.dataset_dir + self.list_partitions[field][self.mode][-1] - data = load(self.current_partition_path[field]) - self.dataset.set(field, data) - - def load_multiple_partitions(self, modes: List[int]) -> None: - """ - | Specialisation of the load_partitions() function. It can load a list of partitions. - - :param List[int] modes: Recommended to use modes['name_of_desired_mode'] in order to correctly load the dataset - """ - - # 1. Initialize multiple partition loading variables - self.mul_part_list_path = {field: [] for field in self.fields} - self.mul_part_slices = [] - self.mul_part_idx = 0 - nb_sample_per_partition = {field: [] for field in self.fields} - - # 2. For each field, load all partitions - for field in self.fields: - for mode in modes: - # 2.1. Add partitions to the list of partitions to read - self.mul_part_list_path[field] += [self.dataset_dir + partition - for partition in self.list_partitions[field][mode]] - # 2.2. Find the number of samples in each partition - nb_sample_per_partition[field] += self.json_dict['nb_samples'][list(self.modes.keys())[mode]] - - # 3. Invert the partitions list structure - nb_partition = len(nb_sample_per_partition[self.fields[0]]) - inverted_list = [{} for _ in range(nb_partition)] - for i in range(nb_partition): - for field in self.fields: - inverted_list[i][field] = self.mul_part_list_path[field][i] - self.mul_part_list_path = inverted_list - - # 4. Define the slicing pattern of reading for partitions - for idx in nb_sample_per_partition[self.fields[0]]: - idx_slicing = [0] - for _ in range(nb_partition - 1): - idx_slicing.append(idx_slicing[-1] + idx // nb_partition + 1) - idx_slicing.append(idx) - self.mul_part_slices.append(idx_slicing) - - def read_multiple_partitions(self) -> None: - """ - | Read data in a list of partitions. - """ - - for i, partitions in enumerate(self.mul_part_list_path): - for field in partitions.keys(): - dataset = load(partitions[field]) - samples = slice(self.mul_part_slices[i][self.mul_part_idx], - self.mul_part_slices[i][self.mul_part_idx + 1]) - self.dataset.add(field, dataset[samples]) - del dataset - self.mul_part_idx = (self.mul_part_idx + 1) % (len(self.mul_part_slices[0]) - 1) - self.current_partition_path['input'] = self.mul_part_list_path[0][self.fields[0]] - self.dataset.current_sample = 0 - - def update_json(self, update_shapes: bool = False, update_nb_samples: bool = False, - update_partitions_lists: bool = False, update_normalization: bool = False) -> None: - """ - | Update the json info file with the current Dataset repository information. - - :param bool update_shapes: If True, data shapes per field are overwritten - :param bool update_nb_samples: If True, number of samples per partition are overwritten - :param bool update_partitions_lists: If True, list of partitions is overwritten - :param bool update_normalization: If True, compute and save current normalization coefficients - """ - - # Update data shapes - if update_shapes: - for field in self.fields: - self.json_dict['data_shape'][field] = self.dataset.get_data_shape(field) - - # Update number of samples - if update_nb_samples: - idx_mode = list(self.modes.keys())[self.mode] - if len(self.json_dict['nb_samples'][idx_mode]) == self.idx_partitions[self.mode]: - self.json_dict['nb_samples'][idx_mode][-1] = self.dataset.current_sample - else: - self.json_dict['nb_samples'][idx_mode].append(self.dataset.current_sample) - - # Update partitions lists - if update_partitions_lists: - for mode in self.modes: - for field in self.fields: - self.json_dict['partitions'][mode][field] = self.list_partitions[field][self.modes[mode]] - - # Update normalization coefficients - if update_normalization: - for field in ['input', 'output']: - # Normalization is only done on training data (mode = 0) - if len(self.list_partitions[field][0]) > 0: - self.json_dict['normalization'][field] = self.compute_normalization(field) - - # Overwrite json file - with open(self.dataset_dir + self.json_filename, 'w') as json_file: - json_dump(self.json_dict, json_file, indent=3, cls=CustomJSONEncoder) - - def empty_json_fields(self) -> bool: - """ - | Check if the json info file contains empty fields. - - :return: The json file contains empty fields or not - """ - - for key in self.json_empty: - if self.json_dict[key] == self.json_empty[key]: - return True - return False - - def save_data(self) -> None: - """ - | Close all open files - """ - - if self.__new_dataset: - for field in self.current_partition_path.keys(): - self.dataset.save(field, self.current_partition_path[field]) - - def set_mode(self, mode: int) -> None: - """ - | Set the DatasetManager working mode. - - :param int mode: Recommended to use modes['name_of_desired_mode'] in order to correctly set up the - DatasetManager - """ - - # Nothing has to be done if you do not change mode - if mode == self.mode: - return - if self.mode == self.modes['Running']: - print(f"[{self.name}] It's not possible to switch dataset mode while running.") - else: - # Save dataset before changing mode - self.save_data() - self.mode = mode - self.dataset.empty() - # Create or load partition for the new mode - if self.idx_partitions[self.mode] == 0: - print(f"[{self.name}] Change to {self.mode} mode, create a new partition") - self.create_partitions() - else: - print(f"[{self.name}] Change to {self.mode} mode, load last partition") - self.read_last_partitions() - - def compute_normalization(self, field: str) -> List[float]: - """ - | Compute the normalization coefficients (mean & standard deviation) of input and output data fields. - | Compute the normalization on the whole set of partitions for a given data field. - - :param str field: Field for which normalization coefficients should be computed - :return: List containing normalization coefficients (mean, standard deviation) - """ - - partitions_content = [0., 0.] - for i, partition in enumerate(self.list_partitions[field][0]): - loaded = load(self.dataset_dir + partition).astype(float64) - partitions_content[0] += loaded.mean() #Computing the mean - partitions_content[1] += (loaded**2).mean() #Computing the variance - if len(self.list_partitions[field][0]) > 0: - partitions_content[0] /= len(self.list_partitions[field][0]) #Computing the global mean - # Computing the global std - partitions_content[1] = (partitions_content[1] / len(self.list_partitions[field][0]) - partitions_content[0]**2)**(0.5) - if self.data_manager is not None: - self.data_manager.normalization[field] = partitions_content - return partitions_content - - def new_dataset(self) -> bool: - """ - | Check if the Dataset is a new entity or not. - - :return: The Dataset is new or not - """ - - return self.__new_dataset - - def get_next_batch(self, batch_size: int) -> Dict[str, ndarray]: - """ - | Specialization of get_data to get a batch. - - :param int batch_size: Size of the batch - :return: Batch with format {'input': numpy.ndarray, 'output': numpy.ndarray} - """ - - return self.get_data(get_inputs=True, get_outputs=True, batch_size=batch_size, batched=True) - - def get_next_sample(self, batched: bool = True) -> Dict[str, ndarray]: - """ - | Specialization of get_data to get a sample. - - :param batched: If True, return the sample as a batch with batch_size = 1 - :return: Sample with format {'input': numpy.ndarray, 'output': numpy.ndarray} - """ - - return self.get_data(get_inputs=True, get_outputs=True, batched=batched) - - def get_next_input(self, batched: bool = False) -> Dict[str, ndarray]: - """ - | Specialization of get_data to get an input sample. - - :param batched: If True, return the sample as a batch with batch_size = 1 - :return: Sample with format {'input': numpy.ndarray, 'output': numpy.ndarray} where only the input field is - filled - """ - - return self.get_data(get_inputs=True, get_outputs=False, batched=batched) - - def getNextOutput(self, batched: bool = False) -> Dict[str, ndarray]: - """ - | Specialization of get_data to get an output sample. - - :param batched: If True, return the sample as a batch with batch_size = 1 - :return: Sample with format {'input': numpy.ndarray, 'output': numpy.ndarray} where only the output field is - filled - """ - - return self.get_data(get_inputs=False, get_outputs=True, batched=batched) - - def close(self) -> None: - """ - | Launch the close procedure of the dataset manager - """ - - if self.__writing: - self.save_data() - if self.offline and self.normalize: - self.update_json(update_normalization=True) - - def __str__(self) -> str: - """ - :return: A string containing valuable information about the DatasetManager - """ - - description = "\n" - description += f"# {self.name}\n" - description += f" Dataset Repository: {self.dataset_dir}\n" - description += f" Partitions size: {self.max_size * 1e-9} Go\n" - description += f" Managed objects: Dataset: {self.dataset.name}\n" - description += str(self.dataset) - return description diff --git a/src/Manager/Manager.py b/src/Manager/Manager.py deleted file mode 100644 index 5ff6fed6..00000000 --- a/src/Manager/Manager.py +++ /dev/null @@ -1,144 +0,0 @@ -from typing import Dict, Tuple, Optional -from os.path import join, exists -from numpy import ndarray - -from DeepPhysX.Core.Manager.DataManager import DataManager -from DeepPhysX.Core.Manager.NetworkManager import NetworkManager -from DeepPhysX.Core.Manager.StatsManager import StatsManager -from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig -from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Utils.path import get_first_caller, create_dir - - -class Manager: - - def __init__(self, - network_config: Optional[BaseNetworkConfig] = None, - database_config: Optional[BaseDatabaseConfig] = None, - environment_config: Optional[BaseEnvironmentConfig] = None, - session_dir: str = 'sessions', - session_name: str = 'default', - pipeline: str = '', - new_session: bool = True, - produce_data: bool = True, - batch_size: int = 1, - debug_session: bool = False): - """ - Collection of all the specialized managers. Allows for some basic functions call. - More specific behaviour have to be directly call from the corresponding manager. - - :param network_config: Specialisation containing the parameters of the network manager. - :param database_config: Specialisation containing the parameters of the dataset manager. - :param environment_config: Specialisation containing the parameters of the environment manager. - :param session_name: Name of the newly created directory if session is not defined. - :param session_dir: Name of the directory in which to write all the necessary data. - :param bool new_session: Flag that indicates whether if the session is new - :param is_training: Flag that indicates whether if this session is training a Network. - :param produce_data: Flag that indicates whether if this session is producing data. - :param int batch_size: Number of samples in a batch. - """ - - self.name = self.__class__.__name__ - - # Define the session repository - root = get_first_caller() - session_dir = join(root, session_dir) - - if pipeline in ['data_generation', 'training']: - # Avoid unwanted overwritten data - if new_session: - self.session = create_dir(session_dir=session_dir, - session_name=session_name) - else: - self.session = join(session_dir, session_name) - else: - self.session = join(session_dir, session_name) - if not exists(self.session): - raise ValueError(f"[{self.name}] The running session directory {self.session} does not exist.") - - # Create a DataManager - self.data_manager = DataManager(database_config=database_config, - environment_config=environment_config, - manager=self, - session=self.session, - new_session=new_session, - pipeline=pipeline, - produce_data=produce_data, - batch_size=batch_size) - database = self.data_manager.get_database() - self.batch_size = batch_size - - # Create a NetworkMmanager - self.network_manager = NetworkManager(network_config=network_config, - manager=self, - session=self.session, - new_session=new_session, - pipeline=pipeline, - data_db=database) - self.network_manager.link_clients(self.data_manager.nb_environment) - - # Create a StatsManager for training only - self.debug_session = debug_session - self.stats_manager = StatsManager(manager=self, - session=self.session) if pipeline == 'training' else None - - def change_database(self, database): - self.network_manager.change_database(database) - - def get_data(self, - epoch: int = 0, - animate: bool = True) -> None: - """ - Fetch data from the DataManager. - - :param int epoch: Epoch ID - :param bool animate: If True allows running environment step - """ - - self.data_manager.get_data(epoch=epoch, - animate=animate) - - def optimize_network(self) -> Tuple[ndarray, Dict[str, float]]: - """ - Compute a prediction and run a back propagation with the current batch. - - :return: The network prediction and the associated loss value - """ - - # Forward pass and optimization step - data_pred, data_loss = self.network_manager.compute_prediction_and_loss( - data_lines=self.data_manager.data_lines, - normalization=self.data_manager.normalization, - optimize=True) - return data_pred, data_loss - - def save_network(self) -> None: - """ - Save network parameters. - """ - - self.network_manager.save_network() - - def close(self) -> None: - """ - Call all managers close procedure. - """ - - if self.network_manager is not None: - self.network_manager.close() - if self.stats_manager is not None: - self.stats_manager.close() - if self.data_manager is not None: - self.data_manager.close() - - def __str__(self) -> str: - - manager_description = "" - if self.network_manager is not None: - manager_description += str(self.network_manager) - if self.data_manager is not None: - manager_description += str(self.data_manager) - if self.stats_manager is not None: - manager_description += str(self.stats_manager) - return manager_description From deaba56915a40e283a88a937dd934a881ba67419 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 13:05:01 +0200 Subject: [PATCH 22/61] Remove previous Dataset package. --- src/Dataset/BaseDataset.py | 252 ------------------------------- src/Dataset/BaseDatasetConfig.py | 116 -------------- src/Dataset/__init__.py | 10 -- 3 files changed, 378 deletions(-) delete mode 100644 src/Dataset/BaseDataset.py delete mode 100644 src/Dataset/BaseDatasetConfig.py delete mode 100644 src/Dataset/__init__.py diff --git a/src/Dataset/BaseDataset.py b/src/Dataset/BaseDataset.py deleted file mode 100644 index decd434c..00000000 --- a/src/Dataset/BaseDataset.py +++ /dev/null @@ -1,252 +0,0 @@ -from typing import Dict, List, Optional, Tuple -from numpy import array, ndarray, concatenate, save, arange -from numpy.random import shuffle -from collections import namedtuple - - -class BaseDataset: - """ - | BaseDataset is a dataset class to store any data from a BaseEnvironment or from files. - | Given data is split into input data and output data. - | Saving data results in multiple partitions of input and output data. - - :param namedtuple config: Namedtuple which contains BaseDataset parameters - """ - - def __init__(self, config: namedtuple): - - self.name: str = self.__class__.__name__ - - # Data fields containers - self.data_type = ndarray - self.fields: List[str] = ['input', 'output'] - self.data: Dict[str, ndarray] = {'input': array([]), 'output': array([])} - self.shape: Dict[str, Optional[List[int]]] = {'input': None, 'output': None} - - # Indexing - self.shuffle_pattern: Optional[List[int]] = None - self.current_sample: int = 0 - - # Dataset memory - self.max_size: int = config.max_size - self.batch_per_field: Dict[str, int] = {field: 0 for field in ['input', 'output']} - self.__empty: bool = True - - @property - def nb_samples(self) -> int: - """ - | Property returning the current number of samples - - :return: The current number of samples in all partitions - """ - - return max([len(self.data[field]) for field in self.fields]) - - def is_empty(self) -> bool: - """ - | Check if the fields of the dataset are empty. A field is considered as non-empty if it is filled with another - sample. - - :return: The Dataset is empty or not - """ - - # The empty flag is set to False once the Dataset is considered as non-empty - if not self.__empty: - return False - # Check each registered data field - for field in self.fields: - # Dataset is considered as non-empty if a field is filled with another sample - if self.batch_per_field[field] > 1: - self.__empty = False - return False - # If all field are considered as non-empty then the Dataset is empty - return True - - def init_data_size(self, field: str, shape: List[int]) -> None: - """ - | Store the original shape of data. Reshape data containers. - - :param str field: Data field name - :param List[int] shape: Shape of the corresponding tensor - """ - - # Store the original data shape - self.shape[field] = shape - # Reshape the data container - self.data[field] = array([]).reshape((0, *shape)) - - def get_data_shape(self, field: str) -> List[int]: - """ - | Returns the data shape of field. - - :param str field: Data field name - :return: Data shape for field - """ - - return self.shape[field] - - def init_additional_field(self, field: str, shape: List[int]) -> None: - """ - | Register a new data field. - - :param str field: Name of the data field - :param List[int] shape: Data shape - """ - - # Register the data field - self.fields.append(field) - # Init the number of adds in the field - self.batch_per_field[field] = 0 - # Init the field shape - self.init_data_size(field, shape) - - def empty(self) -> None: - """ - | Empty the dataset. - """ - - # Reinit each data container - for field in self.fields: - self.data[field] = array([]) if self.shape[field] is None else array([]).reshape((0, *self.shape[field])) - # Reinit indexing variables - self.shuffle_pattern = None - self.current_sample = 0 - self.batch_per_field = {field: 0 for field in self.fields} - self.__empty = True - - def memory_size(self, field: Optional[str] = None) -> int: - """ - | Return the actual memory size of the dataset if field is None. Otherwise, return the actual memory size of the - field. - - :param Optional[str] field: Name of the data field - :return: Size in bytes of the current dataset. - """ - - # Return the total memory size - if field is None: - return sum([self.data[field].nbytes for field in self.fields]) - # Return the memory size for the specified field - return self.data[field].nbytes - - def check_data(self, field: str, data: ndarray) -> None: - """ - | Check if the data is a numpy array. - - :param str field: Values at 'input' or anything else. Define if the associated shape is correspond to input - shape or output one. - :param ndarray data: New data - """ - - if type(data) != self.data_type: - raise TypeError(f"[{self.name}] Wrong data type in field '{field}': numpy array required, got {type(data)}") - - def add(self, field: str, data: ndarray, partition_file: Optional[str] = None) -> None: - """ - | Add data to the dataset. - - :param str field: Name of the data field - :param ndarray data: New data as batch of samples - :param Optional[str] partition_file: Path to the file in which to write the data - """ - - # Check data type - self.check_data(field, data) - - # Check if field is registered - if field not in self.fields: - # Fields can be register only if Dataset is empty - if not self.is_empty(): - raise ValueError(f"[{self.name}] A new field {field} tries to be created as Dataset is non empty. This " - f"will lead to a different number of sample for each field of the dataset.") - # Add new field if not registered - self.init_additional_field(field, data[0].shape) - - # Check data size initialization - if self.shape[field] is None: - self.init_data_size(field, data[0].shape) - - # Add batched samples - self.data[field] = concatenate((self.data[field], data)) - # Save in partition - if partition_file is not None: - self.save(field, partition_file) - - # Update sample indexing in dataset - self.batch_per_field[field] += 1 - self.current_sample = max([len(self.data[f]) for f in self.fields]) - - def save(self, field: str, file: str) -> None: - """ - | Save the corresponding field of the Dataset. - - :param str field: Name of the data field - :param str file: Path to the file in which to write the data - """ - - save(file, self.data[field]) - - def set(self, field: str, data: ndarray) -> None: - """ - | Set a full field of the dataset. - - :param str field: Name of the data field - :param ndarray data: New data as batch of samples - """ - - # Check data type - self.check_data(field, data) - - # Check if field is registered - if field not in self.fields: - # Add new field if not registered - self.init_additional_field(field, data[0].shape) - - # Check data size initialization - if self.shape[field] is None: - self.init_data_size(field, data[0].shape) - - # Set the full field - self.data[field] = data - - # Update sample indexing in dataset - self.__empty = False - self.current_sample = 0 - - def get(self, field: str, idx_begin: int, idx_end: int) -> ndarray: - """ - | Get a batch of data in 'field' container. - - :param str field: Name of the data field - :param int idx_begin: Index of the first sample - :param int idx_end: Index of the last sample - :return: Batch of data from 'field' - """ - - indices = slice(idx_begin, idx_end) if self.shuffle_pattern is None else self.shuffle_pattern[idx_begin:idx_end] - return self.data[field][indices] - - def shuffle(self) -> None: - """ - | Define a random shuffle pattern. - """ - - # Nothing to shuffle if Dataset is empty - if self.is_empty(): - return - # Generate a shuffle pattern - self.shuffle_pattern = arange(self.nb_samples) - shuffle(self.shuffle_pattern) - - def __str__(self) -> str: - """ - :return: String containing information about the BaseDatasetConfig object - """ - - description = "\n" - description += f" {self.name}\n" - description += f" Max size: {self.max_size}\n" - description += f" Data fields: {self.fields}" - for field in self.fields: - description += f" {field} shape: {self.shape[field]}" - return description diff --git a/src/Dataset/BaseDatasetConfig.py b/src/Dataset/BaseDatasetConfig.py deleted file mode 100644 index acec6302..00000000 --- a/src/Dataset/BaseDatasetConfig.py +++ /dev/null @@ -1,116 +0,0 @@ -from typing import Type, Optional -from os.path import isdir -from collections import namedtuple - -from DeepPhysX.Core.Dataset.BaseDataset import BaseDataset - - -class BaseDatasetConfig: - """ - | BaseDatasetConfig is a configuration class to parameterize and create a BaseDataset for the DatasetManager. - - :param Type[BaseDataset] dataset_class: BaseDataset class from which an instance will be created - :param Optional[str] dataset_dir: Name of an existing dataset repository - :param float partition_size: Maximum size in Gb of a single dataset partition - :param bool shuffle_dataset: Specify if existing dataset should be shuffled - :param Optional[str] use_mode: Specify the Dataset mode that should be used between 'Training', 'Validation' and - 'Running' - :param bool normalize: If True, normalizing dataset using standard score - :param bool recompute_normalization: If True, triggers a normalization coefficients computation - """ - - def __init__(self, - dataset_class: Type[BaseDataset] = BaseDataset, - dataset_dir: Optional[str] = None, - partition_size: float = 1., - shuffle_dataset: bool = True, - use_mode: Optional[str] = None, - normalize: bool = True, - recompute_normalization: bool = False): - - self.name: str = self.__class__.__name__ - - # Check dataset_dir type and existence - if dataset_dir is not None: - if type(dataset_dir) != str: - raise TypeError(f"[{self.name}] Wrong dataset_dir type: str required, get {type(dataset_dir)}") - if not isdir(dataset_dir): - raise ValueError(f"[{self.name}] Given dataset_dir doesn't exists: {dataset_dir}") - # Check partition_size type and value - if type(partition_size) != int and type(partition_size) != float: - raise TypeError(f"[{self.name}] Wrong partition_size type: float required, get {type(partition_size)}") - if partition_size <= 0: - raise ValueError(f"[{self.name}] Given partition_size is negative or null") - # Check shuffle_dataset type - if type(shuffle_dataset) != bool: - raise TypeError(f"[{self.name}] Wrong shuffle_dataset type: bool required, get {type(shuffle_dataset)}") - # Check use_mode type and value - if use_mode is not None: - if type(use_mode) != str: - raise TypeError(f"[{self.name}] Wrong use_mode type: str required, get {type(dataset_dir)}") - if use_mode not in ['Training', 'Validation', 'Running']: - raise ValueError(f"[{self.name}] Wrong use_mode value, must be in " - f"{['Training', 'Validation', 'Running']}") - - # BaseDataset parameterization - self.dataset_class: Type[BaseDataset] = dataset_class - self.dataset_config: namedtuple = self.make_config(max_size=int(partition_size * 1e9)) - - # DatasetManager parameterization - self.dataset_dir: str = dataset_dir - self.shuffle_dataset: bool = shuffle_dataset - self.use_mode: Optional[str] = use_mode - self.normalize: bool = normalize - self.recompute_normalization = recompute_normalization - - def make_config(self, **kwargs) -> namedtuple: - """ - | Create a namedtuple which gathers all the parameters for the Dataset configuration. - | For a child config class, only new items are required since parent's items will be added by default. - - :param kwargs: Items to add to the Dataset configuration. - :return: Namedtuple which contains Dataset parameters - """ - - # Get items set as keyword arguments - fields = tuple(kwargs.keys()) - args = tuple(kwargs.values()) - # Check if a dataset_config already exists (child class will have the parent's config by default) - if 'dataset_config' in self.__dict__: - # Get items set in the existing config - for key, value in self.dataset_config._asdict().items(): - # Only new items are required for children, check if the parent's items are set again anyway - if key not in fields: - fields += (key,) - args += (value,) - # Create namedtuple with collected items - return namedtuple('dataset_config', fields)._make(args) - - def create_dataset(self) -> BaseDataset: - """ - | Create an instance of dataset_class with given parameters. - - :return: Dataset object - """ - - try: - dataset = self.dataset_class(config=self.dataset_config) - except: - raise ValueError(f"[{self.name}] Given dataset_class got an unexpected keyword argument 'config'") - if not isinstance(dataset, BaseDataset): - raise TypeError(f"[{self.name}] Wrong dataset_class type: BaseDataset required, get {self.dataset_class}") - return dataset - - def __str__(self) -> str: - """ - :return: String containing information about the BaseDatasetConfig object - """ - - # Todo: fields in Configs are the set in Managers or objects, the remove __str__ method - description = "\n" - description += f"{self.name}\n" - description += f" Dataset class: {self.dataset_class.__name__}\n" - description += f" Max size: {self.dataset_config.max_size}\n" - description += f" Dataset dir: {self.dataset_dir}\n" - description += f" Shuffle dataset: {self.shuffle_dataset}\n" - return description diff --git a/src/Dataset/__init__.py b/src/Dataset/__init__.py deleted file mode 100644 index 6501d21a..00000000 --- a/src/Dataset/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from os.path import dirname -from os import listdir - -package = dirname(__file__) -exceptions = ['__init__.py', '__pycache__'] -modules = [module for module in listdir(package) if module.endswith('.py') and module not in exceptions] -__all__ = [] -for module in sorted(modules): - exec(f"from DeepPhysX.Core.Dataset.{module[:-3]} import {module[:-3]}") - __all__.append(module[:-3]) From 23daa56695179203505f6e14be58e5b3b9b51f4f Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 15:34:55 +0200 Subject: [PATCH 23/61] Adapt DataGeneration Pipeline in remote mode for DB Handlers. --- src/AsyncSocket/AbstractEnvironment.py | 5 ++++ src/AsyncSocket/TcpIpClient.py | 16 ++++++---- src/AsyncSocket/TcpIpServer.py | 35 ++++++++++++++++++++++ src/Database/DatabaseHandler.py | 24 +++++++++++---- src/Environment/BaseEnvironmentConfig.py | 9 ++---- src/Environment/launcherBaseEnvironment.py | 10 +++---- src/Manager/DataManager.py | 13 +------- src/Manager/DatabaseManager.py | 11 ++++--- src/Manager/EnvironmentManager.py | 1 - src/Pipelines/BasePipeline.py | 14 ++------- 10 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index c1d9d0ef..573ef6cb 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -3,6 +3,8 @@ from SSD.Core.Storage.Database import Database +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler + class AbstractEnvironment: @@ -84,6 +86,9 @@ def close(self) -> None: ########################################################################################## ########################################################################################## + def get_database_handler(self) -> DatabaseHandler: + raise NotImplementedError + def _send_training_data(self) -> None: raise NotImplementedError diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 3c19e8a2..8e0fd539 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -17,7 +17,6 @@ def __init__(self, port: int = 10000, instance_id: int = 0, instance_nb: int = 1, - training_db: Optional[Union[Database, Tuple[str, str]]] = None, visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): """ TcpIpClient is both a TcpIpObject which communicate with a TcpIpServer and an AbstractEnvironment to compute @@ -70,6 +69,15 @@ async def __initialize(self) -> None: # Receive number of sub-steps self.simulations_per_step = await self.receive_data(loop=loop, sender=self.sock) + # Receive partitions + partitions_list = await self.receive_data(loop=loop, sender=self.sock) + partitions_list, exchange = partitions_list.split('%%%') + partitions = [[partitions_list.split('///')[0], partition_name] + for partition_name in partitions_list.split('///')[1:]] + exchange = [exchange.split('///')[0], exchange.split('///')[1]] + self.environment.get_database_handler().init_remote(storing_partitions=partitions, + exchange_db=exchange) + # Create the environment self.environment.create() self.environment.init() @@ -278,7 +286,5 @@ async def action_on_change_db(self, :param sender: TcpIpObject sender. """ - new_database = (await self.receive_data(loop=loop, sender=sender), - await self.receive_data(loop=loop, sender=sender)) - self.database = Database(database_dir=new_database[0], - database_name=new_database[1]).load() + new_database = await self.receive_data(loop=loop, sender=sender) + self.environment.get_database_handler().update_list_partitions_remote(new_database.split('///')) diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index e3d17700..e0a7eab2 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -6,6 +6,7 @@ from queue import SimpleQueue from DeepPhysX.Core.AsyncSocket.TcpIpObject import TcpIpObject +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler class TcpIpServer(TcpIpObject): @@ -55,12 +56,28 @@ def __init__(self, # Reference to EnvironmentManager self.environment_manager: Optional[Any] = manager + # Connect the Server to the Database + self.database_handler = DatabaseHandler(on_partitions_handler=self.__database_handler_partitions, + name='ServerHandler') + self.environment_manager.data_manager.connect_handler(self.database_handler) + ########################################################################################## ########################################################################################## # Connect Clients # ########################################################################################## ########################################################################################## + def __database_handler_partitions(self): + + for _, client in self.clients: + self.sync_send_command_change_db(receiver=client) + new_partition = self.database_handler.get_partitions()[-1] + self.sync_send_data(data_to_send=f'{new_partition.get_path()[0]}///{new_partition.get_path()[1]}', + receiver=client) + + def get_database_handler(self) -> DatabaseHandler: + return self.database_handler + def connect(self) -> None: """ Accept connections from clients. @@ -107,9 +124,27 @@ async def __initialize(self) -> None: # Initialisation process for each client for client_id, client in self.clients: + # Send number of sub-steps nb_steps = self.environment_manager.simulations_per_step if self.environment_manager else 1 await self.send_data(data_to_send=nb_steps, loop=loop, receiver=client) + + # Send partitions + partitions = self.database_handler.get_partitions() + if len(partitions) == 0: + partitions_list = 'None' + else: + partitions_list = partitions[0].get_path()[0] + for partition in partitions: + partitions_list += f'///{partition.get_path()[1]}' + partitions_list += '%%%' + exchange = self.database_handler.get_exchange() + if exchange is None: + partitions += 'None' + else: + partitions_list += f'{exchange.get_path()[0]}///{exchange.get_path()[1]}' + await self.send_data(data_to_send=partitions_list, loop=loop, receiver=client) + print(f"[{self.name}] Client n°{client_id} initialisation done") ########################################################################################## diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py index 7eae31f8..4e031ec9 100644 --- a/src/Database/DatabaseHandler.py +++ b/src/Database/DatabaseHandler.py @@ -8,13 +8,15 @@ class DatabaseHandler: def __init__(self, - remote: bool = False, - on_init_handler: Optional[Callable] = None): + name: str = '', + on_init_handler: Optional[Callable] = None, + on_partitions_handler: Optional[Callable] = None): - self.remote = remote + self.name = name self.__storing_partitions: List[Database] = [] self.__exchange_db: Optional[Database] = None self.__on_init_handler = self.__default_handler if on_init_handler is None else on_init_handler + self.__on_partitions_handler = self.__default_handler if on_partitions_handler is None else on_partitions_handler def __default_handler(self): pass @@ -42,6 +44,13 @@ def init_remote(self, def update_list_partitions(self, partition: Database) -> None: self.__storing_partitions.append(partition) + self.__on_partitions_handler() + + def update_list_partitions_remote(self, + partition: List[str]): + self.__storing_partitions.append(Database(database_dir=partition[0], + database_name=partition[1]).load()) + self.__on_partitions_handler() def get_database_dir(self): return self.__storing_partitions[0].get_path()[0] @@ -54,6 +63,9 @@ def load(self): def get_partitions(self): return self.__storing_partitions + def get_exchange(self): + return self.__exchange_db + ### # Database Architecture ### @@ -70,9 +82,9 @@ def create_fields(self, else: if len(self.__storing_partitions) == 1: self.__storing_partitions[0].load() - if len(self.__storing_partitions[0].get_fields(table_name=table_name)) <= 2: - self.__storing_partitions[0].create_fields(table_name=table_name, - fields=fields) + if len(self.__storing_partitions[0].get_fields(table_name=table_name)) <= 2: + self.__storing_partitions[0].create_fields(table_name=table_name, + fields=fields) def get_fields(self, table_name: str) -> List[str]: diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index 825583c8..208f4f3b 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -92,14 +92,12 @@ def __init__(self, def create_server(self, environment_manager: Optional[Any] = None, batch_size: int = 1, - training_db: Optional[str] = None, visualization_db: Optional[str] = None) -> TcpIpServer: """ Create a TcpIpServer and launch TcpIpClients in subprocesses. :param environment_manager: EnvironmentManager. :param batch_size: Number of sample in a batch. - :param training_db: Path of the training Database to connect to. :param visualization_db: Path to the visualization Database to connect to. :return: TcpIpServer object. """ @@ -117,7 +115,7 @@ def create_server(self, # Create clients client_threads = [] for i in range(self.number_of_thread): - client_thread = Thread(target=self.start_client, args=(i + 1, training_db, visualization_db)) + client_thread = Thread(target=self.start_client, args=(i + 1, visualization_db)) client_threads.append(client_thread) for client in client_threads: client.start() @@ -141,20 +139,17 @@ def start_server(self, def start_client(self, idx: int = 1, - training_db: Optional[str] = None, visualization_db: Optional[str] = None) -> None: """ Run a subprocess to start a TcpIpClient. :param idx: Index of client. - :param training_db: Path of the training Database to connect to. :param visualization_db: Path to the visualization Database to connect to. """ script = join(dirname(modules[BaseEnvironment.__module__].__file__), 'launcherBaseEnvironment.py') run([executable, script, self.environment_file, self.environment_class.__name__, - self.ip_address, str(self.port), str(idx), str(self.number_of_thread), - str(training_db), str(visualization_db)]) + self.ip_address, str(self.port), str(idx), str(self.number_of_thread), str(visualization_db)]) def create_environment(self, visualization_db: Optional[Any] = None) -> BaseEnvironment: diff --git a/src/Environment/launcherBaseEnvironment.py b/src/Environment/launcherBaseEnvironment.py index f60112de..d533f1ec 100644 --- a/src/Environment/launcherBaseEnvironment.py +++ b/src/Environment/launcherBaseEnvironment.py @@ -8,9 +8,9 @@ if __name__ == '__main__': # Check script call - if len(argv) != 9: + if len(argv) != 8: print(f"Usage: python3 {argv[0]} " - f" ") + f" ") exit(1) # Import environment_class @@ -19,13 +19,13 @@ exec(f"from {module_name} import {argv[2]} as Environment") # Create, init and run Tcp-Ip environment - training_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] - visualization_db = None if argv[8] == 'None' else [s[1:-1] for s in argv[8][1:-1].split(', ')] + visualization_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[8][1:-1].split(', ')] client = TcpIpClient(environment=Environment, ip_address=argv[3], port=int(argv[4]), instance_id=int(argv[5]), - instance_nb=int(argv[6])) + instance_nb=int(argv[6]), + visualization_db=visualization_db) client.initialize() client.launch() diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index be29997a..9f6a94a7 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -1,6 +1,6 @@ from typing import Any, Optional, Dict, List -from DeepPhysX.Core.Manager.DatabaseManager import DatabaseManager, Database +from DeepPhysX.Core.Manager.DatabaseManager import DatabaseManager from DeepPhysX.Core.Manager.EnvironmentManager import EnvironmentManager from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig @@ -37,7 +37,6 @@ def __init__(self, self.pipeline: Optional[Any] = pipeline self.database_manager: Optional[DatabaseManager] = None self.environment_manager: Optional[EnvironmentManager] = None - self.connected_managers: List[Any] = [] # Create a DatabaseManager self.database_manager = DatabaseManager(database_config=database_config, @@ -63,16 +62,6 @@ def __init__(self, def connect_handler(self, handler): self.database_manager.connect_handler(handler) - def get_database(self) -> Database: - return self.database_manager.database - - def connect_manager(self, manager: Any): - self.connected_managers.append(manager) - - def change_database(self) -> None: - for manager in self.connected_managers: - manager.change_database(self.database_manager.database) - @property def nb_environment(self): if self.environment_manager is None: diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index d0e3a8d6..e2a00f8b 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -362,12 +362,9 @@ def update_json(self, def connect_handler(self, handler: DatabaseHandler) -> None: - if handler.remote: - handler.init_remote(storing_partitions=self.get_partition_names(), - exchange_db=self.exchange.get_path()) - else: - handler.init(storing_partitions=self.get_partition_objects(), - exchange_db=self.exchange) + + handler.init(storing_partitions=self.get_partition_objects(), + exchange_db=self.exchange) self.database_handlers.append(handler) def index_samples(self): @@ -394,6 +391,8 @@ def add_data(self, # 1. Update the json file self.update_json(update_nb_samples=True) if self.first_add: + for handler in self.database_handlers: + handler.load() self.update_json(update_partitions_lists=True, update_shapes=True, update_architecture=True) self.first_add = False if self.normalize and self.mode == 'training' and self.pipeline == 'training' and data_lines is not None: diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index b4d33661..29da4128 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -58,7 +58,6 @@ def __init__(self, batch_size=batch_size, visualization_db=None if visualization_db is None else visualization_db.get_path()) - self.data_manager.get_database().load() else: self.environment = environment_config.create_environment(visualization_db=visualization_db) self.environment.environment_manager = self diff --git a/src/Pipelines/BasePipeline.py b/src/Pipelines/BasePipeline.py index 9f61ef1f..92d1b414 100644 --- a/src/Pipelines/BasePipeline.py +++ b/src/Pipelines/BasePipeline.py @@ -3,7 +3,6 @@ from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Manager.Manager import Manager from DeepPhysX.Core.Manager.DataManager import DataManager from DeepPhysX.Core.Manager.StatsManager import StatsManager from DeepPhysX.Core.Manager.NetworkManager import NetworkManager @@ -64,9 +63,6 @@ def __init__(self, self.new_session = new_session self.type = pipeline - # Main manager - self.manager: Optional[Manager] = None - def execute(self): """ Launch the Pipeline. @@ -83,16 +79,12 @@ def __get_any_manager(self, :return: The desired Manager associated with the Pipeline. """ - # If manager variable is not defined, cannot access other manager - if self.manager is None: - return None - # Direct access to manager if type(manager_names) == str: - return getattr(self.manager, manager_names) if hasattr(self.manager, manager_names) else None + return getattr(self, manager_names) if hasattr(self, manager_names) else None # Intermediates to access manager - accessed_manager = self.manager + accessed_manager = self for next_manager in manager_names: if hasattr(accessed_manager, next_manager): accessed_manager = getattr(accessed_manager, next_manager) @@ -127,7 +119,7 @@ def get_stats_manager(self) -> Optional[StatsManager]: return self.__get_any_manager(manager_names='stats_manager') - def get_dataset_manager(self) -> Optional[DatabaseManager]: + def get_database_manager(self) -> Optional[DatabaseManager]: """ Return the DatabaseManager associated with the pipeline if it exists. From 3426e07cf6dd9688b2a5efc6b4112100729598fa Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 15:51:58 +0200 Subject: [PATCH 24/61] Adapt Training Pipelines in remote mode for DB Handlers. --- src/AsyncSocket/TcpIpClient.py | 3 +-- src/AsyncSocket/TcpIpServer.py | 4 ++-- src/Environment/BaseEnvironment.py | 1 + src/Manager/EnvironmentManager.py | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 8e0fd539..5c58e2fa 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -85,7 +85,7 @@ async def __initialize(self) -> None: self.environment.init_visualization() # Initialization done - await self.send_command_done(loop=loop, receiver=self.sock) + await self.send_data(data_to_send='done', loop=loop, receiver=self.sock) ########################################################################################## ########################################################################################## @@ -270,7 +270,6 @@ async def action_on_step(self, self.environment._update_training_data(self.environment.update_line) line = self.environment.update_line self.environment._reset_training_data() - await self.send_command_done() await self.send_data(data_to_send=line, loop=loop, receiver=sender) async def action_on_change_db(self, diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index e0a7eab2..c99c4455 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -145,6 +145,8 @@ async def __initialize(self) -> None: partitions_list += f'{exchange.get_path()[0]}///{exchange.get_path()[1]}' await self.send_data(data_to_send=partitions_list, loop=loop, receiver=client) + # Wait Client init + await self.receive_data(loop=loop, sender=client) print(f"[{self.name}] Client n°{client_id} initialisation done") ########################################################################################## @@ -215,8 +217,6 @@ async def __communicate(self, if animate: await self.send_command_step(loop=loop, receiver=client) # Receive data - await self.listen_while_not_done(loop=loop, sender=client, data_dict=self.data_dict, - client_id=client_id) line = await self.receive_data(loop=loop, sender=client) self.data_lines.append(line) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 1bc31289..d076306d 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -62,6 +62,7 @@ def __init__(self, ########################################################################################## def __database_handler_init(self): + self.__database_handler.load() if self.instance_id == 1: self.__database_handler.create_fields(table_name='Training', fields=('env_id', int)) diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 29da4128..776d89d0 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -69,7 +69,6 @@ def __init__(self, self.environment.init_database() self.environment.init_visualization() - # Define get_data and dispatch methods self.get_database_handler = self.get_server_database_handler if self.server else self.get_environment_database_handler self.change_database = self.change_database_in_server if self.server else self.change_database_in_environment From 71efa8bada1a2140ae8afd6107d2a18e926b6a05 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 17:01:49 +0200 Subject: [PATCH 25/61] Fix prediction requests in local and remote modes. --- src/AsyncSocket/TcpIpClient.py | 11 ++++++----- src/AsyncSocket/TcpIpServer.py | 4 +++- src/Environment/BaseEnvironment.py | 16 +++++++++++----- src/Pipelines/BaseTraining.py | 1 - 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 5c58e2fa..5b192e12 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -158,13 +158,13 @@ def get_prediction(self, """ # Get a prediction - self.database.update(table_name='Prediction', - data=kwargs, - line_id=self.environment.instance_id) + self.environment.get_database_handler().update(table_name='Exchange', + data=kwargs, + line_id=self.environment.instance_id) self.sync_send_command_prediction() _ = self.sync_receive_data() - data_pred = self.database.get_line(table_name='Prediction', - line_id=self.environment.instance_id) + data_pred = self.environment.get_database_handler().get_line(table_name='Exchange', + line_id=self.environment.instance_id) del data_pred['id'] return data_pred @@ -270,6 +270,7 @@ async def action_on_step(self, self.environment._update_training_data(self.environment.update_line) line = self.environment.update_line self.environment._reset_training_data() + await self.send_command_done(loop=loop, receiver=sender) await self.send_data(data_to_send=line, loop=loop, receiver=sender) async def action_on_change_db(self, diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index c99c4455..b9e89e2a 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -210,13 +210,15 @@ async def __communicate(self, return # Send the sample to the Client await self.send_command_sample(loop=loop, receiver=client) - line = int(self.batch_from_dataset.pop(0)) + line = self.batch_from_dataset.pop(0) await self.send_data(data_to_send=line, loop=loop, receiver=client) # 2. Execute n steps, the last one send data computation signal if animate: await self.send_command_step(loop=loop, receiver=client) # Receive data + await self.listen_while_not_done(loop=loop, sender=client, data_dict=self.data_dict, + client_id=client_id) line = await self.receive_data(loop=loop, sender=client) self.data_lines.append(line) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index d076306d..72c10e73 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -198,8 +198,7 @@ def set_training_data(self, **kwargs) -> None: # Check kwargs if self.__first_add[0]: - if self.instance_id != 0: - self.__database_handler.load() + self.__database_handler.load() self.__first_add[0] = False required_fields = list(set(self.__database_handler.get_fields(table_name='Training')) - {'id', 'env_id'}) for field in kwargs.keys(): @@ -272,8 +271,9 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: # Check kwargs if self.__first_add[1]: - if self.instance_id != 0: - self.__database_handler.load() + if len(kwargs) == 0 and len(self.__data_training) == 0: + raise ValueError(f"[{self.name}] The prediction request requires the network fields.") + self.__database_handler.load() self.__first_add[1] = False required_fields = list(set(self.__database_handler.get_fields(table_name='Exchange')) - {'id'}) for field in kwargs.keys(): @@ -281,9 +281,15 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." f"Required fields are {required_fields}.") + # Avoid empty sample + if len(kwargs) == 0: + required_fields = set(self.__database_handler.get_fields(table_name='Exchange')) - {'id'} + necessary_fields = list(required_fields.intersection(self.__data_training.keys())) + kwargs = {field: self.__data_training[field] for field in necessary_fields} + # If Environment is a TcpIpClient, send request to the Server if self.as_tcp_ip_client: - return TcpIpClient.get_prediction(self, **kwargs) + return self.tcp_ip_client.get_prediction(**kwargs) # Otherwise, check the hierarchy of managers if self.environment_manager.data_manager is None: diff --git a/src/Pipelines/BaseTraining.py b/src/Pipelines/BaseTraining.py index 9578a6ef..c4901910 100644 --- a/src/Pipelines/BaseTraining.py +++ b/src/Pipelines/BaseTraining.py @@ -226,7 +226,6 @@ def train_end(self) -> None: Called once at the end of the training pipeline. """ - self.network_manager.save_network(last_save=True) self.data_manager.close() self.network_manager.close() if self.stats_manager is not None: From 860c51f7177e589c1aa54cc566d2c34bbe1f973b Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 27 Oct 2022 17:48:55 +0200 Subject: [PATCH 26/61] Prevent prediction requests in DataGeneration Pipeline. --- src/AsyncSocket/TcpIpClient.py | 3 +++ src/AsyncSocket/TcpIpServer.py | 4 ++++ src/Environment/BaseEnvironment.py | 5 +++++ src/Manager/EnvironmentManager.py | 10 ++++++---- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 5b192e12..dbcf48d0 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -66,6 +66,9 @@ async def __initialize(self) -> None: loop = get_event_loop() + # Receive prediction requests authorization + self.allow_prediction_requests = await self.receive_data(loop=loop, sender=self.sock) + # Receive number of sub-steps self.simulations_per_step = await self.receive_data(loop=loop, sender=self.sock) diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index b9e89e2a..cd8de58a 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -125,6 +125,10 @@ async def __initialize(self) -> None: # Initialisation process for each client for client_id, client in self.clients: + # Send prediction request authorization + await self.send_data(data_to_send=self.environment_manager.allow_prediction_requests, + loop=loop, receiver=client) + # Send number of sub-steps nb_steps = self.environment_manager.simulations_per_step if self.environment_manager else 1 await self.send_data(data_to_send=nb_steps, loop=loop, receiver=client) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 72c10e73..81f664f4 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -271,6 +271,11 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: # Check kwargs if self.__first_add[1]: + + if (self.environment_manager is not None and not self.environment_manager.allow_prediction_requests) or \ + (self.tcp_ip_client is not None and not self.tcp_ip_client.allow_prediction_requests): + raise ValueError(f"[{self.name}] Prediction request is not available in data generation Pipeline.") + if len(kwargs) == 0 and len(self.__data_training) == 0: raise ValueError(f"[{self.name}] The prediction request requires the network fields.") self.__database_handler.load() diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 776d89d0..5d7275c6 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -19,15 +19,16 @@ def __init__(self, """ Deals with the online generation of data for both training and running of the neural networks. - :param environment_config: Specialisation containing the parameters of the environment manager. - :param session: Path to the session directory. + :param environment_config: Configuration object with the parameters of the Environment. :param data_manager: DataManager that handles the EnvironmentManager. - :param batch_size: Number of samples in a batch of data. + :param pipeline: Type of the pipeline. + :param session: Path to the session repository. + :param batch_size: Number of samples in a single batch. """ self.name: str = self.__class__.__name__ - # Managers architecture + # Session variables self.data_manager: Any = data_manager # Data producing parameters @@ -37,6 +38,7 @@ def __init__(self, self.always_produce: bool = environment_config.always_produce self.simulations_per_step: int = environment_config.simulations_per_step self.max_wrong_samples_per_step: int = environment_config.max_wrong_samples_per_step + self.allow_prediction_requests: bool = pipeline != 'data_generation' self.dataset_batch: Optional[List[List[int]]] = None # Create the Visualizer From c3330a29b3720fdb5287b68991b9fcea7e0996ba Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 28 Oct 2022 09:55:48 +0200 Subject: [PATCH 27/61] Prevent prediction request in DataGeneration Pipeline. --- src/Environment/BaseEnvironment.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 81f664f4..95fd910e 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -293,21 +293,25 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: kwargs = {field: self.__data_training[field] for field in necessary_fields} # If Environment is a TcpIpClient, send request to the Server - if self.as_tcp_ip_client: + if self.tcp_ip_client is not None: return self.tcp_ip_client.get_prediction(**kwargs) # Otherwise, check the hierarchy of managers - if self.environment_manager.data_manager is None: - raise ValueError("Cannot request prediction if DataManager does not exist") - # Get a prediction - self.__database_handler.update(table_name='Exchange', - data=kwargs, - line_id=self.instance_id) - self.environment_manager.data_manager.get_prediction(self.instance_id) - data_pred = self.__database_handler.get_line(table_name='Exchange', - line_id=self.instance_id) - del data_pred['id'] - return data_pred + elif self.environment_manager is not None: + if self.environment_manager.data_manager is None: + raise ValueError("Cannot request prediction if DataManager does not exist") + # Get a prediction + self.__database_handler.update(table_name='Exchange', + data=kwargs, + line_id=self.instance_id) + self.environment_manager.data_manager.get_prediction(self.instance_id) + data_pred = self.__database_handler.get_line(table_name='Exchange', + line_id=self.instance_id) + del data_pred['id'] + return data_pred + + else: + raise ValueError(f"[{self.name}] This Environment has not Manager.") def _get_prediction(self): """ From 22b9c8acde5d25134d2457d98ee72a2eb0f18204 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 28 Oct 2022 15:51:21 +0200 Subject: [PATCH 28/61] Update comments and docstrings. --- src/AsyncSocket/AbstractEnvironment.py | 4 +- src/AsyncSocket/TcpIpClient.py | 22 +- src/AsyncSocket/TcpIpServer.py | 60 ++--- src/Database/BaseDatabaseConfig.py | 4 +- src/Database/DatabaseHandler.py | 139 +++++++++-- src/Environment/BaseEnvironment.py | 241 +++++++++++------- src/Environment/BaseEnvironmentConfig.py | 3 +- src/Manager/DataManager.py | 62 +++-- src/Manager/DatabaseManager.py | 304 ++++++++++++++--------- src/Manager/EnvironmentManager.py | 143 ++++++----- src/Manager/NetworkManager.py | 146 ++++++----- src/Manager/StatsManager.py | 41 ++- src/Network/BaseNetwork.py | 68 ++--- src/Network/BaseNetworkConfig.py | 2 +- src/Network/BaseOptimization.py | 12 +- src/Network/DataTransformation.py | 19 +- src/Pipelines/BaseDataGeneration.py | 27 +- src/Pipelines/BasePipeline.py | 46 ++-- src/Pipelines/BasePrediction.py | 28 +-- src/Pipelines/BaseTraining.py | 33 ++- 20 files changed, 848 insertions(+), 556 deletions(-) diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 573ef6cb..55423c61 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -46,7 +46,7 @@ def __init__(self, ########################################################################################## ########################################################################################## - # Initializing Environment # + # Environment initialization # ########################################################################################## ########################################################################################## @@ -82,7 +82,7 @@ def close(self) -> None: ########################################################################################## ########################################################################################## - # Defining a sample # + # Defining data samples # ########################################################################################## ########################################################################################## diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index dbcf48d0..66c5e821 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -6,7 +6,7 @@ from numpy import ndarray from DeepPhysX.Core.AsyncSocket.TcpIpObject import TcpIpObject -from DeepPhysX.Core.AsyncSocket.AbstractEnvironment import AbstractEnvironment, Database +from DeepPhysX.Core.AsyncSocket.AbstractEnvironment import AbstractEnvironment class TcpIpClient(TcpIpObject): @@ -17,33 +17,33 @@ def __init__(self, port: int = 10000, instance_id: int = 0, instance_nb: int = 1, - visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): + visualization_db: Optional[Union[Tuple[str, str]]] = None): """ - TcpIpClient is both a TcpIpObject which communicate with a TcpIpServer and an AbstractEnvironment to compute - simulated data. + TcpIpClient is a TcpIpObject which communicate with a TcpIpServer and manages an Environment to compute data. + :param environment: Environment class. :param ip_address: IP address of the TcpIpObject. :param port: Port number of the TcpIpObject. :param instance_id: Index of this instance. :param instance_nb: Number of simultaneously launched instances. + :param visualization_db: Path to the visualization Database. """ TcpIpObject.__init__(self, ip_address=ip_address, port=port) + # Create instance self.environment = environment(as_tcp_ip_client=True, instance_id=instance_id, instance_nb=instance_nb, visualization_db=visualization_db) self.environment.tcp_ip_client = self - # Bind to client address + # Bind to client address and send ID self.sock.connect((ip_address, port)) - # Send ID self.sync_send_labeled_data(data_to_send=instance_id, label="instance_ID", receiver=self.sock, send_read_command=False) - # Flag to trigger client's shutdown self.close_client: bool = False ########################################################################################## @@ -81,7 +81,7 @@ async def __initialize(self) -> None: self.environment.get_database_handler().init_remote(storing_partitions=partitions, exchange_db=exchange) - # Create the environment + # Initialize the environment self.environment.create() self.environment.init() self.environment.init_database() @@ -152,8 +152,7 @@ async def __close(self) -> None: ########################################################################################## ########################################################################################## - def get_prediction(self, - **kwargs) -> Dict[str, ndarray]: + def get_prediction(self, **kwargs) -> Dict[str, ndarray]: """ Request a prediction from Network. @@ -288,6 +287,7 @@ async def action_on_change_db(self, :param loop: Asyncio event loop. :param sender: TcpIpObject sender. """ - + + # Update the partition list in the DatabaseHandler new_database = await self.receive_data(loop=loop, sender=sender) self.environment.get_database_handler().update_list_partitions_remote(new_database.split('///')) diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index cd8de58a..7dc5ff64 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -51,32 +51,45 @@ def __init__(self, self.sample_to_client_id: List[int] = [] self.batch_from_dataset: Optional[List[int]] = None self.first_time: bool = True - self.data_lines: List[int] = [] + self.data_lines: List[List[int]] = [] # Reference to EnvironmentManager self.environment_manager: Optional[Any] = manager # Connect the Server to the Database - self.database_handler = DatabaseHandler(on_partitions_handler=self.__database_handler_partitions, - name='ServerHandler') + self.database_handler = DatabaseHandler(on_partitions_handler=self.__database_handler_partitions) self.environment_manager.data_manager.connect_handler(self.database_handler) ########################################################################################## ########################################################################################## - # Connect Clients # + # DatabaseHandler management # ########################################################################################## ########################################################################################## - def __database_handler_partitions(self): + def get_database_handler(self) -> DatabaseHandler: + """ + Get the DatabaseHandler of the TcpIpServer. + """ + + return self.database_handler + + def __database_handler_partitions(self) -> None: + """ + Partition update event of the DatabaseHandler. + """ + # Send the new partition to every Client for _, client in self.clients: self.sync_send_command_change_db(receiver=client) new_partition = self.database_handler.get_partitions()[-1] self.sync_send_data(data_to_send=f'{new_partition.get_path()[0]}///{new_partition.get_path()[1]}', receiver=client) - def get_database_handler(self) -> DatabaseHandler: - return self.database_handler + ########################################################################################## + ########################################################################################## + # Connect Clients # + ########################################################################################## + ########################################################################################## def connect(self) -> None: """ @@ -160,7 +173,7 @@ async def __initialize(self) -> None: ########################################################################################## def get_batch(self, - animate: bool = True) -> List[int]: + animate: bool = True) -> List[List[int]]: """ Build a batch from clients samples. @@ -195,10 +208,7 @@ async def __communicate(self, client_id: Optional[int] = None, animate: bool = True) -> None: """ - | Communication protocol with a client. It goes through different steps: - | 1) Eventually send samples to Client - | 2) Running steps & Receiving training data - | 3) Add data to the Queue + Communication protocol with a client. :param client: TcpIpObject client to communicate with. :param client_id: Index of the client. @@ -231,36 +241,12 @@ def set_dataset_batch(self, """ Receive a batch of data from the Dataset. Samples will be dispatched between clients. - :param data_lines: Batch of data. + :param data_lines: Batch of indices of samples. """ # Define batch from dataset self.batch_from_dataset = data_lines.copy() - def change_database(self, - database: str): - """ - Change the Database to connect to for each Client. - - :param database: Path to the new Database. - """ - - async_run(self.__change_database(database)) - - async def __change_database(self, - database: str): - """ - Change the Database to connect to for each Client. - - :param database: Path to the new Database. - """ - - loop = get_event_loop() - for client_id, client in self.clients: - await self.send_command_change_db(loop=loop, receiver=client) - await self.send_data(data_to_send=database[0], loop=loop, receiver=client) - await self.send_data(data_to_send=database[1], loop=loop, receiver=client) - ########################################################################################## ########################################################################################## # Server & Client shutdown # diff --git a/src/Database/BaseDatabaseConfig.py b/src/Database/BaseDatabaseConfig.py index 804846a1..0ffe5934 100644 --- a/src/Database/BaseDatabaseConfig.py +++ b/src/Database/BaseDatabaseConfig.py @@ -12,7 +12,7 @@ def __init__(self, normalize: bool = False, recompute_normalization: bool = False): """ - Configuration class to parameterize the Dataset and its Manager. + BaseDatabaseConfig is a configuration class to parameterize the Database and the DatabaseManager. :param existing_dir: Path to an existing Dataset repository. :param mode: Specify the Dataset mode that should be used between 'training', 'validation' and 'running'. @@ -59,7 +59,7 @@ def __init__(self, self.normalize: bool = normalize self.recompute_normalization: bool = recompute_normalization - def __str__(self) -> str: + def __str__(self): description = "\n" description += f"{self.name}\n" diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py index 4e031ec9..44ebd548 100644 --- a/src/Database/DatabaseHandler.py +++ b/src/Database/DatabaseHandler.py @@ -8,26 +8,42 @@ class DatabaseHandler: def __init__(self, - name: str = '', on_init_handler: Optional[Callable] = None, on_partitions_handler: Optional[Callable] = None): + """ + DatabaseHandler allows components to be synchronized with the Database partitions and to read / write data. - self.name = name + :param on_init_handler: Event to trigger when the DatabaseHandler is initialized. + :param on_partitions_handler: Event to trigger when the list of partitions is updated. + """ + + # Databases variables self.__storing_partitions: List[Database] = [] self.__exchange_db: Optional[Database] = None - self.__on_init_handler = self.__default_handler if on_init_handler is None else on_init_handler - self.__on_partitions_handler = self.__default_handler if on_partitions_handler is None else on_partitions_handler - def __default_handler(self): + # Event handlers + self.__on_init_handler = self.default_handler if on_init_handler is None else on_init_handler + self.__on_partitions_handler = self.default_handler if on_partitions_handler is None else on_partitions_handler + + def default_handler(self): pass - ### - # Manage partitions - ### + ########################################################################################## + ########################################################################################## + # Partitions management # + ########################################################################################## + ########################################################################################## def init(self, storing_partitions: List[Database], exchange_db: Database) -> None: + """ + Initialize the list of the partitions. + + :param storing_partitions: List of the storing Database partitions. + :param exchange_db: Exchange Database. + """ + self.__storing_partitions = storing_partitions.copy() self.__exchange_db = exchange_db self.__on_init_handler() @@ -35,6 +51,13 @@ def init(self, def init_remote(self, storing_partitions: List[List[str]], exchange_db: List[str]) -> None: + """ + Initialize the list of partitions in remote DatabaseHandlers. + + :param storing_partitions: List of paths to the storing Database partitions. + :param exchange_db: Path to the exchange Database. + """ + self.__storing_partitions = [Database(database_dir=partition[0], database_name=partition[1]).load() for partition in storing_partitions] self.__exchange_db = Database(database_dir=exchange_db[0], @@ -43,42 +66,81 @@ def init_remote(self, def update_list_partitions(self, partition: Database) -> None: + """ + Add a new storing partition to the list. + + :param partition: New storing partition to add. + """ + self.__storing_partitions.append(partition) self.__on_partitions_handler() def update_list_partitions_remote(self, - partition: List[str]): + partition: List[str]) -> None: + """ + Add a new storing partition to the list in remote DatabaseHandler. + + :param partition: Path to the new storing partition. + """ + self.__storing_partitions.append(Database(database_dir=partition[0], database_name=partition[1]).load()) self.__on_partitions_handler() - def get_database_dir(self): - return self.__storing_partitions[0].get_path()[0] + def load(self) -> None: + """ + Load the Database partitions stored by the component. + """ - def load(self): for db in self.__storing_partitions: db.load() self.__exchange_db.load() - def get_partitions(self): + def get_database_dir(self) -> str: + """ + Get the database repository of the session. + """ + + return self.__storing_partitions[0].get_path()[0] + + def get_partitions(self) -> List[Database]: + """ + Get the storing Database partitions. + """ + return self.__storing_partitions - def get_exchange(self): + def get_exchange(self) -> Database: + """ + Get the exchange Database. + """ + return self.__exchange_db - ### - # Database Architecture - ### + ########################################################################################## + ########################################################################################## + # Databases architecture # + ########################################################################################## + ########################################################################################## def create_fields(self, table_name: str, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + """ + Create new Fields in a Table from one of the Databases. + + :param table_name: Name of the Table. + :param fields: Field or list of Fields names and types. + """ + # Create the Field(s) in the exchange Database if table_name == 'Exchange': self.__exchange_db.load() if len(self.__exchange_db.get_fields(table_name=table_name)) == 1: self.__exchange_db.create_fields(table_name=table_name, fields=fields) + + # Create the Field(s) in the storing Database else: if len(self.__storing_partitions) == 1: self.__storing_partitions[0].load() @@ -88,20 +150,36 @@ def create_fields(self, def get_fields(self, table_name: str) -> List[str]: + """ + Get the list of Fields in a Table. + + :param table_name: Name of the Table. + """ database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[0] return database.get_fields(table_name=table_name) - ### - # Data Read / Write - ### + ########################################################################################## + ########################################################################################## + # Databases editing # + ########################################################################################## + ########################################################################################## def add_data(self, table_name: str, data: Dict[str, Any]) -> Union[int, List[int]]: + """ + Add a new line of data in a Database. + + :param table_name: Name of the Table. + :param data: New line of the Table. + """ + # Add data in the exchange Database if table_name == 'Exchange': return self.__exchange_db.add_data(table_name=table_name, data=data) + + # Add data in the storing Database else: return [len(self.__storing_partitions) - 1, self.__storing_partitions[-1].add_data(table_name=table_name, data=data)] @@ -110,6 +188,13 @@ def update(self, table_name: str, data: Dict[str, Any], line_id: Union[int, List[int]]) -> None: + """ + Update a line in a Database. + + :param table_name: Name of the Table. + :param data: Updated line of the Table. + :param line_id: Index of the line to edit. + """ database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[line_id[0]] line_id = line_id[1] if type(line_id) == list else line_id @@ -119,6 +204,13 @@ def get_line(self, table_name: str, line_id: Union[int, List[int]], fields: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]: + """ + Get a line of data from a Database. + + :param table_name: Name of the Table. + :param line_id: Index of the line to get. + :param fields: Data fields to extract. + """ database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[line_id[0]] line_id = line_id[1] if type(line_id) == list else line_id @@ -130,6 +222,13 @@ def get_lines(self, table_name: str, lines_id: List[List[int]], fields: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]: + """ + Get lines of data from a Database. + + :param table_name: Name of the Table. + :param lines_id: Indices of the lines to get. + :param fields: Data fields to extract. + """ # Transform list of lines to batch of lines per partition batch_indices = array(lines_id) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 95fd910e..2f3c2b70 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -17,12 +17,12 @@ def __init__(self, instance_nb: int = 1, visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): """ - BaseEnvironment is an environment class to compute simulated data for the network and its optimization process. + BaseEnvironment computes simulated data for the Network and its training process. + :param as_tcp_ip_client: Environment is a TcpIpObject if True, is owned by an EnvironmentManager if False. :param instance_id: ID of the instance. :param instance_nb: Number of simultaneously launched instances. - :param as_tcp_ip_client: Environment is a TcpIpObject if True, is owned by an EnvironmentManager if False. - :param visualization_db: The path to the visualization Database or the visualization Database object to connect to. + :param visualization_db: The path to the visualization Database or the visualization Database object to connect. """ AbstractEnvironment.__init__(self, @@ -57,21 +57,10 @@ def __init__(self, ########################################################################################## ########################################################################################## - # Initializing Environment # + # Environment initialization # ########################################################################################## ########################################################################################## - def __database_handler_init(self): - self.__database_handler.load() - if self.instance_id == 1: - self.__database_handler.create_fields(table_name='Training', - fields=('env_id', int)) - self.__database_handler.create_fields(table_name='Additional', - fields=('env_id', int)) - - def get_database_handler(self) -> DatabaseHandler: - return self.__database_handler - def create(self) -> None: """ Create the Environment. Automatically called when Environment is launched. @@ -104,6 +93,41 @@ def init_visualization(self) -> None: pass + def save_parameters(self, **kwargs) -> None: + """ + Save a set of parameters in the Database. + """ + + # Create a dedicated Database + database_dir = self.__database_handler.get_database_dir() + if isfile(join(database_dir, 'environment_parameters.db')): + database = Database(database_dir=database_dir, + database_name='environment_parameters').load() + else: + database = Database(database_dir=database_dir, + database_name='environment_parameters').new() + + # Create fields and add data + fields = [(field, type(value)) for field, value in kwargs.items()] + database.create_table(table_name=f'Environment_{self.instance_id}', fields=fields) + database.add_data(table_name=f'Environment_{self.instance_id}', data=kwargs) + database.close() + + def load_parameters(self) -> Dict[str, Any]: + """ + Load a set of parameters from the Database. + """ + + # Load the dedicated Database and the parameters + database_dir = self.__database_handler.get_database_dir() + if isfile(join(database_dir, 'environment_parameters.db')): + database = Database(database_dir=database_dir, + database_name='environment_parameters').load() + parameters = database.get_line(table_name=f'Environment_{self.instance_id}') + del parameters['id'] + return parameters + return {} + ########################################################################################## ########################################################################################## # Environment behavior # @@ -128,7 +152,8 @@ def check_sample(self) -> bool: return True - def apply_prediction(self, prediction: Dict[str, ndarray]) -> None: + def apply_prediction(self, + prediction: Dict[str, ndarray]) -> None: """ Apply network prediction in environment. Not mandatory. @@ -148,46 +173,29 @@ def close(self) -> None: ########################################################################################## ########################################################################################## - # Defining a sample # + # Defining data samples # ########################################################################################## ########################################################################################## - def save_parameters(self, **kwargs) -> None: - """ - Save a set of parameters in the Database. + def define_training_fields(self, + fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: """ + Specify the training data fields names and types. - database_dir = self.__database_handler.get_database_dir() - if isfile(join(database_dir, 'environment_parameters.db')): - database = Database(database_dir=database_dir, - database_name='environment_parameters').load() - else: - database = Database(database_dir=database_dir, - database_name='environment_parameters').new() - fields = [(field, type(value)) for field, value in kwargs.items()] - database.create_table(table_name=f'Environment_{self.instance_id}', fields=fields) - database.add_data(table_name=f'Environment_{self.instance_id}', data=kwargs) - database.close() - - def load_parameters(self) -> Dict[str, Any]: - """ - Load a set of parameters from the Database. + :param fields: Field or list of fields to tag as training data. """ - database_dir = self.__database_handler.get_database_dir() - if isfile(join(database_dir, 'environment_parameters.db')): - database = Database(database_dir=database_dir, - database_name='environment_parameters').load() - parameters = database.get_line(table_name=f'Environment_{self.instance_id}') - del parameters['id'] - return parameters - return {} - - def define_training_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: self.__database_handler.create_fields(table_name='Training', fields=fields) - def define_additional_fields(self, fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + def define_additional_fields(self, + fields: Union[List[Tuple[str, Type]], Tuple[str, Type]]) -> None: + """ + Specify the additional data fields names and types. + + :param fields: Field or list of Fields to tag as additional data. + """ + self.__database_handler.create_fields(table_name='Additional', fields=fields) @@ -217,45 +225,15 @@ def set_training_data(self, **kwargs) -> None: def set_additional_data(self, **kwargs) -> None: + """ + Set the additional data to send to the TcpIpServer or the EnvironmentManager. + """ + # Additional data is also set if the Environment can compute data if self.compute_training_data: self.__data_additional = kwargs self.__data_additional['env_id'] = self.instance_id - def _send_training_data(self) -> int: - line_id = self.__database_handler.add_data(table_name='Training', - data=self.__data_training) - self.__database_handler.add_data(table_name='Additional', - data=self.__data_additional) - return line_id - - def _reset_training_data(self) -> None: - self.__data_training = {} - self.__data_additional = {} - self.sample_training = None - self.sample_additional = None - self.update_line = None - - def _update_training_data(self, - line_id: List[int]) -> None: - if self.__data_training != {}: - self.__database_handler.update(table_name='Training', - data=self.__data_training, - line_id=line_id) - if self.__data_additional != {}: - self.__database_handler.update(table_name='Additional', - data=self.__data_additional, - line_id=line_id) - - def _get_training_data(self, - line: List[int]) -> None: - self.update_line = line - self.sample_training = self.__database_handler.get_line(table_name='Training', - line_id=line) - self.sample_additional = self.__database_handler.get_line(table_name='Additional', - line_id=line) - self.sample_additional = None if len(self.sample_additional) == 1 else self.sample_additional - ########################################################################################## ########################################################################################## # Available requests # @@ -313,9 +291,20 @@ def get_prediction(self, **kwargs) -> Dict[str, ndarray]: else: raise ValueError(f"[{self.name}] This Environment has not Manager.") - def _get_prediction(self): + def update_visualisation(self) -> None: """ + Triggers the Visualizer update. + """ + + # If Environment is a TcpIpClient, request to the Server + if self.as_tcp_ip_client: + self.tcp_ip_client.request_update_visualization() + self.factory.render() + def _get_prediction(self): + """ + Request a prediction from Network and apply it to the Environment. + Should not be used by users. """ training_data = self.__data_training.copy() @@ -325,17 +314,93 @@ def _get_prediction(self): del training_data[field] self.apply_prediction(self.get_prediction(**training_data)) - def update_visualisation(self) -> None: + ########################################################################################## + ########################################################################################## + # Database communication # + ########################################################################################## + ########################################################################################## + + def get_database_handler(self) -> DatabaseHandler: """ - Triggers the Visualizer update. + Get the DatabaseHandler of the Environment. """ - # If Environment is a TcpIpClient, request to the Server - if self.as_tcp_ip_client: - self.request_update_visualization() - self.factory.render() + return self.__database_handler + + def __database_handler_init(self): + """ + Init event of the DatabaseHandler. + """ + + # Load Database and create basic fields + self.__database_handler.load() + if self.instance_id == 1: + self.__database_handler.create_fields(table_name='Training', + fields=('env_id', int)) + self.__database_handler.create_fields(table_name='Additional', + fields=('env_id', int)) + + def _send_training_data(self) -> List[int]: + """ + Add the training data and the additional data in their respective Databases. + Should not be used by users. + + :return: Index of the samples in the Database. + """ + + line_id = self.__database_handler.add_data(table_name='Training', + data=self.__data_training) + self.__database_handler.add_data(table_name='Additional', + data=self.__data_additional) + return line_id + + def _update_training_data(self, + line_id: List[int]) -> None: + """ + Update the training data and the additional data in their respective Databases. + Should not be used by users. + + :param line_id: Index of the samples to update. + """ + + if self.__data_training != {}: + self.__database_handler.update(table_name='Training', + data=self.__data_training, + line_id=line_id) + if self.__data_additional != {}: + self.__database_handler.update(table_name='Additional', + data=self.__data_additional, + line_id=line_id) + + def _get_training_data(self, + line_id: List[int]) -> None: + """ + Get the training data and the additional data from their respective Databases. + Should not be used by users. + + :param line_id: Index of the sample to get. + """ + + self.update_line = line_id + self.sample_training = self.__database_handler.get_line(table_name='Training', + line_id=line_id) + self.sample_additional = self.__database_handler.get_line(table_name='Additional', + line_id=line_id) + self.sample_additional = None if len(self.sample_additional) == 1 else self.sample_additional + + def _reset_training_data(self) -> None: + """ + Reset the training data and the additional data variables. + Should not be used by users. + """ + + self.__data_training = {} + self.__data_additional = {} + self.sample_training = None + self.sample_additional = None + self.update_line = None - def __str__(self) -> str: + def __str__(self): description = "\n" description += f" {self.name}\n" diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index 208f4f3b..25bcfcfb 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -39,6 +39,7 @@ def __init__(self, :param load_samples: If True, the dataset will always be used in the environment. :param only_first_epoch: If True, data will always be created from environment. If False, data will be created from the environment during the first epoch and then re-used from the Dataset. + :param always_produce: If True, data will always be produced in Environment(s). :param visualizer: Class of the Visualizer to use. :param record_wrong_samples: If True, wrong samples are recorded through Visualizer. """ @@ -168,7 +169,7 @@ def create_environment(self, f"BaseEnvironment.") return environment - def __str__(self) -> str: + def __str__(self): description = "\n" description += f"{self.name}\n" diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 9f6a94a7..cc112799 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -4,6 +4,7 @@ from DeepPhysX.Core.Manager.EnvironmentManager import EnvironmentManager from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler class DataManager: @@ -19,21 +20,19 @@ def __init__(self, """ DataManager deals with the generation, storage and loading of training data. - A batch is given with a call to 'get_data' on either the DatabaseManager or the EnvironmentManager according to - the context. :param pipeline: Pipeline that handle the DataManager. - :param database_config: Specialisation containing the parameters of the dataset manager. - :param environment_config: Specialisation containing the parameters of the environment manager. - :param session: Path to the session directory. - :param new_session: Flag that indicates whether if the session is new. - :param produce_data: Flag that indicates whether if this session is producing data. - :param int batch_size: Number of samples in a batch. + :param database_config: Configuration object with the parameters of the Database. + :param environment_config: Configuration object with the parameters of the Environment. + :param session: Path to the session repository. + :param new_session: If True, the session is done in a new repository. + :param produce_data: If True, this session will store data in the Database. + :param batch_size: Number of samples in a single batch. """ self.name: str = self.__class__.__name__ - # Managers variables + # Session variables self.pipeline: Optional[Any] = pipeline self.database_manager: Optional[DatabaseManager] = None self.environment_manager: Optional[EnvironmentManager] = None @@ -59,15 +58,34 @@ def __init__(self, self.batch_size = batch_size self.data_lines: List[List[int]] = [] - def connect_handler(self, handler): - self.database_manager.connect_handler(handler) - @property - def nb_environment(self): + def nb_environment(self) -> Optional[int]: + """ + Get the number of Environments managed by the EnvironmentManager. + """ + if self.environment_manager is None: return None return 1 if self.environment_manager.server is None else self.environment_manager.number_of_thread + @property + def normalization(self) -> Dict[str, List[float]]: + """ + Get the normalization coefficients computed by the DatabaseManager. + """ + + return self.database_manager.normalization + + def connect_handler(self, + handler: DatabaseHandler) -> None: + """ + Add a new DatabaseHandler to the list of handlers of the DatabaseManager. + + :param handler: New handler to register. + """ + + self.database_manager.connect_handler(handler) + def get_data(self, epoch: int = 0, animate: bool = True) -> None: @@ -75,8 +93,7 @@ def get_data(self, Fetch data from the EnvironmentManager or the DatabaseManager according to the context. :param epoch: Current epoch number. - :param animate: Allow EnvironmentManager to generate a new sample. - :return: Dict containing the newly computed data. + :param animate: Allow EnvironmentManager to trigger a step itself in order to generate a new sample. """ # Data generation case @@ -128,9 +145,7 @@ def get_data(self, def get_prediction(self, instance_id: int) -> None: """ - Get a Network prediction from an input array. Normalization is applied on input and prediction. - - :return: Network prediction. + Get a Network prediction for the specified Environment instance. """ # Get a prediction @@ -139,13 +154,9 @@ def get_prediction(self, self.pipeline.network_manager.compute_online_prediction(instance_id=instance_id, normalization=self.normalization) - @property - def normalization(self) -> Dict[str, List[float]]: - return self.database_manager.normalization - def close(self) -> None: """ - Launch the closing procedure of Managers. + Launch the closing procedure of the DataManager. """ if self.environment_manager is not None: @@ -153,10 +164,7 @@ def close(self) -> None: if self.database_manager is not None: self.database_manager.close() - def __str__(self) -> str: - """ - :return: A string containing valuable information about the DataManager - """ + def __str__(self): data_manager_str = "" if self.environment_manager: diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index e2a00f8b..3c7f9b2c 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -26,15 +26,17 @@ def __init__(self, """ DatabaseManager handle all operations with input / output files. Allows saving and read tensors from files. - :param database_config: Specialisation containing the parameters of the dataset manager - :param data_manager: DataManager that handles the DatabaseManager - :param new_session: Define the creation of new directories to store data - :param produce_data: True if this session is a network training + :param database_config: Configuration object with the parameters of the Database. + :param data_manager: DataManager that handles the DatabaseManager. + :param pipeline: Type of the Pipeline. + :param session: Path to the session repository. + :param new_session: If True, the session is done in a new repository. + :param produce_data: If True, this session will store data in the Database. """ self.name: str = self.__class__.__name__ - # Manager variables + # Session variables self.pipeline: str = pipeline self.data_manager: Optional[Any] = data_manager self.database_dir: str = join(session, 'dataset') @@ -138,14 +140,19 @@ def __init__(self, database_name='Exchange').new() self.exchange.create_table(table_name='Exchange') - ######################### - # Partitions Management # - ######################### + ########################################################################################## + ########################################################################################## + # Partitions Management # + ########################################################################################## + ########################################################################################## def load_directory(self, - rename_partitions: bool = False): + rename_partitions: bool = False) -> None: """ + Get the Database information from the json file (partitions, samples, etc). + Load all the partitions or create one if necessary. + :param rename_partitions: If True, the existing partitions should be renamed to match the session name. """ # 1. Check the directory existence to prevent bugs @@ -165,7 +172,8 @@ def load_directory(self, self.update_json() if self.recompute_normalization or ( self.normalize and self.json_content['normalization'] == self.json_default['normalization']): - self.update_json(update_normalization=True) + self.json_content['normalization'] = self.compute_normalization() + self.update_json() # 4. Load partitions for each mode self.partition_names = self.json_content['partitions'] @@ -193,9 +201,9 @@ def load_directory(self, # 6. Index partitions self.index_samples() - def create_partition(self): + def create_partition(self) -> None: """ - + Create a new partition of the Database. """ # 1. Define the partition name @@ -229,33 +237,61 @@ def create_partition(self): for handler in self.database_handlers: handler.update_list_partitions(self.partitions[self.mode][-1]) self.partition_index[self.mode] += 1 - self.update_json(update_partitions_lists=True, update_nb_samples=True) + self.json_content['partitions'] = self.partition_names + self.get_nb_samples() + self.update_json() def get_partition_objects(self) -> List[Database]: + """ + Get the list of partitions of the Database for the current mode. + """ + return self.partitions[self.mode] def get_partition_names(self) -> List[List[str]]: + """ + Get the list of partition paths of the Database for the current mode. + """ + return [db.get_path() for db in self.partitions[self.mode]] def remove_empty_partitions(self): + """ + Remove every empty partitions of the Database. + """ for mode in self.modes: + # Check if the last partition for the mode is empty if len(self.partitions[mode]) > 0 and self.partitions[mode][-1].nb_lines(table_name='Training') == 0: - database = self.partitions[mode].pop(-1) - path = database.get_path() + # Erase partition file + path = self.partitions[mode].pop(-1).get_path() remove(join(path[0], f'{path[1]}.db')) + # Remove from information self.partition_names[mode].pop(-1) self.partition_index[mode] -= 1 self.json_content['nb_samples'][mode].pop(-1) - self.update_json(update_partitions_lists=True) + self.json_content['partitions'] = self.partition_names + self.update_json() - ################## - # JSON info file # - ################## + def change_mode(self, + mode: str) -> None: + """ + Change the current Database mode. - def search_partitions_info(self): + :param mode: Name of the Database mode. """ + pass + + ########################################################################################## + ########################################################################################## + # JSON Information file # + ########################################################################################## + ########################################################################################## + + def search_partitions_info(self) -> None: + """ + Get the information about the Database manually if the json file is not found. """ # 1. Get all the partitions @@ -279,7 +315,12 @@ def search_partitions_info(self): # 4. Get the data shapes self.json_content['data_shape'] = self.get_data_shapes() - def get_database_architecture(self): + def get_database_architecture(self) -> Dict[str, List[str]]: + """ + Get the Tables and Fields structure of the Database. + """ + + # Get a training or validation partition if len(self.json_content['partitions']['training']) != 0: db = Database(database_dir=self.database_dir, database_name=self.json_content['partitions']['training'][0]).load() @@ -288,17 +329,23 @@ def get_database_architecture(self): database_name=self.json_content['partitions']['validation'][0]).load() else: return {} + + # Get the architecture, keep relevant fields only architecture = db.get_architecture() - if 'Prediction' in architecture.keys(): - del architecture['Prediction'] for fields in architecture.values(): for field in fields.copy(): if field.split(' ')[0] in ['id', '_dt_']: fields.remove(field) db.close() + return architecture - def get_data_shapes(self): + def get_data_shapes(self) -> Dict[str, List[int]]: + """ + Get the shape of data Fields. + """ + + # Get a training or validation partition if len(self.json_content['partitions']['training']) != 0: db = Database(database_dir=self.database_dir, database_name=self.json_content['partitions']['training'][0]).load() @@ -307,6 +354,8 @@ def get_data_shapes(self): database_name=self.json_content['partitions']['validation'][0]).load() else: return {} + + # Get the data shape for each numpy Field shapes = {} for table_name, fields in self.json_content['architecture'].items(): if db.nb_lines(table_name=table_name) > 0: @@ -316,90 +365,90 @@ def get_data_shapes(self): field_name = field.split(' ')[0] shapes[f'{table_name}.{field_name}'] = data[field_name].shape db.close() + return shapes - def update_json(self, - update_partitions_lists: bool = False, - update_nb_samples: bool = False, - update_architecture: bool = False, - update_shapes: bool = False, - update_normalization: bool = False) -> None: + def get_nb_samples(self) -> None: """ - - + Get the number of sample in each partition. """ - # Update partitions lists - if update_partitions_lists: - self.json_content['partitions'] = self.partition_names - - # Update number of samples - if update_nb_samples: - if len(self.json_content['nb_samples'][self.mode]) == self.partition_index[self.mode]: - self.json_content['nb_samples'][self.mode][-1] = self.nb_samples - else: - self.json_content['nb_samples'][self.mode].append(self.nb_samples) - - # Update DB architecture - if update_architecture: - self.json_content['architecture'] = self.get_database_architecture() - - # Update data shapes - if update_shapes: - self.json_content['data_shape'] = self.get_data_shapes() + nb_samples = self.partitions[self.mode][-1].nb_lines(table_name='Training') + if len(self.json_content['nb_samples'][self.mode]) == self.partition_index[self.mode]: + self.json_content['nb_samples'][self.mode][-1] = nb_samples + else: + self.json_content['nb_samples'][self.mode].append(nb_samples) - # Update normalization coefficients - if update_normalization: - self.json_content['normalization'] = self.compute_normalization() + def update_json(self) -> None: + """ + Update the JSON info file with the current Database information. + """ # Overwrite json file with open(join(self.database_dir, 'dataset.json'), 'w') as json_file: json_dump(self.json_content, json_file, indent=3, cls=CustomJSONEncoder) - ######################### - # Database read / write # - ######################### + ########################################################################################## + ########################################################################################## + # Database access and edition # + ########################################################################################## + ########################################################################################## def connect_handler(self, handler: DatabaseHandler) -> None: + """ + Add and init a new DatabaseHandler to the list. + + :param handler: New DatabaseHandler. + """ handler.init(storing_partitions=self.get_partition_objects(), exchange_db=self.exchange) self.database_handlers.append(handler) - def index_samples(self): + def index_samples(self) -> None: + """ + Create a new indexing list of samples. Samples are identified by [partition_id, line_id]. + """ + # Create the indices for each sample such as [partition_id, line_id] for i, nb_sample in enumerate(self.json_content['nb_samples'][self.mode]): partition_indices = empty((nb_sample, 2), dtype=int) partition_indices[:, 0] = i partition_indices[:, 1] = arange(1, nb_sample + 1) self.sample_indices = concatenate((self.sample_indices, partition_indices)) + # Init current sample position self.sample_id = 0 + # Shuffle the indices if required if self.shuffle: shuffle(self.sample_indices) - @property - def nb_samples(self) -> int: - return self.partitions[self.mode][-1].nb_lines(table_name='Training') - def add_data(self, - data_lines: Optional[List[int]] = None): + data_lines: Optional[List[int]] = None) -> None: """ + Manage new lines adding in the Database. + :param data_lines: Indices of the newly added lines. """ # 1. Update the json file - self.update_json(update_nb_samples=True) + self.get_nb_samples() + self.update_json() + # 1.1. Init partitions information on the first sample if self.first_add: for handler in self.database_handlers: handler.load() - self.update_json(update_partitions_lists=True, update_shapes=True, update_architecture=True) + self.json_content['partitions'] = self.partition_names + self.json_content['architecture'] = self.get_database_architecture() + self.json_content['data_shape'] = self.get_data_shapes() + self.update_json() self.first_add = False + # 1.2. Update the normalization coefficients if required if self.normalize and self.mode == 'training' and self.pipeline == 'training' and data_lines is not None: self.json_content['normalization'] = self.update_normalization(data_lines=data_lines) self.update_json() - # 2. Check the size of the partition + # 2. Check the size of the current partition if self.max_file_size is not None: if self.partitions[self.mode][-1].memory_size > self.max_file_size: self.create_partition() @@ -407,93 +456,79 @@ def add_data(self, def get_data(self, batch_size: int) -> List[List[int]]: """ + Select a batch of indices to read in the Database. + :param batch_size: Number of sample in a single batch. """ # 1. Check if dataset is loaded and if the current sample is not the last if self.sample_id >= len(self.sample_indices): self.index_samples() - # 2. Update dataset index + # 2. Update dataset index and get a batch of data idx = self.sample_id self.sample_id += batch_size - - # 3. Get a batch of data lines = self.sample_indices[idx:self.sample_id].tolist() - # 4. Ensure the batch has th good size + # 3. Ensure the batch has the good size if len(lines) < batch_size: lines += self.get_data(batch_size=batch_size - len(lines)) - return lines - ############ - # Behavior # - ############ - - def close(self): - """ - - """ - - # Check non-empty last partition - self.remove_empty_partitions() - - if self.normalize and self.pipeline == 'data_generation': - self.update_json(update_normalization=True) + return lines - # Cose databases - for mode in self.modes: - for database in self.partitions[mode]: - database.close() - self.exchange.close(erase_file=True) + ########################################################################################## + ########################################################################################## + # Data normalization computation # + ########################################################################################## + ########################################################################################## - def change_mode(self, mode: int) -> None: + @property + def normalization(self) -> Optional[Dict[str, List[float]]]: """ - + Get the normalization coefficients. """ - pass - - ################# - # Normalization # - ################# + if self.json_content['normalization'] == {} or not self.normalize: + return None + return self.json_content['normalization'] def compute_normalization(self) -> Dict[str, List[float]]: """ - + Compute the mean and the standard deviation of all the training samples for each data field. """ - # Get the fields to normalize + # 1. Get the fields to normalize fields = [] for field in self.json_content['data_shape']: table_name, field_name = field.split('.') fields += [field_name] if table_name == 'Training' else [] - - # Init result normalization = {field: [0., 0.] for field in fields} - # Compute the mean for each field + # 2. Compute the mean of samples for each field means = {field: [] for field in fields} nb_samples = [] + # 2.1. Compute the mean for each partition for partition in self.partitions['training']: - data_to_normalize = self.load_partitions_fields(partition=partition, - fields=fields) + data_to_normalize = self.load_partitions_fields(partition=partition, fields=fields) nb_samples.append(data_to_normalize['id'][-1]) for field in fields: data = array(data_to_normalize[field]) means[field].append(data.mean()) + # 2.2. Compute the global mean for field in fields: normalization[field][0] = sum([(n / sum(nb_samples)) * m for n, m in zip(nb_samples, means[field])]) - # Compute the standard deviation for each field + # 3. Compute the standard deviation of samples for each field stds = {field: [] for field in fields} + # 3.1. Compute the standard deviation for each partition for partition in self.partitions['training']: data_to_normalize = self.load_partitions_fields(partition=partition, fields=fields) for field in fields: data = array(data_to_normalize[field]) stds[field].append(mean(abs(data - normalization[field][0]) ** 2)) + # 3.2. Compute the global standard deviation for field in fields: normalization[field][1] = sqrt(sum([(n / sum(nb_samples)) * std for n, std in zip(nb_samples, stds[field])])) @@ -502,17 +537,22 @@ def compute_normalization(self) -> Dict[str, List[float]]: def update_normalization(self, data_lines: List[int]) -> Dict[str, List[float]]: + """ + Update the mean and the standard deviation of all the training samples with newly added samples for each data + field. - previous_normalization = self.normalization - previous_nb_samples = self.total_nb_sample - self.total_nb_sample += len(data_lines) + :param data_lines: Indices of the newly added lines. + """ - # First update + # 1. Get the previous normalization coefficients and number of samples + previous_normalization = self.normalization if previous_normalization is None: return self.compute_normalization() new_normalization = previous_normalization.copy() + previous_nb_samples = self.total_nb_sample + self.total_nb_sample += len(data_lines) - # Compute the mean for each field + # 2. Compute the global mean of samples for each field fields = list(previous_normalization.keys()) data_to_normalize = self.partitions[self.mode][-1].get_lines(table_name='Training', fields=fields, @@ -524,9 +564,10 @@ def update_normalization(self, (len(data_lines) / self.total_nb_sample) * data.mean() new_normalization[field][0] = m - # Compute standard deviation for each field + # 3. Compute standard deviation of samples for each field stds = {field: [] for field in fields} nb_samples = [] + # 3.1. Recompute the standard deviation for each partition with the new mean value for partition in self.partitions['training']: data_to_normalize = self.load_partitions_fields(partition=partition, fields=fields) @@ -534,29 +575,58 @@ def update_normalization(self, for field in fields: data = array(data_to_normalize[field]) stds[field].append(mean(abs(data - new_normalization[field][0]) ** 2)) + # 3.2. Compute the global standard deviation for field in fields: new_normalization[field][1] = sqrt(sum([(n / sum(nb_samples)) * std for n, std in zip(nb_samples, stds[field])])) return new_normalization - @property - def normalization(self) -> Dict[str, List[float]]: - return None if self.json_content['normalization'] == {} or not self.normalize else self.json_content['normalization'] + @staticmethod + def load_partitions_fields(partition: Database, + fields: List[str]) -> Dict[str, ndarray]: + """ + Load all the samples from a Field of a Table in the Database. + + :param partition: Database partition to load. + :param fields: Data Fields to get. + """ - def load_partitions_fields(self, - partition: Database, - fields: List[str]): partition.load() return partition.get_lines(table_name='Training', fields=fields, batched=True) + ########################################################################################## + ########################################################################################## + # Manager behavior # + ########################################################################################## + ########################################################################################## + + def close(self): + """ + Launch the closing procedure of the DatabaseManager. + """ + + # Check non-empty last partition + self.remove_empty_partitions() + + # Compute final normalization if required + if self.normalize and self.pipeline == 'data_generation': + self.json_content['normalization'] = self.compute_normalization() + self.update_json() + + # Close Database partitions + for mode in self.modes: + for database in self.partitions[mode]: + database.close() + self.exchange.close(erase_file=True) + def __str__(self): description = "\n" description += f"# {self.name}\n" description += f" Dataset Repository: {self.database_dir}\n" - size = f"No limits" if self.max_file_size is None else f"{self.max_file_size * 1e-9} Go" + size = f"No limits" if self.max_file_size is None else f"{self.max_file_size * 1e-9} Gb" description += f" Partitions size: {size}\n" return description diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 5d7275c6..389596fa 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -2,9 +2,8 @@ from asyncio import run as async_run from os.path import join -from SSD.Core.Storage.Database import Database - from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig, TcpIpServer, BaseEnvironment +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer @@ -17,7 +16,7 @@ def __init__(self, session: str = 'sessions/default', batch_size: int = 1): """ - Deals with the online generation of data for both training and running of the neural networks. + EnvironmentManager handle the communication with Environment(s). :param environment_config: Configuration object with the parameters of the Environment. :param data_manager: DataManager that handles the EnvironmentManager. @@ -31,7 +30,7 @@ def __init__(self, # Session variables self.data_manager: Any = data_manager - # Data producing parameters + # Data production variables self.batch_size: int = batch_size self.only_first_epoch: bool = environment_config.only_first_epoch self.load_samples: bool = environment_config.load_samples @@ -41,7 +40,7 @@ def __init__(self, self.allow_prediction_requests: bool = pipeline != 'data_generation' self.dataset_batch: Optional[List[List[int]]] = None - # Create the Visualizer + # Create a Visualizer to provide the visualization Database self.visualizer: Optional[VedoVisualizer] = None visualization_db = None if environment_config.visualizer is not None: @@ -55,140 +54,161 @@ def __init__(self, self.number_of_thread: int = 1 if force_local else environment_config.number_of_thread self.server: Optional[TcpIpServer] = None self.environment: Optional[BaseEnvironment] = None + # Create Server if environment_config.as_tcp_ip_client and not force_local: self.server = environment_config.create_server(environment_manager=self, batch_size=batch_size, visualization_db=None if visualization_db is None else visualization_db.get_path()) + # Create Environment else: self.environment = environment_config.create_environment(visualization_db=visualization_db) self.environment.environment_manager = self self.data_manager.connect_handler(self.environment.get_database_handler()) - - # Create & Init Environment self.environment.create() self.environment.init() self.environment.init_database() self.environment.init_visualization() - # Define get_data and dispatch methods - self.get_database_handler = self.get_server_database_handler if self.server else self.get_environment_database_handler - self.change_database = self.change_database_in_server if self.server else self.change_database_in_environment - self.get_data = self.get_data_from_server if self.server else self.get_data_from_environment - self.dispatch_batch = self.dispatch_batch_to_server if self.server else self.dispatch_batch_to_environment + # Define whether methods are used for environment or server + self.get_database_handler = self.__get_server_db_handler if self.server else self.__get_environment_db_handler + self.get_data = self.__get_data_from_server if self.server else self.__get_data_from_environment + self.dispatch_batch = self.__dispatch_batch_to_server if self.server else self.__dispatch_batch_to_environment - # Init visualizer + # Init the Visualizer once Environments are initialized if self.visualizer is not None: if len(self.visualizer.get_database().get_tables()) == 1: self.visualizer.get_database().load() self.visualizer.init_visualizer() - def get_server_database_handler(self): + ########################################################################################## + ########################################################################################## + # DatabaseHandler management # + ########################################################################################## + ########################################################################################## + + def __get_server_db_handler(self) -> DatabaseHandler: + """ + Get the DatabaseHandler of the TcpIpServer. + """ + return self.server.get_database_handler() - def get_environment_database_handler(self): - return self.environment.get_database_handler() + def __get_environment_db_handler(self) -> DatabaseHandler: + """ + Get the DatabaseHandler of the Environment. + """ - def change_database_in_server(self, database: Database): - self.server.change_database(database.get_path()) + return self.environment.get_database_handler() - def change_database_in_environment(self, database): - self.environment.database = database + ########################################################################################## + ########################################################################################## + # Data creation management # + ########################################################################################## + ########################################################################################## - def get_data_from_server(self, - animate: bool = True) -> List[int]: + def __get_data_from_server(self, + animate: bool = True) -> List[List[int]]: """ Compute a batch of data from Environments requested through TcpIpServer. - :param animate: If True, triggers an environment step. + :param animate: If True, triggers an environment step. """ - # Get data from server return self.server.get_batch(animate) - def get_data_from_environment(self, - animate: bool = True, - save_data: bool = True, - request_prediction: bool = False) -> List[List[int]]: + def __get_data_from_environment(self, + animate: bool = True, + save_data: bool = True, + request_prediction: bool = False) -> List[List[int]]: """ Compute a batch of data directly from Environment. :param animate: If True, triggers an environment step. + :param save_data: If True, data must be stored in the Database. + :param request_prediction: If True, a prediction request will be triggered. """ - # 1. Produce batch while batch size is not complete + # Produce batch while batch size is not complete nb_sample = 0 dataset_lines = [] while nb_sample < self.batch_size: - # 1.1 Send a sample if a batch from dataset is given + # 1. Send a sample from the Database if one is given update_line = None if self.dataset_batch is not None: update_line = self.dataset_batch.pop(0) self.environment._get_training_data(update_line) - # 1.2 Run the defined number of step + # 2. Run the defined number of steps if animate: for current_step in range(self.simulations_per_step): # Sub-steps do not produce data self.environment.compute_training_data = current_step == self.simulations_per_step - 1 async_run(self.environment.step()) - # 1.3 Add the produced sample to the batch if the sample is validated + # 3. Add the produced sample index to the batch if the sample is validated if self.environment.check_sample(): nb_sample += 1 + # 3.1. The prediction Pipeline triggers a prediction request if request_prediction: self.environment._get_prediction() + # 3.2. Add the data to the Database if save_data: + # Update the line if the sample was given by the database if update_line is None: new_line = self.environment._send_training_data() dataset_lines.append(new_line) + # Create a new line otherwise else: self.environment._update_training_data(update_line) dataset_lines.append(update_line) + # 3.3. Rest the data variables self.environment._reset_training_data() return dataset_lines - def dispatch_batch_to_server(self, - data_lines: List[int], - animate: bool = True) -> None: + def __dispatch_batch_to_server(self, + data_lines: List[int], + animate: bool = True) -> None: """ - Send samples from dataset to the Environments. Get back the training data. + Send samples from the Database to the Environments and get back the produced data. - :param data_lines: Batch of samples. + :param data_lines: Batch of indices of samples. :param animate: If True, triggers an environment step. - :return: Batch of training data. """ # Define the batch to dispatch self.server.set_dataset_batch(data_lines) # Get data - self.get_data_from_server(animate=animate) + self.__get_data_from_server(animate=animate) - def dispatch_batch_to_environment(self, - data_lines: List[int], - animate: bool = True, - save_data: bool = True, - request_prediction: bool = False) -> None: + def __dispatch_batch_to_environment(self, + data_lines: List[int], + animate: bool = True, + save_data: bool = True, + request_prediction: bool = False) -> None: """ - Send samples from dataset to the Environments. Get back the training data. + Send samples from the Database to the Environment and get back the produced data. - :param batch: Batch of samples. + :param data_lines: Batch of indices of samples. :param animate: If True, triggers an environment step. - :return: Batch of training data. + :param save_data: If True, data must be stored in the Database. + :param request_prediction: If True, a prediction request will be triggered. """ # Define the batch to dispatch self.dataset_batch = data_lines.copy() # Get data - self.get_data_from_environment(animate=animate, - save_data=save_data, - request_prediction=request_prediction) - - def request_prediction(self): + self.__get_data_from_environment(animate=animate, + save_data=save_data, + request_prediction=request_prediction) - self.environment._get_prediction() + ########################################################################################## + ########################################################################################## + # Requests management # + ########################################################################################## + ########################################################################################## def update_visualizer(self, instance: int) -> None: @@ -201,28 +221,29 @@ def update_visualizer(self, if self.visualizer is not None: self.visualizer.render_instance(instance) + ########################################################################################## + ########################################################################################## + # Manager behavior # + ########################################################################################## + ########################################################################################## + def close(self) -> None: """ - Close the environment + Launch the closing procedure of the EnvironmentManager. """ # Server case if self.server: self.server.close() - # No server case + + # Environment case if self.environment: self.environment.close() def __str__(self) -> str: - """ - :return: A string containing valuable information about the EnvironmentManager - """ description = "\n" description += f"# {self.name}\n" description += f" Always create data: {self.only_first_epoch}\n" - # description += f" Record wrong samples: {self.record_wrong_samples}\n" description += f" Number of threads: {self.number_of_thread}\n" - # description += f" Managed objects: Environment: {self.environment.env_name}\n" - # description += str(self.environment) return description diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index 093a92a0..aef785b3 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -1,6 +1,6 @@ -from typing import Any, Dict, Tuple, Optional, List +from typing import Any, Dict, Optional, List from os import listdir -from os.path import join, isdir, isfile +from os.path import join, isdir, isfile, sep from numpy import ndarray, array from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler @@ -16,13 +16,13 @@ def __init__(self, session: str = 'sessions/default', new_session: bool = True): """ - Deals with all the interactions with the neural network: predictions, saves, initialisation, loading, - back-propagation, etc. + NetworkManager deals with all the interactions with a neural network: predictions, saves, initialisation, + loading, optimization. - :param network_config: Specialisation containing the parameters of the network manager. + :param network_config: Configuration object with the parameters of the Network. :param pipeline: Type of the Pipeline. - :param session: Path to the session directory. - :param new_session: Define the creation of new directories to store data. + :param session: Path to the session repository. + :param new_session: If True, the session is done in a new repository. """ self.name: str = self.__class__.__name__ @@ -33,6 +33,7 @@ def __init__(self, self.session: str = session self.new_session: bool = new_session self.network_dir: Optional[str] = None + self.network_template_name: str = session.split(sep)[-1] + '_network_{}' self.saved_counter: int = 0 self.save_each_epoch: bool = network_config.save_each_epoch @@ -70,31 +71,58 @@ def __init__(self, self.network_dir = join(session, 'network/') self.load_network(which_network=network_config.which_network) - def get_database_handler(self): + ########################################################################################## + ########################################################################################## + # DatabaseHandler management # + ########################################################################################## + ########################################################################################## + + def get_database_handler(self) -> DatabaseHandler: + """ + Get the DatabaseHandler of the NetworkManager. + """ + return self.database_handler def link_clients(self, - nb_clients: Optional[int] = None): + nb_clients: Optional[int] = None) -> None: + """ + Update the data Exchange Database with a new line for each TcpIpClient. + + :param nb_clients: Number of Clients to connect. + """ + if nb_clients is not None: + # Create the network fields in the Exchange Database fields = [(field_name, ndarray) for field_name in self.network.net_fields + self.network.pred_fields] self.database_handler.create_fields(table_name='Exchange', fields=fields) + # Add an empty line for each Client for _ in range(nb_clients): self.database_handler.add_data(table_name='Exchange', data={}) + ########################################################################################## + ########################################################################################## + # Network parameters management # + ########################################################################################## + ########################################################################################## + def load_network(self, which_network: int = -1) -> None: """ - Load an existing set of parameters to the network. + Load an existing set of parameters of the Network. + + :param which_network: If several sets of parameters were saved, specify which one to load. """ - # Get eventual epoch saved networks + # 1. Get the list of all sets of saved parameters networks_list = [join(self.network_dir, f) for f in listdir(self.network_dir) if isfile(join(self.network_dir, f)) and f.__contains__('network_')] networks_list = sorted(networks_list) - # Add the final saved network last_saved_network = [join(self.network_dir, f) for f in listdir(self.network_dir) if isfile(join(self.network_dir, f)) and f.__contains__('network.')] networks_list = networks_list + last_saved_network + + # 2. Check the Network to access if len(networks_list) == 0: raise FileNotFoundError(f"[{self.name}]: There is no network in {self.network_dir}.") elif len(networks_list) == 1: @@ -103,32 +131,59 @@ def load_network(self, print(f"[{self.name}] The network 'network_{self.saved_counter} doesn't exist, loading the most trained " f"by default.") which_network = -1 + + # 3. Load the set of parameters print(f"[{self.name}]: Loading network from {networks_list[which_network]}.") self.network.load_parameters(networks_list[which_network]) + def save_network(self, + last_save: bool = False) -> None: + """ + Save the current set of parameters of the Network. + + :param last_save: If True, the Network training is done then give a special denomination. + """ + + # Final session saving + if last_save: + path = join(self.network_dir, 'network') + print(f"[{self.name}] Saving final network at {self.network_dir}.") + self.network.save_parameters(path) + + # Intermediate states saving + elif self.save_each_epoch: + path = self.network_dir + self.network_template_name.format(self.saved_counter) + self.saved_counter += 1 + print(f"[{self.name}] Saving intermediate network at {path}.") + self.network.save_parameters(path) + + ########################################################################################## + ########################################################################################## + # Network optimization and prediction # + ########################################################################################## + ########################################################################################## + def compute_prediction_and_loss(self, optimize: bool, data_lines: List[List[int]], - normalization: Optional[Dict[str, List[float]]] = None) -> Tuple[ndarray, Dict[str, float]]: + normalization: Optional[Dict[str, List[float]]] = None) -> Dict[str, float]: """ Make a prediction with the data passed as argument, optimize or not the network - :param batch_indices: Indices of the line of the Database that correspond to the current bach - :param optimize: If true run a back propagation - + :param optimize: If true, run a backward propagation. + :param data_lines: Batch of indices of samples in the Database. + :param normalization: Normalization coefficients. :return: The prediction and the associated loss value """ - # Define in and out batches + # 1. Define Network and Optimization batches batches = {} normalization = {} if normalization is None else normalization for side, fields in zip(['net', 'opt'], [self.network.net_fields, self.network.opt_fields]): - # Get the batch from the Database batch = self.database_handler.get_lines(table_name='Training', fields=fields, lines_id=data_lines) - # Apply normalization and convert to tensor for field in batch.keys(): batch[field] = array(batch[field]) @@ -140,27 +195,19 @@ def compute_prediction_and_loss(self, batches[side] = batch data_net, data_opt = batches.values() - # Compute prediction + # 2. Compute prediction data_net = self.data_transformation.transform_before_prediction(data_net) data_pred = self.network.predict(data_net) - # Compute loss + # 3. Compute loss data_pred, data_opt = self.data_transformation.transform_before_loss(data_pred, data_opt) data_loss = self.optimization.compute_loss(data_pred, data_opt) - # Optimizing network if training + # 4. Optimize network if training if optimize: self.optimization.optimize() - # Transform prediction to be compatible with environment - data_pred = self.data_transformation.transform_before_apply(data_pred) - for field in data_pred: - data_pred[field] = self.network.tensor_to_numpy(data_pred[field]) - if field in normalization.keys(): - data_pred[field] = self.normalize_data(data=data_pred[field], - normalization=normalization[field], - reverse=True) - return data_pred, data_loss + return data_loss def compute_online_prediction(self, instance_id: int, @@ -168,8 +215,8 @@ def compute_online_prediction(self, """ Make a prediction with the data passed as argument. - :param network_input: Input of the network - :return: The prediction + :param instance_id: Index of the Environment instance to provide a prediction. + :param normalization: Normalization coefficients. """ # Get Network data @@ -213,40 +260,27 @@ def normalize_data(cls, Apply or unapply normalization following current standard score. :param data: Data to normalize. - :param field: Specify if data is an 'input' or an 'output'. - :param reverse: If False, apply normalization; if False, unapply normalization. + :param normalization: Normalization coefficients. + :param reverse: If True, apply normalization; if False, unapply normalization. :return: Data with applied or misapplied normalization. """ + # Unapply normalization if reverse: - # Unapply normalization return (data * normalization[1]) + normalization[0] + # Apply normalization return (data - normalization[0]) / normalization[1] - def save_network(self, last_save: bool = False) -> None: - """ - | Save the network with the corresponding suffix, so they do not erase the last save. - - :param bool last_save: Do not add suffix if it's the last save - """ - - # Final session saving - if last_save: - path = join(self.network_dir, 'network') - print(f"[{self.name}] Saving final network at {self.network_dir}.") - self.network.save_parameters(path) - - # Intermediate states saving - elif self.save_each_epoch: - path = self.network_dir + self.network_template_name.format(self.saved_counter) - self.saved_counter += 1 - print(f"[{self.name}] Saving intermediate network at {path}.") - self.network.save_parameters(path) + ########################################################################################## + ########################################################################################## + # Manager behavior # + ########################################################################################## + ########################################################################################## def close(self) -> None: """ - Closing procedure. + Launch the closing procedure of the NetworkManager. """ if self.is_training: diff --git a/src/Manager/StatsManager.py b/src/Manager/StatsManager.py index c119c89c..4ee80d3b 100644 --- a/src/Manager/StatsManager.py +++ b/src/Manager/StatsManager.py @@ -1,4 +1,4 @@ -from typing import Dict, Union, Any, Iterable, Optional +from typing import Dict, Any, Iterable, Optional from tensorboardX import SummaryWriter from tensorboard import program from webbrowser import open as w_open @@ -22,11 +22,11 @@ def __init__(self, session: str, keep_losses: bool = False): """ - Record all given values using the tensorboard framework. Open a tab in the navigator to inspect these values - during the training. + StatsManager records all the given values using the Tensorboard framework. + Open a tab in the navigator to inspect these values during the training. - :param str log_dir: Path of the created directory - :param bool keep_losses: If True Allow saving loss to .csv file + :param session: Path to the session repository. + :param keep_losses: If True, allow saving loss to .csv file. """ self.name: str = self.__class__.__name__ @@ -49,7 +49,7 @@ def __init__(self, def add_train_batch_loss(self, value: float, count: int) -> None: """ - | Add batch loss to tensorboard framework. Also compute mean and variance. + Add batch loss to tensorboard framework. Also compute mean and variance. :param float value: Value to store :param int count: ID of the value @@ -65,7 +65,7 @@ def add_train_batch_loss(self, value: float, count: int) -> None: def add_train_epoch_loss(self, value: float, count: int) -> None: """ - | Add epoch loss to tensorboard framework. Also compute mean and variance. + Add epoch loss to tensorboard framework. Also compute mean and variance. :param float value: Value to store :param int count: ID of the value @@ -79,7 +79,7 @@ def add_train_epoch_loss(self, value: float, count: int) -> None: def add_train_test_batch_loss(self, train_value: float, test_value: float, count: int) -> None: """ - | Add train and test batch loss to tensorboard framework. + Add train and test batch loss to tensorboard framework. :param float train_value: Value of the training batch :param float test_value: Value of the testing batch @@ -93,7 +93,7 @@ def add_train_test_batch_loss(self, train_value: float, test_value: float, count def add_values_multi_plot(self, graph_name: str, tags: Iterable, values: Iterable, counts: Iterable) -> None: """ - | Plot multiples value on the same graph + Plot multiples value on the same graph :param str graph_name: Name of the graph :param Iterable tags: Iterable containing the names of the values @@ -106,7 +106,7 @@ def add_values_multi_plot(self, graph_name: str, tags: Iterable, values: Iterabl def add_test_loss(self, value: float, count: int) -> None: """ - | Add test loss to tensorboard framework. Also compute mean and variance. + Add test loss to tensorboard framework. Also compute mean and variance. :param float value: Value to store :param int count: ID of the value @@ -120,7 +120,7 @@ def add_test_loss(self, value: float, count: int) -> None: def add_test_loss_OOB(self, value: float, count: int) -> None: """ - | Add out of bound test loss to tensorboard framework. Also compute mean and variance. + Add out of bound test loss to tensorboard framework. Also compute mean and variance. :param float value: Value to store :param int count: ID of the value @@ -134,7 +134,7 @@ def add_test_loss_OOB(self, value: float, count: int) -> None: def add_custom_scalar(self, tag: str, value: float, count: int) -> None: """ - | Add a custom scalar to tensorboard framework. + Add a custom scalar to tensorboard framework. :param str tag: Graph name :param float value: Value to store @@ -145,7 +145,7 @@ def add_custom_scalar(self, tag: str, value: float, count: int) -> None: def add_custom_scalar_full(self, tag: str, value: float, count: int) -> None: """ - | Add a custom scalar to tensorboard framework. Also compute mean and variance. + Add a custom scalar to tensorboard framework. Also compute mean and variance. :param str tag: Graph name :param float value: Value to store @@ -164,7 +164,7 @@ def add_custom_scalar_full(self, tag: str, value: float, count: int) -> None: def update_mean_get_var(self, index: int, value: float, count: int) -> Optional[ndarray]: """ - | Update mean and return the variance of the selected value + Update mean and return the variance of the selected value :param float value: Value to add in the computation of the mean :param int index: Target that is updated by the value @@ -214,7 +214,7 @@ def add_3D_mesh(self, tag: str, vertices: ndarray, colors: Optional[ndarray] = N faces: Optional[ndarray] = None, b_n_3: bool = False, config_dict: Optional[Dict[Any, Any]] = None) -> None: """ - | Add 3D Mesh cloud to tensorboard framework. + Add 3D Mesh cloud to tensorboard framework. :param str tag: Data identifier :param ndarray vertices: List of the 3D coordinates of vertices. @@ -242,7 +242,7 @@ def add_3D_mesh(self, tag: str, vertices: ndarray, colors: Optional[ndarray] = N def add_network_weight_grad(self, network: Any, count: int, save_weights: bool = False, save_gradients: bool = True) -> None: """ - | Add network weights and gradiant if specified to tensorboard framework. + Add network weights and gradiant if specified to tensorboard framework. :param BaseNetwork network: Network you want to display :param int count: ID of the sample @@ -259,18 +259,13 @@ def add_network_weight_grad(self, network: Any, count: int, save_weights: bool = def close(self) -> None: """ - | Closing procedure - - :return: + Launch the closing procedure of the StatsManager. """ self.writer.close() del self.train_loss - def __str__(self) -> str: - """ - :return: A string containing valuable information about the StatsManager - """ + def __str__(self): description = "\n" description += f"# {self.name}\n" diff --git a/src/Network/BaseNetwork.py b/src/Network/BaseNetwork.py index 4a6d8881..eef6bb3f 100644 --- a/src/Network/BaseNetwork.py +++ b/src/Network/BaseNetwork.py @@ -8,9 +8,9 @@ class BaseNetwork: def __init__(self, config: namedtuple): """ - BaseNetwork is a network class to compute predictions from input data according to actual state. + BaseNetwork computes predictions from input data according to actual set of weights. - :param namedtuple config: namedtuple containing BaseNetwork parameters + :param config: Set of BaseNetwork parameters. """ # Config @@ -22,9 +22,10 @@ def __init__(self, self.opt_fields = ['ground_truth'] self.pred_fields = ['prediction'] - def predict(self, data_net: Dict[str, Any]) -> Dict[str, Any]: + def predict(self, + data_net: Dict[str, Any]) -> Dict[str, Any]: """ - Compute a forward pass of the network. + Compute a forward pass of the Network. :param data_net: Data used by the Network. :return: Data produced by the Network. @@ -32,26 +33,27 @@ def predict(self, data_net: Dict[str, Any]) -> Dict[str, Any]: return {'prediction': self.forward(data_net['input'])} - def forward(self, input_data: Any) -> Any: + def forward(self, + input_data: Any) -> Any: """ - | Gives input_data as raw input to the neural network. + Compute a forward pass of the Network. - :param Any input_data: Input tensor - :return: Network prediction + :param input_data: Input tensor. + :return: Output tensor. """ raise NotImplementedError def set_train(self) -> None: """ - | Set the Network in train mode (compute gradient). + Set the Network in training mode (compute gradient). """ raise NotImplementedError def set_eval(self) -> None: """ - | Set the Network in eval mode (does not compute gradient). + Set the Network in prediction mode (does not compute gradient). """ raise NotImplementedError @@ -63,67 +65,69 @@ def set_device(self) -> None: raise NotImplementedError - def load_parameters(self, path: str) -> None: + def load_parameters(self, + path: str) -> None: """ - | Load network parameter from path. + Load network parameter from path. - :param str path: Path to Network parameters to load + :param path: Path to Network parameters to load. """ raise NotImplementedError def get_parameters(self) -> Dict[str, Any]: """ - | Return the current state of Network parameters. + Return the current state of Network parameters. - :return: Network parameters + :return: Network parameters. """ raise NotImplementedError - def save_parameters(self, path) -> None: + def save_parameters(self, + path: str) -> None: """ - | Saves the network parameters to the path location. + Saves the network parameters to the path location. - :param str path: Path where to save the parameters. + :param path: Path where to save the parameters. """ raise NotImplementedError def nb_parameters(self) -> int: """ - | Return the number of parameters of the network. + Return the number of parameters of the network. - :return: Number of parameters + :return: Number of parameters. """ raise NotImplementedError - def numpy_to_tensor(self, data: ndarray, grad: bool = True) -> Any: + def numpy_to_tensor(self, + data: ndarray, + grad: bool = True) -> Any: """ - | Transform and cast data from numpy to the desired tensor type. + Transform and cast data from numpy to the desired tensor type. - :param ndarray data: Array data to convert - :param bool grad: If True, gradient will record operations on this tensor - :return: Converted tensor + :param data: Array data to convert. + :param grad: If True, gradient will record operations on this tensor. + :return: Converted tensor. """ return data.astype(self.config.data_type) - def tensor_to_numpy(self, data: Any) -> ndarray: + def tensor_to_numpy(self, + data: Any) -> ndarray: """ - | Transform and cast data from tensor type to numpy. + Transform and cast data from tensor type to numpy. - :param Any data: Any to convert - :return: Converted array + :param data: Tensor to convert. + :return: Converted array. """ return data.astype(self.config.data_type) def __str__(self) -> str: - """ - :return: String containing information about the BaseNetwork object - """ description = "\n" description += f" {self.__class__.__name__}\n" diff --git a/src/Network/BaseNetworkConfig.py b/src/Network/BaseNetworkConfig.py index 69f93caa..6d05b36c 100644 --- a/src/Network/BaseNetworkConfig.py +++ b/src/Network/BaseNetworkConfig.py @@ -142,7 +142,7 @@ def create_data_transformation(self) -> DataTransformation: f"must be a DataTransformation.") return data_transformation - def __str__(self) -> str: + def __str__(self): description = "\n" description += f"{self.__class__.__name__}\n" diff --git a/src/Network/BaseOptimization.py b/src/Network/BaseOptimization.py index 2419d866..eaa49edc 100644 --- a/src/Network/BaseOptimization.py +++ b/src/Network/BaseOptimization.py @@ -8,10 +8,9 @@ class BaseOptimization: def __init__(self, config: namedtuple): """ - BaseOptimization is dedicated to network optimization: compute loss between prediction and target, update - network parameters. + BaseOptimization computes loss between prediction and target and optimizes the Network parameters. - :param config: Namedtuple containing BaseOptimization parameters + :param config: Set of BaseOptimization parameters """ self.manager: Any = None @@ -53,12 +52,13 @@ def transform_loss(self, Apply a transformation on the loss value using the potential additional data. :param data_opt: Additional data sent as dict to compute loss value - :return: Transformed loss value + :return: Transformed loss value. """ raise NotImplementedError - def set_optimizer(self, net: BaseNetwork) -> None: + def set_optimizer(self, + net: BaseNetwork) -> None: """ Define an optimization process. @@ -74,7 +74,7 @@ def optimize(self) -> None: raise NotImplementedError - def __str__(self) -> str: + def __str__(self): description = "\n" description += f" {self.__class__.__name__}\n" diff --git a/src/Network/DataTransformation.py b/src/Network/DataTransformation.py index cb758fdc..e41e8ca4 100644 --- a/src/Network/DataTransformation.py +++ b/src/Network/DataTransformation.py @@ -1,16 +1,14 @@ from typing import Callable, Any, Optional, Tuple, Dict from collections import namedtuple -DTYPE = Dict[str, Any] - class DataTransformation: def __init__(self, config: namedtuple): """ - DataTransformation is dedicated to data operations before and after network predictions. + DataTransformation manages data operations before and after network predictions. - :param namedtuple config: Namedtuple containing the parameters of the network manager. + :param config: Set of DataTransformation parameters. """ self.name = self.__class__.__name__ @@ -30,7 +28,7 @@ def inner(self, *args): return inner def transform_before_prediction(self, - data_net: DTYPE) -> DTYPE: + data_net: Dict[str, Any]) -> Dict[str, Any]: """ Apply data operations before network's prediction. @@ -41,8 +39,8 @@ def transform_before_prediction(self, return data_net def transform_before_loss(self, - data_pred: DTYPE, - data_opt: Optional[DTYPE] = None) -> Tuple[DTYPE, Optional[DTYPE]]: + data_pred: Dict[str, Any], + data_opt: Optional[Dict[str, Any]] = None) -> Tuple[Dict[str, Any], Optional[Dict[str, Any]]]: """ Apply data operations between network's prediction and loss computation. @@ -54,7 +52,7 @@ def transform_before_loss(self, return data_pred, data_opt def transform_before_apply(self, - data_pred: DTYPE) -> DTYPE: + data_pred: Dict[str, Any]) -> Dict[str, Any]: """ Apply data operations between loss computation and prediction apply in environment. @@ -64,10 +62,7 @@ def transform_before_apply(self, return data_pred - def __str__(self) -> str: - """ - :return: String containing information about the DataTransformation object - """ + def __str__(self): description = "\n" description += f" {self.__class__.__name__}\n" diff --git a/src/Pipelines/BaseDataGeneration.py b/src/Pipelines/BaseDataGeneration.py index 25342895..dad878e2 100644 --- a/src/Pipelines/BaseDataGeneration.py +++ b/src/Pipelines/BaseDataGeneration.py @@ -23,11 +23,11 @@ def __init__(self, """ BaseDataGeneration implements the main loop that only produces and stores data (no Network training). - :param database_config: Specialisation containing the parameters of the dataset manager. - :param environment_config: Specialisation containing the parameters of the environment manager. - :param session_dir: Relative path to the directory which contains sessions directories. - :param session_name: Name of the new the session directory. - :param new_session: If True, the session will be run in a new repository. + :param database_config: Configuration object with the parameters of the Database. + :param environment_config: Configuration object with the parameters of the Environment. + :param session_dir: Relative path to the directory which contains sessions repositories. + :param session_name: Name of the new the session repository. + :param new_session: If True, a new repository will be created for this session. :param batch_nb: Number of batches to produce. :param batch_size: Number of samples in a single batch. """ @@ -50,6 +50,7 @@ def __init__(self, if new_session: session_name = create_dir(session_dir=session_dir, session_name=session_name).split(sep)[-1] + self.session = join(session_dir, session_name) # Create a DataManager self.data_manager = DataManager(pipeline=self, @@ -63,13 +64,14 @@ def __init__(self, # Data generation variables self.batch_nb: int = batch_nb self.batch_id: int = 0 + self.batch_size = batch_size self.progress_bar = Progressbar(start=0, stop=self.batch_id, c='orange', title="Data Generation") def execute(self) -> None: """ Launch the data generation Pipeline. - Each event is already implemented for a basic pipeline but can also be rewritten via inheritance to describe a - more complex pipeline. + Each event is already implemented for a basic Pipeline but can also be rewritten via inheritance to describe a + more complex Pipeline. """ self.data_generation_begin() @@ -82,7 +84,7 @@ def execute(self) -> None: def data_generation_begin(self) -> None: """ - Called once at the beginning of the data generation pipeline. + Called once at the beginning of the data generation Pipeline. """ pass @@ -125,7 +127,14 @@ def batch_end(self) -> None: def data_generation_end(self) -> None: """ - Called once at the beginning of the data generation pipeline. + Called once at the beginning of the data generation Pipeline. """ self.data_manager.close() + + def __str__(self): + + description = BasePipeline.__str__(self) + description += f" Number of batches: {self.batch_nb}\n" + description += f" Number of sample per batch: {self.batch_size}\n" + return description diff --git a/src/Pipelines/BasePipeline.py b/src/Pipelines/BasePipeline.py index 92d1b414..31103f01 100644 --- a/src/Pipelines/BasePipeline.py +++ b/src/Pipelines/BasePipeline.py @@ -1,4 +1,5 @@ from typing import Optional, Any, List, Union +from os.path import join from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig @@ -23,12 +24,12 @@ def __init__(self, """ Pipelines implement the main loop that defines data flow through components (Environment, Dataset, Network...). - :param network_config: Specialisation containing the parameters of the network manager. - :param database_config: Specialisation containing the parameters of the dataset manager. - :param environment_config: Specialisation containing the parameters of the environment manager. - :param session_dir: Name of the directory in which to write all the necessary data. - :param session_name: Name of the newly created directory if session is not defined. - :param new_session: If True, the session will be run in a new repository. + :param network_config: Configuration object with the parameters of the Network. + :param database_config: Configuration object with the parameters of the Database. + :param environment_config: Configuration object with the parameters of the Environment. + :param session_dir: Relative path to the directory which contains sessions repositories. + :param session_name: Name of the new the session repository. + :param new_session: If True, a new repository will be created for this session. :param pipeline: Name of the Pipeline. """ @@ -58,8 +59,7 @@ def __init__(self, self.environment_config: BaseEnvironmentConfig = environment_config # Session variables - self.session_dir = session_dir - self.session_name = session_name + self.session = join(session_dir, session_name) self.new_session = new_session self.type = pipeline @@ -73,7 +73,7 @@ def execute(self): def __get_any_manager(self, manager_names: Union[str, List[str]]) -> Optional[Any]: """ - Return the desired Manager associated with the pipeline if it exists. + Return the desired Manager associated with the Pipeline if it exists. :param manager_names: Name of the desired Manager or order of access to this desired Manager. :return: The desired Manager associated with the Pipeline. @@ -94,45 +94,53 @@ def __get_any_manager(self, def get_network_manager(self) -> Optional[NetworkManager]: """ - Return the NetworkManager associated with the pipeline if it exists. + Return the NetworkManager associated with the Pipeline if it exists. - :return: The NetworkManager associated with the pipeline. + :return: The NetworkManager associated with the Pipeline. """ return self.__get_any_manager(manager_names='network_manager') def get_data_manager(self) -> Optional[DataManager]: """ - Return the DataManager associated with the pipeline if it exists. + Return the DataManager associated with the Pipeline if it exists. - :return: The DataManager associated with the pipeline. + :return: The DataManager associated with the Pipeline. """ return self.__get_any_manager(manager_names='data_manager') def get_stats_manager(self) -> Optional[StatsManager]: """ - Return the StatsManager associated with the pipeline if it exists. + Return the StatsManager associated with the Pipeline if it exists. - :return: The StatsManager associated with the pipeline. + :return: The StatsManager associated with the Pipeline. """ return self.__get_any_manager(manager_names='stats_manager') def get_database_manager(self) -> Optional[DatabaseManager]: """ - Return the DatabaseManager associated with the pipeline if it exists. + Return the DatabaseManager associated with the Pipeline if it exists. - :return: The DatabaseManager associated with the pipeline. + :return: The DatabaseManager associated with the Pipeline. """ return self.__get_any_manager(manager_names=['data_manager', 'database_manager']) def get_environment_manager(self) -> Optional[EnvironmentManager]: """ - Return the EnvironmentManager associated with the pipeline if it exists. + Return the EnvironmentManager associated with the Pipeline if it exists. - :return: The EnvironmentManager associated with the pipeline. + :return: The EnvironmentManager associated with the Pipeline. """ return self.__get_any_manager(manager_names=['data_manager', 'environment_manager']) + + def __str__(self): + + description = "\n" + description += f"# {self.name}\n" + description += f" Pipeline type: {self.type}\n" + description += f" Session repository: {self.session}\n" + return description diff --git a/src/Pipelines/BasePrediction.py b/src/Pipelines/BasePrediction.py index de5c3778..290a4fc1 100644 --- a/src/Pipelines/BasePrediction.py +++ b/src/Pipelines/BasePrediction.py @@ -24,13 +24,13 @@ def __init__(self, BasePrediction is a pipeline defining the running process of an artificial neural network. It provides a highly tunable learning process that can be used with any machine learning library. - :param network_config: Specialisation containing the parameters of the network manager. - :param environment_config: Specialisation containing the parameters of the environment manager. - :param database_config: Specialisation containing the parameters of the dataset manager. - :param session_name: Name of the newly created directory if session is not defined. - :param session_dir: Name of the directory in which to write all the necessary data. + :param network_config: Configuration object with the parameters of the Network. + :param environment_config: Configuration object with the parameters of the Environment. + :param database_config: Configuration object with the parameters of the Database. + :param session_dir: Relative path to the directory which contains sessions repositories. + :param session_name: Name of the new the session repository. :param step_nb: Number of simulation step to play. - :param record: Save or not the prediction data. + :param record: If True, prediction data will be saved in a dedicated Database. """ BasePipeline.__init__(self, @@ -74,7 +74,7 @@ def execute(self) -> None: """ Launch the prediction Pipeline. Each event is already implemented for a basic pipeline but can also be rewritten via inheritance to describe a - more complex pipeline. + more complex Pipeline. """ self.prediction_begin() @@ -86,14 +86,14 @@ def execute(self) -> None: def prediction_begin(self) -> None: """ - Called once at the beginning of the prediction pipeline. + Called once at the beginning of the prediction Pipeline. """ pass def prediction_condition(self) -> bool: """ - Condition that characterize the end of the prediction pipeline. + Condition that characterize the end of the prediction Pipeline. """ running = self.step_id < self.step_nb if self.step_nb > 0 else True @@ -124,16 +124,14 @@ def sample_end(self) -> None: def run_end(self) -> None: """ - Called once at the end of the prediction pipeline. + Called once at the end of the prediction Pipeline. """ self.data_manager.close() self.network_manager.close() - def __str__(self) -> str: - - description = "" - description += f"Running statistics :\n" - description += f"Number of simulation step: {self.step_nb}\n" + def __str__(self): + description = BasePipeline.__str__(self) + description += f" Number of step: {self.step_nb}\n" return description diff --git a/src/Pipelines/BaseTraining.py b/src/Pipelines/BaseTraining.py index c4901910..5cc7d480 100644 --- a/src/Pipelines/BaseTraining.py +++ b/src/Pipelines/BaseTraining.py @@ -32,13 +32,13 @@ def __init__(self, Training can be launched with several data sources (from a Dataset, from an Environment, from combined sources). It provides a highly tunable learning process that can be used with any machine learning library. - :param network_config: Specialisation containing the parameters of the network manager. - :param database_config: Specialisation containing the parameters of the dataset manager. - :param environment_config: Specialisation containing the parameters of the environment manager. - :param session_dir: Relative path to the directory which contains sessions directories. - :param session_name: Name of the new the session directory. - :param new_session: Define the creation of new directories to store data. - :param epoch_nb: Number of epochs to run. + :param network_config: Configuration object with the parameters of the Network. + :param database_config: Configuration object with the parameters of the Database. + :param environment_config: Configuration object with the parameters of the Environment. + :param session_dir: Relative path to the directory which contains sessions repositories. + :param session_name: Name of the new the session repository. + :param new_session: If True, a new repository will be created for this session. + :param epoch_nb: Number of epochs to perform. :param batch_nb: Number of batches to use. :param batch_size: Number of samples in a single batch. :param debug: If True, main training features will not be launched. @@ -117,7 +117,7 @@ def execute(self) -> None: """ Launch the training Pipeline. Each event is already implemented for a basic pipeline but can also be rewritten via inheritance to describe a - more complex pipeline. + more complex Pipeline. """ self.train_begin() @@ -134,7 +134,7 @@ def execute(self) -> None: def train_begin(self) -> None: """ - Called once at the beginning of the training pipeline. + Called once at the beginning of the training Pipeline. """ pass @@ -180,9 +180,10 @@ def optimize(self) -> None: self.data_manager.get_data(epoch=self.epoch_id, animate=True) - _, self.loss_dict = self.network_manager.compute_prediction_and_loss(data_lines=self.data_manager.data_lines, - normalization=self.data_manager.normalization, - optimize=True) + self.loss_dict = self.network_manager.compute_prediction_and_loss( + data_lines=self.data_manager.data_lines, + normalization=self.data_manager.normalization, + optimize=True) def batch_count(self) -> None: """ @@ -223,7 +224,7 @@ def epoch_end(self) -> None: def train_end(self) -> None: """ - Called once at the end of the training pipeline. + Called once at the end of the training Pipeline. """ self.data_manager.close() @@ -253,11 +254,9 @@ def save_info_file(self) -> None: f.write(str(self.stats_manager)) f.close() - def __str__(self) -> str: + def __str__(self): - description = "\n" - description += f"# {self.__class__.__name__}\n" - description += f" Session directory: {self.session}\n" + description = BasePipeline.__str__(self) description += f" Number of epochs: {self.epoch_nb}\n" description += f" Number of batches per epoch: {self.batch_nb}\n" description += f" Number of samples per batch: {self.batch_size}\n" From bf9856a4d70e0e3ae17a970e287958dde693fabc Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 2 Nov 2022 11:21:01 +0100 Subject: [PATCH 29/61] Fix number of samples production and Client synchronization with Database. --- src/AsyncSocket/TcpIpClient.py | 4 ++++ src/AsyncSocket/TcpIpServer.py | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 66c5e821..4d9f7cd0 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -90,6 +90,10 @@ async def __initialize(self) -> None: # Initialization done await self.send_data(data_to_send='done', loop=loop, receiver=self.sock) + # Synchronize Database + _ = await self.receive_data(loop=loop, sender=self.sock) + self.environment.get_database_handler().load() + ########################################################################################## ########################################################################################## # Running Client # diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index 7dc5ff64..f54e0074 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -166,6 +166,10 @@ async def __initialize(self) -> None: await self.receive_data(loop=loop, sender=client) print(f"[{self.name}] Client n°{client_id} initialisation done") + # Synchronize Clients + for client_id, client in self.clients: + await self.send_data(data_to_send='sync', loop=loop, receiver=client) + ########################################################################################## ########################################################################################## # Data: produce batch & dispatch batch # @@ -197,11 +201,12 @@ async def __request_data_to_clients(self, self.data_lines = [] # Launch the communication protocol while the batch needs to be filled while nb_sample < self.batch_size: + clients = self.clients[:min(len(self.clients), self.batch_size - nb_sample)] # Run communicate protocol for each client and wait for the last one to finish await gather(*[self.__communicate(client=client, client_id=client_id, - animate=animate) for client_id, client in self.clients]) - nb_sample += len(self.clients) + animate=animate) for client_id, client in clients]) + nb_sample += len(clients) async def __communicate(self, client: Optional[socket] = None, From be901b03fca0c82807c88b2ea1436ccd0868486e Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 2 Nov 2022 11:44:37 +0100 Subject: [PATCH 30/61] Change the moment of 'env_id' Field creation to avoid duplication. --- src/Environment/BaseEnvironment.py | 5 ----- src/Manager/DatabaseManager.py | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 2f3c2b70..c519f975 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -334,11 +334,6 @@ def __database_handler_init(self): # Load Database and create basic fields self.__database_handler.load() - if self.instance_id == 1: - self.__database_handler.create_fields(table_name='Training', - fields=('env_id', int)) - self.__database_handler.create_fields(table_name='Additional', - fields=('env_id', int)) def _send_training_data(self) -> List[int]: """ diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index 3c7f9b2c..b922f993 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -232,6 +232,11 @@ def create_partition(self) -> None: for table_name in fields.keys(): self.partitions[self.mode][-1].create_fields(table_name=table_name, fields=fields[table_name]) + else: + self.partitions[self.mode][-1].create_fields(table_name='Training', + fields=('env_id', int)) + self.partitions[self.mode][-1].create_fields(table_name='Additional', + fields=('env_id', int)) # 4. Update the partitions in handlers for handler in self.database_handlers: From 06addf30e0eb4769da3e2fcaaa2413714c854288 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 2 Nov 2022 12:02:00 +0100 Subject: [PATCH 31/61] Update tutorials. --- examples/tutorial/T1_environment.py | 66 +++++++++++-------------- examples/tutorial/T2_network.py | 46 ++++++++++++----- examples/tutorial/T3_configuration.py | 53 +++++++++++--------- examples/tutorial/T4_dataGeneration.py | 29 ++++++++--- examples/tutorial/T5_offlineTraining.py | 46 ++++++++++------- examples/tutorial/T6_onlineTraining.py | 46 +++++++++++++---- examples/tutorial/T7_prediction.py | 32 ++++++------ 7 files changed, 196 insertions(+), 122 deletions(-) diff --git a/examples/tutorial/T1_environment.py b/examples/tutorial/T1_environment.py index 81390f4b..36732817 100644 --- a/examples/tutorial/T1_environment.py +++ b/examples/tutorial/T1_environment.py @@ -4,7 +4,7 @@ """ # Python related imports -from numpy import array +from numpy import array, ndarray # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -14,57 +14,50 @@ class DummyEnvironment(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, + as_tcp_ip_client=True, instance_id=1, - number_of_instances=1, - as_tcp_ip_client=True, - environment_manager=None): + instance_nb=1, + visualization_db=None): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, + as_tcp_ip_client=as_tcp_ip_client, instance_id=instance_id, - number_of_instances=number_of_instances, - as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_nb=instance_nb, + visualization_db=visualization_db) - self.nb_step = 0 - self.increment = 0 + self.step_nb: int = 0 """ INITIALIZING ENVIRONMENT - Methods will be automatically called it this order: - - recv_parameters: Receive a dictionary of parameters that can be set in EnvironmentConfig - create: Create the Environment - init: Initialize the Environment if required - - send_parameters: Same as recv_parameters, Environment can send back a set of parameters if required - - send_visualization: Send initial visualization data (see Example/CORE/Features to add visualization data) + - init_database: Define the training data fields + - init_visualization: Define and send initial visualization data """ - # Optional - def recv_parameters(self, param_dict): - # Set data size - self.increment = param_dict['increment'] if 'increment' in param_dict else 1 - # MANDATORY def create(self): + # Nothing to create in our DummyEnvironment pass # Optional def init(self): + # Nothing to init in our DummyEnvironment pass - # Optional - def send_parameters(self): - # Nothing to send back - return {} + # MANDATORY + def init_database(self): + + # Define the fields of the training Database + self.define_training_fields(fields=[('input', ndarray), ('ground_truth', ndarray)]) # Optional - def send_visualization(self): - # Nothing to visualize (see Example/CORE/Features to add visualization data) - return {} + def init_visualization(self): + + # Nothing to visualize + pass """ ENVIRONMENT BEHAVIOR - Methods will be automatically called at each simulation step in this order: @@ -79,25 +72,26 @@ def send_visualization(self): # MANDATORY async def step(self): + # Setting (and sending) training data - self.set_training_data(input_array=array([self.nb_step]), - output_array=array([self.nb_step])) - self.nb_step += self.increment - # Other data fields can be filled: - # - set_loss_data: Define an additional data to compute loss value (see Optimization.transform_loss) - # - set_additional_dataset: Add a field to the dataset + self.step_nb += 1 + self.set_training_data(input=array([self.step_nb]), + ground_truth=array([self.step_nb])) # Optional - def check_sample(self, check_input=True, check_output=True): + def check_sample(self): + # Nothing to check in our DummyEnvironment return True # Optional def apply_prediction(self, prediction): + # Nothing to apply in our DummyEnvironment - print(f"Prediction at step {self.nb_step - 1} = {prediction}") + print(f"Prediction at step {self.step_nb} = {prediction}") # Optional def close(self): + # Shutdown procedure print("Bye!") diff --git a/examples/tutorial/T2_network.py b/examples/tutorial/T2_network.py index c59ad8bb..9af80612 100644 --- a/examples/tutorial/T2_network.py +++ b/examples/tutorial/T2_network.py @@ -5,24 +5,27 @@ """ # Python related imports -from numpy import save, array +from numpy import save, array, ndarray # DeepPhysX related imports from DeepPhysX.Core.Network.BaseNetwork import BaseNetwork from DeepPhysX.Core.Network.BaseOptimization import BaseOptimization +from DeepPhysX.Core.Network.DataTransformation import DataTransformation # Create a Network as a BaseNetwork child class class DummyNetwork(BaseNetwork): def __init__(self, config): + BaseNetwork.__init__(self, config) # There is no Network architecture to define in our DummyNetwork # MANDATORY - def forward(self, x): + def forward(self, input_data): + # Return the input - return x + return input_data """ The following methods should be already defined in a DeepPhysX AI package. @@ -57,19 +60,12 @@ def save_parameters(self, path): def nb_parameters(self): return 0 - # MANDATORY - def transform_from_numpy(self, x, grad=True): - return x - - # MANDATORY - def transform_to_numpy(self, x): - return x - # Create an Optimization as a BaseOptimization child class class DummyOptimization(BaseOptimization): def __init__(self, config): + BaseOptimization.__init__(self, config) """ @@ -82,11 +78,11 @@ def set_loss(self): pass # MANDATORY - def compute_loss(self, prediction, ground_truth, data): + def compute_loss(self, data_pred, data_opt): return {'loss': 0.} # Optional - def transform_loss(self, data): + def transform_loss(self, data_opt): pass # MANDATORY @@ -96,3 +92,27 @@ def set_optimizer(self, net): # MANDATORY def optimize(self): pass + + +# Create a DataTransformation as a DataTransformation child class +class DummyTransformation(DataTransformation): + + def __init__(self, config): + + DataTransformation.__init__(self, config) + self.data_type = ndarray + + def transform_before_prediction(self, data_net): + + # Do not transform the Network data + return data_net + + def transform_before_loss(self, data_pred, data_opt=None): + + # Do not transform the Prediction data and the Optimizer data + return data_pred, data_opt + + def transform_before_apply(self, data_pred): + + # Do not transform Prediction data + return data_pred diff --git a/examples/tutorial/T3_configuration.py b/examples/tutorial/T3_configuration.py index 964831d0..69d965f2 100644 --- a/examples/tutorial/T3_configuration.py +++ b/examples/tutorial/T3_configuration.py @@ -6,35 +6,42 @@ # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig # Tutorial related imports from T1_environment import DummyEnvironment -from T2_network import DummyNetwork, DummyOptimization - +from T2_network import DummyNetwork, DummyOptimization, DummyTransformation # Create the Environment config -env_config = BaseEnvironmentConfig(environment_class=DummyEnvironment, # The Environment class to create - visualizer=None, # The Visualizer to use - simulations_per_step=1, # The number of bus-steps to run - use_dataset_in_environment=False, # Dataset will not be sent to Environment - param_dict={'increment': 1}, # Parameters to send at init - as_tcp_ip_client=True, # Create a Client / Server architecture - number_of_thread=3, # Number of Clients connected to Server - ip_address='localhost', # IP address to use for communication - port=10001) # Port number to use for communication +env_config = BaseEnvironmentConfig(environment_class=DummyEnvironment, # The Environment class to create + as_tcp_ip_client=True, # Create a Client / Server architecture + number_of_thread=3, # Number of Clients connected to Server + ip_address='localhost', # IP address to use for communication + port=10001, # Port number to use for communication + simulations_per_step=1, # The number of bus-steps to run + load_samples=False, # Load samples from Database to Environment + only_first_epoch=True, # Use the Environment on the first epoch only + always_produce=False) # Environment is always producing data # Create the Network config -net_config = BaseNetworkConfig(network_class=DummyNetwork, # The Network class to create - optimization_class=DummyOptimization, # The Optimization class to create - network_name='DummyNetwork', # Nickname of the Network - network_type='Dummy', # Type of the Network - save_each_epoch=False, # Do not save the network at each epoch - require_training_stuff=False, # loss and optimizer can remain at None - lr=None, # Learning rate - loss=None, # Loss class - optimizer=None) # Optimizer class +net_config = BaseNetworkConfig(network_class=DummyNetwork, # The Network class to create + optimization_class=DummyOptimization, # The Optimization class to create + data_transformation_class=DummyTransformation, # The DataTransformation class to create + network_dir=None, # Path to an existing Network repository + network_name='DummyNetwork', # Nickname of the Network + network_type='Dummy', # Type of the Network + which_network=-1, # The index of Network to load + save_each_epoch=False, # Do not save the network at each epoch + data_type='float32', # Training data type + require_training_stuff=False, # loss and optimizer can remain at None + lr=None, # Learning rate + loss=None, # Loss class + optimizer=None) # Optimizer class # Create the Dataset config -dataset_config = BaseDatasetConfig(partition_size=1, # Max size of the Dataset - shuffle_dataset=False) # Dataset should be shuffled +database_config = BaseDatabaseConfig(existing_dir=None, # Path to an existing Database + mode='training', # Database mode + max_file_size=1, # Max size of the Dataset (Gb) + shuffle=False, # Dataset should be shuffled + normalize=False, # Database should be normalized + recompute_normalization=False) # Normalization should be recomputed at loading diff --git a/examples/tutorial/T4_dataGeneration.py b/examples/tutorial/T4_dataGeneration.py index e5039e11..729ea9d5 100644 --- a/examples/tutorial/T4_dataGeneration.py +++ b/examples/tutorial/T4_dataGeneration.py @@ -4,19 +4,34 @@ """ # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseDataGenerator import BaseDataGenerator +from DeepPhysX.Core.Pipelines.BaseDataGeneration import BaseDataGeneration +from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig # Tutorial related imports -from T3_configuration import env_config, dataset_config +from T1_environment import DummyEnvironment def launch_data_generation(): + + # Create the Environment config + env_config = BaseEnvironmentConfig(environment_class=DummyEnvironment, + as_tcp_ip_client=True, + number_of_thread=3) + + # Create the Database config + database_config = BaseDatabaseConfig(max_file_size=1, + normalize=False) + # Create the Pipeline - pipeline = BaseDataGenerator(session_name='sessions/tutorial_data_generation', - dataset_config=dataset_config, - environment_config=env_config, - nb_batches=100, - batch_size=10) + pipeline = BaseDataGeneration(environment_config=env_config, + database_config=database_config, + session_dir='sessions', + session_name='tutorial_data_generation', + new_session=True, + batch_nb=20, + batch_size=10) + # Launch the Pipeline pipeline.execute() diff --git a/examples/tutorial/T5_offlineTraining.py b/examples/tutorial/T5_offlineTraining.py index d8761412..63fece14 100644 --- a/examples/tutorial/T5_offlineTraining.py +++ b/examples/tutorial/T5_offlineTraining.py @@ -3,31 +3,41 @@ Launch a training session with an existing Dataset. """ -# Python related imports -import os - # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseTrainer import BaseTrainer -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BaseTraining import BaseTraining +from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig # Tutorial related imports -from T3_configuration import env_config, net_config +from T2_network import DummyNetwork, DummyOptimization, DummyTransformation def launch_training(): - # Adapt the Dataset config with the existing dataset directory - dataset_config = BaseDatasetConfig(dataset_dir=os.path.join(os.getcwd(), 'sessions/tutorial_data_generation'), - partition_size=1, - shuffle_dataset=False) + + # Create the Network config + net_config = BaseNetworkConfig(network_class=DummyNetwork, + optimization_class=DummyOptimization, + data_transformation_class=DummyTransformation, + network_name='DummyNetwork', + network_type='Dummy', + save_each_epoch=False, + require_training_stuff=False) + + # Create the Dataset config + database_config = BaseDatabaseConfig(existing_dir='sessions/tutorial_data_generation', + shuffle=False) + # Create the Pipeline - pipeline = BaseTrainer(session_dir='sessions', - session_name='tutorial_offline_training', - environment_config=env_config, - dataset_config=dataset_config, - network_config=net_config, - nb_epochs=2, - nb_batches=100, - batch_size=10) + pipeline = BaseTraining(network_config=net_config, + database_config=database_config, + session_dir='sessions', + session_name='tutorial_offline_training', + new_session=True, + epoch_nb=2, + batch_nb=20, + batch_size=10, + debug=True) + # Launch the Pipeline pipeline.execute() diff --git a/examples/tutorial/T6_onlineTraining.py b/examples/tutorial/T6_onlineTraining.py index 3d77eb4b..3d73587a 100644 --- a/examples/tutorial/T6_onlineTraining.py +++ b/examples/tutorial/T6_onlineTraining.py @@ -4,22 +4,48 @@ """ # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseTrainer import BaseTrainer +from DeepPhysX.Core.Pipelines.BaseTraining import BaseTraining +from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig +from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig # Tutorial related imports -from T3_configuration import env_config, net_config, dataset_config +from T1_environment import DummyEnvironment +from T2_network import DummyNetwork, DummyOptimization, DummyTransformation def launch_training(): + + # Create the Environment config + env_config = BaseEnvironmentConfig(environment_class=DummyEnvironment, + as_tcp_ip_client=True, + number_of_thread=3) + + # Create the Network config + net_config = BaseNetworkConfig(network_class=DummyNetwork, + optimization_class=DummyOptimization, + data_transformation_class=DummyTransformation, + network_name='DummyNetwork', + network_type='Dummy', + save_each_epoch=False, + require_training_stuff=False) + + # Create the Dataset config + database_config = BaseDatabaseConfig(max_file_size=1, + normalize=False) + # Create the Pipeline - pipeline = BaseTrainer(session_dir='sessions', - session_name='tutorial_online_training', - environment_config=env_config, - dataset_config=dataset_config, - network_config=net_config, - nb_epochs=2, - nb_batches=100, - batch_size=10) + pipeline = BaseTraining(network_config=net_config, + database_config=database_config, + environment_config=env_config, + session_dir='sessions', + session_name='tutorial_online_training', + new_session=True, + epoch_nb=2, + batch_nb=20, + batch_size=10, + debug=True) + # Launch the Pipeline pipeline.execute() diff --git a/examples/tutorial/T7_prediction.py b/examples/tutorial/T7_prediction.py index 526eb26a..4752f349 100644 --- a/examples/tutorial/T7_prediction.py +++ b/examples/tutorial/T7_prediction.py @@ -4,28 +4,30 @@ """ # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig +from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig # Tutorial related imports -from T3_configuration import DummyEnvironment, net_config, dataset_config +from T1_environment import DummyEnvironment +from T2_network import DummyNetwork def launch_prediction(): - # Adapt the Environment config to avoid using Client / Server Architecture - env_config = BaseEnvironmentConfig(environment_class=DummyEnvironment, - visualizer=None, - simulations_per_step=1, - use_dataset_in_environment=False, - param_dict={'increment': 1}, - as_tcp_ip_client=False) + + # Create the Environment config + env_config = BaseEnvironmentConfig(environment_class=DummyEnvironment) + + # Create the Network config + net_config = BaseNetworkConfig(network_class=DummyNetwork) + # Create the Pipeline - pipeline = BaseRunner(session_dir='sessions', - session_name='tutorial_online_training', - environment_config=env_config, - dataset_config=dataset_config, - network_config=net_config, - nb_steps=20) + pipeline = BasePrediction(network_config=net_config, + environment_config=env_config, + session_dir='sessions', + session_name='tutorial_online_training', + step_nb=20) + # Launch the Pipeline pipeline.execute() From 69a6278072e724d040138f1fb8577d6a855d8a6e Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 2 Nov 2022 16:11:23 +0100 Subject: [PATCH 32/61] Rename DataTransformation class. --- src/Network/BaseNetwork.py | 4 ++-- src/Network/BaseNetworkConfig.py | 18 +++++++++--------- src/Network/BaseOptimization.py | 2 +- ...Transformation.py => BaseTransformation.py} | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) rename src/Network/{DataTransformation.py => BaseTransformation.py} (93%) diff --git a/src/Network/BaseNetwork.py b/src/Network/BaseNetwork.py index eef6bb3f..b390795c 100644 --- a/src/Network/BaseNetwork.py +++ b/src/Network/BaseNetwork.py @@ -39,7 +39,7 @@ def forward(self, Compute a forward pass of the Network. :param input_data: Input tensor. - :return: Output tensor. + :return: Network prediction. """ raise NotImplementedError @@ -60,7 +60,7 @@ def set_eval(self) -> None: def set_device(self) -> None: """ - | Set computer device on which Network's parameters will be stored and tensors will be computed. + Set computer device on which Network's parameters will be stored and tensors will be computed. """ raise NotImplementedError diff --git a/src/Network/BaseNetworkConfig.py b/src/Network/BaseNetworkConfig.py index 6d05b36c..efde937a 100644 --- a/src/Network/BaseNetworkConfig.py +++ b/src/Network/BaseNetworkConfig.py @@ -4,7 +4,7 @@ from DeepPhysX.Core.Network.BaseNetwork import BaseNetwork from DeepPhysX.Core.Network.BaseOptimization import BaseOptimization -from DeepPhysX.Core.Network.DataTransformation import DataTransformation +from DeepPhysX.Core.Network.BaseTransformation import BaseTransformation from DeepPhysX.Core.Utils.configs import make_config, namedtuple @@ -13,7 +13,7 @@ class BaseNetworkConfig: def __init__(self, network_class: Type[BaseNetwork] = BaseNetwork, optimization_class: Type[BaseOptimization] = BaseOptimization, - data_transformation_class: Type[DataTransformation] = DataTransformation, + data_transformation_class: Type[BaseTransformation] = BaseTransformation, network_dir: Optional[str] = None, network_name: str = 'Network', network_type: str = 'BaseNetwork', @@ -26,11 +26,11 @@ def __init__(self, optimizer: Optional[Any] = None): """ BaseNetworkConfig is a configuration class to parameterize and create BaseNetwork, BaseOptimization and - DataTransformation for the NetworkManager. + BaseTransformation for the NetworkManager. :param network_class: BaseNetwork class from which an instance will be created. :param optimization_class: BaseOptimization class from which an instance will be created. - :param data_transformation_class: DataTransformation class from which an instance will be created. + :param data_transformation_class: BaseTransformation class from which an instance will be created. :param network_dir: Name of an existing network repository. :param network_name: Name of the network. :param network_type: Type of the network. @@ -92,7 +92,7 @@ def __init__(self, self.training_stuff: bool = (loss is not None) and (optimizer is not None) or (not require_training_stuff) # NetworkManager parameterization - self.data_transformation_class: Type[DataTransformation] = data_transformation_class + self.data_transformation_class: Type[BaseTransformation] = data_transformation_class self.data_transformation_config: namedtuple = make_config(configuration_object=self, configuration_name='data_transformation_config') @@ -128,18 +128,18 @@ def create_optimization(self) -> BaseOptimization: f"BaseOptimization.") return optimization - def create_data_transformation(self) -> DataTransformation: + def create_data_transformation(self) -> BaseTransformation: """ Create an instance of data_transformation_class with given parameters. - :return: DataTransformation object from data_transformation_class and its parameters. + :return: BaseTransformation object from data_transformation_class and its parameters. """ # Create instance data_transformation = self.data_transformation_class(config=self.data_transformation_config) - if not isinstance(data_transformation, DataTransformation): + if not isinstance(data_transformation, BaseTransformation): raise TypeError(f"[{self.name}] The given 'data_transformation_class'={self.data_transformation_class} " - f"must be a DataTransformation.") + f"must be a BaseTransformation.") return data_transformation def __str__(self): diff --git a/src/Network/BaseOptimization.py b/src/Network/BaseOptimization.py index eaa49edc..729c54b5 100644 --- a/src/Network/BaseOptimization.py +++ b/src/Network/BaseOptimization.py @@ -10,7 +10,7 @@ def __init__(self, config: namedtuple): """ BaseOptimization computes loss between prediction and target and optimizes the Network parameters. - :param config: Set of BaseOptimization parameters + :param config: Set of BaseOptimization parameters. """ self.manager: Any = None diff --git a/src/Network/DataTransformation.py b/src/Network/BaseTransformation.py similarity index 93% rename from src/Network/DataTransformation.py rename to src/Network/BaseTransformation.py index e41e8ca4..e2f4cb7d 100644 --- a/src/Network/DataTransformation.py +++ b/src/Network/BaseTransformation.py @@ -2,13 +2,13 @@ from collections import namedtuple -class DataTransformation: +class BaseTransformation: def __init__(self, config: namedtuple): """ - DataTransformation manages data operations before and after network predictions. + BaseTransformation manages data operations before and after network predictions. - :param config: Set of DataTransformation parameters. + :param config: Set of BaseTransformation parameters. """ self.name = self.__class__.__name__ From 53392c1b68eae9c6a2d5afdbcbba96b0bc5ac700 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 2 Nov 2022 16:11:57 +0100 Subject: [PATCH 33/61] Restaure additional parameters sending to Environments. --- src/AsyncSocket/AbstractEnvironment.py | 3 ++- src/AsyncSocket/TcpIpClient.py | 26 +++++++++++++++------- src/AsyncSocket/TcpIpServer.py | 15 ++++++++++--- src/Environment/BaseEnvironment.py | 3 ++- src/Environment/BaseEnvironmentConfig.py | 12 ++++++---- src/Environment/launcherBaseEnvironment.py | 2 +- 6 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 55423c61..791bfab5 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -12,7 +12,8 @@ def __init__(self, as_tcp_ip_client: bool = True, instance_id: int = 0, instance_nb: int = 1, - visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): + visualization_db: Optional[Union[Database, Tuple[str, str]]] = None, + **kwargs): """ AbstractEnvironment sets the Environment API for TcpIpClient. Do not use AbstractEnvironment to implement an Environment, use BaseEnvironment instead. diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 4d9f7cd0..bd722ae2 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -33,12 +33,11 @@ def __init__(self, ip_address=ip_address, port=port) - # Create instance - self.environment = environment(as_tcp_ip_client=True, - instance_id=instance_id, - instance_nb=instance_nb, - visualization_db=visualization_db) - self.environment.tcp_ip_client = self + # Environment instance + self.environment: AbstractEnvironment + self.environment_class = environment + self.environment_instance = (instance_id, instance_nb) + self.environment_visualization = visualization_db # Bind to client address and send ID self.sock.connect((ip_address, port)) @@ -66,6 +65,17 @@ async def __initialize(self) -> None: loop = get_event_loop() + # Receive additional arguments + env_kwargs = {} + await self.receive_dict(recv_to=env_kwargs, loop=loop, sender=self.sock) + + self.environment = self.environment_class(as_tcp_ip_client=True, + instance_id=self.environment_instance[0], + instance_nb=self.environment_instance[1], + visualization_db=self.environment_visualization, + **env_kwargs['env_kwargs']) + self.environment.tcp_ip_client = self + # Receive prediction requests authorization self.allow_prediction_requests = await self.receive_data(loop=loop, sender=self.sock) @@ -279,8 +289,8 @@ async def action_on_step(self, await self.send_command_done(loop=loop, receiver=sender) await self.send_data(data_to_send=line, loop=loop, receiver=sender) - async def action_on_change_db(self, - data: Dict[Any, Any], + async def action_on_change_db(self, + data: Dict[Any, Any], client_id: int, sender: socket, loop: EventLoop) -> None: """ diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index f54e0074..3eddf1b1 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -120,17 +120,23 @@ async def __connect(self) -> None: ########################################################################################## ########################################################################################## - def initialize(self) -> None: + def initialize(self, + env_kwargs: Dict[str, Any]) -> None: """ Send parameters to the clients to create their environments. + + :param env_kwargs: Additional arguments to pass to the Environment. """ print(f"[{self.name}] Initializing clients...") - async_run(self.__initialize()) + async_run(self.__initialize(env_kwargs)) - async def __initialize(self) -> None: + async def __initialize(self, + env_kwargs: Dict[str, Any]) -> None: """ Send parameters to the clients to create their environments. + + :param env_kwargs: Additional arguments to pass to the Environment. """ loop = get_event_loop() @@ -138,6 +144,9 @@ async def __initialize(self) -> None: # Initialisation process for each client for client_id, client in self.clients: + # Send additional arguments + await self.send_dict(name='env_kwargs', dict_to_send=env_kwargs, loop=loop, receiver=client) + # Send prediction request authorization await self.send_data(data_to_send=self.environment_manager.allow_prediction_requests, loop=loop, receiver=client) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index c519f975..712a3feb 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -15,7 +15,8 @@ def __init__(self, as_tcp_ip_client: bool = True, instance_id: int = 0, instance_nb: int = 1, - visualization_db: Optional[Union[Database, Tuple[str, str]]] = None): + visualization_db: Optional[Union[Database, Tuple[str, str]]] = None, + **kwargs): """ BaseEnvironment computes simulated data for the Network and its training process. diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index 25bcfcfb..ba75ef63 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Type +from typing import Any, Optional, Type, Dict from os import cpu_count from os.path import join, dirname from threading import Thread @@ -24,7 +24,8 @@ def __init__(self, only_first_epoch: bool = True, always_produce: bool = False, visualizer: Optional[Type[VedoVisualizer]] = None, - record_wrong_samples: bool = False): + record_wrong_samples: bool = False, + env_kwargs: Optional[Dict[str, Any]] = None): """ BaseEnvironmentConfig is a configuration class to parameterize and create a BaseEnvironment for the EnvironmentManager. @@ -42,6 +43,7 @@ def __init__(self, :param always_produce: If True, data will always be produced in Environment(s). :param visualizer: Class of the Visualizer to use. :param record_wrong_samples: If True, wrong samples are recorded through Visualizer. + :param env_kwargs: Additional arguments to pass to the Environment. """ self.name: str = self.__class__.__name__ @@ -85,6 +87,7 @@ def __init__(self, self.load_samples: bool = load_samples self.only_first_epoch: bool = only_first_epoch self.always_produce: bool = always_produce + self.env_kwargs: Dict[str, Any] = {} if env_kwargs is None else env_kwargs # Visualizer variables self.visualizer: Optional[Type[VedoVisualizer]] = visualizer @@ -135,7 +138,7 @@ def start_server(self, """ server.connect() - server.initialize() + server.initialize(env_kwargs=self.env_kwargs) self.server_is_ready = True def start_client(self, @@ -163,7 +166,8 @@ def create_environment(self, # Create instance environment = self.environment_class(as_tcp_ip_client=False, - visualization_db=visualization_db) + visualization_db=visualization_db, + **self.env_kwargs) if not isinstance(environment, BaseEnvironment): raise TypeError(f"[{self.name}] The given 'environment_class'={self.environment_class} must be a " f"BaseEnvironment.") diff --git a/src/Environment/launcherBaseEnvironment.py b/src/Environment/launcherBaseEnvironment.py index d533f1ec..6449468b 100644 --- a/src/Environment/launcherBaseEnvironment.py +++ b/src/Environment/launcherBaseEnvironment.py @@ -19,7 +19,7 @@ exec(f"from {module_name} import {argv[2]} as Environment") # Create, init and run Tcp-Ip environment - visualization_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[8][1:-1].split(', ')] + visualization_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] client = TcpIpClient(environment=Environment, ip_address=argv[3], port=int(argv[4]), From 0a3f18dca9cc0ec77be3b99ecd7cd320cd86fcf8 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 2 Nov 2022 18:20:48 +0100 Subject: [PATCH 34/61] Update mean example. --- examples/features/Environment.py | 132 +++++++++++---------- examples/features/dataGeneration_multi.py | 33 ++++-- examples/features/dataGeneration_single.py | 36 +++--- examples/features/gradientDescent.py | 46 +++---- examples/features/offlineTraining.py | 35 +++--- examples/features/onlineTraining.py | 46 +++---- examples/features/prediction.py | 31 ++--- examples/tutorial/T2_network.py | 8 +- src/AsyncSocket/AbstractEnvironment.py | 4 +- src/Environment/BaseEnvironment.py | 4 +- src/Manager/NetworkManager.py | 5 +- src/Network/BaseOptimization.py | 1 - src/Network/BaseTransformation.py | 5 +- src/Utils/configs.py | 2 +- 14 files changed, 215 insertions(+), 173 deletions(-) diff --git a/examples/features/Environment.py b/examples/features/Environment.py index e8034717..818ecd5f 100644 --- a/examples/features/Environment.py +++ b/examples/features/Environment.py @@ -5,7 +5,7 @@ """ # Python related imports -from numpy import mean, pi, array +from numpy import mean, pi, array, ndarray from numpy.random import random, randint from time import sleep @@ -17,69 +17,65 @@ class MeanEnvironment(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None, - visu_db=None): + instance_id=1, + instance_nb=1, + visualization_db=None, + constant=False, + data_size=(30, 2), + delay=False, + allow_request=True): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager, - visu_db=visu_db) + instance_id=instance_id, + instance_nb=instance_nb, + visualization_db=visualization_db) # Define training data values - self.input_value = array([]) - self.output_value = array([]) + self.pcd = array([]) + self.mean = array([]) # Environment parameters - self.constant = False - self.data_size = [30, 2] - self.sleep = False - self.allow_requests = True + self.constant = constant + self.data_size = data_size + self.delay = delay + self.allow_requests = allow_request """ ENVIRONMENT INITIALIZATION Methods will be automatically called in this order to create and initialize Environment. """ - def recv_parameters(self, param_dict): - # If True, the same data is always sent so one can observe how the prediction crawl toward the ground truth. - self.constant = param_dict['constant'] if 'constant' in param_dict else self.constant - # Define the data size - self.data_size = param_dict['data_size'] if 'data_size' in param_dict else self.data_size - # If True, step will sleep a random time to simulate longer processes - self.sleep = param_dict['sleep'] if 'sleep' in param_dict else self.sleep - # If True, requests can be performed - self.allow_requests = param_dict['allow_requests'] if 'allow_requests' in param_dict else self.allow_requests - def create(self): - # The vector is the input of the network and the ground truth is the mean. - self.input_value = pi * random(self.data_size) - self.output_value = mean(self.input_value, axis=0) + + self.pcd = pi * random(self.data_size) + self.mean = mean(self.pcd, axis=0) + + def init_database(self): + + # Define the fields of the training Database + self.define_training_fields(fields=[('input', ndarray), ('ground_truth', ndarray)]) def init_visualization(self): - # Point cloud (object will have id = 0) - self.factory.add_points(positions=self.input_value, - at=self.instance_id, - c='blue', - point_size=8) - # Ground truth value (object will have id = 1) - self.factory.add_points(positions=self.output_value, - at=self.instance_id, - c='green', - point_size=10) - # Prediction value (object will have id = 2) - self.factory.add_points(positions=self.output_value, - at=self.instance_id, - c='orange', - point_size=12) + + if self.factory is not None: + + # Point cloud (object will have id = 0) + self.factory.add_points(positions=self.pcd, + at=self.instance_id, + c='blue', + point_size=8) + # Ground truth value (object will have id = 1) + self.factory.add_points(positions=self.mean, + at=self.instance_id, + c='green', + point_size=10) + # Prediction value (object will have id = 2) + self.factory.add_points(positions=self.mean, + at=self.instance_id, + c='orange', + point_size=12) """ ENVIRONMENT BEHAVIOR @@ -88,34 +84,42 @@ def init_visualization(self): """ async def step(self): - # Compute new data - if not self.constant: - self.input_value = pi * random(self.data_size) - self.output_value = mean(self.input_value, axis=0) + # Simulate longer process - if self.sleep: + if self.delay: sleep(0.01 * randint(0, 10)) + + # Compute new data + if not self.constant: + self.pcd = pi * random(self.data_size) + self.mean = mean(self.pcd, axis=0) + + # Update visualization data + if not self.constant: + # Point cloud + self.factory.update_points(object_id=0, + positions=self.pcd) + # Ground truth value + self.factory.update_points(object_id=1, + positions=self.mean) + # Send the training data - self.set_training_data(input_array=self.input_value, - output_array=self.output_value) + self.set_training_data(input=self.pcd, + ground_truth=self.mean) + if self.allow_requests: # Request a prediction for the given input - prediction = self.get_prediction(input_array=self.input_value) + prediction = self.get_prediction(input=self.pcd) # Apply this prediction in Environment self.apply_prediction(prediction) + else: + self.update_visualisation() def apply_prediction(self, prediction): - # Update visualization with new input and ground truth - if not self.constant: - # Point cloud - self.factory.update_points(object_id=0, - positions=self.input_value) - # Ground truth value - self.factory.update_points(object_id=1, - positions=self.output_value) + # Update visualization with prediction self.factory.update_points(object_id=2, - positions=prediction) + positions=prediction['prediction']) # Send visualization data to update self.update_visualisation() diff --git a/examples/features/dataGeneration_multi.py b/examples/features/dataGeneration_multi.py index 74aa3533..e9e98994 100644 --- a/examples/features/dataGeneration_multi.py +++ b/examples/features/dataGeneration_multi.py @@ -9,34 +9,41 @@ from time import time # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseDataGenerator import BaseDataGenerator +from DeepPhysX.Core.Pipelines.BaseDataGeneration import BaseDataGeneration from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer # Session related imports from Environment import MeanEnvironment def launch_data_generation(use_tcp_ip): + # Define the number of points and the dimension nb_points = 30 dimension = 3 + # Environment configuration environment_config = BaseEnvironmentConfig(environment_class=MeanEnvironment, - param_dict={'constant': False, - 'data_size': [nb_points, dimension], - 'sleep': True, - 'allow_requests': False}, + visualizer=VedoVisualizer, as_tcp_ip_client=use_tcp_ip, - number_of_thread=10) + number_of_thread=5, + env_kwargs={'constant': False, + 'data_size': [nb_points, dimension], + 'delay': True, + 'allow_request': False}) # Dataset configuration - dataset_config = BaseDatasetConfig(normalize=False) + database_config = BaseDatabaseConfig(max_file_size=1, + normalize=False) # Create DataGenerator - data_generator = BaseDataGenerator(session_name='sessions/data_generation_compare', - environment_config=environment_config, - dataset_config=dataset_config, - nb_batches=20, - batch_size=10) + data_generator = BaseDataGeneration(environment_config=environment_config, + database_config=database_config, + session_dir='sessions', + session_name='data_generation_compare', + batch_nb=20, + batch_size=10) + # Launch the training session start_time = time() data_generator.execute() diff --git a/examples/features/dataGeneration_single.py b/examples/features/dataGeneration_single.py index 23c5de46..200d3d9b 100644 --- a/examples/features/dataGeneration_single.py +++ b/examples/features/dataGeneration_single.py @@ -4,33 +4,41 @@ """ # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseDataGenerator import BaseDataGenerator +from DeepPhysX.Core.Pipelines.BaseDataGeneration import BaseDataGeneration from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer # Session related imports from Environment import MeanEnvironment def launch_data_generation(): + # Define the number of points and the dimension nb_points = 30 dimension = 3 + # Environment configuration environment_config = BaseEnvironmentConfig(environment_class=MeanEnvironment, - param_dict={'constant': False, - 'data_size': [nb_points, dimension], - 'sleep': False, - 'allow_requests': False}, - as_tcp_ip_client=False) - # Dataset configuration - dataset_config = BaseDatasetConfig(normalize=False) + visualizer=VedoVisualizer, + as_tcp_ip_client=False, + env_kwargs={'constant': False, + 'data_size': (nb_points, dimension), + 'delay': False, + 'allow_request': False}) + # Database configuration + database_config = BaseDatabaseConfig(max_file_size=1, + normalize=False) + # Create DataGenerator - data_generator = BaseDataGenerator(session_name='sessions/data_generation', - environment_config=environment_config, - dataset_config=dataset_config, - nb_batches=500, - batch_size=10) + data_generator = BaseDataGeneration(environment_config=environment_config, + database_config=database_config, + session_dir='sessions', + session_name='data_generation', + batch_nb=500, + batch_size=10) + # Launch the training session data_generator.execute() diff --git a/examples/features/gradientDescent.py b/examples/features/gradientDescent.py index dcb5de75..87fe600b 100644 --- a/examples/features/gradientDescent.py +++ b/examples/features/gradientDescent.py @@ -9,9 +9,9 @@ from torch.optim import Adam # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseTrainer import BaseTrainer +from DeepPhysX.Core.Pipelines.BaseTraining import BaseTraining from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.FC.FCConfig import FCConfig @@ -20,37 +20,43 @@ def launch_training(): + # Define the number of points and the dimension nb_points = 30 dimension = 3 + # Environment configuration environment_config = BaseEnvironmentConfig(environment_class=MeanEnvironment, visualizer=VedoVisualizer, - param_dict={'constant': True, - 'data_size': [nb_points, dimension], - 'sleep': False, - 'allow_requests': True}, as_tcp_ip_client=True, - number_of_thread=2) + number_of_thread=1, + env_kwargs={'constant': True, + 'data_size': [nb_points, dimension], + 'delay': False, + 'allow_request': True}) + # Fully Connected configuration (the number of neurones on the first and last layer is defined by the total amount # of parameters in the input and the output vectors respectively) - network_config = FCConfig(loss=MSELoss, - lr=1e-3, + network_config = FCConfig(lr=1e-3, + loss=MSELoss, optimizer=Adam, dim_layers=[nb_points * dimension, nb_points * dimension, dimension], dim_output=dimension) - # Dataset configuration - dataset_config = BaseDatasetConfig(normalize=False) + + # Database configuration + database_config = BaseDatabaseConfig(normalize=False) + # Create Trainer - trainer = BaseTrainer(session_dir='sessions', - session_name='gradient_descent', - environment_config=environment_config, - dataset_config=dataset_config, - network_config=network_config, - nb_epochs=1, - nb_batches=200, - batch_size=1, - debug=True) + trainer = BaseTraining(network_config=network_config, + environment_config=environment_config, + database_config=database_config, + session_dir='sessions', + session_name='gradient_descent', + epoch_nb=1, + batch_nb=200, + batch_size=1, + debug=True) + # Launch the training session trainer.execute() diff --git a/examples/features/offlineTraining.py b/examples/features/offlineTraining.py index b03e986e..bc5abb6c 100644 --- a/examples/features/offlineTraining.py +++ b/examples/features/offlineTraining.py @@ -9,42 +9,49 @@ from torch.optim import Adam # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseTrainer import BaseTrainer -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BaseTraining import BaseTraining +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Torch.FC.FCConfig import FCConfig def launch_training(): + # Define the number of points and the dimension nb_points = 30 dimension = 3 + # Fully Connected configuration (the number of neurones on the first and last layer is defined by the total amount # of parameters in the input and the output vectors respectively) - network_config = FCConfig(loss=MSELoss, - lr=1e-3, + network_config = FCConfig(lr=1e-3, + loss=MSELoss, optimizer=Adam, dim_layers=[nb_points * dimension, nb_points * dimension, dimension], dim_output=dimension) + # Dataset configuration with the path to the existing Dataset - dataset_config = BaseDatasetConfig(dataset_dir=os.path.join(os.getcwd(), 'sessions/data_generation'), - shuffle_dataset=True, - normalize=False) + database_config = BaseDatabaseConfig(existing_dir='sessions/data_generation', + shuffle=True, + normalize=False) + # Create DataGenerator - trainer = BaseTrainer(session_dir='sessions', - session_name='offline_training', - dataset_config=dataset_config, - network_config=network_config, - nb_epochs=1, - nb_batches=500, - batch_size=10) + trainer = BaseTraining(network_config=network_config, + database_config=database_config, + session_dir='sessions', + session_name='offline_training', + epoch_nb=1, + batch_nb=500, + batch_size=10) + # Launch the training session trainer.execute() if __name__ == '__main__': + if not os.path.exists(os.path.join(os.getcwd(), 'sessions/data_generation')): print("Existing Dataset required, 'sessions/data_generation' not found. " "Run dataGeneration_single.py script first.") from dataGeneration_single import launch_data_generation launch_data_generation() + launch_training() diff --git a/examples/features/onlineTraining.py b/examples/features/onlineTraining.py index 172b5a78..780849ec 100644 --- a/examples/features/onlineTraining.py +++ b/examples/features/onlineTraining.py @@ -8,48 +8,54 @@ from torch.optim import Adam # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseTrainer import BaseTrainer +from DeepPhysX.Core.Pipelines.BaseTraining import BaseTraining from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.FC.FCConfig import FCConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer # Session imports from Environment import MeanEnvironment def launch_training(): + # Define the number of points and the dimension nb_points = 30 dimension = 3 + # Environment configuration environment_config = BaseEnvironmentConfig(environment_class=MeanEnvironment, visualizer=VedoVisualizer, - param_dict={'constant': False, - 'data_size': [nb_points, dimension], - 'sleep': False, - 'allow_requests': True}, as_tcp_ip_client=True, - number_of_thread=10) + number_of_thread=5, + env_kwargs={'constant': False, + 'data_size': [nb_points, dimension], + 'delay': False, + 'allow_request': True}) + # Fully Connected configuration (the number of neurones on the first and last layer is defined by the total amount # of parameters in the input and the output vectors respectively) - network_config = FCConfig(loss=MSELoss, - lr=1e-3, + network_config = FCConfig(lr=1e-3, + loss=MSELoss, optimizer=Adam, dim_layers=[nb_points * dimension, nb_points * dimension, dimension], dim_output=dimension) + # Dataset configuration with the path to the existing Dataset - dataset_config = BaseDatasetConfig(shuffle_dataset=True, - normalize=False) + database_config = BaseDatabaseConfig(max_file_size=1, + shuffle=True, + normalize=False) # Create DataGenerator - trainer = BaseTrainer(session_dir='sessions', - session_name='online_training', - environment_config=environment_config, - dataset_config=dataset_config, - network_config=network_config, - nb_epochs=5, - nb_batches=100, - batch_size=10) + trainer = BaseTraining(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='online_training', + epoch_nb=5, + batch_nb=100, + batch_size=10) + # Launch the training session trainer.execute() diff --git a/examples/features/prediction.py b/examples/features/prediction.py index 67a8e34e..7fd8adf3 100644 --- a/examples/features/prediction.py +++ b/examples/features/prediction.py @@ -7,41 +7,42 @@ import os # DeepPhysX related imports -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.FC.FCConfig import FCConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig + # Session imports from Environment import MeanEnvironment def launch_prediction(session): + # Define the number of points and the dimension nb_points = 30 dimension = 3 + # Environment configuration environment_config = BaseEnvironmentConfig(environment_class=MeanEnvironment, visualizer=VedoVisualizer, - param_dict={'constant': False, + env_kwargs={'constant': False, 'data_size': [nb_points, dimension], - 'sleep': True, - 'allow_requests': False}, - as_tcp_ip_client=False) + 'delay': True, + 'allow_request': False}) + # Fully Connected configuration (the number of neurones on the first and last layer is defined by the total amount # of parameters in the input and the output vectors respectively) network_config = FCConfig(dim_layers=[nb_points * dimension, nb_points * dimension, dimension], dim_output=dimension) - # Dataset configuration - dataset_config = BaseDatasetConfig(normalize=False) + # Create DataGenerator - trainer = BaseRunner(session_dir='sessions', - session_name=session, - environment_config=environment_config, - network_config=network_config, - dataset_config=dataset_config, - nb_steps=100) + trainer = BasePrediction(environment_config=environment_config, + network_config=network_config, + session_dir='sessions', + session_name=session, + step_nb=100) + # Launch the training session trainer.execute() diff --git a/examples/tutorial/T2_network.py b/examples/tutorial/T2_network.py index 9af80612..df8b5aa8 100644 --- a/examples/tutorial/T2_network.py +++ b/examples/tutorial/T2_network.py @@ -10,7 +10,7 @@ # DeepPhysX related imports from DeepPhysX.Core.Network.BaseNetwork import BaseNetwork from DeepPhysX.Core.Network.BaseOptimization import BaseOptimization -from DeepPhysX.Core.Network.DataTransformation import DataTransformation +from DeepPhysX.Core.Network.BaseTransformation import BaseTransformation # Create a Network as a BaseNetwork child class @@ -94,12 +94,12 @@ def optimize(self): pass -# Create a DataTransformation as a DataTransformation child class -class DummyTransformation(DataTransformation): +# Create a BaseTransformation as a BaseTransformation child class +class DummyTransformation(BaseTransformation): def __init__(self, config): - DataTransformation.__init__(self, config) + BaseTransformation.__init__(self, config) self.data_type = ndarray def transform_before_prediction(self, data_net): diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 791bfab5..1373a901 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -10,7 +10,7 @@ class AbstractEnvironment: def __init__(self, as_tcp_ip_client: bool = True, - instance_id: int = 0, + instance_id: int = 1, instance_nb: int = 1, visualization_db: Optional[Union[Database, Tuple[str, str]]] = None, **kwargs): @@ -30,7 +30,7 @@ def __init__(self, raise ValueError(f"[{self.name}] Instance ID ({instance_id}) is bigger than max instances " f"({instance_nb}).") self.as_tcp_ip_client: bool = as_tcp_ip_client - self.instance_id: int = instance_id + self.instance_id: int = max(1, instance_id) self.instance_nb: int = instance_nb # Manager of the Environment diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 712a3feb..51df8963 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -13,7 +13,7 @@ class BaseEnvironment(AbstractEnvironment): def __init__(self, as_tcp_ip_client: bool = True, - instance_id: int = 0, + instance_id: int = 1, instance_nb: int = 1, visualization_db: Optional[Union[Database, Tuple[str, str]]] = None, **kwargs): @@ -300,6 +300,8 @@ def update_visualisation(self) -> None: # If Environment is a TcpIpClient, request to the Server if self.as_tcp_ip_client: self.tcp_ip_client.request_update_visualization() + else: + self.environment_manager.update_visualizer(self.instance_id) self.factory.render() def _get_prediction(self): diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index aef785b3..1a4d8c5c 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -224,10 +224,11 @@ def compute_online_prediction(self, sample = self.database_handler.get_line(table_name='Exchange', fields=self.network.net_fields, line_id=instance_id) + del sample['id'] # Apply normalization and convert to tensor for field in sample.keys(): - sample[field] = array(sample[field]) + sample[field] = array([sample[field]]) if field in normalization.keys(): sample[field] = self.normalize_data(data=sample[field], normalization=normalization[field]) @@ -241,7 +242,7 @@ def compute_online_prediction(self, # Return the prediction for field in data_pred.keys(): - data_pred[field] = self.network.tensor_to_numpy(data=data_pred[field]) + data_pred[field] = self.network.tensor_to_numpy(data=data_pred[field][0]) if field in normalization.keys(): data_pred[field] = self.normalize_data(data=data_pred[field], normalization=normalization[field], diff --git a/src/Network/BaseOptimization.py b/src/Network/BaseOptimization.py index 729c54b5..03116fac 100644 --- a/src/Network/BaseOptimization.py +++ b/src/Network/BaseOptimization.py @@ -43,7 +43,6 @@ def compute_loss(self, :return: Loss value. """ - # WARNING: self.optimization.compute_loss(data_pred.reshape(data_gt.shape), data_gt, loss_data) raise NotImplementedError def transform_loss(self, diff --git a/src/Network/BaseTransformation.py b/src/Network/BaseTransformation.py index e2f4cb7d..522bfc58 100644 --- a/src/Network/BaseTransformation.py +++ b/src/Network/BaseTransformation.py @@ -21,8 +21,9 @@ def check_type(func: Callable[[Any, Any], Any]): def inner(self, *args): for data in args: - if data is not None and type(data) != self.data_type: - raise TypeError(f"[{self.name}] Wrong data type: {self.data_type} required, get {type(data)}") + for value in data.values(): + if value is not None and type(value) != self.data_type: + raise TypeError(f"[{self.name}] Wrong data type: {self.data_type} required, get {type(value)}") return func(self, *args) return inner diff --git a/src/Utils/configs.py b/src/Utils/configs.py index 692290d7..8ac16c6f 100644 --- a/src/Utils/configs.py +++ b/src/Utils/configs.py @@ -21,7 +21,7 @@ def make_config(configuration_object: Any, # Check if a dataset_config already exists (child class will have the parent's config by default) if configuration_name in configuration_object.__dict__: - configuration: namedtuple = configuration_object.__getattr__(configuration_name) + configuration: namedtuple = configuration_object.__getattribute__(configuration_name) # Get items set in the existing config for key, value in configuration._asdict().items(): # Only new items are required for children, check if the parent's items are set again anyway From f069e4d6568950dd28eb051339a397095ec6bb0c Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 3 Nov 2022 15:32:50 +0100 Subject: [PATCH 35/61] Dataset converter from Numpy to SQL. --- src/Database/DatabaseHandler.py | 16 ++++ src/Utils/converter.py | 146 ++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 src/Utils/converter.py diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py index 44ebd548..7c284cf5 100644 --- a/src/Database/DatabaseHandler.py +++ b/src/Database/DatabaseHandler.py @@ -184,6 +184,22 @@ def add_data(self, return [len(self.__storing_partitions) - 1, self.__storing_partitions[-1].add_data(table_name=table_name, data=data)] + def add_batch(self, + table_name: str, + batch: Dict[str, List[Any]]) -> None: + """ + Add a batch of data in a Database. + + :param table_name: Name of the Table. + :param batch: New lines of the Table. + """ + + # Only available in the storing Database + if table_name == 'Exchange': + raise ValueError(f"Cannot add a batch in the Exchange Database.") + self.__storing_partitions[-1].add_batch(table_name=table_name, + batch=batch) + def update(self, table_name: str, data: Dict[str, Any], diff --git a/src/Utils/converter.py b/src/Utils/converter.py new file mode 100644 index 00000000..bec3c387 --- /dev/null +++ b/src/Utils/converter.py @@ -0,0 +1,146 @@ +from typing import Dict, List, Optional +from os import listdir +from os.path import join, exists, isfile, dirname, sep +from numpy import ndarray, load +from vedo import ProgressBar + +from DeepPhysX.Core.Utils.path import get_first_caller, create_dir +from DeepPhysX.Core.Manager.DatabaseManager import DatabaseManager +from DeepPhysX.Core.Database.DatabaseHandler import DatabaseHandler +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig + + +class DatasetConverter: + + def __init__(self, + session_path: str): + """ + Convert a Dataset between the previous Numpy partitions and the new SQL Database. + + :param session_path: Relative path to the session to convert. + """ + + root = get_first_caller() + self.dataset_dir = join(root, session_path, 'dataset') + if not exists(self.dataset_dir): + raise ValueError(f"The given path does not exist: {self.dataset_dir}") + + def numpy_to_database(self, + batch_size: int, + max_file_size: Optional[float] = None, + normalize: bool = False): + """ + Convert a Database from the previous Numpy partitions to the new SQL Database. + """ + + # 1. Get the partitions files + partitions = self.load_numpy_partitions() + + # 2. Create a new repository + session_name = f'{self.dataset_dir.split(sep)[-2]}_converted' + session = create_dir(session_dir=dirname(dirname(self.dataset_dir)), + session_name=session_name) + database_manager = DatabaseManager(pipeline='data_generation', + session=session) + database_manager.close() + + # 3. Create the Database for each mode + for mode in partitions.keys(): + print(f"\nConverting {mode} partitions...") + + # 3.1. Create a DatabaseManager with a DatabaseHandler + database_config = BaseDatabaseConfig(mode=mode, + max_file_size=max_file_size, + normalize=normalize) + database_manager = DatabaseManager(database_config=database_config, + pipeline='data_generation', + session=session, + new_session=False) + database_handler = DatabaseHandler() + database_manager.connect_handler(database_handler) + + # 3.2. Create Fields in Tables + training_fields = [] + additional_fields = [] + nb_partition = 0 + for field in partitions[mode].keys(): + if field in ['input', 'ground_truth']: + training_fields.append((field, ndarray)) + + else: + additional_fields.append((field.split('_')[-1], ndarray)) + if nb_partition == 0: + nb_partition = len(partitions[mode][field]) + elif len(partitions[mode][field]) != nb_partition: + raise ValueError(f"The number of partition is not consistent in {mode} mode.") + database_handler.create_fields(table_name='Training', fields=training_fields) + database_handler.create_fields(table_name='Additional', fields=additional_fields) + + # 3.3. Add each partition to the Database + if nb_partition == 0: + print(" No partition.") + for i in range(nb_partition): + data_training = {field: load(join(self.dataset_dir, f'{partitions[mode][field][i]}.npy')) + for field, _ in training_fields} + data_additional = {field: load(join(self.dataset_dir, f'{partitions[mode][field][i]}.npy')) + for field, _ in additional_fields} + nb_sample = 0 + for data in [data_training, data_additional]: + for field in data.keys(): + if nb_sample == 0: + nb_sample = data[field].shape[0] + elif data[field].shape[0] != nb_sample: + raise ValueError(f"The number of sample is not consistent in {mode} mode.") + id_sample = 0 + pb = ProgressBar(0, nb_sample, c='orange', title=f" Loading partition {i + 1}/{nb_partition}") + while id_sample < nb_sample: + last_sample = min(nb_sample, id_sample + batch_size) + if len(data_training.keys()) > 0: + sample_training = {field: data_training[field][id_sample:last_sample] + for field in data_training.keys()} + database_handler.add_batch(table_name='Training', batch=sample_training) + if len(data_additional.keys()) > 0: + sample_additional = {field: data_additional[field][id_sample:last_sample] + for field in data_additional.keys()} + database_handler.add_batch(table_name='Additional', batch=sample_additional) + database_manager.add_data() + id_sample += batch_size + pb.print(counts=id_sample) + + # 3. Close DatabaseManager + database_manager.close() + + print("\nConversion done.") + + def load_numpy_partitions(self) -> Dict[str, Dict[str, List[str]]]: + """ + Get all the partition files in the repository. Do not use the JSON file to prevent bugs. + """ + + # 1. Get the partition files for each mode + modes = ['training', 'validation', 'prediction'] + partitions = {mode: [f.split('.')[0] for f in listdir(self.dataset_dir) if isfile(join(self.dataset_dir, f)) + and f.endswith('.npy') and f.__contains__(mode)] for mode in modes} + + # 2. Sort partitions by field (IN, OUT, ADD) and by name + sorted_partitions = {mode: {field[1:-1]: sorted([f for f in partitions[mode] if f.__contains__(field)]) + for field in ['_IN_', '_OUT_', '_ADD_']} + for mode in modes} + all_partitions = {} + for mode in modes: + all_partitions[mode] = {} + for field, name in zip(['IN', 'OUT', 'ADD'], ['input', 'ground_truth', '']): + for partition in sorted_partitions[mode][field]: + # Extract information from the filename + partition_name = partition.split('_') + partition_name = partition_name[partition_name.index(field):] + # Additional data: ___ + if len(partition_name) == 3: + name = partition_name[-2] + # Add partition + if name not in all_partitions[mode]: + all_partitions[mode][name] = [] + all_partitions[mode][name].append(partition) + + return all_partitions + From e1a2e268e15f41334c66599adf926dd246ee2eb7 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 3 Nov 2022 15:39:05 +0100 Subject: [PATCH 36/61] Fix Environment kwargs in TcpIpClient. --- src/AsyncSocket/TcpIpClient.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index bd722ae2..59a67995 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -68,12 +68,13 @@ async def __initialize(self) -> None: # Receive additional arguments env_kwargs = {} await self.receive_dict(recv_to=env_kwargs, loop=loop, sender=self.sock) + env_kwargs = env_kwargs['env_kwargs'] if 'env_kwargs' in env_kwargs else {} self.environment = self.environment_class(as_tcp_ip_client=True, instance_id=self.environment_instance[0], instance_nb=self.environment_instance[1], visualization_db=self.environment_visualization, - **env_kwargs['env_kwargs']) + **env_kwargs) self.environment.tcp_ip_client = self # Receive prediction requests authorization From a5ca637ebb00c910b6e131e1bbaf6ab0e6ef9d41 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 3 Nov 2022 15:45:40 +0100 Subject: [PATCH 37/61] Use VedoProgressBar. --- src/Pipelines/BaseDataGeneration.py | 8 +++---- src/Pipelines/BaseTraining.py | 30 +++++++++++-------------- src/Utils/progressbar.py | 34 ----------------------------- 3 files changed, 16 insertions(+), 56 deletions(-) delete mode 100644 src/Utils/progressbar.py diff --git a/src/Pipelines/BaseDataGeneration.py b/src/Pipelines/BaseDataGeneration.py index dad878e2..fb2751b1 100644 --- a/src/Pipelines/BaseDataGeneration.py +++ b/src/Pipelines/BaseDataGeneration.py @@ -1,12 +1,11 @@ from typing import Optional from os.path import join, sep, exists -from sys import stdout +from vedo import ProgressBar from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline from DeepPhysX.Core.Manager.DataManager import DataManager from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Utils.progressbar import Progressbar from DeepPhysX.Core.Utils.path import get_first_caller, create_dir @@ -65,7 +64,7 @@ def __init__(self, self.batch_nb: int = batch_nb self.batch_id: int = 0 self.batch_size = batch_size - self.progress_bar = Progressbar(start=0, stop=self.batch_id, c='orange', title="Data Generation") + self.progress_bar = ProgressBar(start=0, stop=self.batch_nb, c='orange', title="Data Generation") def execute(self) -> None: """ @@ -122,8 +121,7 @@ def batch_end(self) -> None: Called once at the end of a batch production. """ - stdout.write("\033[K") - self.progress_bar.print(counts=self.batch_id + 1) + self.progress_bar.print(counts=self.batch_id) def data_generation_end(self) -> None: """ diff --git a/src/Pipelines/BaseTraining.py b/src/Pipelines/BaseTraining.py index 5cc7d480..f326d515 100644 --- a/src/Pipelines/BaseTraining.py +++ b/src/Pipelines/BaseTraining.py @@ -1,7 +1,7 @@ from typing import Optional -from sys import stdout from os.path import join, isfile, exists, sep from datetime import datetime +from vedo import ProgressBar from DeepPhysX.Core.Pipelines.BasePipeline import BasePipeline from DeepPhysX.Core.Manager.DataManager import DataManager @@ -10,7 +10,6 @@ from DeepPhysX.Core.Network.BaseNetworkConfig import BaseNetworkConfig from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Utils.progressbar import Progressbar from DeepPhysX.Core.Utils.path import get_first_caller, create_dir @@ -102,14 +101,13 @@ def __init__(self, self.debug = debug # Progressbar - if not self.debug: - self.progress_counter = 0 - self.digits = ['{' + f':0{len(str(self.epoch_nb))}d' + '}', - '{' + f':0{len(str(self.batch_nb))}d' + '}'] - epoch_id, epoch_nb = self.digits[0].format(0), self.digits[0].format(self.epoch_nb) - batch_id, batch_nb = self.digits[1].format(0), self.digits[1].format(self.batch_nb) - self.progress_bar = Progressbar(start=0, stop=self.batch_nb * self.epoch_nb, c='orange', - title=f'Epoch n°{epoch_id}/{epoch_nb} - Batch n°{batch_id}/{batch_nb}') + self.progress_counter = 0 + self.digits = ['{' + f':0{len(str(self.epoch_nb))}d' + '}', + '{' + f':0{len(str(self.batch_nb))}d' + '}'] + epoch_id, epoch_nb = self.digits[0].format(0), self.digits[0].format(self.epoch_nb) + batch_id, batch_nb = self.digits[1].format(0), self.digits[1].format(self.batch_nb) + self.progress_bar = ProgressBar(start=0, stop=self.batch_nb * self.epoch_nb, c='orange', + title=f'Epoch n°{epoch_id}/{epoch_nb} - Batch n°{batch_id}/{batch_nb}') self.save_info_file() @@ -165,13 +163,11 @@ def batch_begin(self) -> None: Called one at the beginning of a batch production. """ - if not self.debug: - stdout.write("\033[K") - self.progress_counter += 1 - id_epoch, nb_epoch = self.digits[0].format(self.epoch_id + 1), self.digits[0].format(self.epoch_nb) - id_batch, nb_batch = self.digits[1].format(self.batch_id + 1), self.digits[1].format(self.batch_nb) - self.progress_bar.title = f'Epoch n°{id_epoch}/{nb_epoch} - Batch n°{id_batch}/{nb_batch} ' - self.progress_bar.print(counts=self.progress_counter) + self.progress_counter += 1 + id_epoch, nb_epoch = self.digits[0].format(self.epoch_id + 1), self.digits[0].format(self.epoch_nb) + id_batch, nb_batch = self.digits[1].format(self.batch_id + 1), self.digits[1].format(self.batch_nb) + self.progress_bar.title = f'Epoch n°{id_epoch}/{nb_epoch} - Batch n°{id_batch}/{nb_batch} ' + self.progress_bar.print(counts=self.progress_counter) def optimize(self) -> None: """ diff --git a/src/Utils/progressbar.py b/src/Utils/progressbar.py deleted file mode 100644 index 836223f5..00000000 --- a/src/Utils/progressbar.py +++ /dev/null @@ -1,34 +0,0 @@ -from vedo import ProgressBar - - -class Progressbar(ProgressBar): - - def __init__(self, start, stop, step=1, c=None, title=''): - - ProgressBar.__init__(self, start=start, stop=stop, step=step, c=c, title=title) - - self.percent_int = 0 - - def _update(self, counts): - if counts < self.start: - counts = self.start - elif counts > self.stop: - counts = self.stop - self._counts = counts - self.percent = (self._counts - self.start) * 100 - dd = self.stop - self.start - if dd: - self.percent /= self.stop - self.start - else: - self.percent = 0 - self.percent_int = int(round(self.percent)) - af = self.width - 2 - nh = int(round(self.percent_int / 100 * af)) - br_bk = "\x1b[2m" + self.char_back * (af - nh) - br = "%s%s%s" % (self.char * (nh - 1), self.char_arrow, br_bk) - self.bar = self.title + self.char0 + br + self.char1 - if self.percent < 100: - ps = " " + str(self.percent_int) + "%" - else: - ps = "" - self.bar += ps From 3963fa53729b49f6aea6a6d9114c821d10c640eb Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 3 Nov 2022 16:01:13 +0100 Subject: [PATCH 38/61] Fix Visualization in local mode. --- examples/features/dataGeneration_single.py | 2 +- examples/features/gradientDescent.py | 3 +-- examples/features/offlineTraining.py | 2 +- src/Environment/BaseEnvironment.py | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/features/dataGeneration_single.py b/examples/features/dataGeneration_single.py index 200d3d9b..c9c0295b 100644 --- a/examples/features/dataGeneration_single.py +++ b/examples/features/dataGeneration_single.py @@ -36,7 +36,7 @@ def launch_data_generation(): database_config=database_config, session_dir='sessions', session_name='data_generation', - batch_nb=500, + batch_nb=100, batch_size=10) # Launch the training session diff --git a/examples/features/gradientDescent.py b/examples/features/gradientDescent.py index 87fe600b..23e8d613 100644 --- a/examples/features/gradientDescent.py +++ b/examples/features/gradientDescent.py @@ -28,8 +28,7 @@ def launch_training(): # Environment configuration environment_config = BaseEnvironmentConfig(environment_class=MeanEnvironment, visualizer=VedoVisualizer, - as_tcp_ip_client=True, - number_of_thread=1, + as_tcp_ip_client=False, env_kwargs={'constant': True, 'data_size': [nb_points, dimension], 'delay': False, diff --git a/examples/features/offlineTraining.py b/examples/features/offlineTraining.py index bc5abb6c..0f5b8646 100644 --- a/examples/features/offlineTraining.py +++ b/examples/features/offlineTraining.py @@ -39,7 +39,7 @@ def launch_training(): session_dir='sessions', session_name='offline_training', epoch_nb=1, - batch_nb=500, + batch_nb=100, batch_size=10) # Launch the training session diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 51df8963..714c53bf 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -300,8 +300,6 @@ def update_visualisation(self) -> None: # If Environment is a TcpIpClient, request to the Server if self.as_tcp_ip_client: self.tcp_ip_client.request_update_visualization() - else: - self.environment_manager.update_visualizer(self.instance_id) self.factory.render() def _get_prediction(self): From c7280541dfb27c67c192f5b14d5c77cca49d32f9 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 4 Nov 2022 10:19:39 +0100 Subject: [PATCH 39/61] Manage non-existing Database case. --- src/Database/DatabaseHandler.py | 5 ++++- src/Environment/BaseEnvironment.py | 17 +++++++++-------- src/Pipelines/BasePrediction.py | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py index 7c284cf5..fce07dcb 100644 --- a/src/Database/DatabaseHandler.py +++ b/src/Database/DatabaseHandler.py @@ -94,7 +94,8 @@ def load(self) -> None: for db in self.__storing_partitions: db.load() - self.__exchange_db.load() + if self.__exchange_db is not None: + self.__exchange_db.load() def get_database_dir(self) -> str: """ @@ -156,6 +157,8 @@ def get_fields(self, :param table_name: Name of the Table. """ + if self.__exchange_db is None and len(self.__storing_partitions) == 0: + return [] database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[0] return database.get_fields(table_name=table_name) diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 714c53bf..7a11f031 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -210,14 +210,15 @@ def set_training_data(self, **kwargs) -> None: self.__database_handler.load() self.__first_add[0] = False required_fields = list(set(self.__database_handler.get_fields(table_name='Training')) - {'id', 'env_id'}) - for field in kwargs.keys(): - if field not in required_fields: - raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." - f"Required fields are {required_fields}.") - for field in required_fields: - if field not in kwargs.keys(): - raise ValueError(f"[{self.name}] The field '{field}' was not defined in training data." - f"Required fields are {required_fields}.") + if len(required_fields) > 0: + for field in kwargs.keys(): + if field not in required_fields: + raise ValueError(f"[{self.name}] The field '{field}' is not in the training Database." + f"Required fields are {required_fields}.") + for field in required_fields: + if field not in kwargs.keys(): + raise ValueError(f"[{self.name}] The field '{field}' was not defined in training data." + f"Required fields are {required_fields}.") # Training data is set if the Environment can compute data if self.compute_training_data: diff --git a/src/Pipelines/BasePrediction.py b/src/Pipelines/BasePrediction.py index 290a4fc1..a7bd0062 100644 --- a/src/Pipelines/BasePrediction.py +++ b/src/Pipelines/BasePrediction.py @@ -82,7 +82,7 @@ def execute(self) -> None: self.sample_begin() self.predict() self.sample_end() - self.run_end() + self.prediction_end() def prediction_begin(self) -> None: """ @@ -122,7 +122,7 @@ def sample_end(self) -> None: pass - def run_end(self) -> None: + def prediction_end(self) -> None: """ Called once at the end of the prediction Pipeline. """ From 995385c0cb0530cb5674b32fa9a6830d758a2e06 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 4 Nov 2022 15:01:03 +0100 Subject: [PATCH 40/61] Remove visualization_db from Environment parameters. --- examples/features/Environment.py | 36 ++++++++++------------ examples/tutorial/T1_environment.py | 6 ++-- src/AsyncSocket/AbstractEnvironment.py | 7 +++-- src/AsyncSocket/TcpIpClient.py | 16 +++++----- src/AsyncSocket/TcpIpServer.py | 16 +++++++--- src/Environment/BaseEnvironment.py | 33 +++++++++++--------- src/Environment/BaseEnvironmentConfig.py | 26 ++++++++-------- src/Environment/launcherBaseEnvironment.py | 8 ++--- src/Manager/EnvironmentManager.py | 12 ++++---- 9 files changed, 83 insertions(+), 77 deletions(-) diff --git a/examples/features/Environment.py b/examples/features/Environment.py index 818ecd5f..9093e6b7 100644 --- a/examples/features/Environment.py +++ b/examples/features/Environment.py @@ -20,7 +20,6 @@ def __init__(self, as_tcp_ip_client=True, instance_id=1, instance_nb=1, - visualization_db=None, constant=False, data_size=(30, 2), delay=False, @@ -29,8 +28,7 @@ def __init__(self, BaseEnvironment.__init__(self, as_tcp_ip_client=as_tcp_ip_client, instance_id=instance_id, - instance_nb=instance_nb, - visualization_db=visualization_db) + instance_nb=instance_nb) # Define training data values self.pcd = array([]) @@ -59,23 +57,21 @@ def init_database(self): def init_visualization(self): - if self.factory is not None: - - # Point cloud (object will have id = 0) - self.factory.add_points(positions=self.pcd, - at=self.instance_id, - c='blue', - point_size=8) - # Ground truth value (object will have id = 1) - self.factory.add_points(positions=self.mean, - at=self.instance_id, - c='green', - point_size=10) - # Prediction value (object will have id = 2) - self.factory.add_points(positions=self.mean, - at=self.instance_id, - c='orange', - point_size=12) + # Point cloud (object will have id = 0) + self.factory.add_points(positions=self.pcd, + at=self.instance_id, + c='blue', + point_size=8) + # Ground truth value (object will have id = 1) + self.factory.add_points(positions=self.mean, + at=self.instance_id, + c='green', + point_size=10) + # Prediction value (object will have id = 2) + self.factory.add_points(positions=self.mean, + at=self.instance_id, + c='orange', + point_size=12) """ ENVIRONMENT BEHAVIOR diff --git a/examples/tutorial/T1_environment.py b/examples/tutorial/T1_environment.py index 36732817..4220745f 100644 --- a/examples/tutorial/T1_environment.py +++ b/examples/tutorial/T1_environment.py @@ -16,14 +16,12 @@ class DummyEnvironment(BaseEnvironment): def __init__(self, as_tcp_ip_client=True, instance_id=1, - instance_nb=1, - visualization_db=None): + instance_nb=1): BaseEnvironment.__init__(self, as_tcp_ip_client=as_tcp_ip_client, instance_id=instance_id, - instance_nb=instance_nb, - visualization_db=visualization_db) + instance_nb=instance_nb) self.step_nb: int = 0 diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/AsyncSocket/AbstractEnvironment.py index 1373a901..229f2de7 100644 --- a/src/AsyncSocket/AbstractEnvironment.py +++ b/src/AsyncSocket/AbstractEnvironment.py @@ -11,9 +11,7 @@ class AbstractEnvironment: def __init__(self, as_tcp_ip_client: bool = True, instance_id: int = 1, - instance_nb: int = 1, - visualization_db: Optional[Union[Database, Tuple[str, str]]] = None, - **kwargs): + instance_nb: int = 1): """ AbstractEnvironment sets the Environment API for TcpIpClient. Do not use AbstractEnvironment to implement an Environment, use BaseEnvironment instead. @@ -90,6 +88,9 @@ def close(self) -> None: def get_database_handler(self) -> DatabaseHandler: raise NotImplementedError + def _create_visualization(self, visualization_db: Union[Database, Tuple[str, str]]) -> None: + raise NotImplementedError + def _send_training_data(self) -> None: raise NotImplementedError diff --git a/src/AsyncSocket/TcpIpClient.py b/src/AsyncSocket/TcpIpClient.py index 59a67995..c46a9fa2 100644 --- a/src/AsyncSocket/TcpIpClient.py +++ b/src/AsyncSocket/TcpIpClient.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Type, Optional, Union, Tuple +from typing import Any, Dict, Type from socket import socket from asyncio import get_event_loop from asyncio import AbstractEventLoop as EventLoop @@ -16,8 +16,7 @@ def __init__(self, ip_address: str = 'localhost', port: int = 10000, instance_id: int = 0, - instance_nb: int = 1, - visualization_db: Optional[Union[Tuple[str, str]]] = None): + instance_nb: int = 1): """ TcpIpClient is a TcpIpObject which communicate with a TcpIpServer and manages an Environment to compute data. @@ -26,7 +25,6 @@ def __init__(self, :param port: Port number of the TcpIpObject. :param instance_id: Index of this instance. :param instance_nb: Number of simultaneously launched instances. - :param visualization_db: Path to the visualization Database. """ TcpIpObject.__init__(self, @@ -37,7 +35,6 @@ def __init__(self, self.environment: AbstractEnvironment self.environment_class = environment self.environment_instance = (instance_id, instance_nb) - self.environment_visualization = visualization_db # Bind to client address and send ID self.sock.connect((ip_address, port)) @@ -73,7 +70,6 @@ async def __initialize(self) -> None: self.environment = self.environment_class(as_tcp_ip_client=True, instance_id=self.environment_instance[0], instance_nb=self.environment_instance[1], - visualization_db=self.environment_visualization, **env_kwargs) self.environment.tcp_ip_client = self @@ -92,11 +88,17 @@ async def __initialize(self) -> None: self.environment.get_database_handler().init_remote(storing_partitions=partitions, exchange_db=exchange) + # Receive visualization database + visualization_db = await self.receive_data(loop=loop, sender=self.sock) + visualization_db = None if visualization_db == 'None' else visualization_db.split('///') + # Initialize the environment self.environment.create() self.environment.init() self.environment.init_database() - self.environment.init_visualization() + if visualization_db is not None: + self.environment._create_visualization(visualization_db=visualization_db) + self.environment.init_visualization() # Initialization done await self.send_data(data_to_send='done', loop=loop, receiver=self.sock) diff --git a/src/AsyncSocket/TcpIpServer.py b/src/AsyncSocket/TcpIpServer.py index 3eddf1b1..e8b3f350 100644 --- a/src/AsyncSocket/TcpIpServer.py +++ b/src/AsyncSocket/TcpIpServer.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple from asyncio import get_event_loop, gather from asyncio import AbstractEventLoop as EventLoop from asyncio import run as async_run @@ -121,22 +121,26 @@ async def __connect(self) -> None: ########################################################################################## def initialize(self, - env_kwargs: Dict[str, Any]) -> None: + env_kwargs: Dict[str, Any], + visualization_db: Optional[Tuple[str, str]] = None) -> None: """ Send parameters to the clients to create their environments. :param env_kwargs: Additional arguments to pass to the Environment. + :param visualization_db: Path to the visualization Database to connect to. """ print(f"[{self.name}] Initializing clients...") - async_run(self.__initialize(env_kwargs)) + async_run(self.__initialize(env_kwargs, visualization_db)) async def __initialize(self, - env_kwargs: Dict[str, Any]) -> None: + env_kwargs: Dict[str, Any], + visualization_db: Optional[Tuple[str, str]] = None) -> None: """ Send parameters to the clients to create their environments. :param env_kwargs: Additional arguments to pass to the Environment. + :param visualization_db: Path to the visualization Database to connect to. """ loop = get_event_loop() @@ -171,6 +175,10 @@ async def __initialize(self, partitions_list += f'{exchange.get_path()[0]}///{exchange.get_path()[1]}' await self.send_data(data_to_send=partitions_list, loop=loop, receiver=client) + # Send visualization Database + visualization = 'None' if visualization_db is None else f'{visualization_db[0]}///{visualization_db[1]}' + await self.send_data(data_to_send=visualization, loop=loop, receiver=client) + # Wait Client init await self.receive_data(loop=loop, sender=client) print(f"[{self.name}] Client n°{client_id} initialisation done") diff --git a/src/Environment/BaseEnvironment.py b/src/Environment/BaseEnvironment.py index 7a11f031..41bc90b6 100644 --- a/src/Environment/BaseEnvironment.py +++ b/src/Environment/BaseEnvironment.py @@ -15,7 +15,6 @@ def __init__(self, as_tcp_ip_client: bool = True, instance_id: int = 1, instance_nb: int = 1, - visualization_db: Optional[Union[Database, Tuple[str, str]]] = None, **kwargs): """ BaseEnvironment computes simulated data for the Network and its training process. @@ -23,7 +22,6 @@ def __init__(self, :param as_tcp_ip_client: Environment is a TcpIpObject if True, is owned by an EnvironmentManager if False. :param instance_id: ID of the instance. :param instance_nb: Number of simultaneously launched instances. - :param visualization_db: The path to the visualization Database or the visualization Database object to connect. """ AbstractEnvironment.__init__(self, @@ -47,14 +45,6 @@ def __init__(self, # Connect the Factory to the visualization Database self.factory: Optional[VedoFactory] = None - if visualization_db is not None: - if type(visualization_db) == list: - self.factory = VedoFactory(database_path=visualization_db, - idx_instance=instance_id, - remote=True) - else: - self.factory = VedoFactory(database=visualization_db, - idx_instance=instance_id) ########################################################################################## ########################################################################################## @@ -298,10 +288,11 @@ def update_visualisation(self) -> None: Triggers the Visualizer update. """ - # If Environment is a TcpIpClient, request to the Server - if self.as_tcp_ip_client: - self.tcp_ip_client.request_update_visualization() - self.factory.render() + if self.factory is not None: + # If Environment is a TcpIpClient, request to the Server + if self.as_tcp_ip_client: + self.tcp_ip_client.request_update_visualization() + self.factory.render() def _get_prediction(self): """ @@ -337,6 +328,20 @@ def __database_handler_init(self): # Load Database and create basic fields self.__database_handler.load() + def _create_visualization(self, + visualization_db: Union[Database, Tuple[str, str]]) -> None: + """ + Create a Factory for the Environment. + """ + + if type(visualization_db) == list: + self.factory = VedoFactory(database_path=visualization_db, + idx_instance=self.instance_id, + remote=True) + else: + self.factory = VedoFactory(database=visualization_db, + idx_instance=self.instance_id) + def _send_training_data(self) -> List[int]: """ Add the training data and the additional data in their respective Databases. diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Environment/BaseEnvironmentConfig.py index ba75ef63..94d840a9 100644 --- a/src/Environment/BaseEnvironmentConfig.py +++ b/src/Environment/BaseEnvironmentConfig.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Type, Dict +from typing import Any, Optional, Type, Dict, Tuple from os import cpu_count from os.path import join, dirname from threading import Thread @@ -96,7 +96,7 @@ def __init__(self, def create_server(self, environment_manager: Optional[Any] = None, batch_size: int = 1, - visualization_db: Optional[str] = None) -> TcpIpServer: + visualization_db: Optional[Tuple[str, str]] = None) -> TcpIpServer: """ Create a TcpIpServer and launch TcpIpClients in subprocesses. @@ -113,13 +113,13 @@ def create_server(self, max_client_count=self.max_client_connections, batch_size=batch_size, manager=environment_manager) - server_thread = Thread(target=self.start_server, args=(server,)) + server_thread = Thread(target=self.start_server, args=(server, visualization_db)) server_thread.start() # Create clients client_threads = [] for i in range(self.number_of_thread): - client_thread = Thread(target=self.start_client, args=(i + 1, visualization_db)) + client_thread = Thread(target=self.start_client, args=(i + 1,)) client_threads.append(client_thread) for client in client_threads: client.start() @@ -130,43 +130,41 @@ def create_server(self, return server def start_server(self, - server: TcpIpServer) -> None: + server: TcpIpServer, + visualization_db: Optional[Tuple[str, str]] = None) -> None: """ Start TcpIpServer. :param server: TcpIpServer. + :param visualization_db: Path to the visualization Database to connect to. """ server.connect() - server.initialize(env_kwargs=self.env_kwargs) + server.initialize(visualization_db=visualization_db, + env_kwargs=self.env_kwargs) self.server_is_ready = True def start_client(self, - idx: int = 1, - visualization_db: Optional[str] = None) -> None: + idx: int = 1) -> None: """ Run a subprocess to start a TcpIpClient. :param idx: Index of client. - :param visualization_db: Path to the visualization Database to connect to. """ script = join(dirname(modules[BaseEnvironment.__module__].__file__), 'launcherBaseEnvironment.py') run([executable, script, self.environment_file, self.environment_class.__name__, - self.ip_address, str(self.port), str(idx), str(self.number_of_thread), str(visualization_db)]) + self.ip_address, str(self.port), str(idx), str(self.number_of_thread)]) - def create_environment(self, - visualization_db: Optional[Any] = None) -> BaseEnvironment: + def create_environment(self) -> BaseEnvironment: """ Create an Environment that will not be a TcpIpObject. - :param visualization_db: Path to the visualization Database to connect to. :return: Environment object. """ # Create instance environment = self.environment_class(as_tcp_ip_client=False, - visualization_db=visualization_db, **self.env_kwargs) if not isinstance(environment, BaseEnvironment): raise TypeError(f"[{self.name}] The given 'environment_class'={self.environment_class} must be a " diff --git a/src/Environment/launcherBaseEnvironment.py b/src/Environment/launcherBaseEnvironment.py index 6449468b..2d5b63e6 100644 --- a/src/Environment/launcherBaseEnvironment.py +++ b/src/Environment/launcherBaseEnvironment.py @@ -8,9 +8,9 @@ if __name__ == '__main__': # Check script call - if len(argv) != 8: + if len(argv) != 7: print(f"Usage: python3 {argv[0]} " - f" ") + f"") exit(1) # Import environment_class @@ -19,13 +19,11 @@ exec(f"from {module_name} import {argv[2]} as Environment") # Create, init and run Tcp-Ip environment - visualization_db = None if argv[7] == 'None' else [s[1:-1] for s in argv[7][1:-1].split(', ')] client = TcpIpClient(environment=Environment, ip_address=argv[3], port=int(argv[4]), instance_id=int(argv[5]), - instance_nb=int(argv[6]), - visualization_db=visualization_db) + instance_nb=int(argv[6])) client.initialize() client.launch() diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 389596fa..1fb426e9 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -42,12 +42,10 @@ def __init__(self, # Create a Visualizer to provide the visualization Database self.visualizer: Optional[VedoVisualizer] = None - visualization_db = None if environment_config.visualizer is not None: self.visualizer = environment_config.visualizer(database_dir=join(session, 'dataset'), database_name='Visualization', remote=environment_config.as_tcp_ip_client) - visualization_db = self.visualizer.get_database() # Create a single Environment or a TcpIpServer force_local = pipeline == 'prediction' @@ -58,17 +56,19 @@ def __init__(self, if environment_config.as_tcp_ip_client and not force_local: self.server = environment_config.create_server(environment_manager=self, batch_size=batch_size, - visualization_db=None if visualization_db is None else - visualization_db.get_path()) + visualization_db=None if self.visualizer is None else + self.visualizer.get_path()) # Create Environment else: - self.environment = environment_config.create_environment(visualization_db=visualization_db) + self.environment = environment_config.create_environment() self.environment.environment_manager = self self.data_manager.connect_handler(self.environment.get_database_handler()) self.environment.create() self.environment.init() self.environment.init_database() - self.environment.init_visualization() + if self.visualizer is not None: + self.environment._create_visualization(visualization_db=self.visualizer.get_database()) + self.environment.init_visualization() # Define whether methods are used for environment or server self.get_database_handler = self.__get_server_db_handler if self.server else self.__get_environment_db_handler From 63668408542cb5af211d13e117ae14531ccd1389 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 4 Nov 2022 18:10:52 +0100 Subject: [PATCH 41/61] Fix sample loading in SofaPrediction. Fix unormalization in Prediction. --- src/Database/DatabaseHandler.py | 2 ++ src/Manager/DataManager.py | 10 ++++++++-- src/Manager/DatabaseManager.py | 3 ++- src/Manager/NetworkManager.py | 4 ++-- src/Network/BaseNetwork.py | 1 + 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Database/DatabaseHandler.py b/src/Database/DatabaseHandler.py index fce07dcb..ca3d16a8 100644 --- a/src/Database/DatabaseHandler.py +++ b/src/Database/DatabaseHandler.py @@ -233,6 +233,8 @@ def get_line(self, database = self.__exchange_db if table_name == 'Exchange' else self.__storing_partitions[line_id[0]] line_id = line_id[1] if type(line_id) == list else line_id + if database.nb_lines(table_name=table_name) == 0: + return {} return database.get_line(table_name=table_name, line_id=line_id, fields=fields) diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index cc112799..103446d9 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -88,7 +88,8 @@ def connect_handler(self, def get_data(self, epoch: int = 0, - animate: bool = True) -> None: + animate: bool = True, + load_samples: bool = True) -> None: """ Fetch data from the EnvironmentManager or the DatabaseManager according to the context. @@ -129,7 +130,8 @@ def get_data(self, # Get data from Dataset if self.environment_manager.load_samples: - self.data_lines = self.database_manager.get_data(batch_size=1) + if load_samples: + self.data_lines = self.database_manager.get_data(batch_size=1) self.environment_manager.dispatch_batch(data_lines=self.data_lines, animate=animate, request_prediction=True, @@ -142,6 +144,10 @@ def get_data(self, if self.produce_data: self.database_manager.add_data(self.data_lines) + def load_sample(self) -> List[int]: + self.data_lines = self.database_manager.get_data(batch_size=1) + return self.data_lines[0] + def get_prediction(self, instance_id: int) -> None: """ diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index b922f993..dbf47624 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -195,7 +195,8 @@ def load_directory(self, self.partitions[mode].append(db) if len(self.partitions[self.mode]) == 0: self.create_partition() - elif self.max_file_size is not None and self.partitions[self.mode][-1].memory_size > self.max_file_size: + elif self.max_file_size is not None and self.partitions[self.mode][-1].memory_size > self.max_file_size \ + and self.produce_data: self.create_partition() # 6. Index partitions diff --git a/src/Manager/NetworkManager.py b/src/Manager/NetworkManager.py index 1a4d8c5c..d00d995a 100644 --- a/src/Manager/NetworkManager.py +++ b/src/Manager/NetworkManager.py @@ -243,9 +243,9 @@ def compute_online_prediction(self, # Return the prediction for field in data_pred.keys(): data_pred[field] = self.network.tensor_to_numpy(data=data_pred[field][0]) - if field in normalization.keys(): + if self.network.pred_norm_fields[field] in normalization.keys(): data_pred[field] = self.normalize_data(data=data_pred[field], - normalization=normalization[field], + normalization=normalization[self.network.pred_norm_fields[field]], reverse=True) data_pred[field].reshape(-1) self.database_handler.update(table_name='Exchange', diff --git a/src/Network/BaseNetwork.py b/src/Network/BaseNetwork.py index b390795c..32830ded 100644 --- a/src/Network/BaseNetwork.py +++ b/src/Network/BaseNetwork.py @@ -21,6 +21,7 @@ def __init__(self, self.net_fields = ['input'] self.opt_fields = ['ground_truth'] self.pred_fields = ['prediction'] + self.pred_norm_fields = {'prediction': 'ground_truth'} def predict(self, data_net: Dict[str, Any]) -> Dict[str, Any]: From 4af2f24a97b0d03f4cdb0913bf8c4f66c0c33bf9 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 7 Nov 2022 11:45:17 +0100 Subject: [PATCH 42/61] Use Visualizer in local mode in Prediction. --- src/Manager/EnvironmentManager.py | 4 ++-- src/Pipelines/BasePrediction.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index 1fb426e9..d8963b4d 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -41,14 +41,14 @@ def __init__(self, self.dataset_batch: Optional[List[List[int]]] = None # Create a Visualizer to provide the visualization Database + force_local = pipeline == 'prediction' self.visualizer: Optional[VedoVisualizer] = None if environment_config.visualizer is not None: self.visualizer = environment_config.visualizer(database_dir=join(session, 'dataset'), database_name='Visualization', - remote=environment_config.as_tcp_ip_client) + remote=environment_config.as_tcp_ip_client and not force_local) # Create a single Environment or a TcpIpServer - force_local = pipeline == 'prediction' self.number_of_thread: int = 1 if force_local else environment_config.number_of_thread self.server: Optional[TcpIpServer] = None self.environment: Optional[BaseEnvironment] = None diff --git a/src/Pipelines/BasePrediction.py b/src/Pipelines/BasePrediction.py index a7bd0062..34ca2b52 100644 --- a/src/Pipelines/BasePrediction.py +++ b/src/Pipelines/BasePrediction.py @@ -49,6 +49,12 @@ def __init__(self, raise ValueError(f"[{self.name}] The following directory does not exist: {join(session_dir, session_name)}") self.session = join(session_dir, session_name) + # Create a NetworkManager + self.network_manager = NetworkManager(network_config=network_config, + pipeline=self.type, + session=self.session, + new_session=False) + # Create a DataManager self.data_manager = DataManager(pipeline=self, database_config=database_config, @@ -57,12 +63,6 @@ def __init__(self, new_session=False, produce_data=record, batch_size=1) - - # Create a NetworkManager - self.network_manager = NetworkManager(network_config=network_config, - pipeline=self.type, - session=self.session, - new_session=False) self.data_manager.connect_handler(self.network_manager.get_database_handler()) self.network_manager.link_clients(self.data_manager.nb_environment) From 0725ebbc3a9c2929b22170717ff309c8afcb94e8 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 7 Nov 2022 12:15:13 +0100 Subject: [PATCH 43/61] Manage Exchange and Visualization Databases files. --- src/Manager/DataManager.py | 8 ++++++++ src/Manager/DatabaseManager.py | 2 +- src/Manager/EnvironmentManager.py | 9 ++++++++- src/Visualization/VedoVisualizer.py | 13 ++++++++++++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Manager/DataManager.py b/src/Manager/DataManager.py index 103446d9..c4241984 100644 --- a/src/Manager/DataManager.py +++ b/src/Manager/DataManager.py @@ -51,6 +51,7 @@ def __init__(self, data_manager=self, pipeline=pipeline.type, session=session, + produce_data=produce_data, batch_size=batch_size) # DataManager variables @@ -95,6 +96,7 @@ def get_data(self, :param epoch: Current epoch number. :param animate: Allow EnvironmentManager to trigger a step itself in order to generate a new sample. + :param load_samples: If True, trigger a sample loading from the Database. """ # Data generation case @@ -145,6 +147,12 @@ def get_data(self, self.database_manager.add_data(self.data_lines) def load_sample(self) -> List[int]: + """ + Load a sample from the Database. + + :return: Index of the loaded line. + """ + self.data_lines = self.database_manager.get_data(batch_size=1) return self.data_lines[0] diff --git a/src/Manager/DatabaseManager.py b/src/Manager/DatabaseManager.py index dbf47624..3efc35b1 100644 --- a/src/Manager/DatabaseManager.py +++ b/src/Manager/DatabaseManager.py @@ -137,7 +137,7 @@ def __init__(self, # Finally create an exchange database self.exchange = Database(database_dir=self.database_dir, - database_name='Exchange').new() + database_name='Exchange').new(remove_existing=True) self.exchange.create_table(table_name='Exchange') ########################################################################################## diff --git a/src/Manager/EnvironmentManager.py b/src/Manager/EnvironmentManager.py index d8963b4d..1e85c39f 100644 --- a/src/Manager/EnvironmentManager.py +++ b/src/Manager/EnvironmentManager.py @@ -14,6 +14,7 @@ def __init__(self, data_manager: Optional[Any] = None, pipeline: str = '', session: str = 'sessions/default', + produce_data: bool = True, batch_size: int = 1): """ EnvironmentManager handle the communication with Environment(s). @@ -22,6 +23,7 @@ def __init__(self, :param data_manager: DataManager that handles the EnvironmentManager. :param pipeline: Type of the pipeline. :param session: Path to the session repository. + :param produce_data: If True, this session will store data in the Database. :param batch_size: Number of samples in a single batch. """ @@ -46,7 +48,8 @@ def __init__(self, if environment_config.visualizer is not None: self.visualizer = environment_config.visualizer(database_dir=join(session, 'dataset'), database_name='Visualization', - remote=environment_config.as_tcp_ip_client and not force_local) + remote=environment_config.as_tcp_ip_client and not force_local, + record=produce_data) # Create a single Environment or a TcpIpServer self.number_of_thread: int = 1 if force_local else environment_config.number_of_thread @@ -240,6 +243,10 @@ def close(self) -> None: if self.environment: self.environment.close() + # Visualizer + if self.visualizer: + self.visualizer.close() + def __str__(self) -> str: description = "\n" diff --git a/src/Visualization/VedoVisualizer.py b/src/Visualization/VedoVisualizer.py index d84e367c..d4187f1d 100644 --- a/src/Visualization/VedoVisualizer.py +++ b/src/Visualization/VedoVisualizer.py @@ -12,7 +12,8 @@ def __init__(self, database_name: Optional[str] = None, remove_existing: bool = False, offscreen: bool = False, - remote: bool = False): + remote: bool = False, + record: bool = True): """ Manage the creation, update and rendering of Vedo Actors. @@ -22,6 +23,7 @@ def __init__(self, :param remove_existing: If True, overwrite a Database with the same path. :param offscreen: If True, visual data will be saved but not rendered. :param remote: If True, the Visualizer will treat the Factories as remote. + :param record: If True, the visualization Database is saved in memory. """ # Define Database @@ -47,6 +49,7 @@ def __init__(self, if not remote: self.__database.register_post_save_signal(table_name='Sync', handler=self.__sync_visualizer) + self.record = record def render_instance(self, instance: int): """ @@ -80,3 +83,11 @@ def render_instance(self, instance: int): # 3. Render Plotter if offscreen is False if not self.__offscreen: self.__plotter.render() + + def close(self): + """ + Launch the closing procedure of the Visualizer. + """ + + if not self.record: + self.__database.close(erase_file=True) From 66513613afce0a24caeb057ffc15759a3323c974 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 9 Nov 2022 11:44:57 +0100 Subject: [PATCH 44/61] Update demos. --- .../Armadillo/FC/Environment/Armadillo.py | 122 ++++----- .../FC/Environment/ArmadilloInteractive.py | 25 +- examples/demos/Armadillo/FC/download.py | 8 +- examples/demos/Armadillo/FC/interactive.py | 31 +-- examples/demos/Armadillo/FC/prediction.py | 38 ++- .../Armadillo/UNet/Environment/Armadillo.py | 171 ++++++------ examples/demos/Armadillo/UNet/download.py | 7 +- examples/demos/Armadillo/UNet/prediction.py | 52 ++-- examples/demos/Beam/FC/Environment/Beam.py | 126 ++++----- .../Beam/FC/Environment/BeamInteractive.py | 25 +- examples/demos/Beam/FC/download.py | 8 +- examples/demos/Beam/FC/interactive.py | 31 +-- examples/demos/Beam/FC/prediction.py | 38 ++- examples/demos/Beam/UNet/Environment/Beam.py | 133 ++++------ .../demos/Beam/UNet/Environment/parameters.py | 3 +- examples/demos/Beam/UNet/download.py | 6 +- examples/demos/Beam/UNet/prediction.py | 50 ++-- examples/demos/Liver/FC/Environment/Liver.py | 188 ++++++-------- .../Liver/FC/Environment/LiverInteractive.py | 27 +- examples/demos/Liver/FC/download.py | 8 +- examples/demos/Liver/FC/interactive.py | 31 +-- examples/demos/Liver/FC/prediction.py | 40 ++- .../demos/Liver/UNet/Environment/Liver.py | 245 ++++++++---------- examples/demos/Liver/UNet/download.py | 9 +- examples/demos/Liver/UNet/prediction.py | 53 ++-- 25 files changed, 642 insertions(+), 833 deletions(-) diff --git a/examples/demos/Armadillo/FC/Environment/Armadillo.py b/examples/demos/Armadillo/FC/Environment/Armadillo.py index 0140e923..e5fd74ca 100644 --- a/examples/demos/Armadillo/FC/Environment/Armadillo.py +++ b/examples/demos/Armadillo/FC/Environment/Armadillo.py @@ -10,10 +10,10 @@ import os import sys -import numpy as np +from numpy import ndarray, array, concatenate, arange, zeros, reshape +from numpy.random import randint, uniform from vedo import Mesh from math import pow -from time import sleep # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -28,20 +28,14 @@ class Armadillo(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topology self.mesh = None @@ -53,12 +47,12 @@ def __init__(self, # Force fields self.forces = [] self.areas = [] - self.compute_sample = True # Force pattern step = 0.05 - self.amplitudes = np.concatenate((np.arange(0, 1, step), - np.arange(1, 0, -step))) + self.amplitudes = concatenate((arange(0, 1, step), + arange(1, 0, -step))) + self.amplitudes[0] = 0 self.idx_amplitude = 0 self.force_value = None self.idx_zone = 0 @@ -73,11 +67,10 @@ def __init__(self, Methods will be automatically called in this order to create and initialize Environment. """ - def recv_parameters(self, param_dict): + def init_database(self): - # Get the model definition parameters - self.compute_sample = param_dict['compute_sample'] if 'compute_sample' in param_dict else True - self.amplitudes[0] = 0 if self.compute_sample else 1 + # Define the fields of the Training database + self.define_training_fields(fields=[('input', ndarray), ('ground_truth', ndarray)]) def create(self): @@ -97,34 +90,26 @@ def create(self): if sphere(pts, p_forces.centers[zone]) <= pow(p_forces.radius[zone], 2): self.areas[-1].append(i) # Init force value - self.forces.append(np.zeros(3, )) + self.forces.append(zeros(3, )) - def send_visualization(self): + def init_visualization(self): # Mesh representing detailed Armadillo (object will have id = 0) - self.factory.add_object(object_type="Mesh", - data_dict={"positions": self.mesh.points(), - 'cells': self.mesh.cells(), - 'wireframe': True, - "c": "orange", - "at": self.instance_id}) - + self.factory.add_mesh(positions=self.mesh.points(), + cells=self.mesh.cells(), + wireframe=True, + c='orange', + at=self.instance_id) # Arrows representing the force fields (object will have id = 1) - self.factory.add_object(object_type='Arrows', - data_dict={'positions': [0, 0, 0], - 'vectors': [0, 0, 0], - 'c': 'green', - 'at': self.instance_id}) - + self.factory.add_arrows(positions=array([0., 0., 0.]), + vectors=array([0., 0., 0.]), + c='green', + at=self.instance_id) # Points representing the grid (object will have id = 2) - self.factory.add_object(object_type='Points', - data_dict={'positions': self.sparse_grid.points(), - 'r': 1., - 'c': 'black', - 'at': self.instance_id}) - - # Return the visualization data - return self.factory.objects_dict + self.factory.add_points(positions=self.sparse_grid.points(), + point_size=1, + c='black', + at=self.instance_id) """ ENVIRONMENT BEHAVIOR @@ -134,38 +119,31 @@ def send_visualization(self): async def step(self): - # Compute a force sample - if self.compute_sample: - # Generate a new force - if self.idx_amplitude == 0: - self.idx_zone = np.random.randint(0, len(self.forces)) - zone = p_forces.zones[self.idx_zone] - self.force_value = np.random.uniform(low=-1, high=1, size=(3,)) * p_forces.amplitude[zone] - - # Update current force amplitude - self.forces[self.idx_zone] = self.force_value * self.amplitudes[self.idx_amplitude] + # Generate a new force + if self.idx_amplitude == 0: + self.idx_zone = randint(0, len(self.forces)) + zone = p_forces.zones[self.idx_zone] + self.force_value = uniform(low=-1, high=1, size=(3,)) * p_forces.amplitude[zone] - # Update force amplitude index - self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) + # Update current force amplitude + self.forces[self.idx_zone] = self.force_value * self.amplitudes[self.idx_amplitude] - # Create input array - F = np.zeros(self.input_size) - F[self.areas[self.idx_zone]] = self.forces[self.idx_zone] + # Update force amplitude index + self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) - # Load a force sample from Dataset - else: - sleep(0.5) - F = np.zeros(self.input_size) if self.sample_in is None else self.sample_in + # Create input array + F = zeros(self.input_size) + F[self.areas[self.idx_zone]] = self.forces[self.idx_zone] # Set training data self.F = F - self.set_training_data(input_array=F.copy(), - output_array=np.zeros(self.output_size)) + self.set_training_data(input=F.copy(), + ground_truth=zeros(self.output_size)) def apply_prediction(self, prediction): # Reshape to correspond to sparse grid - U = np.reshape(prediction, self.output_size) + U = reshape(prediction['prediction'], self.output_size) self.update_visual(U) def update_visual(self, U): @@ -176,20 +154,20 @@ def update_visual(self, U): mesh_coarse_position = self.mapping_coarse.apply(updated_position) # Update surface mesh - self.factory.update_object_dict(object_id=0, - new_data_dict={'positions': mesh_position}) + self.factory.update_mesh(object_id=0, + positions=mesh_position) # Update arrows representing force fields - self.factory.update_object_dict(object_id=1, - new_data_dict={'positions': mesh_coarse_position, - 'vectors': 0.25 * self.F / p_model.scale}) + self.factory.update_arrows(object_id=1, + positions=mesh_coarse_position, + vectors=0.25 * self.F / p_model.scale) # Update sparse grid positions - self.factory.update_object_dict(object_id=2, - new_data_dict={'positions': updated_position}) + self.factory.update_points(object_id=2, + positions=updated_position) # Send visualization data to update - self.update_visualisation(visu_dict=self.factory.updated_object_dict) + self.update_visualisation() def close(self): # Shutdown message diff --git a/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py b/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py index 2baad605..cc7893fe 100644 --- a/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py +++ b/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py @@ -28,20 +28,14 @@ class Armadillo(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topologies & mappings self.mesh = None @@ -68,6 +62,11 @@ def __init__(self, self.input_size = (p_model.nb_nodes_mesh, 3) self.output_size = (p_model.nb_nodes_grid, 3) + def init_database(self): + + # Define the fields of the Training database + self.define_training_fields(fields=[('input', np.ndarray), ('ground_truth', np.ndarray)]) + def create(self): # Load the meshes and the sparse grid @@ -121,8 +120,8 @@ async def step(self): # Launch Vedo window self.plotter.show().close() # Smooth close - self.set_training_data(input_array=np.zeros(self.input_size), - output_array=np.zeros(self.output_size)) + self.set_training_data(input=np.zeros(self.input_size), + ground_truth=np.zeros(self.output_size)) def key_press(self, evt): @@ -179,7 +178,7 @@ def mouse_move(self, evt): F[self.areas[self.selected]] = move_3D * amp # Apply output displacement - U = self.get_prediction(F).reshape(self.output_size) + U = self.get_prediction(input=F)['prediction'].reshape(self.output_size) updated_grid = self.sparse_grid.points().copy() + U updated_coarse = self.mapping_coarse.apply(updated_grid) diff --git a/examples/demos/Armadillo/FC/download.py b/examples/demos/Armadillo/FC/download.py index 983b2f07..ad285792 100644 --- a/examples/demos/Armadillo/FC/download.py +++ b/examples/demos/Armadillo/FC/download.py @@ -20,11 +20,11 @@ def __init__(self): 'session': [240], 'network': [193], 'stats': [192], - 'dataset_info': [242], - 'dataset_valid': [244, 239], - 'dataset_train': [241, 243]} + 'dataset_info': [317], + 'dataset_valid': [319], + 'dataset_train': [318]} if __name__ == '__main__': - ArmadilloDownloader().get_session('valid_data') + ArmadilloDownloader().get_session('all') diff --git a/examples/demos/Armadillo/FC/interactive.py b/examples/demos/Armadillo/FC/interactive.py index 15ea305b..440f89af 100644 --- a/examples/demos/Armadillo/FC/interactive.py +++ b/examples/demos/Armadillo/FC/interactive.py @@ -8,15 +8,15 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Torch.FC.FCConfig import FCConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import ArmadilloDownloader -ArmadilloDownloader().get_session('predict') +ArmadilloDownloader().get_session('all') from Environment.ArmadilloInteractive import Armadillo from Environment.parameters import p_model @@ -24,31 +24,28 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Armadillo, - as_tcp_ip_client=False) + environment_config = BaseEnvironmentConfig(environment_class=Armadillo) # FC config nb_hidden_layers = 3 nb_neurons = p_model.nb_nodes_mesh * 3 nb_final_neurons = p_model.nb_nodes_grid * 3 layers_dim = [nb_neurons] + [nb_neurons for _ in range(nb_hidden_layers)] + [nb_final_neurons] - net_config = FCConfig(network_name='armadillo_FC', - dim_output=3, - dim_layers=layers_dim, - biases=True) + network_config = FCConfig(dim_layers=layers_dim, + dim_output=3, + biases=True) # Dataset config - dataset_config = BaseDatasetConfig() + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='armadillo_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=1) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='armadillo_dpx', + step_nb=1) runner.execute() - runner.close() if __name__ == '__main__': diff --git a/examples/demos/Armadillo/FC/prediction.py b/examples/demos/Armadillo/FC/prediction.py index dbc339e2..65d40446 100644 --- a/examples/demos/Armadillo/FC/prediction.py +++ b/examples/demos/Armadillo/FC/prediction.py @@ -8,16 +8,16 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.FC.FCConfig import FCConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import ArmadilloDownloader -ArmadilloDownloader().get_session('valid_data') +ArmadilloDownloader().get_session('all') from Environment.Armadillo import Armadillo from Environment.parameters import p_model @@ -25,35 +25,29 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Armadillo, - visualizer=VedoVisualizer, - as_tcp_ip_client=False, - param_dict={'compute_sample': True}) + environment_config = BaseEnvironmentConfig(environment_class=Armadillo, + visualizer=VedoVisualizer) # FC config nb_hidden_layers = 3 nb_neurons = p_model.nb_nodes_mesh * 3 nb_final_neurons = p_model.nb_nodes_grid * 3 layers_dim = [nb_neurons] + [nb_neurons for _ in range(nb_hidden_layers)] + [nb_final_neurons] - net_config = FCConfig(network_name='armadillo_FC', - dim_output=3, - dim_layers=layers_dim, - biases=True) + network_config = FCConfig(dim_layers=layers_dim, + dim_output=3, + biases=True) # Dataset config - dataset_config = BaseDatasetConfig(dataset_dir='sessions/armadillo_dpx', - normalize=True, - use_mode='Validation') + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='armadillo_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=500) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='armadillo_dpx', + step_nb=500) runner.execute() - runner.close() if __name__ == '__main__': diff --git a/examples/demos/Armadillo/UNet/Environment/Armadillo.py b/examples/demos/Armadillo/UNet/Environment/Armadillo.py index 41691080..7d321de6 100644 --- a/examples/demos/Armadillo/UNet/Environment/Armadillo.py +++ b/examples/demos/Armadillo/UNet/Environment/Armadillo.py @@ -9,11 +9,10 @@ # Python related imports import os import sys - -import numpy as np +from numpy import ndarray, array, concatenate, arange, zeros, unique, reshape +from numpy.random import randint, uniform from vedo import Mesh from math import pow -from time import sleep, time # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -28,20 +27,14 @@ class Armadillo(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topology self.mesh = None @@ -56,12 +49,12 @@ def __init__(self, self.forces = [] self.areas = [] self.g_areas = [] - self.compute_sample = None # Force pattern step = 0.05 - self.amplitudes = np.concatenate((np.arange(step, 1, step), - np.arange(1, step, -step))) + self.amplitudes = concatenate((arange(step, 1, step), + arange(1, step, -step))) + self.amplitudes[0] = 0 self.idx_amplitude = 0 self.force_value = None self.idx_zone = 0 @@ -75,11 +68,10 @@ def __init__(self, Methods will be automatically called in this order to create and initialize Environment. """ - def recv_parameters(self, param_dict): + def init_database(self): - # Get the model definition parameters - self.compute_sample = param_dict['compute_sample'] if 'compute_sample' in param_dict else True - self.amplitudes[0] = 0 if self.compute_sample else 1 + # Define the fields of the Training database + self.define_training_fields(fields=[('input', ndarray), ('ground_truth', ndarray)]) def create(self): @@ -92,19 +84,19 @@ def create(self): x_reg = [p_grid.origin[0] + i * p_grid.size[0] / p_grid.nb_cells[0] for i in range(p_grid.nb_cells[0] + 1)] y_reg = [p_grid.origin[1] + i * p_grid.size[1] / p_grid.nb_cells[1] for i in range(p_grid.nb_cells[1] + 1)] z_reg = [p_grid.origin[2] + i * p_grid.size[2] / p_grid.nb_cells[2] for i in range(p_grid.nb_cells[2] + 1)] - grid_positions = np.array([[[[x, y, z] for x in x_reg] for y in y_reg] for z in z_reg]).reshape(-1, 3) + grid_positions = array([[[[x, y, z] for x in x_reg] for y in y_reg] for z in z_reg]).reshape(-1, 3) cell_corner = lambda x, y, z: len(x_reg) * (len(y_reg) * z + y) + x - grid_cells = np.array([[[[[cell_corner(ix, iy, iz), - cell_corner(ix + 1, iy, iz), - cell_corner(ix + 1, iy + 1, iz), - cell_corner(ix, iy + 1, iz), - cell_corner(ix, iy, iz + 1), - cell_corner(ix + 1, iy, iz + 1), - cell_corner(ix + 1, iy + 1, iz + 1), - cell_corner(ix, iy + 1, iz + 1)] - for ix in range(p_grid.nb_cells[0])] - for iy in range(p_grid.nb_cells[1])] - for iz in range(p_grid.nb_cells[2])]]).reshape(-1, 8) + grid_cells = array([[[[[cell_corner(ix, iy, iz), + cell_corner(ix + 1, iy, iz), + cell_corner(ix + 1, iy + 1, iz), + cell_corner(ix, iy + 1, iz), + cell_corner(ix, iy, iz + 1), + cell_corner(ix + 1, iy, iz + 1), + cell_corner(ix + 1, iy + 1, iz + 1), + cell_corner(ix, iy + 1, iz + 1)] + for ix in range(p_grid.nb_cells[0])] + for iy in range(p_grid.nb_cells[1])] + for iz in range(p_grid.nb_cells[2])]]).reshape(-1, 8) self.grid = Mesh([grid_positions, grid_cells]) # Init mappings between meshes and sparse grid @@ -112,16 +104,16 @@ def create(self): self.mapping_coarse = GridMapping(self.sparse_grid, self.mesh_coarse) # Init correspondences between sparse and regular grid - self.grid_correspondences = np.zeros(self.sparse_grid.N(), dtype=int) - x_sparse = np.unique(self.sparse_grid.points()[:, 0]) - y_sparse = np.unique(self.sparse_grid.points()[:, 1]) - z_sparse = np.unique(self.sparse_grid.points()[:, 2]) + self.grid_correspondences = zeros(self.sparse_grid.N(), dtype=int) + x_sparse = unique(self.sparse_grid.points()[:, 0]) + y_sparse = unique(self.sparse_grid.points()[:, 1]) + z_sparse = unique(self.sparse_grid.points()[:, 2]) if len(x_reg) != len(x_sparse) or len(y_reg) != len(y_sparse) or len(z_reg) != len(z_sparse): raise ValueError('Grids should have the same dimension') d = [x_sparse[1] - x_sparse[0], y_sparse[1] - y_sparse[0], z_sparse[1] - z_sparse[0]] origin = [x_sparse[0], y_sparse[0], z_sparse[0]] for i, node in enumerate(self.sparse_grid.points()): - p = np.array(node) - np.array(origin) + p = array(node) - array(origin) ix = int(round(p[0] / d[0])) iy = int(round(p[1] / d[1])) iz = int(round(p[2] / d[2])) @@ -129,7 +121,8 @@ def create(self): self.grid_correspondences[i] = idx # Define force fields - sphere = lambda x, z: sum([pow(x_i - c_i, 2) for x_i, c_i in zip(x, p_forces.centers[z])]) <= pow(p_forces.radius[z], 2) + sphere = lambda x, z: sum([pow(x_i - c_i, 2) for x_i, c_i in zip(x, p_forces.centers[z])]) <= pow( + p_forces.radius[z], 2) for zone in p_forces.zones: self.areas.append([]) self.g_areas.append([]) @@ -137,36 +130,28 @@ def create(self): if sphere(node, zone): self.areas[-1].append(i) self.g_areas[-1] += self.mapping_coarse.cells[i].tolist() - self.areas[-1] = np.array(self.areas[-1]) - self.g_areas[-1] = np.unique(self.g_areas[-1]) - self.forces.append(np.zeros((len(self.areas[-1]), 3))) + self.areas[-1] = array(self.areas[-1]) + self.g_areas[-1] = unique(self.g_areas[-1]) + self.forces.append(zeros((len(self.areas[-1]), 3))) - def send_visualization(self): + def init_visualization(self): # Mesh representing detailed Armadillo (object will have id = 0) - self.factory.add_object(object_type='Mesh', - data_dict={'positions': self.mesh.points(), - 'cells': self.mesh.cells(), - 'wireframe': True, - 'c': 'orange', - 'at': self.instance_id}) - + self.factory.add_mesh(positions=self.mesh.points(), + cells=self.mesh.cells(), + wireframe=True, + c='orange', + at=self.instance_id) # Arrows representing the force fields (object will have id = 1) - self.factory.add_object(object_type='Arrows', - data_dict={'positions': [0, 0, 0], - 'vectors': [0., 0., 0.], - 'c': 'green', - 'at': self.instance_id}) - + self.factory.add_arrows(positions=array([0., 0., 0.]), + vectors=array([0., 0., 0.]), + c='green', + at=self.instance_id) # Sparse grid (object will have id = 2) - self.factory.add_object(object_type='Points', - data_dict={'positions': self.sparse_grid.points(), - 'r': 2, - 'c': 'grey', - 'at': self.instance_id}) - - # Return the visualization data - return self.factory.objects_dict + self.factory.add_points(positions=self.sparse_grid.points(), + point_size=2, + c='grey', + at=self.instance_id) """ ENVIRONMENT BEHAVIOR @@ -176,38 +161,30 @@ def send_visualization(self): async def step(self): - # Compute a force sample - if self.compute_sample: - # Generate a new force - if self.idx_amplitude == 0: - self.idx_zone = np.random.randint(0, len(self.forces)) - zone = p_forces.zones[self.idx_zone] - self.force_value = np.random.uniform(low=-1, high=1, size=(3,)) * p_forces.amplitude[zone] - - # Update current force amplitude - self.forces[self.idx_zone] = self.force_value * self.amplitudes[self.idx_amplitude] - self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) - - # Create input array - F = np.zeros(self.data_size) - F[self.grid_correspondences[self.g_areas[self.idx_zone]]] = self.forces[self.idx_zone] - self.F = np.zeros((self.mesh_coarse.N(), 3)) - self.F[self.areas[self.idx_zone]] = self.forces[self.idx_zone] - - # Load a force sample from Dataset - else: - sleep(0.5) - F = np.zeros(self.data_size) if self.sample_in is None else self.sample_in - self.F = F[self.grid_correspondences] + # Generate a new force + if self.idx_amplitude == 0: + self.idx_zone = randint(0, len(self.forces)) + zone = p_forces.zones[self.idx_zone] + self.force_value = uniform(low=-1, high=1, size=(3,)) * p_forces.amplitude[zone] + + # Update current force amplitude + self.forces[self.idx_zone] = self.force_value * self.amplitudes[self.idx_amplitude] + self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) + + # Create input array + F = zeros(self.data_size) + F[self.grid_correspondences[self.g_areas[self.idx_zone]]] = self.forces[self.idx_zone] + self.F = zeros((self.mesh_coarse.N(), 3)) + self.F[self.areas[self.idx_zone]] = self.forces[self.idx_zone] # Set training data - self.set_training_data(input_array=F.copy(), - output_array=np.zeros(self.data_size)) + self.set_training_data(input=F.copy(), + ground_truth=zeros(self.data_size)) def apply_prediction(self, prediction): # Reshape to correspond to sparse grid - U = np.reshape(prediction, self.data_size) + U = reshape(prediction['prediction'], self.data_size) U_sparse = U[self.grid_correspondences] self.update_visual(U_sparse) @@ -219,20 +196,20 @@ def update_visual(self, U): mesh_coarse_position = self.mapping_coarse.apply(updated_position) # Update surface mesh - self.factory.update_object_dict(object_id=0, - new_data_dict={'positions': mesh_position}) + self.factory.update_mesh(object_id=0, + positions=mesh_position) # Update arrows representing force fields - self.factory.update_object_dict(object_id=1, - new_data_dict={'positions': mesh_coarse_position if self.compute_sample else updated_position, - 'vectors': 0.25 * self.F / p_model.scale}) + self.factory.update_arrows(object_id=1, + positions=mesh_coarse_position, + vectors=0.25 * self.F / p_model.scale) # Update sparse grid positions - self.factory.update_object_dict(object_id=2, - new_data_dict={'positions': updated_position}) + self.factory.update_points(object_id=2, + positions=updated_position) # Send visualization data to update - self.update_visualisation(visu_dict=self.factory.updated_object_dict) + self.update_visualisation() def close(self): # Shutdown message diff --git a/examples/demos/Armadillo/UNet/download.py b/examples/demos/Armadillo/UNet/download.py index e34eae10..8172d9f6 100644 --- a/examples/demos/Armadillo/UNet/download.py +++ b/examples/demos/Armadillo/UNet/download.py @@ -20,10 +20,9 @@ def __init__(self): 'session': [247], 'network': [230], 'stats': [224], - 'dataset_info': [253], - 'dataset_valid': [245, 257], - 'dataset_train': [252, 255, 246, 256, 258, 251, - 250, 248, 254, 249, 259, 260]} + 'dataset_info': [321], + 'dataset_valid': [320], + 'dataset_train': [323, 322]} if __name__ == '__main__': diff --git a/examples/demos/Armadillo/UNet/prediction.py b/examples/demos/Armadillo/UNet/prediction.py index 2f4019e5..9752f206 100644 --- a/examples/demos/Armadillo/UNet/prediction.py +++ b/examples/demos/Armadillo/UNet/prediction.py @@ -8,16 +8,16 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.UNet.UNetConfig import UNetConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import ArmadilloDownloader -ArmadilloDownloader().get_session('valid_data') +ArmadilloDownloader().get_session('all') from Environment.Armadillo import Armadillo from Environment.parameters import grid_resolution @@ -25,38 +25,32 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Armadillo, - visualizer=VedoVisualizer, - as_tcp_ip_client=False, - param_dict={'compute_sample': True}) + environment_config = BaseEnvironmentConfig(environment_class=Armadillo, + visualizer=VedoVisualizer) # UNet config - net_config = UNetConfig(network_name='armadillo_UNet', - input_size=grid_resolution, - nb_dims=3, - nb_input_channels=3, - nb_first_layer_channels=128, - nb_output_channels=3, - nb_steps=3, - two_sublayers=True, - border_mode='same', - skip_merge=False) + network_config = UNetConfig(input_size=grid_resolution, + nb_dims=3, + nb_input_channels=3, + nb_first_layer_channels=128, + nb_output_channels=3, + nb_steps=3, + two_sublayers=True, + border_mode='same', + skip_merge=False) # Dataset config - dataset_config = BaseDatasetConfig(dataset_dir='sessions/armadillo_dpx', - normalize=True, - use_mode='Validation') + database_config = BaseDatabaseConfig(normalize=True) + # Runner - runner = BaseRunner(session_dir='sessions', - session_name='armadillo_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=0) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='armadillo_dpx', + step_nb=500) runner.execute() - runner.close() if __name__ == '__main__': - launch_runner() diff --git a/examples/demos/Beam/FC/Environment/Beam.py b/examples/demos/Beam/FC/Environment/Beam.py index ad59fd24..e99644be 100644 --- a/examples/demos/Beam/FC/Environment/Beam.py +++ b/examples/demos/Beam/FC/Environment/Beam.py @@ -12,7 +12,6 @@ import numpy as np from vedo import Mesh -from time import sleep # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -26,30 +25,24 @@ class Beam(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topology self.mesh = None self.surface = None # Force - self.compute_sample = True step = 0.1 self.amplitudes = np.concatenate((np.arange(0, 1, step), np.arange(1, 0, -step))) + self.amplitudes[0] = 0 self.idx_amplitude = 0 self.force_value = None self.zone = None @@ -63,11 +56,10 @@ def __init__(self, Methods will be automatically called in this order to create and initialize Environment. """ - def recv_parameters(self, param_dict): + def init_database(self): - # Get the model definition parameters - self.compute_sample = param_dict['compute_sample'] if 'compute_sample' in param_dict else True - self.amplitudes[0] = 0 if self.compute_sample else 1 + # Define the fields of the Training database + self.define_training_fields(fields=[('input', np.ndarray), ('ground_truth', np.ndarray)]) def create(self): @@ -83,24 +75,19 @@ def create(self): np.argwhere(pts[:, 2] == np.max(pts[:, 2])))).reshape(-1) self.surface = np.unique(self.surface) - def send_visualization(self): + def init_visualization(self): # Mesh representing the grid (object will have id = 0) - self.factory.add_object(object_type='Mesh', - data_dict={'positions': self.mesh.points(), - 'cells': self.mesh.cells(), - 'wireframe': True, - 'c': 'orange', - 'at': self.instance_id}) - + self.factory.add_mesh(positions=self.mesh.points(), + cells=self.mesh.cells(), + wireframe=True, + c='orange', + at=self.instance_id) # Arrows representing the force fields (object will have id = 1) - self.factory.add_object(object_type='Arrows', - data_dict={'positions': [0, 0, 0], - 'vectors': [0, 0, 0], - 'c': 'green', - 'at': self.instance_id}) - - return self.factory.objects_dict + self.factory.add_arrows(positions=np.array([0., 0., 0.]), + vectors=np.array([0., 0., 0.]), + c='green', + at=self.instance_id) """ ENVIRONMENT BEHAVIOR @@ -110,65 +97,58 @@ def send_visualization(self): async def step(self): - # Compute a force sample - if self.compute_sample: - # Generate a new force - if self.idx_amplitude == 0: - # Define zone - pts = self.mesh.points().copy() - side = np.random.randint(0, 6) - x_min = np.min(pts[:, 0]) if side == 0 else np.random.randint(np.min(pts[:, 0]), np.max(pts[:, 0]) - 10) - y_min = np.min(pts[:, 1]) if side == 1 else np.random.randint(np.min(pts[:, 1]), np.max(pts[:, 1]) - 10) - z_min = np.min(pts[:, 2]) if side == 2 else np.random.randint(np.min(pts[:, 2]), np.max(pts[:, 2]) - 10) - x_max = np.max(pts[:, 0]) if side == 3 else np.random.randint(x_min + 10, np.max(pts[:, 0]) + 1) - y_max = np.max(pts[:, 1]) if side == 4 else np.random.randint(y_min + 10, np.max(pts[:, 1]) + 1) - z_max = np.max(pts[:, 2]) if side == 5 else np.random.randint(z_min + 10, np.max(pts[:, 2]) + 1) - # Find points on surface and in box - self.zone = np.where((pts[:, 0] >= x_min) & (pts[:, 0] <= x_max) & (pts[:, 1] >= y_min) & - (pts[:, 1] <= y_max) & (pts[:, 2] >= z_min) & (pts[:, 2] <= z_max)) - self.zone = np.array(list(set(self.zone[0].tolist()).intersection(set(list(self.surface))))) - # Force value - F = np.random.uniform(low=-1, high=1, size=(3,)) * np.random.randint(20, 30) - self.force_value = F - - # Update current amplitude - self.force = self.force_value * self.amplitudes[self.idx_amplitude] - self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) - - # Create input array - F = np.zeros(self.data_size) - F[self.zone] = self.force - - # Load a force sample from Dataset - else: - sleep(0.5) - F = np.zeros(self.data_size) if self.sample_in is None else self.sample_in + # Generate a new force + if self.idx_amplitude == 0: + # Define zone + pts = self.mesh.points().copy() + side = np.random.randint(0, 6) + x_min = np.min(pts[:, 0]) if side == 0 else np.random.randint(np.min(pts[:, 0]), np.max(pts[:, 0]) - 10) + y_min = np.min(pts[:, 1]) if side == 1 else np.random.randint(np.min(pts[:, 1]), np.max(pts[:, 1]) - 10) + z_min = np.min(pts[:, 2]) if side == 2 else np.random.randint(np.min(pts[:, 2]), np.max(pts[:, 2]) - 10) + x_max = np.max(pts[:, 0]) if side == 3 else np.random.randint(x_min + 10, np.max(pts[:, 0]) + 1) + y_max = np.max(pts[:, 1]) if side == 4 else np.random.randint(y_min + 10, np.max(pts[:, 1]) + 1) + z_max = np.max(pts[:, 2]) if side == 5 else np.random.randint(z_min + 10, np.max(pts[:, 2]) + 1) + # Find points on surface and in box + self.zone = np.where((pts[:, 0] >= x_min) & (pts[:, 0] <= x_max) & (pts[:, 1] >= y_min) & + (pts[:, 1] <= y_max) & (pts[:, 2] >= z_min) & (pts[:, 2] <= z_max)) + self.zone = np.array(list(set(self.zone[0].tolist()).intersection(set(list(self.surface))))) + # Force value + F = np.random.uniform(low=-1, high=1, size=(3,)) * np.random.randint(20, 30) + self.force_value = F + + # Update current amplitude + force = self.force_value * self.amplitudes[self.idx_amplitude] + self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) + + # Create input array + F = np.zeros(self.data_size) + F[self.zone] = force # Set training data self.F = F - self.set_training_data(input_array=F.copy(), - output_array=np.zeros(self.data_size)) + self.set_training_data(input=F.copy(), + ground_truth=np.zeros(self.data_size)) def apply_prediction(self, prediction): # Reshape to correspond to sparse grid - U = np.reshape(prediction, self.data_size) + U = np.reshape(prediction['prediction'], self.data_size) self.update_visual(U) def update_visual(self, U): # Update surface mesh updated_mesh = self.mesh.clone().points(self.mesh.points().copy() + U) - self.factory.update_object_dict(object_id=0, - new_data_dict={'positions': updated_mesh.points().copy()}) + self.factory.update_mesh(object_id=0, + positions=updated_mesh.points().copy()) # Update arrows representing force fields - self.factory.update_object_dict(object_id=1, - new_data_dict={'positions': updated_mesh.points().copy(), - 'vectors': 0.25 * self.F}) + self.factory.update_arrows(object_id=1, + positions=updated_mesh.points().copy(), + vectors=0.25 * self.F) # Send visualization data to update - self.update_visualisation(visu_dict=self.factory.updated_object_dict) + self.update_visualisation() def close(self): # Shutdown message diff --git a/examples/demos/Beam/FC/Environment/BeamInteractive.py b/examples/demos/Beam/FC/Environment/BeamInteractive.py index c60830d4..6950405c 100644 --- a/examples/demos/Beam/FC/Environment/BeamInteractive.py +++ b/examples/demos/Beam/FC/Environment/BeamInteractive.py @@ -26,20 +26,14 @@ class Beam(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topologies & mappings self.mesh = None @@ -62,6 +56,11 @@ def __init__(self, # Data sizes self.data_size = (p_model.nb_nodes, 3) + def init_database(self): + + # Define the fields of the Training database + self.define_training_fields(fields=[('input', np.ndarray), ('ground_truth', np.ndarray)]) + def create(self): # Load the meshes and the sparse grid @@ -138,8 +137,8 @@ async def step(self): # Launch Vedo window self.plotter.show().close() # Smooth close - self.set_training_data(input_array=np.zeros(self.data_size), - output_array=np.zeros(self.data_size)) + self.set_training_data(input=np.zeros(self.data_size), + ground_truth=np.zeros(self.data_size)) def key_press(self, evt): @@ -195,7 +194,7 @@ def mouse_move(self, evt): F[self.areas[self.selected]] = move_3D * 2 # Apply output displacement - U = self.get_prediction(F).reshape(self.data_size) + U = self.get_prediction(input=F)['prediction'].reshape(self.data_size) updated_grid = self.mesh_init + U # Update view diff --git a/examples/demos/Beam/FC/download.py b/examples/demos/Beam/FC/download.py index 530d0457..4397eb7a 100644 --- a/examples/demos/Beam/FC/download.py +++ b/examples/demos/Beam/FC/download.py @@ -20,11 +20,11 @@ def __init__(self,): 'session': [266], 'network': [219], 'stats': [216], - 'dataset_info': [262], - 'dataset_valid': [263, 264], - 'dataset_train': [261, 265]} + 'dataset_info': [324], + 'dataset_valid': [326], + 'dataset_train': [325]} if __name__ == '__main__': - BeamDownloader().get_session('valid_data') + BeamDownloader().get_session('all') diff --git a/examples/demos/Beam/FC/interactive.py b/examples/demos/Beam/FC/interactive.py index 463dbe40..1b399d86 100644 --- a/examples/demos/Beam/FC/interactive.py +++ b/examples/demos/Beam/FC/interactive.py @@ -8,15 +8,15 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Torch.FC.FCConfig import FCConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import BeamDownloader -BeamDownloader().get_session('predict') +BeamDownloader().get_session('all') from Environment.BeamInteractive import Beam from Environment.parameters import p_model @@ -24,30 +24,27 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Beam, - as_tcp_ip_client=False) + environment_config = BaseEnvironmentConfig(environment_class=Beam) # FC config nb_hidden_layers = 3 nb_neurons = p_model.nb_nodes * 3 layers_dim = [nb_neurons] + [nb_neurons for _ in range(nb_hidden_layers)] + [nb_neurons] - net_config = FCConfig(network_name='beam_FC', - dim_output=3, - dim_layers=layers_dim, - biases=True) + network_config = FCConfig(dim_layers=layers_dim, + dim_output=3, + biases=True) # Dataset config - dataset_config = BaseDatasetConfig() + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='beam_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=1) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='beam_dpx', + step_nb=1) runner.execute() - runner.close() if __name__ == '__main__': diff --git a/examples/demos/Beam/FC/prediction.py b/examples/demos/Beam/FC/prediction.py index 17af8802..c0fc1a70 100644 --- a/examples/demos/Beam/FC/prediction.py +++ b/examples/demos/Beam/FC/prediction.py @@ -8,16 +8,16 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.FC.FCConfig import FCConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import BeamDownloader -BeamDownloader().get_session('valid_data') +BeamDownloader().get_session('all') from Environment.Beam import Beam from Environment.parameters import p_model @@ -25,34 +25,28 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Beam, - visualizer=VedoVisualizer, - as_tcp_ip_client=False, - param_dict={'compute_sample': True}) + environment_config = BaseEnvironmentConfig(environment_class=Beam, + visualizer=VedoVisualizer) # FC config nb_hidden_layers = 3 nb_neurons = p_model.nb_nodes * 3 layers_dim = [nb_neurons] + [nb_neurons for _ in range(nb_hidden_layers)] + [nb_neurons] - net_config = FCConfig(network_name='beam_FC', - dim_output=3, - dim_layers=layers_dim, - biases=True) + network_config = FCConfig(dim_layers=layers_dim, + dim_output=3, + biases=True) # Dataset config - dataset_config = BaseDatasetConfig(normalize=True, - dataset_dir='sessions/beam_dpx', - use_mode='Validation') + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='beam_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=500) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='beam_dpx', + step_nb=500) runner.execute() - runner.close() if __name__ == '__main__': diff --git a/examples/demos/Beam/UNet/Environment/Beam.py b/examples/demos/Beam/UNet/Environment/Beam.py index e255ef39..4e827773 100644 --- a/examples/demos/Beam/UNet/Environment/Beam.py +++ b/examples/demos/Beam/UNet/Environment/Beam.py @@ -12,7 +12,6 @@ import numpy as np from vedo import Mesh -from time import sleep # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -26,31 +25,24 @@ class Beam(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topology self.mesh = None self.surface = None # Force - self.compute_sample = True - step = 0.1 - self.amplitudes = np.concatenate((np.arange(0, 1, step), - np.arange(1, -1, -step), - np.arange(-1, 0, step))) + step = 0.05 + self.amplitudes = np.concatenate((np.arange(step, 1, step), + np.arange(1, step, -step))) + self.amplitudes[0] = 0 self.idx_amplitude = 0 self.force_value = None self.zone = None @@ -64,11 +56,10 @@ def __init__(self, Methods will be automatically called in this order to create and initialize Environment. """ - def recv_parameters(self, param_dict): + def init_database(self): - # Get the model definition parameters - self.compute_sample = param_dict['compute_sample'] if 'compute_sample' in param_dict else True - self.amplitudes[0] = 0 if self.compute_sample else 1 + # Define the fields of the Training database + self.define_training_fields(fields=[('input', np.ndarray), ('ground_truth', np.ndarray)]) def create(self): @@ -84,24 +75,19 @@ def create(self): np.argwhere(pts[:, 2] == np.max(pts[:, 2])))).reshape(-1) self.surface = np.unique(self.surface) - def send_visualization(self): + def init_visualization(self): # Mesh representing the grid (object will have id = 0) - self.factory.add_object(object_type='Mesh', - data_dict={'positions': self.mesh.points(), - 'cells': self.mesh.cells(), - 'wireframe': True, - 'c': 'orange', - 'at': self.instance_id}) - + self.factory.add_mesh(positions=self.mesh.points(), + cells=self.mesh.cells(), + wireframe=True, + c='orange', + at=self.instance_id) # Arrows representing the force fields (object will have id = 1) - self.factory.add_object(object_type='Arrows', - data_dict={'positions': [0, 0, 0], - 'vectors': [0, 0, 0], - 'c': 'green', - 'at': self.instance_id}) - - return self.factory.objects_dict + self.factory.add_arrows(positions=np.array([0., 0., 0.]), + vectors=np.array([0., 0., 0.]), + c='green', + at=self.instance_id) """ ENVIRONMENT BEHAVIOR @@ -111,65 +97,58 @@ def send_visualization(self): async def step(self): - # Compute a force sample - if self.compute_sample: - # Generate a new force - if self.idx_amplitude == 0: - # Define zone - pts = self.mesh.points().copy() - side = np.random.randint(0, 11) - x_min = np.min(pts[:, 0]) if side in (3, 4) else np.random.randint(np.min(pts[:, 0]), np.max(pts[:, 0]) - 10) - y_min = np.min(pts[:, 1]) if side in (7, 8) else np.random.randint(np.min(pts[:, 1]), np.max(pts[:, 1]) - 10) - z_min = np.min(pts[:, 2]) if side == 0 else np.random.randint(np.min(pts[:, 2]), np.max(pts[:, 2]) - 10) - x_max = np.max(pts[:, 0]) if side in (5, 6) else np.random.randint(x_min + 10, np.max(pts[:, 0]) + 1) - y_max = np.max(pts[:, 1]) if side in (9, 10) else np.random.randint(y_min + 10, np.max(pts[:, 1]) + 1) - z_max = np.max(pts[:, 2]) if side in (1, 2, 3) else np.random.randint(z_min + 10, np.max(pts[:, 2]) + 1) - # Find points on surface and in box - self.zone = np.where((pts[:, 0] >= x_min) & (pts[:, 0] <= x_max) & (pts[:, 1] >= y_min) & - (pts[:, 1] <= y_max) & (pts[:, 2] >= z_min) & (pts[:, 2] <= z_max)) - self.zone = np.array(list(set(self.zone[0].tolist()).intersection(set(list(self.surface))))) - # Force value - F = np.random.uniform(low=-1, high=1, size=(3,)) - self.force_value = (F / np.linalg.norm(F)) * np.random.randint(10, 50) - - # Update current amplitude - self.force = self.force_value * self.amplitudes[self.idx_amplitude] - self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) - - # Create input array - F = np.zeros(self.data_size) - F[self.zone] = self.force - - # Load a force sample from Dataset - else: - sleep(0.5) - F = np.zeros(self.data_size) if self.sample_in is None else self.sample_in + # Generate a new force + if self.idx_amplitude == 0: + # Define zone + pts = self.mesh.points().copy() + side = np.random.randint(0, 11) + x_min = np.min(pts[:, 0]) if side in (3, 4) else np.random.randint(np.min(pts[:, 0]), np.max(pts[:, 0]) - 10) + y_min = np.min(pts[:, 1]) if side in (7, 8) else np.random.randint(np.min(pts[:, 1]), np.max(pts[:, 1]) - 10) + z_min = np.min(pts[:, 2]) if side == 0 else np.random.randint(np.min(pts[:, 2]), np.max(pts[:, 2]) - 10) + x_max = np.max(pts[:, 0]) if side in (5, 6) else np.random.randint(x_min + 10, np.max(pts[:, 0]) + 1) + y_max = np.max(pts[:, 1]) if side in (9, 10) else np.random.randint(y_min + 10, np.max(pts[:, 1]) + 1) + z_max = np.max(pts[:, 2]) if side in (1, 2, 3) else np.random.randint(z_min + 10, np.max(pts[:, 2]) + 1) + # Find points on surface and in box + self.zone = np.where((pts[:, 0] >= x_min) & (pts[:, 0] <= x_max) & (pts[:, 1] >= y_min) & + (pts[:, 1] <= y_max) & (pts[:, 2] >= z_min) & (pts[:, 2] <= z_max)) + self.zone = np.array(list(set(self.zone[0].tolist()).intersection(set(list(self.surface))))) + # Force value + F = np.random.uniform(low=-1, high=1, size=(3,)) + self.force_value = (F / np.linalg.norm(F)) * np.random.randint(10, 50) + + # Update current amplitude + force = self.force_value * self.amplitudes[self.idx_amplitude] + self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) + + # Create input array + F = np.zeros(self.data_size) + F[self.zone] = force # Set training data self.F = F - self.set_training_data(input_array=F.copy(), - output_array=np.zeros(self.data_size)) + self.set_training_data(input=F.copy(), + ground_truth=np.zeros(self.data_size)) def apply_prediction(self, prediction): # Reshape to correspond to sparse grid - U = np.reshape(prediction, self.data_size) + U = np.reshape(prediction['prediction'], self.data_size) self.update_visual(U) def update_visual(self, U): # Update surface mesh updated_position = self.mesh.points().copy() + U - self.factory.update_object_dict(object_id=0, - new_data_dict={'positions': updated_position}) + self.factory.update_mesh(object_id=0, + positions=updated_position) # Update arrows representing force fields - self.factory.update_object_dict(object_id=1, - new_data_dict={'positions': updated_position, - 'vectors': 0.25 * self.F}) + self.factory.update_arrows(object_id=1, + positions=updated_position, + vectors=0.25 * self.F) # Send visualization data to update - self.update_visualisation(visu_dict=self.factory.updated_object_dict) + self.update_visualisation() def close(self): # Shutdown message diff --git a/examples/demos/Beam/UNet/Environment/parameters.py b/examples/demos/Beam/UNet/Environment/parameters.py index 60485aa0..aa1a9da3 100644 --- a/examples/demos/Beam/UNet/Environment/parameters.py +++ b/examples/demos/Beam/UNet/Environment/parameters.py @@ -5,14 +5,13 @@ """ import os -from numpy import array from vedo import Mesh from collections import namedtuple # Model parameters grid = os.path.dirname(os.path.abspath(__file__)) + '/models/beam.obj' -grid_resolution = array([5, 5, 25]) +grid_resolution = [5, 5, 25] model = {'grid': grid, 'nb_nodes': Mesh(grid).N()} p_model = namedtuple('p_model', model)(**model) diff --git a/examples/demos/Beam/UNet/download.py b/examples/demos/Beam/UNet/download.py index f78be871..8ddcdab8 100644 --- a/examples/demos/Beam/UNet/download.py +++ b/examples/demos/Beam/UNet/download.py @@ -20,9 +20,9 @@ def __init__(self): 'session': [312], 'network': [315], 'stats': [314], - 'dataset_info': [309], - 'dataset_valid': [313, 316], - 'dataset_train': [310, 311]} + 'dataset_info': [329], + 'dataset_valid': [328], + 'dataset_train': [327]} if __name__ == '__main__': diff --git a/examples/demos/Beam/UNet/prediction.py b/examples/demos/Beam/UNet/prediction.py index 067ff21f..8f194c29 100644 --- a/examples/demos/Beam/UNet/prediction.py +++ b/examples/demos/Beam/UNet/prediction.py @@ -8,16 +8,16 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.UNet.UNetConfig import UNetConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import BeamDownloader -BeamDownloader().get_session('valid_data') +BeamDownloader().get_session('all') from Environment.Beam import Beam from Environment.parameters import grid_resolution @@ -25,37 +25,31 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Beam, - visualizer=VedoVisualizer, - as_tcp_ip_client=False, - param_dict={'compute_sample': True}) + environment_config = BaseEnvironmentConfig(environment_class=Beam, + visualizer=VedoVisualizer) # UNet config - net_config = UNetConfig(network_name='beam_UNet', - input_size=grid_resolution.tolist(), - nb_dims=3, - nb_input_channels=3, - nb_first_layer_channels=128, - nb_output_channels=3, - nb_steps=3, - two_sublayers=True, - border_mode='same', - skip_merge=False) + network_config = UNetConfig(input_size=grid_resolution, + nb_dims=3, + nb_input_channels=3, + nb_first_layer_channels=128, + nb_output_channels=3, + nb_steps=3, + two_sublayers=True, + border_mode='same', + skip_merge=False) # Dataset config - dataset_config = BaseDatasetConfig(normalize=True, - dataset_dir='sessions/beam_dpx', - use_mode='Validation') + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='beam_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=0) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='beam_dpx', + step_nb=500) runner.execute() - runner.close() if __name__ == '__main__': diff --git a/examples/demos/Liver/FC/Environment/Liver.py b/examples/demos/Liver/FC/Environment/Liver.py index c34f741d..d8cb9963 100644 --- a/examples/demos/Liver/FC/Environment/Liver.py +++ b/examples/demos/Liver/FC/Environment/Liver.py @@ -9,10 +9,11 @@ # Python related imports import os import sys -import numpy as np +from numpy import ndarray, array, concatenate, arange, zeros, reshape +from numpy.random import randint, uniform +from numpy.linalg import norm from vedo import Mesh from math import pow -from time import sleep # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -27,20 +28,15 @@ class Liver(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1, + nb_forces=3): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topology self.mesh = None @@ -50,15 +46,15 @@ def __init__(self, self.mapping_coarse = None # Force fields - self.forces = None - self.areas = None - self.compute_sample = True + self.nb_forces = nb_forces + self.forces = [[]] * self.nb_forces + self.areas = [[]] * self.nb_forces # Force pattern step = 0.05 - self.amplitudes = np.concatenate((np.arange(0, 1, step), - np.arange(1, 0, -step))) - self.nb_forces = p_forces.nb_simultaneous_forces + self.amplitudes = concatenate((arange(0, 1, step), + arange(1, 0, -step))) + self.amplitudes[0] = 0 self.idx_amplitude = 0 self.force_value = None self.F = None @@ -72,16 +68,10 @@ def __init__(self, Methods will be automatically called in this order to create and initialize Environment. """ - def recv_parameters(self, param_dict): - - # Get the model definition parameters - self.compute_sample = param_dict['compute_sample'] if 'compute_sample' in param_dict else True - self.amplitudes[0] = 0 if self.compute_sample else 1 + def init_database(self): - # Receive the number of forces - self.nb_forces = min(param_dict['nb_forces'], self.nb_forces) if 'nb_forces' in param_dict else self.nb_forces - self.forces = [None] * self.nb_forces - self.areas = [None] * self.nb_forces + # Define the fields of the Training database + self.define_training_fields(fields=[('input', ndarray), ('ground_truth', ndarray)]) def create(self): @@ -92,32 +82,24 @@ def create(self): self.mapping = GridMapping(self.sparse_grid, self.mesh) self.mapping_coarse = GridMapping(self.sparse_grid, self.mesh_coarse) - def send_visualization(self): + def init_visualization(self): # Mesh representing detailed Armadillo (object will have id = 0) - self.factory.add_object(object_type="Mesh", - data_dict={"positions": self.mesh.points(), - 'cells': self.mesh.cells(), - 'wireframe': True, - "c": "orange", - "at": self.instance_id}) - + self.factory.add_mesh(positions=self.mesh.points(), + cells=self.mesh.cells(), + wireframe=True, + c='orange', + at=self.instance_id) # Arrows representing the force fields (object will have id = 1) - self.factory.add_object(object_type='Arrows', - data_dict={'positions': p_model.fixed_point, - 'vectors': [0, 0, 0], - 'c': 'green', - 'at': self.instance_id}) - + self.factory.add_arrows(positions=p_model.fixed_point, + vectors=array([0., 0., 0.]), + c='green', + at=self.instance_id) # Points representing the grid (object will have id = 2) - self.factory.add_object(object_type='Points', - data_dict={'positions': self.sparse_grid.points(), - 'r': 1., - 'c': 'black', - 'at': self.instance_id}) - - # Return the visualization data - return self.factory.objects_dict + self.factory.add_points(positions=self.sparse_grid.points(), + point_size=1, + c='black', + at=self.instance_id) """ ENVIRONMENT BEHAVIOR @@ -127,63 +109,55 @@ def send_visualization(self): async def step(self): - # Compute a force sample - if self.compute_sample: - - # Generate a new force - if self.idx_amplitude == 0: - - # Define zones - selected_centers = [] - pts = self.mesh_coarse.points().copy() - for i in range(self.nb_forces): - # Pick a random sphere center, check distance with other spheres - current_point = pts[np.random.randint(0, self.mesh_coarse.N())] - distance_check = True - for p in selected_centers: - distance = np.linalg.norm(current_point - p) - if distance < p_forces.inter_distance_thresh: - distance_check = False - break - # Reset force field value and indices - self.areas[i] = [] - self.forces[i] = np.array([0, 0, 0]) - # Fill the force field - if distance_check: - # Add center - selected_centers.append(current_point) - # Find node in the sphere - sphere = lambda x, y: sum([pow(x_i - y_i, 2) for x_i, y_i in zip(x, y)]) - for j, p in enumerate(pts): - if sphere(p, current_point) <= pow(p_forces.inter_distance_thresh / 2, 2): - self.areas[i].append(j) - # If the sphere is non-empty, create a force vector - if len(self.areas[i]) > 0: - f = np.random.uniform(low=-1, high=1, size=(3,)) - self.forces[i] = (f / np.linalg.norm(f)) * p_forces.amplitude - - # Create input array - F = np.zeros(self.input_size) - for i, force in enumerate(self.forces): - F[self.areas[i]] = self.forces[i] * self.amplitudes[self.idx_amplitude] - - # Update current force amplitude - self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) - - # Load a force sample from Dataset - else: - sleep(0.5) - F = np.zeros(self.input_size) if self.sample_in is None else self.sample_in + # Generate a new force + if self.idx_amplitude == 0: + + # Define zones + selected_centers = [] + pts = self.mesh_coarse.points().copy() + for i in range(self.nb_forces): + # Pick a random sphere center, check distance with other spheres + current_point = pts[randint(0, self.mesh_coarse.N())] + distance_check = True + for p in selected_centers: + distance = norm(current_point - p) + if distance < p_forces.inter_distance_thresh: + distance_check = False + break + # Reset force field value and indices + self.areas[i] = [] + self.forces[i] = array([0, 0, 0]) + # Fill the force field + if distance_check: + # Add center + selected_centers.append(current_point) + # Find node in the sphere + sphere = lambda x, y: sum([pow(x_i - y_i, 2) for x_i, y_i in zip(x, y)]) + for j, p in enumerate(pts): + if sphere(p, current_point) <= pow(p_forces.inter_distance_thresh / 2, 2): + self.areas[i].append(j) + # If the sphere is non-empty, create a force vector + if len(self.areas[i]) > 0: + f = uniform(low=-1, high=1, size=(3,)) + self.forces[i] = (f / norm(f)) * p_forces.amplitude + + # Create input array + F = zeros(self.input_size) + for i, force in enumerate(self.forces): + F[self.areas[i]] = self.forces[i] * self.amplitudes[self.idx_amplitude] + + # Update current force amplitude + self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) # Set training data self.F = F - self.set_training_data(input_array=F.copy(), - output_array=np.zeros(self.output_size)) + self.set_training_data(input=F.copy(), + ground_truth=zeros(self.output_size)) def apply_prediction(self, prediction): # Reshape to correspond to sparse grid - U = np.reshape(prediction, self.output_size) + U = reshape(prediction['prediction'], self.output_size) self.update_visual(U) def update_visual(self, U): @@ -194,20 +168,20 @@ def update_visual(self, U): mesh_coarse_position = self.mapping_coarse.apply(updated_position) # Update surface mesh - self.factory.update_object_dict(object_id=0, - new_data_dict={'positions': mesh_position}) + self.factory.update_mesh(object_id=0, + positions=mesh_position) # Update arrows representing force fields - self.factory.update_object_dict(object_id=1, - new_data_dict={'positions': mesh_coarse_position, - 'vectors': self.F}) + self.factory.update_arrows(object_id=1, + positions=mesh_coarse_position, + vectors=self.F) # Update sparse grid positions - self.factory.update_object_dict(object_id=2, - new_data_dict={'positions': updated_position}) + self.factory.update_points(object_id=2, + positions=updated_position) # Send visualization data to update - self.update_visualisation(visu_dict=self.factory.updated_object_dict) + self.update_visualisation() def close(self): # Shutdown message diff --git a/examples/demos/Liver/FC/Environment/LiverInteractive.py b/examples/demos/Liver/FC/Environment/LiverInteractive.py index f7e432c9..4efed83b 100644 --- a/examples/demos/Liver/FC/Environment/LiverInteractive.py +++ b/examples/demos/Liver/FC/Environment/LiverInteractive.py @@ -12,7 +12,7 @@ import vtk import numpy as np -from vedo import Mesh, Sphere, Arrows, Plotter, Text2D, Box, fitSphere +from vedo import Mesh, Sphere, Arrows, Plotter, Text2D, Box # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -28,20 +28,14 @@ class Liver(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topologies & mappings self.mesh = None @@ -68,6 +62,11 @@ def __init__(self, self.input_size = (p_model.nb_nodes_mesh, 3) self.output_size = (p_model.nb_nodes_grid, 3) + def init_database(self): + + # Define the fields of the Training database + self.define_training_fields(fields=[('input', np.ndarray), ('ground_truth', np.ndarray)]) + def create(self): # Load the meshes and the sparse grid @@ -114,8 +113,8 @@ async def step(self): # Launch Vedo window self.plotter.show().close() # Smooth close - self.set_training_data(input_array=np.zeros(self.input_size), - output_array=np.zeros(self.output_size)) + self.set_training_data(input=np.zeros(self.input_size), + ground_truth=np.zeros(self.output_size)) def key_press(self, evt): @@ -171,7 +170,7 @@ def mouse_move(self, evt): F[self.areas[self.selected]] = move_3D * p_forces.amplitude # Apply output displacement - U = self.get_prediction(F).reshape(self.output_size) + U = self.get_prediction(input=F)['prediction'].reshape(self.output_size) updated_grid = self.sparse_grid.points().copy() + U updated_coarse = self.mapping_coarse.apply(updated_grid) diff --git a/examples/demos/Liver/FC/download.py b/examples/demos/Liver/FC/download.py index f17fa317..1e7d616f 100644 --- a/examples/demos/Liver/FC/download.py +++ b/examples/demos/Liver/FC/download.py @@ -20,11 +20,11 @@ def __init__(self): 'session': [281], 'network': [195], 'stats': [198], - 'dataset_info': [276], - 'dataset_valid': [279, 278], - 'dataset_train': [277, 282, 280, 283]} + 'dataset_info': [331], + 'dataset_valid': [330], + 'dataset_train': [332]} if __name__ == '__main__': - LiverDownloader().get_session('valid_data') + LiverDownloader().get_session('all') diff --git a/examples/demos/Liver/FC/interactive.py b/examples/demos/Liver/FC/interactive.py index e16fc4ba..1e2eb974 100644 --- a/examples/demos/Liver/FC/interactive.py +++ b/examples/demos/Liver/FC/interactive.py @@ -8,15 +8,15 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig from DeepPhysX.Torch.FC.FCConfig import FCConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import LiverDownloader -LiverDownloader().get_session('predict') +LiverDownloader().get_session('all') from Environment.LiverInteractive import Liver from Environment.parameters import p_model @@ -24,31 +24,28 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Liver, - as_tcp_ip_client=False) + environment_config = BaseEnvironmentConfig(environment_class=Liver) # FC config nb_hidden_layers = 3 nb_neurons = p_model.nb_nodes_mesh * 3 nb_final_neurons = p_model.nb_nodes_grid * 3 layers_dim = [nb_neurons] + [nb_neurons for _ in range(nb_hidden_layers)] + [nb_final_neurons] - net_config = FCConfig(network_name='liver_FC', - dim_output=3, - dim_layers=layers_dim, - biases=True) + network_config = FCConfig(dim_layers=layers_dim, + dim_output=3, + biases=True) # Dataset config - dataset_config = BaseDatasetConfig() + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='liver_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=1) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='liver_dpx', + step_nb=1) runner.execute() - runner.close() if __name__ == '__main__': diff --git a/examples/demos/Liver/FC/prediction.py b/examples/demos/Liver/FC/prediction.py index 9833fa58..83b3a02e 100644 --- a/examples/demos/Liver/FC/prediction.py +++ b/examples/demos/Liver/FC/prediction.py @@ -8,16 +8,16 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.FC.FCConfig import FCConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import LiverDownloader -LiverDownloader().get_session('valid_data') +LiverDownloader().get_session('all') from Environment.Liver import Liver from Environment.parameters import p_model @@ -25,36 +25,30 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Liver, - visualizer=VedoVisualizer, - as_tcp_ip_client=False, - param_dict={'compute_sample': True, - 'nb_forces': 3}) + environment_config = BaseEnvironmentConfig(environment_class=Liver, + visualizer=VedoVisualizer, + env_kwargs={'nb_forces': 3}) # FC config nb_hidden_layers = 3 nb_neurons = p_model.nb_nodes_mesh * 3 nb_final_neurons = p_model.nb_nodes_grid * 3 layers_dim = [nb_neurons] + [nb_neurons for _ in range(nb_hidden_layers)] + [nb_final_neurons] - net_config = FCConfig(network_name='liver_FC', - dim_output=3, - dim_layers=layers_dim, - biases=True) + network_config = FCConfig(dim_layers=layers_dim, + dim_output=3, + biases=True) # Dataset config - dataset_config = BaseDatasetConfig(dataset_dir='sessions/liver_dpx', - normalize=True, - use_mode='Validation') + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='liver_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=500) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='liver_dpx', + step_nb=500) runner.execute() - runner.close() if __name__ == '__main__': diff --git a/examples/demos/Liver/UNet/Environment/Liver.py b/examples/demos/Liver/UNet/Environment/Liver.py index ee2ef3e1..6e9c10f4 100644 --- a/examples/demos/Liver/UNet/Environment/Liver.py +++ b/examples/demos/Liver/UNet/Environment/Liver.py @@ -9,10 +9,11 @@ # Python related imports import os import sys -import numpy as np +from numpy import ndarray, array, concatenate, arange, zeros, unique, reshape +from numpy.random import randint, uniform +from numpy.linalg import norm from vedo import Mesh from math import pow -from time import sleep # DeepPhysX related imports from DeepPhysX.Core.Environment.BaseEnvironment import BaseEnvironment @@ -27,20 +28,15 @@ class Liver(BaseEnvironment): def __init__(self, - ip_address='localhost', - port=10000, - instance_id=0, - number_of_instances=1, as_tcp_ip_client=True, - environment_manager=None): + instance_id=1, + instance_nb=1, + nb_forces=3): BaseEnvironment.__init__(self, - ip_address=ip_address, - port=port, - instance_id=instance_id, - number_of_instances=number_of_instances, as_tcp_ip_client=as_tcp_ip_client, - environment_manager=environment_manager) + instance_id=instance_id, + instance_nb=instance_nb) # Topology self.mesh = None @@ -52,9 +48,10 @@ def __init__(self, self.grid_correspondences = None # Force fields - self.forces = None - self.areas = None - self.g_areas = None + self.nb_forces = nb_forces + self.forces = [[]] * self.nb_forces + self.areas = [[]] * self.nb_forces + self.g_areas = [[]] * self.nb_forces self.compute_sample = True self.cell_corner = None self.sparse_grid_position = None @@ -64,9 +61,9 @@ def __init__(self, # Force pattern step = 0.05 - self.amplitudes = np.concatenate((np.arange(0, 1, step), - np.arange(1, 0, -step))) - self.nb_forces = p_forces.nb_simultaneous_forces + self.amplitudes = concatenate((arange(step, 1, step), + arange(1, step, -step))) + self.amplitudes[0] = 0 self.idx_amplitude = 0 self.force_value = None self.F = None @@ -80,17 +77,10 @@ def __init__(self, Methods will be automatically called in this order to create and initialize Environment. """ - def recv_parameters(self, param_dict): - - # Get the model definition parameters - self.compute_sample = param_dict['compute_sample'] if 'compute_sample' in param_dict else True - self.amplitudes[0] = 0 if self.compute_sample else 1 + def init_database(self): - # Receive the number of forces - self.nb_forces = min(param_dict['nb_forces'], self.nb_forces) if 'nb_forces' in param_dict else self.nb_forces - self.forces = [None] * self.nb_forces - self.areas = [None] * self.nb_forces - self.g_areas = [None] * self.nb_forces + # Define the fields of the Training database + self.define_training_fields(fields=[('input', ndarray), ('ground_truth', ndarray)]) def create(self): @@ -103,19 +93,19 @@ def create(self): x_reg = [p_grid.origin[0] + i * p_grid.size[0] / p_grid.nb_cells[0] for i in range(p_grid.nb_cells[0] + 1)] y_reg = [p_grid.origin[1] + i * p_grid.size[1] / p_grid.nb_cells[1] for i in range(p_grid.nb_cells[1] + 1)] z_reg = [p_grid.origin[2] + i * p_grid.size[2] / p_grid.nb_cells[2] for i in range(p_grid.nb_cells[2] + 1)] - grid_positions = np.array([[[[x, y, z] for x in x_reg] for y in y_reg] for z in z_reg]).reshape(-1, 3) + grid_positions = array([[[[x, y, z] for x in x_reg] for y in y_reg] for z in z_reg]).reshape(-1, 3) cell_corner = lambda x, y, z: len(x_reg) * (len(y_reg) * z + y) + x - grid_cells = np.array([[[[[cell_corner(ix, iy, iz), - cell_corner(ix + 1, iy, iz), - cell_corner(ix + 1, iy + 1, iz), - cell_corner(ix, iy + 1, iz), - cell_corner(ix, iy, iz + 1), - cell_corner(ix + 1, iy, iz + 1), - cell_corner(ix + 1, iy + 1, iz + 1), - cell_corner(ix, iy + 1, iz + 1)] - for ix in range(p_grid.nb_cells[0])] - for iy in range(p_grid.nb_cells[1])] - for iz in range(p_grid.nb_cells[2])]]).reshape(-1, 8) + grid_cells = array([[[[[cell_corner(ix, iy, iz), + cell_corner(ix + 1, iy, iz), + cell_corner(ix + 1, iy + 1, iz), + cell_corner(ix, iy + 1, iz), + cell_corner(ix, iy, iz + 1), + cell_corner(ix + 1, iy, iz + 1), + cell_corner(ix + 1, iy + 1, iz + 1), + cell_corner(ix, iy + 1, iz + 1)] + for ix in range(p_grid.nb_cells[0])] + for iy in range(p_grid.nb_cells[1])] + for iz in range(p_grid.nb_cells[2])]]).reshape(-1, 8) self.regular_grid = Mesh([grid_positions, grid_cells]) # Init mappings between meshes and sparse grid @@ -123,16 +113,16 @@ def create(self): self.mapping_coarse = GridMapping(self.sparse_grid, self.mesh_coarse) # Init correspondences between sparse and regular grid - self.grid_correspondences = np.zeros(self.sparse_grid.N(), dtype=int) - x_sparse = np.unique(self.sparse_grid.points()[:, 0]) - y_sparse = np.unique(self.sparse_grid.points()[:, 1]) - z_sparse = np.unique(self.sparse_grid.points()[:, 2]) + self.grid_correspondences = zeros(self.sparse_grid.N(), dtype=int) + x_sparse = unique(self.sparse_grid.points()[:, 0]) + y_sparse = unique(self.sparse_grid.points()[:, 1]) + z_sparse = unique(self.sparse_grid.points()[:, 2]) if len(x_reg) != len(x_sparse) or len(y_reg) != len(y_sparse) or len(z_reg) != len(z_sparse): raise ValueError('Grids should have the same dimension') d = [x_sparse[1] - x_sparse[0], y_sparse[1] - y_sparse[0], z_sparse[1] - z_sparse[0]] origin = [x_sparse[0], y_sparse[0], z_sparse[0]] for i, node in enumerate(self.sparse_grid.points()): - p = np.array(node) - np.array(origin) + p = array(node) - array(origin) ix = int(round(p[0] / d[0])) iy = int(round(p[1] / d[1])) iz = int(round(p[2] / d[2])) @@ -140,40 +130,32 @@ def create(self): self.grid_correspondences[i] = idx # Init grid force field stuff - self.x_sparse = np.unique(self.sparse_grid.points()[:, 0]) - self.y_sparse = np.unique(self.sparse_grid.points()[:, 1]) - self.z_sparse = np.unique(self.sparse_grid.points()[:, 2]) + self.x_sparse = unique(self.sparse_grid.points()[:, 0]) + self.y_sparse = unique(self.sparse_grid.points()[:, 1]) + self.z_sparse = unique(self.sparse_grid.points()[:, 2]) self.sparse_grid_position = self.sparse_grid.points().tolist() self.cell_corner = lambda x, y, z: self.sparse_grid_position.index([self.x_sparse[x], self.y_sparse[y], self.z_sparse[z]]) - def send_visualization(self): + def init_visualization(self): # Mesh representing detailed Armadillo (object will have id = 0) - self.factory.add_object(object_type="Mesh", - data_dict={"positions": self.mesh.points(), - 'cells': self.mesh.cells(), - 'wireframe': True, - "c": "orange", - "at": self.instance_id}) - + self.factory.add_mesh(positions=self.mesh.points(), + cells=self.mesh.cells(), + wireframe=True, + c='orange', + at=self.instance_id) # Arrows representing the force fields (object will have id = 1) - self.factory.add_object(object_type='Arrows', - data_dict={'positions': p_model.fixed_point, - 'vectors': [0, 0, 0], - 'c': 'green', - 'at': self.instance_id}) - + self.factory.add_arrows(positions=p_model.fixed_point, + vectors=array([0., 0., 0.]), + c='green', + at=self.instance_id) # Sparse grid (object will have id = 2) - self.factory.add_object(object_type='Points', - data_dict={'positions': self.sparse_grid.points(), - 'r': 2, - 'c': 'grey', - 'at': self.instance_id}) - - # Return the visualization data - return self.factory.objects_dict + self.factory.add_points(positions=self.sparse_grid.points(), + point_size=2, + c='grey', + at=self.instance_id) """ ENVIRONMENT BEHAVIOR @@ -183,70 +165,61 @@ def send_visualization(self): async def step(self): - # Compute a force sample - if self.compute_sample: - - # Generate a new force - if self.idx_amplitude == 0: - - # Define zones - selected_centers = [] - pts = self.mesh_coarse.points().copy() - for i in range(self.nb_forces): - # Pick a random sphere center, check distance with other spheres - current_point = pts[np.random.randint(0, self.mesh_coarse.N())] - distance_check = True - for p in selected_centers: - distance = np.linalg.norm(current_point - p) - if distance < p_forces.inter_distance_thresh: - distance_check = False - break - # Reset force field value and indices - self.areas[i] = [] - self.g_areas[i] = [] - self.forces[i] = np.array([0, 0, 0]) - # Fill the force field - if distance_check: - # Add center - selected_centers.append(current_point) - # Find node in the sphere - sphere = lambda x, c: sum([pow(x_i - c_i, 2) for x_i, c_i in zip(x, c)]) <= \ - pow(p_forces.inter_distance_thresh / 2, 2) - for j, p in enumerate(pts): - if sphere(p, current_point): - self.areas[i].append(j) - self.g_areas[i] += self.mapping_coarse.cells[j].tolist() - # If the sphere is non-empty, create a force vector - if len(self.areas[i]) > 0: - f = np.random.uniform(low=-1, high=1, size=(3,)) - self.forces[i] = (f / np.linalg.norm(f)) * p_forces.amplitude - self.areas[i] = np.array(self.areas[i]) - self.g_areas[i] = np.unique(self.g_areas[i]) - - # Create input array - F = np.zeros(self.data_size) - self.F = np.zeros((self.mesh_coarse.N(), 3)) - for i, force in enumerate(self.forces): - F[self.grid_correspondences[self.g_areas[i]]] = force * self.amplitudes[self.idx_amplitude] - self.F[self.areas[i]] = force * self.amplitudes[self.idx_amplitude] - - # Update current force amplitude - self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) - - # Load a force sample from Dataset - else: - sleep(0.5) - F = np.zeros(self.data_size) if self.sample_in is None else self.sample_in - self.F = F[self.grid_correspondences] + # Generate a new force + if self.idx_amplitude == 0: + + # Define zones + selected_centers = [] + pts = self.mesh_coarse.points().copy() + for i in range(self.nb_forces): + # Pick a random sphere center, check distance with other spheres + current_point = pts[randint(0, self.mesh_coarse.N())] + distance_check = True + for p in selected_centers: + distance = norm(current_point - p) + if distance < p_forces.inter_distance_thresh: + distance_check = False + break + # Reset force field value and indices + self.areas[i] = [] + self.g_areas[i] = [] + self.forces[i] = array([0, 0, 0]) + # Fill the force field + if distance_check: + # Add center + selected_centers.append(current_point) + # Find node in the sphere + sphere = lambda x, c: sum([pow(x_i - c_i, 2) for x_i, c_i in zip(x, c)]) <= \ + pow(p_forces.inter_distance_thresh / 2, 2) + for j, p in enumerate(pts): + if sphere(p, current_point): + self.areas[i].append(j) + self.g_areas[i] += self.mapping_coarse.cells[j].tolist() + # If the sphere is non-empty, create a force vector + if len(self.areas[i]) > 0: + f = uniform(low=-1, high=1, size=(3,)) + self.forces[i] = (f / norm(f)) * p_forces.amplitude + self.areas[i] = array(self.areas[i]) + self.g_areas[i] = unique(self.g_areas[i]) + + # Create input array + F = zeros(self.data_size) + self.F = zeros((self.mesh_coarse.N(), 3)) + for i, force in enumerate(self.forces): + F[self.grid_correspondences[self.g_areas[i]]] = force * self.amplitudes[self.idx_amplitude] + self.F[self.areas[i]] = force * self.amplitudes[self.idx_amplitude] + + # Update current force amplitude + self.idx_amplitude = (self.idx_amplitude + 1) % len(self.amplitudes) # Set training data - self.set_training_data(input_array=F.copy(), - output_array=np.zeros(self.data_size)) + self.set_training_data(input=F.copy(), + ground_truth=zeros(self.data_size)) def apply_prediction(self, prediction): # Reshape to correspond to sparse grid - U = np.reshape(prediction, self.data_size) + U = reshape(prediction['prediction'], self.data_size) U_sparse = U[self.grid_correspondences] self.update_visual(U_sparse) @@ -258,20 +231,20 @@ def update_visual(self, U): mesh_coarse_position = self.mapping_coarse.apply(updated_position) # Update surface mesh - self.factory.update_object_dict(object_id=0, - new_data_dict={'positions': mesh_position}) + self.factory.update_mesh(object_id=0, + positions=mesh_position) # Update arrows representing force fields - self.factory.update_object_dict(object_id=1, - new_data_dict={'positions': mesh_coarse_position if self.compute_sample else updated_position, - 'vectors': self.F}) + self.factory.update_arrows(object_id=1, + positions=mesh_coarse_position, + vectors=self.F) # Update sparse grid positions - self.factory.update_object_dict(object_id=2, - new_data_dict={'positions': updated_position}) + self.factory.update_points(object_id=2, + positions=updated_position) # Send visualization data to update - self.update_visualisation(visu_dict=self.factory.updated_object_dict) + self.update_visualisation() def close(self): # Shutdown message diff --git a/examples/demos/Liver/UNet/download.py b/examples/demos/Liver/UNet/download.py index 490d6aed..5c354326 100644 --- a/examples/demos/Liver/UNet/download.py +++ b/examples/demos/Liver/UNet/download.py @@ -20,12 +20,11 @@ def __init__(self): 'session': [290], 'network': [302], 'stats': [285], - 'dataset_info': [291], - 'dataset_valid': [292, 296], - 'dataset_train': [295, 286, 297, 299, 294, 287, - 289, 300, 288, 301, 298, 293]} + 'dataset_info': [333], + 'dataset_valid': [336], + 'dataset_train': [335, 334]} if __name__ == '__main__': - LiverDownloader().get_session('valid_data') + LiverDownloader().get_session('all') diff --git a/examples/demos/Liver/UNet/prediction.py b/examples/demos/Liver/UNet/prediction.py index 47770dd2..9d937001 100644 --- a/examples/demos/Liver/UNet/prediction.py +++ b/examples/demos/Liver/UNet/prediction.py @@ -8,16 +8,16 @@ import sys # DeepPhysX related imports -from DeepPhysX.Core.Dataset.BaseDatasetConfig import BaseDatasetConfig +from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction from DeepPhysX.Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig -from DeepPhysX.Core.Visualizer.VedoVisualizer import VedoVisualizer -from DeepPhysX.Core.Pipelines.BaseRunner import BaseRunner +from DeepPhysX.Core.Database.BaseDatabaseConfig import BaseDatabaseConfig +from DeepPhysX.Core.Visualization.VedoVisualizer import VedoVisualizer from DeepPhysX.Torch.UNet.UNetConfig import UNetConfig # Session related imports sys.path.append(os.path.dirname(os.path.abspath(__file__))) from download import LiverDownloader -LiverDownloader().get_session('valid_data') +LiverDownloader().get_session('all') from Environment.Liver import Liver from Environment.parameters import grid_resolution @@ -25,39 +25,32 @@ def launch_runner(): # Environment config - env_config = BaseEnvironmentConfig(environment_class=Liver, - visualizer=VedoVisualizer, - as_tcp_ip_client=False, - param_dict={'compute_sample': True, - 'nb_forces': 3}) + environment_config = BaseEnvironmentConfig(environment_class=Liver, + visualizer=VedoVisualizer, + env_kwargs={'nb_forces': 3}) # UNet config - net_config = UNetConfig(network_name='liver_UNet', - save_each_epoch=True, - input_size=grid_resolution, - nb_dims=3, - nb_input_channels=3, - nb_first_layer_channels=128, - nb_output_channels=3, - nb_steps=3, - two_sublayers=True, - border_mode='same', - skip_merge=False) + network_config = UNetConfig(input_size=grid_resolution, + nb_dims=3, + nb_input_channels=3, + nb_first_layer_channels=128, + nb_output_channels=3, + nb_steps=3, + two_sublayers=True, + border_mode='same', + skip_merge=False) # Dataset config - dataset_config = BaseDatasetConfig(dataset_dir='sessions/liver_dpx', - normalize=True, - use_mode='Validation') + database_config = BaseDatabaseConfig(normalize=True) # Runner - runner = BaseRunner(session_dir='sessions', - session_name='liver_dpx', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=500) + runner = BasePrediction(network_config=network_config, + database_config=database_config, + environment_config=environment_config, + session_dir='sessions', + session_name='liver_dpx', + step_nb=500) runner.execute() - runner.close() if __name__ == '__main__': From f47c6ecb440e7e3e5bd36670aa7a166c780becc2 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 10 Nov 2022 17:25:31 +0100 Subject: [PATCH 45/61] Update Vedo version. --- .../demos/Armadillo/FC/Environment/ArmadilloInteractive.py | 3 ++- examples/demos/Beam/FC/Environment/BeamInteractive.py | 5 +++-- examples/demos/Liver/FC/Environment/LiverInteractive.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py b/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py index cc7893fe..ba9e071c 100644 --- a/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py +++ b/examples/demos/Armadillo/FC/Environment/ArmadilloInteractive.py @@ -100,6 +100,7 @@ def create(self): # Create plotter self.plotter = Plotter(title='Interactive Armadillo', N=1, interactive=True, offscreen=False, bg2='lightgray') + self.plotter.render() self.plotter.add(*self.spheres) self.plotter.add(self.mesh) self.plotter.add(Plane(pos=plane_origin, normal=[0, 1, 0], s=(10 * p_model.scale, 10 * p_model.scale), @@ -169,7 +170,7 @@ def mouse_move(self, evt): if not self.interactive_window and self.selected is not None: # Compute input force vector - mouse_3D = self.plotter.computeWorldPosition(evt.picked2d) + mouse_3D = self.plotter.compute_world_position(evt.picked2d) move_3D = (mouse_3D - self.spheres_init[self.selected]) / self.mouse_factor if np.linalg.norm(move_3D) > 2: move_3D = 2 * move_3D / np.linalg.norm(move_3D) diff --git a/examples/demos/Beam/FC/Environment/BeamInteractive.py b/examples/demos/Beam/FC/Environment/BeamInteractive.py index 6950405c..3c98d7d9 100644 --- a/examples/demos/Beam/FC/Environment/BeamInteractive.py +++ b/examples/demos/Beam/FC/Environment/BeamInteractive.py @@ -114,10 +114,11 @@ def create(self): fixed = np.where(mesh_x <= np.min(mesh_x) + 0.05 * (np.max(mesh_x) - np.min(mesh_x))) plane_origin = [np.min(mesh_x), np.mean(self.mesh.points()[:, 1][fixed]), - np.mean(self.mesh.points()[:, 2][fixed])] + np.mean(self.mesh.points()[:, 2][fixed])+10] # Create plotter self.plotter = Plotter(title='Interactive Beam', N=1, interactive=True, offscreen=False, bg2='lightgray') + self.plotter.render() self.plotter.add(*self.spheres) self.plotter.add(self.mesh) self.plotter.add(Plane(pos=plane_origin, normal=[1, 0, 0], s=(20, 20), c='darkred', alpha=0.2)) @@ -186,7 +187,7 @@ def mouse_move(self, evt): if not self.interactive_window and self.selected is not None: # Compute input force vector - mouse_3D = self.plotter.computeWorldPosition(evt.picked2d) + mouse_3D = self.plotter.compute_world_position(evt.picked2d) move_3D = (mouse_3D - self.mesh_init[self.spheres_init[self.selected]]) / self.mouse_factor if np.linalg.norm(move_3D) > 10: move_3D = 10 * move_3D / np.linalg.norm(move_3D) diff --git a/examples/demos/Liver/FC/Environment/LiverInteractive.py b/examples/demos/Liver/FC/Environment/LiverInteractive.py index 4efed83b..81fed433 100644 --- a/examples/demos/Liver/FC/Environment/LiverInteractive.py +++ b/examples/demos/Liver/FC/Environment/LiverInteractive.py @@ -94,6 +94,7 @@ def create(self): # Create plotter self.plotter = Plotter(title='Interactive Armadillo', N=1, interactive=True, offscreen=False, bg2='lightgray') + self.plotter.render() self.plotter.add(*self.spheres) self.plotter.add(box) self.plotter.add(self.mesh) @@ -162,7 +163,7 @@ def mouse_move(self, evt): if not self.interactive_window and self.selected is not None: # Compute input force vector - mouse_3D = self.plotter.computeWorldPosition(evt.picked2d) + mouse_3D = self.plotter.compute_world_position(evt.picked2d) move_3D = (mouse_3D - self.mesh_coarse.points()[self.spheres_init[self.selected]]) / self.mouse_factor if np.linalg.norm(move_3D) > 1.5: move_3D = 1.5 * move_3D / np.linalg.norm(move_3D) From 927b1858ef57031c6fd467572bf61de7ecb0e21f Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 14 Nov 2022 17:55:12 +0100 Subject: [PATCH 46/61] Update doc (1/2). --- README.md | 2 + docs/source/_static/image/about_tree.png | Bin 0 -> 85571 bytes .../_static/image/about_working_session.png | Bin 102935 -> 0 bytes docs/source/_static/image/database.png | Bin 0 -> 92351 bytes docs/source/_static/image/dataset.png | Bin 136354 -> 0 bytes .../_static/image/environment_tcp_ip.png | Bin 0 -> 224014 bytes .../_static/image/environment_tcpip.png | Bin 235474 -> 0 bytes docs/source/_static/image/logo.png | Bin 0 -> 552105 bytes .../_static/image/overview_architecture.png | Bin 136782 -> 0 bytes .../_static/image/overview_components.png | Bin 0 -> 123487 bytes docs/source/component/dataset.rst | 4 +- docs/source/component/environment.rst | 2 +- docs/source/conf.py | 25 ++++++------ docs/source/index.rst | 2 + docs/source/presentation/about.rst | 2 +- docs/source/presentation/install.rst | 36 +++++++++--------- docs/source/presentation/overview.rst | 24 ++++-------- 17 files changed, 47 insertions(+), 50 deletions(-) create mode 100644 docs/source/_static/image/about_tree.png delete mode 100644 docs/source/_static/image/about_working_session.png create mode 100644 docs/source/_static/image/database.png delete mode 100644 docs/source/_static/image/dataset.png create mode 100644 docs/source/_static/image/environment_tcp_ip.png delete mode 100644 docs/source/_static/image/environment_tcpip.png create mode 100644 docs/source/_static/image/logo.png delete mode 100644 docs/source/_static/image/overview_architecture.png create mode 100644 docs/source/_static/image/overview_components.png diff --git a/README.md b/README.md index 196f9ccd..3af98e74 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## DeepPhysX +![logo](docs/source/_static/image/logo.png) + ### Interfacing AI with simulation The **DeepPhysX** project provides Python packages allowing users to easily interface their **numerical simulations** diff --git a/docs/source/_static/image/about_tree.png b/docs/source/_static/image/about_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..47caba3ed1a41ee45f3ece83f5f8849c77639a76 GIT binary patch literal 85571 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
Vn6GSc(; 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
BKMpoUvTal(33k!sgTuJA7#kng6 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 literal 0 HcmV?d00001 diff --git a/docs/source/_static/image/about_working_session.png b/docs/source/_static/image/about_working_session.png deleted file mode 100644 index ec46dffa21b7630e4f24274d72b5321196852a06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102935 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 Zynvmy^k&h zMO@cRE8C`>u+OZtCU|&-eX^k}eEC)j9P>Xq%_X&MTTaoAl}V!52FrjS(km*nLuJlg0m;ASOOejeL9J{UTpxqpKDi`o$Qtb z*KaYNIFgiCYMb3O0C$Zt41`9YBPE0oXgPP7nwnl6jfS-KkoLR9xvVyK~h$E4f1gYT(FAo4B1%ef`y`qEbhP%&OB$wxD1*-RMlt|CyiHbE! zuwz+(e{og{_$QwK@%?*9x}|22`&{BPv)@Oh<1?*tBaIXFE*SG&d?PNP)Ns)l01q)> zMTD!!hWevJRROPCakw)$XL9yhr-vcBj-i?w}Cps0pkBnry6|#tD!bJ9zoX z)=v0zT~Y3rTIX;eq@8%j3)moc=1&um#d?2)(ee?99|ho4%R-ROPoIX%mOIA4ovNeX z52P=kSbzFMA>WRk3KvwM$Gv8tNR$SWyMfASEW@PY_ZA(Q*8nm&R%}0#`wi2iSQ3pD zM#vv)PtX-&Z4n}kxK?lsssds}3yE*2MgVi2#|(kWpfhOa@|^%h;Mf(s&r8^fwDoaL zuV5o*5Jkh;7rJA;giRE|kFe7|0-8@KIyTdJN{}_>!X63DamlJ^FMr}|)P7NvS$~Nk zXbP-oW7V(R;r$O60Bkl8A)Y(W!tm%3JkHUosy^FBvStaX2HFpjY=F}^I&K0Ec2{sz zTD`p{z8M%T3{|cG(EGA^4iBB!tvw@9z;`^nFv#O5y7in4ahz;+YqBTCB-lgOGc&+J zP>q^c`}q$^5-qa}eoAa<9(w;dN)1mn_MY3i9h(F|YfbEYpvjRGZ3!i%L)(AC!mtor z3_1!6o|r<{0A?DQB3Tch$QyNT3>2D2t+{m?3H1E*sRX=s9_t8w+3Bqns4v@iOcwJx z-S@EB04JF5yFgHMNr^UDt$-!&Y<0EKKebr#koTmX?YjO=L1=gpTncam%pBbJ(~A|; zaq*o&`O?v;#vj~aE7zDMmDO|=hHQ8@;KV-6JV7kLKEUmn6j}Kfx8Ty=0WgTFRtb9a zJc*dhcEIHMixyRz(AyTjq<(7@G zds`u$DLmterAf-`-lVP3hfz(>n}EV80x%wl;J_i*n=8r_Bo|q+^5a8KoR~9TmI0k3 zb`;1yjYEx!#^bdh#}(l%yilhW19`&S7fKjMYTu@hCT;^!V4Xj%%fBhZr)5O#b2CMkxK zC0(%yfk7XJY?lM|Ax5^aY9$&+5_684>vH=j9KPr%5@AKm$obeh&=LO&asjKa|FoE4e>`7LPY)(P*Dyd(x#0F}?U5{I&%9&8_K;~3I_lQ? zrub^UtWs(}p$| znBpTso@}G=bO<3Q3ON_><;7Nvf}9kZ&XzmAbOO4C5v%})1feG;O;p0RpdlPzgb;;ga0fsr&kY-3 zByer|Vtby4Z5=KIFf}4Z_~vlYRF0T)HGa1X-3ud5Vu^?QLoeK_9K-IO>@2;u=!cOcKD{D zmCm{Bqs7+Vlpq=yKq7^6lYa)*tmt$6d#RCSiXN=JPz`i*8#IAy;r-z~!9+PfvY;R# zScJLrNsoavisPk@on1l(9^5H< zILy1j&Q7k^j-4R-IAU+rv4x1`Da@Ud(|RW1pm7N}O)TsIJJzDT_nuVrnI73m6++)_ z)YrL3iWi&#dfJ?A8i>JYd+1enwvQH0rN=ypQuQ`&eq)R6*ww*vt?3K85DVHkCFxsH zbbj>VlAWC85GWU4Lox?IB@#je$Om94dV2BIs}uLhNq)Kqs2N0zHyEJZ-w09>=Ix1f z>nduDA*Llklc<#7hPRZ=c_Jeh0Xn*>!`J`QKm@k~Youh~38?Z2!fEg^GDKIJm~eu& zLcHaHcHzfbQHTttySO_5>tir27y<|hkXQG)*J4>$LXw`@>WTRZnN+9PTK(z8)D996 ze_-b%kAh4sXsrQs?c=LK$%6C(7-t(?gm6@B&}3ld7+&pmJ}XwW&wRYMZtrws>u5gg zZ2NxSmEw=z>ee5!KWZyU-ys_?z`v1;YSXbFr}zxIZNh9j@Dy=nn=X#52*O_0fs&lN zAPaV%R3Xlp?sQ?3v&SwkA3a+A>0YO&HyV`@Ws5RxvFWL}Go+kZ4G}pgqkvms;!g5*h^dXzJw?SjDmCffyoycY24R zTqK70chl|swOPKRZ6pIMjKJ{&bqs*kKI0GotsPanFr^O1u=iMBNO4H2!$$p#*|V6f z^tu+{U}aX>k5VS)bI&l&cS{5flOT0EbyJDrZGs+ue?)qp;$T_M#h$)+I?;A)b=x-E zI6!V?glH4s_7U@Os1V5DnfNO9dM}n>Qz=#&K}lUYlS*>Ubg=Ctl6yyXSqz$Mk+bY07<3ia||@Wcc<2FUkinKFDhlloh&=rwY}! zhlyFbNYeUtZO%95G?@>qJ9NPvzz^D#1yB}4YGCeioZM2lNXT{N-PF%7&&?tNeF1*Z zBVw}v_VXV+SZ7+5>7u;Qx12kxBP)Q1l;myJB~wkg><)04>k*2J&NDqqMEjq4bPJq(Q|>C5&*nhf+~zv<{C z>KYALvyMPPP9ny;8#C4rY=+98VyA{1tS3;;xvJ);USVqURZMiZNKr<}25f{Ly=w1* ziW<;&DjEsmf`WD-q5gb`%ot0331)YkaF#!i_|l*YT4|2kYmfS)>av8co&;I|t_g+m zzqD~`(1saNWC8ObYOlonMB*Y}}uxRfQL=9BwnYMXFauyRB$CQ>#GCpN(5lVk^B zB!tJb-z`VaPZ~bALRW*uHQ4!p?#8Uf-i8r|Ac{0!X7OF~PoHNHsnPkS%gz$E5FFrb7U?;lsxDx z=o8pPnQ&s3!T9Eq%7s6e_r##>!7>x}g!8x+7n6k=JvF~do3|gmbG{m{-Rx4aj?*S5 z3&*7y2NN~#I8@*>qYqnK0#Kmm%t#s9ryXlsNTchhC#jfiMF9*eOkl$WiWCa}=>bPy zE$xz_AGIjJ#UW=0`Jb>!Fit65#zG?3t`@d~o?lKr6rdan9MKt>X(+i+RakmB1HV)H znRx5~3h|2v5(D3~7os%P$C=dkfv(+?Q+?WG5RS0|j!oo3q|4?bUGkK*Yg4H?E#WE; z+XGZ57%{<>3@GXY0}v{}EDOMN1EeScmV17fAy=LXsI&QfcNt~4uTns#(j_~%desRt8vimB`cTld4x?1_Dm?=bn;lm4N5l zohAYqHAMlv=E^BY0Ny{%)@Y|3Ew9eKMaR%4%n z8Pzlc5FjGW6q-H){wL4QN{5Djc9?E@J?Zikw;%qWn86Fwx233>P!y_LXJp>B5t{WP zQ7`?z;O=eigC%4j9Ah&xgt{=dXn^ZGoO(zFK>X-Y1h=6XFG8h%e_&esTo1`$?M8+n z5T;b?nmFSCKQxr23wrhAwg~{W!BLe^ZGV#61_l^$q9A^vum-0Jt-@7)j(Y)HC`XN{ zkGqYph2ZtU;S0?G8b(Oy3q`+z?-GekAeQ>1ApyOzu(AcT0+PN$mms7oWvAHu;<(ya ze*+&7(o%e^2)w7eS`d-A0j0IeUiFEYJbRBI06_T&43mR2s@A4*kyTsYVDj*hTyVOU zAYEGN2d6f(Hh!HpHgUgkJ8ots&ci0$l+%f`ueenY(~^#g;o9(Pfe0m@^6q_1jD-z5 zJ>vs2t=h~P7`K<=fC@pCV1bSvL7V%o7XL*{6^`SGN{~HvXfAh&j*sUHx`8U}MWyC3 zkb2VQ%-c|k)4)>QS;yRXoD5D8=irC_2DmSdE8CX?YIA?NP`9oO(}zMLIpgY8qTsWH z#g^qR;vVDhX@4yn06W%=r#_I11nq?~Y-v9aDG~LsMJKAlnTNLI6Q!Edp+LqMn#^?0 z!AK8$Ueq>1D4xrJvG@b{;zznoVp?Z37*^)WKYG5jsaz^Z={v7A1D|o? zGprBCW-uW&F`I#BK$jqTF7gU^@E9mK+w3&`xevqw66FhNCk+SB{Qrj?oL8gi(W8W= z)CsDX?U&3frTJ-s+46sCbrKCH0~wX?@0(t)uMZ^)F)+h1s%)0vyfsQyL;d$MI`ukR zu;;ij04p{UJRrlbD(+Y9-tV>zH?uWHUB>PGk6+4W&2E^{WSd&jZPe!$V_XADMddF~ z=YXW2XF)-bj(&4tncUDU!iO1{nLuv<+UL^ zpnRwgI!OK_!NG8KQJ=NLc2OG}80a22wTSCA4reXt8||<{PIeBHGkZ;pX-T`E3hVxJ zgU!n@@Obt6i-~vGas^99V&jjKnRJ9$@GqQ2K3OZG=Y#%9O^P>k^XI~7h(S1(Z*nNX z6G_7#(Ry;g0!Kdz-csapA&=JxLkZJqGxXm}0N~j@Jv`_RT!2_tlAK!5Grpc_s-HqXtV+#nrjF8gMm~~)iEYRU#utNY;02$1)oa{ z(IA7y0Ink;5;J7iVmtaEO`h9KdSFojfMwEB1-~TCr~0`z?~c-~eXkh( z({tStHn268(u@PZrIcyWlW}32(VN(6ijien70XQd@et;hy4?Cv!|c`o*3zSMlD7bD zru}Rhd*(T^RbRht-G&VZIG+7*IsY~23#ul}MnKH^^H=y+i~LuUy-alexOY_BX>J4C zOe_;%6(B?qcTsf5N?|=MdD~DYkr>l26URV|%b%OQKwuy2FiiSN#(xkYGP>pCg}dt6qf;w|?_e9FL?79YL-~-HfH; zCmbUR2=;_;G$1*mMIlb^CjBEJ2`5dROP73o?Ybs-u?RWpV^Db^lT9fs2TM5(UXI3> zN3mTIz8z#hbd1Mzb`2w%k&Zg`@j>WupTA5{o-z|#$!II^7>LiKIdEf6ZyQ&k%}!$Y zPfmbRkYaOr>OC;YgrceU6C3eKaNff~o{s>$N{?E0EY3MO(p&%@5JW1c;)i33bK1MkX#BU!}_Ly%@uCU-ClL0GH3Dmz60mwui;m z`^6QxKd$Dt)3kUQ@9tN6DxEID^Pxjl&X*2${R|x>7b#-=J~4M&PKVY0xjloZ&7Iqx z>liTXv@rGXWi8!5#F^-ql+U|t87pYK%uFrbtG|}&#hSCKubsHyJ22v5vrdqmE{=O` zrXjb!87e4>nW6ICfLqqv^W;TZ)0zeVG^<8y(2L7cNtX^5zg^Yt)zZo(Fho20D)WM` zhzv~t6-Jt92KoDEIkm;&+K9h2>Q!R$-j0~MB7Ajcq=Ev_rGv&wlFahZ!811-ZUT@+ zP-bE?365sH5I^Fu4sb9ht(TO7)l7#{bL%X9UDHEQlwp-Y4;~Tb4D%Dk4BLyq3oX&L z-cj|QT$#N>4u;?m&zA#wPDMmGT!WsL9ayH#KS=`P3A%J7+hrwrI)Qd|pl~Q#a@f?* z#37prdrTQrYEBI&BkMdL0_|@Dg0Ti8PiG9Bm~(%gCus%Xq7b+K4(u@gsLNn9Rg->N z51_0dum_t@%)J@egRhAb_+>2LPD~IxT6a~R0k91cW+4toJz}$mhXy`i*mD;#a|#6# zSA{Q6zLM1d@)(KpTi*w!Q94;al{9?LwXY%={t&>WVvGdO1*2 zzpZk>?Q+mQUq3n;?NIOcOyc(dHfp)O5AT+N)g z*952hsBzt!y?%b~Q#O1uCq@_Z3dMOAj2IpEO-b>j#P}5g{4lA;13Z0wdssn7*6=Y- zenBdK=;+X}v3}Fqtg`&WdF(TFO-y@_Q?`_WBr}|8>mHyr; zhfF7^>KlG*VD+1Ytmb)5dp|i(u@1=&lpu9Q7%pq7EGes2w0O)l!_3`nE zDmMkSDYAbwJjB=teW^TL$#frX&#vjhY}TfB!~L1b-?qa_PS6J8j-HGvUw0%`y?J99 zIgOrwyvv!9Se5Y)Mz6%Ul03|cy$X1SNhmPpf~7;_B8x9hOr3dgEKlgSp#3t!VE{9i ze(+Drb?`k}Ew6Ip>+aAQ;<<+TSPt!=nbzfNoGY(PF0%RPG`u2FM0R>fMnVr<$g7n* zISf7c*)HRr;EAQYE??557BLR4TKCO2P_JZ3zXMlnaFEp+(-=reYzy+JI{bKevi2gd>>hx z&wbw4aX@31MI%F7q6mxSeB!;O$^=Y^z#ao@>mXfm6Bdr%H<;;t%!&E@)y<t53T+zpLNwJ&BNspV z$p_{?h-a5e{jstIny%cDQ(1uY=c(VHV%8qrMd&{Emtg~kQk1lq*ECM#yj%IVxOkH%wL%cE1$7*+E7lpAxd-cPH4~ZX|>T`d>+XLEQVbY!dG~ zU5A-yFay~a1v%C_WV$Tip=cQ`W#GWV3oiOfC_ZrrYRE;t0nDtVlu@&HSic+G9w>@2 z0}aB64waYjk1Mjcq7pXVWZVkM0V&$>-as8U67~jg0u<1TiN7}qAwd>`%-4PP8Lw_~ z8wi5^L)i3a>!26q3MiuBNCd2DR=kxBCl2K59@|$CD*WAz7xx;!l#hM!KSm`~35y$_ zmehJc(}AA8OJNy*(tL&fEGu$Y%T+EV-*(!qy~xQ~t!QXx8xHSnjt78$upK+(#|3XV~V z0$=2Kpg2IAoHhr0V~u%OGqgx|jd^+ly!|b*n&=GIrI$I4eZtoyWJV+rfT96V1Vl|J zOW@b#A;RvA>y8l^CIgl-2n~7V#l1k*WXm>h1FK*YmQlbr4mKiRW_9E9mz9=gh7g5F zNbc$`&>$Dg1%&BDEP)Z@)uC7sWJwhi*^as&l@^{{%ZM#FEdh?e5}QyIoq`2{^Fd?D zOppHvX#BxSZmHrfU3@wytY9~UpJfLw%793S9RdVF+y?5JM`ckSv)#ik;moOun3PvZcZeKlSc^S%CAx6YLo$I;;bcEHUFTq68Q zaLzggI2g&T3~3CGS;T+|^vTrw>h{_uJ}=HMVr+TzDToIoL*4}%XAq6h8*?w;RX?Pl zbEGU-^Rl}-?k!eKDSi9`0}}d4kRZt>0&7pOG&=f)Vr+u1>O8U#VAQvwHjrh=L?r?|kv-XoB1EfkoEJ2{WU-^@d84W?`#!VB(7gfc@ zAYSR%8#54$Vrg%~**Z3>ukJF*;-es%2(U5uBg(wxj0IMi&6;y+r~xI-IC%ZW}ej^gZa3 zbW(?!vk!8x2L9MW$;((XW;LyoYcTXS+&FweuyC1Ba-_T8=XR%%9s9|rlcEY^Vt9Zn z&|2S6;Ns%TS@-p120gaPWV0jgG zj?@Y<;-dczTx0+D9#N=iYZtKJg?vLZU|g48%t^m(A69*=e#nj?Ck~EW_B)z9pYfMG z*3ptQPtBLxOYr#`z1ox3A^)l4X%a<`vKkwBX^gsDD>n-QQUz<#ezfyFup zg@vBJnF&g^ZBi`tg=9tIgpV#NuAh}>AsvQ9+$1yz+8J#8{47DC*thX8p)?RG6%v=f z28BU{4$ERH&~OC4lwidcq3|mez=ELY*zdq^Z>7LvLZD0$+i`U%8VI)UXs%X-mpi5% zWZ|GfypcP^3yvY>GjzO2dQ7C<7RxWDH=AuHF=bYgQG$d1tw~G_sZHH-%)EwJOI?O4 z&-o?IE?u^)XEgf}j)$r!eb&^xsr=#W&JhoNOo8NS0xb1(cw^>IvA?qSkPE2#{i*(c zxB%L*SBoefrCq;cs0nYWNAhg1Jk6;Xoua3&dzplWA36 zkc2LBgpY*nd2)LtBU4N6=~$Ma{X>Nmt@VdSQuL+`8$NYq_RhTADt;nCZ;t2EwExpT zqC*Wkw0|E`k>O_X-uX4Uc$+-cJE+zo{vbwmNUvSh^>Mkp3#y*{tv5Xb$Fnd07_*LQ>UF-dtHKnaG_Cq{RwJX=@M^s&S znWt|!wj$~{X6vwSre@i1xGvGUe>wG>K-;#U}NU|`gdnZa;9u?Qv*AnF+NO|W{wFXIS8 zs!3pDM#k~-?_anLWO!5fYR?thW+P1rSp=KjCX2`EOPQ_`YvR+B6>HMCWMP_CZnIw=g zB}Qxk80-RoA=V_ALDx-A|EnM65K?5p8AZ5QAe4opk1ihS#KE905kXRCI_&d2fAdDS zw!gV(>%H9(O&dTyx`SSrvg1E>qLkJ^7gn8ka207mP_RL>XOY~M?=d1lc*jr>&|~3< zka;P-B@6-=gU9d&pc#G3CX7J9w#5{kqJhx~Uu-tuGyKO}L52akm)KHuYhiidpE3`E zO0M{k-n?j$XbIBsjz>3fq0%H)S>#RwlC@DO2Xa7o$nO8MHG;Ame z@guO#IFJxmP?Tk4o&=qRSn|l3z7lKs>5>iFDGl?YTug6B;L81By9KS&`~ogO;X~#F zAjJ?m9Cb+YGye4|hK$Tm3zXpdqbNnFr(O(({XSJAV0g5lZ6Nup;y2zQAwav?wce~7 zs2o;B5MiyMJ3{D82_R8o!bH{N zg6T0fB-Ber6yG4D(4%tIK!^buuOSz~96o$9va-L<`(SPOLze)@at<^ah}g2gbj-Rw zMFi}N0Py~Vx41BpNW#}{1D6OrFo8{yglFRd5IZG8%*LR~c6&h?N>Dy6KeD3$h1W(P zJQTGgJ9({1CI`qSgocK38<~-Xj)R&Ngr*AY7K$R{8-fv#)k(-QsYnwdAu>{4|L%-7$^I)iNKhmP z6M3$-&rOf2wu&OphESKt@<4CqQ$=`x50HEcg2B!O{m-CK*TiV_+!c_KVlhDh{HKXW z6O+fQ;5RW$cyIa%@Cv!tu?;I2DDqm)PYX#(18zz56z0XtKz%6((0q|_7EpzQFVISi zu6$OM7&9%NAyvs%UOY&J}C(6~7`*ey? zgHoUqf>te+A4b9UIuPD8R^UaWl|7R0m8~aFLc$dIJ&>Dk+`QC2Jq6S)iq z6Dllo=qfPPN^PZ~sA|WoyBKNF>rKyADs>FtCdd;h(RQp#l6(yQ=_8>bg1IdGip!Kr zxWEDPYAXOYRFd)|Hp5hJ3zt|Fm>**Jj-IJ*yV%9#a#qCt5J> zjp~>)0eNJ6f&j>VtSwG`T zfxC4yTLmBQ3{KGq6$VxT--&KZ2oKK{*Jlz^G!{~tDW4r-kOG|xwY zz{9{SSvP}83&@`*^THi3MIQ5HMdwQPBJ4odA|m{73V_=~jwkQQv5}@Q98#B$b1mEO z_)9qiF(ok?0SKOf&a8L*j?c_wooATaK+*>Jp{t?)+b`>PT zHnHSZbBaP2O~N_7P;6i}@XW(O1HQP(S8sg$NBIv~ox~1t-k}T!hZCeHFhJx4V}e2d z_w*v1*hKbG9LTErs4Hs!>42v5_!Br)W8tyOneV8zQd-&)%bD!o#OWO#YP?V!6yv3+ zd0HVE1tXuS?DWsT)Z7Ew+Kd39d$)?l1D6C(yF37g3lNd$A@=1e^NslY{V^Eu7vf-% zd>2&H{97e|D^L^=!H<~UFF;u!xZOjZTyywZ@??F31(t+FOpUeBw=Yjs%OHjcCAP3+ z@&y=`OsKKdky}W-!3_*}z=I{*95`B+H9}Jz3P2XAZ+sEn8#H=b(QCqqW4SU1VKV>} z4HIK^`p>UJtTysU@j^%Eqy}F+fY}Cp zBbcr}bH=ECh8g(*fY&=Ntw|~T^kgsL2fcJ`ZRy%!Q!9L@owvjHlA_^j zLh!>$pu#^xt>LHzECKzIQ9Lbahz*{d}jVjC>_(^B$^M(cIk@( zlx)V~D)1wO{^&^OAEGxRbtu>YVF(%iBJ-626q6l~ni^`$zh*g#;z?=Gno;o_DuR%K<5|Xr=9SX$f zu2x@h(h!b}lEWOp_L3KPctwY2rEetG4A}KrkwF!vcqQ;xqHoE<8WvQv=B6brsFutGKnXsO(tlq7e0Um5n}xy| zEf86;40^1=+|m71AzV^JK=-YEq6kd{q%7fWZX9Hf#*P^bltq|us!34<|0E^bFEQZZlerIyvL9#X z)MiJ*aqKWaJaEx>+V{fvidL(cIp^uWX`}stl@GcUGgi7B*}{LzGVV=%>;s7{x8JYd z8mi47WhfD^DWJJk?EWpseT&{37XQ>&h*jjjyZZk4Q2001`tglZUZ<=^8`_-ulbOHAD5Td978d&#(x((S#Tx{R)rR3mCkvR!}fdvZ=+fbx~?JoD_eJ zjeSr$<%?(}IBLY;_+N)S7dRFr9gGjVr+CJtW=c@OG7!Ua*Qo914`A)<0}QK0|567Z z7+{)>*=OThe5UMeg=esPeZ5q1D*X&hByH+bqJaO`0R#Wt-*6E4Ws+gybqFF*7{mj^ z+WiR?uM20*2{q*$>_f-!c-)MRjs{uw^hz8?+1vs%bMqKX;8pWb z%r$}_C!}Z{L+&p?eH@yf(79xQOJJKHh*x+9qth>cP=tbKe4QvD$ixU+##KVS-Jzq? zi&y-Fsci-l$(LDuZ*Pyq!?apnemK{ej#~I-diH{4%${4alOW&MWuH9_je*`I9<(vk z-`{0B@mIhWFvDX|bwe=W(6ifEE6=eCOo3_NQ}fLS7e4x#E=c$pn4sf;?gqEU7^N4g z=_E9I#wSy92Dc7;P18aPai6e>clS1By+BTK)a90)$bI;Yw@SjJIXPOTcI(=;7~~pl zZw;zE^%-yf9JP>q{Vy=ZY_Nu3quz1FCX5F{7fjBIbZ<<$6k*1&y#n;Nb+AmsaeM}~ z`8|~jnNnwvdTxVq<>9X&1;{-_Han?9R#1(rr-XS~{12QmFY$?)7#WXL&@(XHx_R?4 zDwH1F0wbg!LI4-By@`3zB3tZTD?NXm{P$wXtZUwS^O_m9@(d8a^3dnlG-W2s9!^>7 z=;&CNcyK3Lqb&H+KgODS-8`FCz5>G*e{ye;pL^cZQx<6(#EKJ|lxIT)bH|`Osl!{Q z*wiJhM(lmEamM>Y<>=MX=(w*&c4~d~wjCMaXwT4gn0ca=seSEgdnn0pH@u*ej-K2{ z8VMzb<|D|W2>N^GV?|Sw2^!MF_{gxJd4m7FgRdcC%*fy0x9QtsvJznL@ER*~)J}B8 ziWD3p2XHSw+h=`yie-ibN=Ebk3W3S#WmQ6$Jfby^D9O;SSX@}+I@2BbpuFh zQOH#!Tfy)!eit7~Jz@=yBgem=j-u@iD52QCV{^MHg{%CoG9u>X`;Q;LU{G0yzl_af z7o1CB32bmxNC|VQRr^qzLVi(|6-Kz|%J-AiZ&>X;U}SU~)|L2yC}-8`)1Pdo{wba{ zyD*TkbV`X@`uOq7XmCB)9n`?H!Z>T`)FEo=cl4w+po-OH+MCoR7-+S$wBTVs01OFb z%(TeE&K-dS3>aw1GXW3v z`eW!NK2#dSDx%)F0ZivPwx91u$5F!cprklxX&Fb(6=+Vv+hwRlFGbsmqqdyY(c3<< zun{6Jtm-u?r!%M@?`=FChF&)bTgV7EBi@qc;#WeMHo`U9A^V3wUqN18o|0t=%O;(` zPuAeaVZxtt6J8u#TwD;K=;7^hu$PI!t%y90(O$nop~#3Yn4R1U=%jXHr1P?U{tY}h zTL(7p)6=WRvAJ&dJ-7i*jt@o0UwQGuF|CC0)qziqdblbtfqKRFff^Nq^|AvS%@>r9 z?JuvyubOM}+q-Jx#)IVTNc*{a3)dzT;=AxA4q00#00BtBXx9LwyDtdXc#21Y0Axm9 zUb#Zbc1PAKR7R^E$G-nQ-3wpA6WyHDZfJ=Pg36_Y5*F_@}uWs9cTlUdL+rv6UeqtNZtsxAe*oR^13 zC}b*w*cgp?z@d>Ba%CHskDUj?MGOt}?7@+W_44?Fko-1gnrl-}$xb0}A5wU<&g&%E zHzc4?e|zm7#o({555yXcQ&#Ss?61Fw*ujTl05kDQ$a;VcT@3K^`O!xP=Nkl7MfuDX zdN``}W5mW4F+Uz+9BWJ(__5 zbiNC4**tZb&UMaHD`+R}=RDSRKE{w`W$0C@b;O>AFS%wh>bV;XVo4ALWm4d<-`2FP zTDej!7_-3CZt^XQSUJ2p(`;&rRg<>o_59M^%9rl8u&_W62a2`Bt_`+TH5fim^*{Q3 z>~T-ZJY4^yj@$53#+2^~K3e zrFL&y!Qdi+YGw)*|4+fr&Mq{49u4^^R8X7|m?jX{GZiVeUMMt1`&2Q9A(?%EPGwU7t_SKZ1!;8+A}sa<%2fkpAU{D1qQU!5S?LJSoK9!I<{NBcj%9>Vq@db#m4FcCtq6*RNMZHo`_lcF;(C zQkSuo!=y6x)c6Lgp-;8yCZ=cTsen6|H|m&*r|u{BW!p;D|233)#J^Meq0p>sEd4H} ztNZ5h2Ihm*7+O{`081aEc?{t>#HIJE57SP9GxA^>)q;nU7$TNoeNLhqdw6)jsZ|)V zt{GyCP|pi9iK)9!S=>xGwQ8U_F%f=ClD?Cd7Bw_9beCAO7dU4}`kjn7lZTMnM!s$( zPGmeF;sGEshMjr2=$So~d<*4~#AiZuN3Da<9BmnWVk;n>iSFvUMy&%{9}at$6=w=W zX(=4)M>T=m&70o}_iZwx);c<`w?P4=MrjhX(fVi-Fq6lX0%aMo9ds-7*f2mDux%MB z%%yw1dKI&J-<|h57G3ao0{~MN=BKr&$=hy|XOyGg$rff{(7Ezp)kYHU0?k0JO#$K= zZ_+dq+e;c7<_gB*SOZJ|wqS_Xe1N#~DO7y%Mm(`!WFESpl3+I*#gHVx(**biNAZG$ zO)!5_bbv##_}^racBiA%igC-95LDS}l@+XV`E5AzC+fwsgn!!+1{bP!X7N`F>AX=1 z0W@|(BkIm0VFDTxG&KM+H=x!W!!;vu*l7w?{>xyKJTo$Ef@Vp>gLqn&0_<50NS;Yp zKz#&{hAqhvn2coo>!-Q#=S+Fn#|-vm`;T687KoEqD|lFj4Lwp&6_n52^-^>M11)Q@6iD`gO z{MFoSRBa|8Y}i$r#NfMoIPwm1kNWoCz3dPWz}i{aFv%##smj9g)#UBQfKUK7oQRBP=rS;2wNcNJDLFi{%ygLXVO4%l2Sm zkjf(A&=a$(NL-Z-4P#(4LLr>jLi{j`|JFM&FrHgECJHlm2kIi8f%Lhy(V89%YcPlk zfJUIrX($gvj9>7IR(G|NC*$*Lt52pzRh7cM;cN?2EV$l*e zNDR(N+++j|U0m?n3|@-hyGAxGGx>^gu8}R|PPE^aZkl<3vZ^c*=hEg0`PZ(4|1xT%jIWF}n`x<0jpZBii^+u08sse&7 zJPa;s#)l8D^B#E}2i3*h9nZySg3wAzQt|}_JsUeJYrQrivGqDX$8`iKJr|Xr3jVBI zdywHV?%o}P27ib8?j%_Q%n6HfVEP(jCAN-mdazaz$5m9-hlt5_xsYd zPmybHVPT0UnK6t%K$2~x_l|Lkie5!q5+K;Y2+c=F%*tE|g$eZ#Jsis3qv3MQ-abBW z@JC5H0WyRHp2?w(Wd|c;1YQF+=J_`Q>&l|0B-I*(s&crCI1 z(5d$?HtCeZ9>;SCjN(G<4u@|*?vagh1vVF&yV$Jn{JsF1*WPzupU`!^{*^iJ_Q zCob*{uZ7YI&%K=D*dzSt@WjT!QC?kLEfu~T@3wRfbV?9cSc6wudbiWI$pStnq!Lj& zb@>&4shuL3?`37U(-TmZ5qtsNBFMF;w6E?6E2}u}ozk&`lug*Sy?LnwgRJ=Gk~}&9 zfXjb_UN&P>mzkkpio+k(P1jut6!NA%!TUKM`%@dh`*zIth#@WrDP}DiD0PjzWS~8S zmtxk2vo(C~w{Pp#4`;qf`?|O?V;WVNOF~NjxkAX^>Amt z7WCo1zP`IU?YUABREvPk1MBFCB_cVoze#p!`*m*vI1$74>@?sB;q!CTMnG+(s8o6A ze#8a6k3j)SGCb_QXq0Y%0+k|JASypUv$V2}gL*4x3d2T-KDRO`0^d-qMf-TsOITMU zypoHnYXh>^c9!kV2O(X213J_IB`Nf991!c#j=;4>(wTT0HVTCkn+LHZor33o{&e4w zB>Zz_kcb8fpFAj-VjcrdxQP zea^MFp*OJ65D$?DM|F@S9iGGr0IzqsU?b*3MM7i2jg1Y&WMmTw?l~8A9^5Qig)12>Mv$+9x@ zIe=5#`u6$*WJU2tnn;guB(!*i`K9qmHWU^YAHvK$sV%9uS`-k9b=z=V2OvRK=+N&6 zr~r8a$*-F%Y9W!vPni%-+$nRD`(*qD%z+Z5ef@WCd*V{Ys31hFtWuovv5kd|JPw+> z7^m%jx~YnO05}OgOP0`ai~()L7aZWt9cs-i-%7T!(g6ArC z(7QzSq0`ZGj^Y4Bzv-Agy>j8nR~=JfJWErXfP##kF2ZhVl=ro&j-eM89XC@rS~3?H z$q75;=i}p}%d>goMrB+I(OHhg+~fCIOP(_%j+GCt3EL5d@(P5@37d9fq6_TtA?!(< zf66)+GXD#eglJ<*KlD3mkE@f(sN9?5ZzkT_n6b05sg2+H)Y9gb%kt$bnkB0lbD;3& z%CQibnkL1*)Zn_ppqtV%GU<<*=@vp!ENz>g-hbwa^cQOzJAaaW1@=!vvX1mNL*6B? zC#H7s2Ao1&UEHSK#guzq!t(Fy16F$C1r9NHee&S)*uZHtB- zvRAOUjeIsHuir25>%oKk?E=&jg!`biC6*NribXh5y=R$1m5uHw(;MyBTPZeWEp0Vf zPnX-j<~exq9I$*SWe+u%-iOJJ-Pp2yc^~_7*hS0SNAf|5f<5=!B-Fn1(VPP!ox8&)4A z_Co2Pkm(T1VVpn}Jso_s@PLnh#8%O`BDdFf_!&8E``ljqfLI^$Wc)-u$;Xlrc)gB~qopW+kefu&7PR*gfuD)cOhJdy& z{hzpA1_T6{w$E7CG{kE(4;t^``CWaL<+b=KpQMrcBz!?X47>8E0+G0*| zVv6G)X4w;Q`^kjz#FS2Ez2PUDF45NL1$Iu!SQlr= z4?X3!&pD+vXMX8mW~=FS?0PO%)`FhwKR-XfZjQ_NeeZUCN-f!Cv<)Pwd>AXUy_n<4 zFw65kQJq^(-3RjB{Ku^aXd8+%b`F)UwLf^jY8P$Y)Wfzc&wE0*d4=rPbtdujG^=UR zhKoC{bM-jdf9P=B+qq-8i!@TZ@ykq-3BiA6WNa*^+&^B}_&A{5X>Z%cwff|Sg-b|J z&nJ0HlgPfTA(r5(Z_^Vx*KfS|u79@w%cp3^JPx06BUZP-*DF1=o`?p{bMg$WeQ@9G z(!rCSo%5xBzd9_s_mPk`gmQq7A>?C8;)*BNFFOCKRohI<9B12_xtc@{oJQQ4$V316 zG3)>TGG6`X>HZsm{nCH&Uk`|RF(;Y|R2lz=O2|Y_FVH+x4 z*2l~NPEcXNKk&Git!F!N0^s#!2n>eVIt9&shd?s!L<$gi+8`~hQ96_}VF?xM;td+v z#NccR=NbqMhmA^bcMMniymzZJo#1M(L>;mz(O_c;9O}=yTq+UQ=tp3HtUt>nXZVB- zC};dgxWLt%LvC(E@ZBZA5auPI8BBEC;03rJ>IQ~g*8%VeVT>dKnDa~7l9-=346!tN z8+l_EFB_F~NffYNQK$pLF&kCu6lzdV#Q4rx$Vy0#IEL`whV#9|96AG81ENE4adphW z_t7BPFV17noC+Dt2)_Z?z&1K|4<|1gRH>l!s-2}sA{s;&G9;HvKt?~Ok0zUmkjIvD zp8Tzk(^JP^I8ve2`lzE_@ULVz~v|tvo3tOamen4)B{3v%praUOfmxyB2iD8|;(QZD@LdoQnskfa$vG7ch9hz$?N6DSVJrF{k4|S_&sR|N_ zRZlZ4)lG?p5Msth#9=lS@ZeY~a}MGhg0*BrO0k29i4#2DqlXWpP}gHOQG3xEYkRKn z1Q@(Qdz+~osXB9`Ld_2ifR?qdDRPXnwbdT`Cfq*GO__W@lp%91y;OaDu*VJhV(XOikXc(nGQy zbTLN6`3A@uV0RLOpy%Bmq?j{g^eb(by#rB~-Arl0^wUGs)?E7xC}cu`^^#vcc~X^Z zSIEs`QAl;V?F<@ZHciih+UVIc2u(wLGGfhLR_`8RIS--tJCYbFIxxUQXy{@Pc@e4! z5kNxzf?Xw~dT3IcSTA(duAdFR3x>N=-RI@YC}@|I6R&Ezi_YpNjDe>n*n8uf%X^fg z6(o=w$6QGaLYa6JIG$j^UPQ=Irk!~%fBf+Rm;uI#HH3<OfX9DM=+kXkvSY&~zDlF2T>?@een z5JJc>Kns9!Re-fv;rkDw%Yxa=9@6$LW)3|qyu2($+sz6#V*h%&ySt;dJ`AVB0P*6C z~wBs{mOvkPqMn!*WlM{4PSxtyQn5epS;nZF{S&85X zTFLQMoA1Li)idHD>$ur5FRIvo% zOlvDHF4h>l5~q5dB_-ty_%2jYd(qk!(ExxQx+K367MjjSf}96OF_ClT>H(S$<2PcFbQ0?mqoHK6{CbJZU zdZ(+?p{ROW)q=LY1^mXX4LU3UxQ;*By#MxYaYx>%*}u@zau2l#cZMj531{86?a9N3 zFHx*QYeOf^>84qXB>uK%5TvpR#Sfp!AI9R`{=r9?5Z%Rfeur+bblS$%#pM_Zyle+Z zx%+d<_I>+IT={yVBZJD)+L3z4C&N=zPR}Zbo?AH%)q(JH0b$`b(zm^nsV&t&8&Igp z{4~H&%f>G;@w+`O_mkqnvG`6rR>doU@>a5ZGd`ox6DUvJZGM2Q{Sq>@UJWgP(E(R| z0W2DMw;(s`$8VGiVL>ke`=}p1d1fTf-FqUv0sUC(b8JSX)6Zb1bt#av<0!bk&VG+NI5)XGaCv2VO$b7e9%PMOBQ_XN5-5yyVtt5s}LPX)E9^EAg}y*dwOF25Mq^jLj1Ie4Whc0UN*qnCvr zEGsi;)#DI`r9kF74v**0rTFG0_K+GMX;z(;ZZjko4S3nZLg=R!MhB4>0?d1TB!AH{ zwqBZU3q>V9^C#Op=o);J(hcg1X6L1!Az&Z;M6>4Jtz7|y@^gw}yt;sAV4yLN*1J1N zX%2v3E1(an0NksGx&$LUiw>ztH=2iY@rvwNPu&1=qG+IIdSZef-T3%GM;(R5(}iqC z<%uy!^f;laVa}YIOhMC*TS%cR0Vc;!!jeTLPD)E+8`r&oiwgsQ(u$aaOch) z)zUN=%oMXP=Te|J7)McR-@v7*A3yj=20fq;Z{l&2Fr~@$e<5&bvYvqkooB-QD(9T- zPHbQ>V+odgNa`U=ZKqi`DmVQA=X*q!Ud+b_B_f^HDS;w^N`O|+`HIlHyA6UCCMvN4 zWLgtGkbo}sL@yzftetGZeU5*eMK1xh!C0l}8%6F|mBg#Y2(EOXv$u{aJn8jbo6et=oqbq1yC@wc_}&!8Drse13rsEScL6bbjz znn%$IppZ0f)kPr(`7Un=HRR?1$B%7sX~U;cJtWn=kXrI#b>j*p^^n&Osz+Yz-)?jdPL|zCF-#ihUJ?wXT$L?DtolPK)i_mXO$XW38;E zP7d;uCAdqWA$!>s4K(pLEJ@=uOcD8(_hc12w93OL>AYEe+5M*AElYH9g%Sx@ghdv1?^P-zEpDw8jG z<>@`>7gSBxDM2xEtK;3N>h!+6dlTs8-lIPHiI>d}0p=-L7C7c6(>+_Z$bFZ|c$O>r zY5R(qLjA;oj@t=nMNez4h6Yf*EqLz2y+Cbxaf=35VPO!{i<`)I1F!l+h!+?BiZcFw z#pZA7rm`D;TfN#JMt2Vk^KtE>4W77mY`{DXH_xgcpTxH9MGTytpat&vZIc7+^_z2# z)?FyGHC(_cwoNB~sS-;QJUGp*f8#wL_4Z(V#w(|V84lTbaI41qHAv#(Db zQ>94oqBn!-Lt!;Fa#RbUJTvpgc~l=s({Y7Kyk%KI0c-UX2}8jTjExE+DA|X6#95%C_^bVNde!!e&{+|BjKX#3kTe!# z7vmsg2NQ79R64p@h5<6{3V2`)H9lFmZR|h7Ap992JXrSVvda5GS@N8(&mo zd^JkcwV;tINpTPpbMAn|7R)T;?5D`f{_IPPIT24nGSkNeg11>8N`87cBAh|onRmi5 zfcVT*)WcEhz&R#@T@>aKMA6TnC|L6-(n8cA3wywK`VTVyVD@h4v|6ddbW&ZN51hAi z@*l`3SE2c$I0=+eh*4}!(Q`*|rhS?BP6C))Bfu_n3{h=u4Rh^boO&R<=3yWyfUkjAl5ZP}I5 zM(6PKO<*Sd^)&3CNI`p`jLsdtbDU3yo-U)>;pmOWEq)Jk$J(rR3mgUr4b`SE9;-W5 z^Apr->cL08eYV-LfBz9G`ND)Q@$G?f4AI9zb-$3!$KevuXftHQ1lINKf$Jv51`SFf@iT7ER)$ouSy5Ib0!~%!>Am&ld6CoHN+nqpkeqALv(e5v zz8ZK)sm&be-%-OCcnIQ4>CK|g;&FcIe_or6eY5V#HWn-*>ULWCfI zf+nfJlU6!nh`DbJtTUl0#(CP1jt=e~Nl5=T0xNDRjU^FN^UmPdDi&Rj zDJOTE7ctcYQ(WqagbuB+)e8hAPc5oqnQFkg9ntTE4ry1aCTumN!m7csfKyCc#;v<{ zo|oV2gczt=qp0JQfom;3OZ?&{kooYZX$Z;iN|%V5x$&v?{tM{YQek%^BNfKQ9jcc& z)4Z7r_iZdR(gC$1_E!W{H{{Wv6Gb)J~);`-C%p? zPUg_f953@W$h|(!-g1f&JmVG^2$Dgjd`DkeDCYpU2yb`yYTI&xoZvQPmH}ws{D8=G zh{TX+hoExRjZevsu17!TNbw=sx<^?Fk_k>1uT1Z{(lZWi5;H)OKMjv4eCa;Crufr3 z1tbsbS@b1_yn$6%aaP z;Y}lIEZ%Edrr;l`W%0t;KRv%h99D1wG}9##!OI;R>q02BhnTJ7OL zFNoH)nP&MMP$1aTmj|ELFFefcuFqM?H9C^He!D+warfb}FRr{1_lt~8?fZN(fH6YW z5@a{PML0`B@#W_`J!PK#b|dl7?SzpdQ{hsO+Xxucg)cAe>Fe_y+4*^Xw)_Wmtv=z;ggd3|Gzd>y#!V>bu7T)glwGS+@3ooaTu#iLdNt4#Z@-o` zXJT(LJ$jcs3Q~tq z+JE#o<{G&Z(JdHveSC3P4`1MSVX$g|DAl)1wIjhMgf^moO;ULf{%(A|qymi=FnJT< zkhG>7tCm+^?!0gZROyW#LsBFK?-}`{Edfq~piq=}EmS?oE+%N@cg%i9$aK)XPoV_2 z!#etz`XRLb4nY|`JT@VlyLuLxZZteReL=AJ$^7L&pn*A)mjhuoz9Z_L@i9N`s8{S8 zOd1y}RQ=kGU5J;%JP&^OqRK$wD+)4^W+1vKj6*_0F_oFySXhi579imqO=_LB+zatKUVLv9ZDBWUV>&QTKHDs5n zo{~N*NwJ_n!Z@8xns!^2)FdGM^F7Az+kyE=a64MO%}yxnY*I{1=v{|CVb1Vy*5KNR zepcm)l!2qh`vf@AvB^ng2+5HOMqtC1GKIkl0dYMlYddGF6?*Ow?didbMK2FJj4c3z zOB--NCvTe1@uKg*7Ok_ZSj>f!-3RA^M3dFyEk_?@8d!ZVuknZDGwd_GrDJpPl%DJh znTGaLPZhUs+hzrM9=x@*g;QI3MMbJ3haTBXx$?ck!|{j{ljJ7bs%J>VBu{t>%lIC%&<`bi{I812lc zR{SoPhgo|m8rGOX!DfmmnmrFoBs#eGJ`O%zpdCkmT9lR<5Md;T?U9 zk}u{!>8D5()E6!kJzA-@9H^xT%Dw5~WFI0<77L0De$EGiLMcHe#*zrmN8gRRq3(nQ zn3ncyOn@*N$hbp%CcllD2uBZ$g= zC|{0$13jzr6EL3`%;|IEBLJ@q_Vm zG(b}IP>lp6%ps7BbDM;9j%A&rp|Zjsj4t`0`ZXbEp$=?7-@of30wXfO!c&E=u;B9| z*cwEl;!7b4eC#70rFL|at|8ibuD*A-?>_rzeuiWflfC!nE7_?;?;aQV9(FViAVE*d z0+D>yM^@W*-aUEc(K^Nn#OP9GS$Qa+s_73n2B(-7Br}S6V~wkZhlZoy8uz1lupCS$ zr60*v4IHvxq$}SAsQ;Rgcu}Z8WQhPYbjngRVpvp#{x`?^Et%+c8xUv)} zZ=m%1Xqd|F;wTMbypg10Xe}U492AU88f3f< zurh@BssAG3a?qrBun7MNBd8VdJMh0B?L3RT@ofxdC;AcHzC>S~bUS9m;Vyrk(6Xw!{UQ&8g2 zk5so7rI}tqK?$&)8~2`+L%!(?-h)&HnRcAU->P-|ORE7!Pj=Xk_TyXD6E8;;(7+92 zwDR-F)HMe%1|*eZ>#4Zh{qtL%=KrmPkUpAsNGU1=M?MNNH*z9D45ni2ASn#|^5qLD zm4OUA?)O@JvjFzZyebArc^dvsdDM!SQc%GMMB4N?nkB3Vxp~s8%NYbCKiy*fe`h-| zf9LF{Tm4hzSc0C0L^u{(pWh;-((P4^cx4NA)crw#N9viR3Y_+7Ycf|#g;_5@ zCF~!bh^=!x0yQ|j@JrU&^PsRU!;aES+QO;ZQ_wEdx9?eOx}hr{4gu8MCuWLZmjpn) zvqvUv+%I}sb_Plu^}%Wzkx!`KiiRo6o6AQ`OE|HyTR?%X##Ug&J~V&!AjIhD?hXS!KS27S!gfX? zenLxnZMv7 zH_dk?$m@ZQ+?>hIgvVQ@tW2xo10h_)&(x za@8tsetso<%-ruMQG299N<+v1IbyV(of(LJp5#?T^A9Ii*`u*mf^iyTY$L{Ip&pc0 z!$%6A@NJt=vmFC&87)a9|KZY!qmI|iQ9Ak>S1evKHpG75&Fc37%lz%ILjLJf)G zFudr0TFS}G;f|d1HA#=rGav?Y>X^*I>si75EF*XP^)51s?j?AhF0_I560+gW8 z6Bi(rVsX&<>H{Ig1f|-QELv0rC4|VWe}~8=x~_cWJ0g0)l=5hFD##XQXUL=wF&(7@ z-RiBV!gUBfe7%`)5m34;^1%Mw7qx7Q!&D`{;*r{Zc%aZCyz*$6Z0yVXq zVeQhJN~83^`4|TZ&dEjuz1IL_EOPr_BduxDkOl)*q@7iJ89t4;%iC+bNlZTX9>W1A zLgxd#V&l?#tpws-@ZxV?GO28Mx~@8C%0en^yY8)?Abtt{iO3wtcrxpe&!otZNM%xQ z532(iw-iE+*t>V{_#u+8d2;tG!Ht*7X<#M@bc%+~(sVKdQSjm_FJo-4X6*iGETTS= zxLGXfH+)nWmMmfZ5T#HaVk|}=v=Q^G$2*XuiNuG*4L)Z+egRVJQpGA52@;_|3i5eN zPoXfHzXWw>V#~UL#%pn9v*2}*5HYwEfQU%5 zpEe=a diff --git a/docs/source/_static/image/database.png b/docs/source/_static/image/database.png new file mode 100644 index 0000000000000000000000000000000000000000..bcf95044656367dc2ac2d1dc24f14ef458dcb5ad GIT binary patch literal 92351 zcmbrmby$^c_bs}>LX?v3Qj|{VP(TnVC8WE%q$EXBN<`^UL6DYiP#TdCq)S@5OKQ&t zzu*4OIoCOV?CY%S4TTlYbKf(@9An%oS|1LU-%cx+$!xO_e82(QB?1B0-WjoVnt_F@KC^K6-8xvM1BS#YxTPJh7XX_X0 zMd3+zkS9qvnixE@u(PF6v9K{gSv%XZ(Qxw6*xI?U(Qt8aanW${3Et%vyvvg-_ss-_ zqCv?>-dAx;S{Zlud^=Zrb~bpEdsssJ28}-nhS0-%xO9dDZy)^pSX!!DxYU^U)Y2k{ zD(jWswU{!d_rLbcBQ?wSf`baK74gvJtjMa1W6)ls^+(A(`H*JkLvO(CL45 z!&{Wf-PqA?a3LlB-n{qZpmV%1Rpb@C|30F4aeuF)OaAvUJN=19{J(#d90~Ko`0vy4 zEVwc0{x82x@4OR?``=$j@uvO%d9l%+D;gagUEbWL+p&&caBy&&y9rnPfBs0Sj7v*L zx2iN_KV=-LSSklsF>5%Wg!|KNIAobn8=f63#)w=@y%4AQhWB0FLAS$@W#n4fR407D z8FStB7&};qDMPMSK#xEYp0Ah0qW`&|fUmB6XXt1 z9>OypYCS=g$7ZPs)iCEss)rqPp?ciAmp!QQ=gqepYsxx0;f+(~Hze=8h82pEm6@5q zH7$3Q7H&=6+-jhz4DrAv4O1d{gl9N)fu^Y`~BiG2C;Wuia6X>p|t%?A!7Bs@Qz zT*k}S)HXTV_*H$Z)2_j3&@kp4SKon)w<%=D@f+(}jRd^k`En^Pj@K-_IysXbklRy@2P_X!jhPIevMnZE+CRj@OEWvD zz7BhDA)NKh%d3{NNoi)|n`yB@@$eVTJQcWrKRvV&R3hIuXIkN^EH*2MLIc{`9>QB= z;NXaLo83Y#NEFsSJ3IT!ur<7*ydr$qnt_Y!4Vz9CE;Y5HoE*GA9xMYcDXEOQdK}&( zx0hka9c(KiTb0CIe+6%E?9)(jeUnB_bE?TfQUeBFrOI7wT1`xv+^5W{s;Vlgst!AU zp6sm*OK50lM5kaI7#O5yWH`>fQ@cS-EDZ}$SXk&S%nY9sbuFmh@@`#GQPEI|1^%T= zm*mT?Ub~j&vA?#nHlFzBkGbHpKl)=EKlxvOQD<4AgsrE`n=hwnh&rR}Ctpyq6IzL! z@4gm*N9UMjF?Ov1Ld>2&>_bcqS`(}QBP;7IX6DewAfE#<8YZS!Q;k8<2?^ilV)Z7B zizMOG@hp;idzE-?$J#RF6WiL`Ib(u?g8Its^w_FbxCC8R%>8dnO-)JBOeGIhInW>< z?#SEafjgDO+E}(1$&Z~YnA-VQ-aa)kgyiHS5+4>$;Sw0@H-Gi__YYa~GBGnVva@H_ z)Fg>`?AdSpz7KhXYHx2Ja~{-5PftHMybjAO$%fS$No_Vb+;rcrIMN zPQ%GrC_@9+c;DRoGv|j=SY=3P;)T1x_5}Ig%Kn-NJGXYLMX&lH%Rl5Ufl)pRq5nQV zKVoUg5N??myD<=J+i4eWhAuYa`}apgY}#$E zlU`p&L>#yOsT@%rc?4e z&SIqcoC-|(oQd>s6z&G&(f|3GcE!QLp{c0}2|717x6o~=^I+?8ze;DFqt{Zmo>OL< zF|JHPOGlg-BaV^NZ~5cG6D6gFKrHglxw#khXWOEL_lAtrS%}2c*pOSh{r$%e5Hzh^r;d*9sOWbjvlpiT)^Zl^sU|_q0Ne}h* zr<(#s_4ve1e}0!0k_QhSSdUdi!Lp*GqgO1Xc*OBLz-}YOFG~3iE9+dh&nY)NDNc^z z@BVr>gqK35LUM3uC_Sv;#)dss-%Yq6vs?D_KjOJZmwrcFU@pw_uFS*sx$hXe@Yqg7 z@^R$yiO#FwHi2z7VJY0HWc&spioQalR(|{6C~{uw_hn@bvdaLV2NNt_v zff_<0@|-6Kn{vA6gT&jInCWMNA;G~kG&H_nzuueaAQyM54@FW|F7XoHZol+r&13#w zLL<`~#5_pC&{|Q5^Cv5;xK!hbQ;0l&`|=7-k@v}Sh!m0MJHG>nb$=Iiux+~zBdh5c zh<@Qh<=>^8tC*OE`)lKGV`FFR`Ga4*qT}GeEgrVMi1*^f3#mv9cwcxj*L<1a7n@FI zx1@d>=Ywl0B4(VvffhHSu0`t zjC|UHd2hPRv-OGO;bODs#6($006fu)7cY{C(E7c68ICJ%fOM&no9|h%m-xTjF#8!; z#=rBRIi;9nG@JC4O+s=u%-uu6?RI89lt3=_CL~C*vS=9@L$Z`J|5j_m#^G6f`u6e) zx5;+`6x5zOcka9|DS7+q6{a;Wxl1)=W}gfc*Uj>e@G)&6Bk9tTyDjcE4nv`Do=XL{ z1xz>5#@B3%yGaCxgx7ibJw zY&2QtMIr14Yy5+|TUg@RvyysQLgf0XtB%&H8Ch727rK(ICu-s$&R}g|dm0Q2N$}M@ zJS5G78Ib$@u^_?SZ|eDbSwi>5ia=QrCOxXPH|bD;M#$~#kUH15m|j*!l-Mct z;K8M%z11`*dQDA|-hca2|5mHO>+md$pu>#$oSmRnM$02-S|gQJRUgR9W5M3Wc6`D` zx_rW>1@08f#8OA2P7wiIv&YOT)|5RzwC-lhQl%$P?(6DO^>CPefx8UWChP6(efaR< z`;3ghu`z8}g2G{IsCi_K+*xW!Eb`3vwP$;AvP8FE^uIiHweSZg9|ZA({eYA{ z(aCPp@c6r!$;iZH2o+8)S+IX{PFu;eGvR@$DFZZ#--ipS`>q0zb1`xZ_+m%H-En+& zRF##LH*ep@hf)fy_QQt{P!-Y9JwBDZ9EI*DWDr6nWuN&|T`>Kik_+!_GY}U$=p_Ij`rx+i z?km6Rj@GuF0_aw|Tf)f&+%{;QJb4mEEjBSZcks5~(gfNXDvUzl4Q!8?8kE6P`+ilm zsJCw=4Gd^u%Wn$`CL|<~K@4DAy~=58&Kb|PtGEXVgf2>v&~C#jxa=ioaNmtBG*C48 zz0*w|Oj^m~yb15?M^Pp!tKKm)TGSb9Z8tV5j&)+%+~-bEi!RmS2Jw)@5ET$eQE%Re z*L$BF>tsBvE{2u_t?uFT=T-Hxn*urg-x~t1X;j$LLjBxAmT+3yb=;X4b$EEF^7N?^ z0LgLN)+zhn@Q(Gj&cW;eeS@-=!#ueb`g_?c!j$4{hq^e92A|8RKM3yISmJoym+aOv z2pQnuakMZOW{HMooRM)kvC~xBvu0>*hbU@$My>uYA)vDyKqDI)R)}5z+CNNFBQ7I|keDNNH+ZAMvdg+L?(rYeE0p4%{UvSg%6e*Y{VBv> zRz0vHbG}c`1(#&C)Y59R5yRWVf`0Gl$jZr~=iqn^Rqm)-4zCi`+|u&q^=oLzN%gXj ze^0BHvU5=Uo(CLv@7@KxVo*NXPk){l8#=g8^n6Ban>ch>}9I_GAyh1q;%od zQu5p>+k^B@_aGCf;At5dEq}0JM$jMcWPwX{3?Ucw9&JM;KFnQvXP}8~Wpi+~w;eU$?u(Caq7P4XM{YzY77Tp7)f6 z=o%*{=gwkZ5cK)pOvO}XDlx#S@0it!T-SABhQPRf{U!rL;Qg1EIlYhFs+^YxroELj z6+S_qs$Q!K)v~L7x3Xd#!=fRspnwga+dm-SXO0>>gF@27M~}XfCg;F_XZzI`%|9?u zNlQ!C-CaPZ>KW&JFbsl&>HSEGB?Zwm5a@rb)8ff!`(0-HXtpND^{sN{c75;)AC~M6 zhfgg1wDRpPoT*hjgHlbjOL)EBgaiZ|3u0&SuU=(rt5J%2#x(}v6nh>zdY>%orEZbj zx^?SEibx^=KwgbCmug04<}|>AtXgIE+ns#dp?b&DNlVu9EgMGLA%|h{^J!hv6;P5JG-*_j=AY%*9n8wky7~>p=5;h?3!=YBi{&q$KX>$OZEygxm>f zSP~5^S!^IL4xMl^9f5e;gU*@!Z&mAeKVGRVLPtzbe#a0xUB|QAZuO(%J3A_{k8n~@gv}6{q6Zq>+!00m;-&^%^(ML0u2l@oLxGfN3oHGSD^v7e>8% z_W*JOM`Q;V1+#6QZR2t$-z1NC@ULGg`88XQkPpnxHiSh5G>Zodsdx5Q4Irt6`Vda7 zQnJGBV$;r#@86?Bn;QU+lk%A3($dnxo%lmczDZ5(j$ze0SU)?pqw+a+Krtw$gms#x zL3fAIf!LS&?4WgLc@XEljMswYH@{0#7*Wu|;y8`|{K~?2sUCMzSXx?A|Dum};R-g- z$Xd*WKg8&U*rbi<}|$~pj-;t;XZl#H1J>TcB~AeLJ%pP4Y{Ob zz&&fXL|)onDmU$?*+sf^mK#=a=({Pgp+SI31(N}l&R}n+h@(bd@c!>F$OBh#(nU?UGXhl(j&FSV`t@4QF{OSm(?0t~% zCP$t5w2${8g?qXuwMz!sq%tE;;OnlaEh zIxG(r()aW%ciEQnT?4jEQgS3i+W)Gsj-Wnf4<(MHg#*>3U)r;~_ZS_unB zcg%4yKsrUq5>4Eg3Tt6Bku# z$EBEolAkb_1H6N1hvJYxCGy<lzQD}N z8a^~M6kUd(rtTDx_KprCC>E>}pGF~=pYQznIep3y9iosdNE#g)8jNxA^5q+(q#^y5 zp8$)OkB;X`BS=_#WP~9&7(3w0Heca~haO5g8=^50^c zC;0VV*5>%4*U_HHT-oO4=ChTdD1hpGTEwKJ!`9dYfbSg~I02r~@$tp(E)SMx4-Ayx z5fU~-OhDVn%+7wLQEVCopytsd%#**nX?Bx!w^vKwCMI6vIgSv6s&1x|&5m{;t1Puc6x$P1LIZBRu1Mk>yK4YFE3*W+6cja>DbvL z0Zm0v2=wNf$-o$-P+4DoS-ydZZtEq)o?)pbWW={Q$Ep80WmRj_ z2#yNh`8O%cK4uBfI-51phX#fP$)O zzd*lb@o<+#eeqq#*@r(&xl=v2u1ZHxe1NRJYc=r2w3F;T?N?GjX;8GT0oG~p4iCx^ zjQ%h~wjT%0!IDT4_|eim6=0Y=;=LE3OZVE+9W!5jfSuUcqIbGQY(y0VjKtraMG43* zoobg`oSadoCr8`AU*i1D3-|R!*;cRe+SVPKBD3<-r$oq12c4*VR9;5r>NR3EdEDrc_DDxZr}&)4Z`zmOeqhBT`2{ zxUG0iLgig{dkL&n{=f0|D*p+C8ST(@+5~d(i(ehAOY3EEE#BcTgly~u_B;@>C?Hd0 zONx(o|85fr;XVCD4p27&41A%Ru2sxL5M4lDUvI25w;^whprpTjJLvfM*mPN3T>PQD zya{aObt+*JUtmwMDfm%6v*;(VpNbj_`8w4HgY~D)W~p8-x@!LT zYe*v(G?w6lJ>H6k;hD_SChU{<+`6T|{Ssa0B@}pvFUG-}V%`%=%}{?ZeeHBR_3ZJJ zCsV~nx^iTZpOW6ZY50|;?8U|Rt=V$p_cx%C9_#6Ke|!)M_aU;#AreZ=ZtLK#p%K5S z3wZ5FX7qbo8~=i!)@Vz%YK}V0hiGVM$1~Jt(81G(d7%K(N(6p!T2OfZA@d?nO(Y~4 zTWshj+1TECpR?$)di`(cd+U>msiIy7huuEjEeo#snAfhk1NDuHOB*XdX-pY-mforQ?8@aG4nw(qe59i8a($$rUZCku?f zl9Ejwph=Z78Ym@%9!4EGMR*5xQUMR6QCG`)fLRNZ{s8e{#DTJYV@l-c2a0@M4&)e;OGA?qjvR zz?w?UbKRp9FcaLC)y^q@^6ad!5q4<6HDWT_#*i}WVMAeCVb|uo@(D~He|)=|O{wUs zxwWuZhoh6HmwA2^Z>CY7{0Xzf$BW|5JkL|j={~Jekq<=joy@(##g;fPrbHC$KRgub zkD;c*p%%@>$;|v&A${@d2Z^9K0Vf7-vo4kH;AjL8d;`Fk1!#M*_Y)A}POGJZy;;h) zfhQO(w=;v`4;T_)&w$jC--`?h0o?lnYAhEw_aSV2+vg`~$V3S<*2+X}VmLY94P4xN z!05r?48z0gxVSc;uwh)g=#4@C=+Pr+O}Ali140hi;K?Okm?R!5C@_eLrQVW_c^w}w zqp3*-WH+$I0Co$W*~?z!@S~`lV7z|QlMjQ zeh2WQQTv-)$PiIIc@n{C)Y6lu#Ru68?71=2%@%t(=)l)01%D0s{QCUFdbBJYCeEnX z*j8mlk>;U7qeY9J=G8XJ^2Ti%6x#>O{c_MJ<#tD~i-e_2x_JmI-54=_}OJ{YN&9kJ}K|C;)R zo=PXP`L5*AC0@8WfONMH`ch9NpFF_}2ng^5Dvec?T20&G5{~GBezXa2V>JiUp~%b% z>9@Bw-T`Pbadk9J8Yjj3uf^NJZ|{P&A0bG=GO9Hv|Qd~>UV{ir(3Wp489luyrVSG9N! zlU{)pVzd5ujy*jyv)H1K0dPR!=&Gc&G`Icld$v`J41m+V4eI%fPoE<6AdH1`IR$le z+}zQi^uQHC0$hWc-K@d?s((-rJ{j5fD0*qoFm5q2X0=~BvdXUv1S)C^@P-piTc*TD znxj$>dN8mu<){^yLKp!i0I3Iz12n+h!DU9p#I$@dXw(#L1+`AseU9*Ho@O(Qtq7+M zQ~sAP9BE$CZ@MtaH@`)C{1w zVM=!bnGOjAAY4ELn}h0P=}aylAOJjD z$S_U(`u%&F06fS0T#2247Gcj8`*Vn)h|;sMbxoQeof>g8=>Ic0IJ(prG|95pO4{18 zF+{hkyMjQi>$&;B^hex>&HYb0izB7X)SeqJ5cTx@A@(>DsQ;*IJz3ACfRo#t4rR|a zLnaI4QF86vmcjOTg5#rJy*Fre{~qb%N{B7M5$zf#=5*yk3L+H#d#uLzoxfdDv*DBf z=MO`D&qY<_V=W5Kof6tNK??Zyc<7OiG@V(xkLS`S@;M{q{}zBW0B@QdGLkU*wxa3Q zY`gVv(Om9+cUKqh-XzSHB%Fyd&vKJ^Nfn=iz|V)`V-k;rsdX9V*X_`-nKlAQ$($ zJYnPw==CZ4fP<5FPgm!h@sZVw8sI*vMj^{6G{IpAgzL(1@s;RyEsd0VZ^Ec4VRpJdBM`N8*B?eD^?uNCzA5nCZ#fO)$5%ssST~V`1$#DS)3tN4kali z^|y!x`3$^G&Q3gJkE4p!S%iVRxK6?UMWZ_yi=6kfnSTlS{rmTC-n{u5ra^oAFZCUp z0_CGN!V$s28%DQQrJ*dqwA0kwoZNRhu^)ms@=CJ>SA6|V=4`SPh z#jqpU1j_J9Back=+vMcVQ1&{EufW%m^4sf2@4|Y4T<=dQJ^Ba z0C})?R!3u(3+hvV7)sr8b9Z+KP9X_6;_u(T11AYQaPKEsVn8DxU(vj_cr4%$URNP%FqWJju>m(e$ z3#mR6{N?z`E6&vr>0lHB;375!Osn~(aB)$QdZptnuyK3>aRX*Q7;#B}QH1e66WAWs zH9qGh6@WjLKx9?y4rsM^b(#KyCyzm>18i0MeAjTgDFpg%iQ9GHDghODfc|}liK)T1 z9Ezwo$esvAiO(R%zc7Jtvrw3BoTq0jZUEjwH#GA0&Q6f>Zjh6Ayrz}-Gget#-%+Ix zVq`dwtw1DC7+3aNBK{D-G`)R&@uFVO6Lp4I+1VFL2X$fmeBZtWa)P6i6THuPssRmc z(Ds_vM(L%RmzPMfS+^++oANQ#V%|vr3K7c#tdQHzPwA`IuUnU$!3fkstmpaD+8u;P zApN1q6+6y7GVMx|@$@{FSsto#W(M{bWGb{C(50WI#IILqvrV`!Py%2cHLe7av!Wsn zaA@7ho)%1G*xoRPq;9E$)G^bDBWB&3_QSIDO;prbhd$Is*LFt43pZ@976CIt-c-|5f}j zquKfSHz_F*ou)>>`^R;fdLMS^@eaGfU!*YbVDF(O$~=5XbLUPl469Fb)$hZE15h5p zg2J0k_^bnum6Qzqabd6no+u3XUoN0dU^0LVmnw+-53$|twx-amRxk=zGYmc@6r>MO zcQ0U~0gMXh12T;wv#%XiAh6wdx@^?eYLg#+yfRh^Dmg|=B()+CY7Uj!$N+(G+#rWT zWq=9PruXG6!qQK$1Cyi;W1{4P2TxR0o6BrQhbkQCLCvc_-7Ns$NYIxrU$_O?&+FE; zsPmpC+mqE047}KdJ7N4+K*^{%{ktsBaLd*jdNGiHn?Ra^h#a)7Jsw~MfhMIGEKTk8 z_DPx)(AkYxd>nQY!m##DFd897DQMn*-r27VaT<+wK4gkt$@aW3cWSv z)P46VnUb=y%%exswfhs0e}w>~)6|+^t-?~cVeXpy5JY9^_~ZYTn>X|HMly(wm`wgD z=(CO?g8df69g^Ze-vCLI9ShmI^q(1^9#(R~v1}47tbeU+>9cE@LUUvTi1Sqp!)AGqz=Z#~S%A34wsMZ>$9$kdE6BW?7L?&w6#N@+B`82o zBsCBRs{^pxh^J}3GqJVCeOp0*y#Y2$K}!PkE?_b{G@efEf$P!o*!$C`PmuH;y*A29 zN`HonZ})KEw1=Ewi%Zx=L}R4_y%y%qNp^M$<)I(L57uRw$Rh@Ze?%|=S4DF;M6>Db z+sKVNzW4_zL%0(=5+q*%c8vUc&h`=zIq%n_4oqwDd@Z;32w14 zp+nIAf^eu!K?CyKY$k?z<7HS_SRa)S3_bFJ&Q|PXiVWUWgE^bLuo5=>Cov?^FN5Ya&xuG`MxEzn#R)>Qw zXG*Mi*Q_ZXihxMYs_dgjBcI=dD#BFIm#Mfg6DbDka`)HZAdry~h$drCTLD^A7%6uk z^zOB{B1ByQu5pyb!=6&x1Wn*EfTyXdzEfN_&xK4fE0v%5;D-XD6qtG}&^(NxU+^b) zF)TMWHY$!bW_~&(7+C5Zo&S++ky)RPtJ3H+!eMuuUP<1Ge>0N#D^(-y~l2B z)S^FIwPt_9L+SujD+vhYV4wu72J;d19_lX$2@Sx*vZfg6Z7*06;o^R^DIafvs`dls zSm1mO!NUdS0dSJQXlgLk5P;ya%vA4@KQiY#?|@)$4UL&^)+XBw{nVT;3S# zJQL=Nii$$a2Ebikz`~cY8!id}T?(<+0x~&-{v0k7?Db8Ewgk);mZd3WYLY-@KC#jg zh~6Io3v77ngeD+Fw}oU^Kxjz)Yom|s0lR@y_BSD7DUZGbcXm+eVMjB^CXSw?u_~O7 zYeJ)k3c~GqV2^_E7^-x-4VZ%-kP>hRz$i9?0HQ3n}`W)0uhKLdBnNLI9b#1cDI zr1VTeY6D1WJ}skxK|$Owvh(K7Pb;ab0|%xM+NkjWs0!OLeqHZF6W~_RhHBi46-VtT z$e>>@za1U1<~4HTUccF9V*m{^bDTj)h}`@Sq5ZH)EE=L-q`5W$nX5=94oHbw_4(;t z?+LhcuaVW-0t-Nm09!X@u>SGYna%FN0R0bJ-QqtDr2)$2*V{ITCo;H1dUcx@F~}K! z!(hx~y%vVJX1lw9Ccr`L8yFg2rL{i`ovsDAy{x^+Uhlel~s3AWutEN%|TL`l26Ls}A;KwUZ z_C^&2)tcaC;a~KJz8HvlbmwZ8z5-qqc*(C|a=6)9gX}pOIE6G0yZYk8BL8;p81we>eMKA=0#@D6puB5>FMbNmV&G-1CY*JD&eP{EmdIHe?R5k*432( zf`R;=bM?3<2=b!!LTB13`vWz$fZ&<87rI+*%Mr&2hs{Wdse}n35z$t8{Rx(NT=$f; z*x#QTgKk_PE?y#=Uqq}2a6fj%HcQLP!*yOlJ@)x95Q68pAgl~2@wZD2bA z+(V?1YQXHmSpa~_pfQs)nkPT9(k%ycQD`&DJCLU(4UR}fkzE3)>z@FNgP{OI0>->g zKRxFDLk8z|t*iVW@!e*t|FV(q-*FqyHULKe457YX;t4CA)V;lHBMbg6{uvu;_t_}r z%XpwlB0KaWkuMUaGcGPJFk+aOCI3U+M$k3oWA;}^iKE;7TW1UqQ@vK%b>G8n;1}Cc z@B;z^w;HaoBWqIZbLI_&@!()4GSP+@5AU0u_cr<8-DOv)RuMJ6@n_L^Uy%VHD0`k! zQz#LLyIej-2j*}f$=|Mymcvn#&a)$*vw~r7#9;x1>W?G=d|#l>{9!tu%Y7LPeCYJd z3{vwUZdyFC{_)4FB}e6Z&K@g;tr*zYrl3v&TT=%lwCN~FQ}$4&xh#6qiv`R3IOFAz z0R>^o!6_6B1hptgphV@h;2Ly;Z15VXLkihL52r;hCWJ2(h-7QV{m6=V*P#9;+tr`M zfIk@+;;g*9Td=91+;i%GMcGzN1rm8A0U5G6WE8mx6tM%`pA#7a24cs7i8Ts(8oBF; zR^C&npU~K4Zu7y zY%1a4jyXDbOBfg(Aosb=duUKVjr0>URt09 z$RkpCE0{C)DJa39dBCy&V+6Pa6Tlfg&?RQhW88!=*}=TX@8sH}KY!USn@EwU*s^tyM%~bq_V33zGIQJ@plk}XOom-N( zfe74NOu7t*Q{JVdbOGt72*?y{a10>q0bGGj2i4}AHSdwNxEt(L<;KM%?{nr1ndjgU z<&<`=a3R$QdHf%qM*Gi!G61d5LAydmTf_yAJZ5vV&TSb;q#%zH(sT5Vv;*YPYkE$5 z{`(!oQsTct`0tlso0|Wan)`f&y!tTpjG2Gc1<$}#(B%v6AZMJxg|y1&53loS_lIEP z#gwrtoqyWDkEC<{c?Hkx`_q|*r!&*~GOVM;Ob76K8m_S)7s_2123_~*cm@B|43@Y# zU#)h^Mv+AIMlCA~CGV4sq&>=sK09TZJUDdS-(_g~*d_8mmjXxPqWprDzdWsW+n&)D z)p{PhZ>G~8e4n!0>L^q#c4Ph41C(54b*5kNnck<)_SDHyvt!}8wUqO^GV%fhj8{Hq zN0Ya8s3|DqKu$tvHwymO5X(2HseP!JcWQwd+C=Ed4P1IwdJSk`HkUaiG zQRCCgv6ub16P{sbI+~L1pYVO(^a|RZ>|6B4<1KJ@jRu4~-1oQ9 zQAun#kzHe5Tl@L9vaCiuAD5|l%jSc9M!g1a9~WmpJ%6mOP7 z3rxa-GY}96p^UNxT~|ll<_d6Hp{+BV-g|X8h}lEzsk1k9dOomPb9o;hxR6W!*+)#) zk|Pbbh$WM(0&MIJs;urYfIkCquYCJmLk5S|AR!ckeE+ zRW)!J0ugo(HVhDe3VaD18eAxRv>5~h0Sel;xT-4Q6>JK9FpLsEzy))n_4R(JA;3eQ z$Lt6WR;~|OeRVnCAlBK`TFs8L^l+ltuF(TzwTSD$JeA_LrvX-8)IFe&WMf!1om6`V zfRMk#WEpp16~tQuP)$)3;D`n1$x$&+Dx3wO1E~&Pxyx`)En7&6&?z(;b8Q9%9Th#7 zWkgZocpmhl>7I;CR{Mo{QJ=&(opRN2C5dh?#7I!&xImeIrdJ8N-0;ZM|f z>SeRnJ3GQE8Oc13^xSJs3WR4C1bj$-)JDHauvtL%pfrrVj@h(z;t?j!pnjf<0cYYL zT!RmTCR5sX`2(wM>%w$n15)>n)ocY@&OT8a%9>tkZnZ2NUA~IL-FJ&O?!K3x{dVc* z7?&Vb=;048R;$wJGaaR--csoy*RN6<0WX6&xUVLQSDa=mjJYe*oxGt>T-}fC-ac$h z`au*qY;>$N<1nZ|yYkm?j1+Mg)18_Wsw0G``&RpHhw5>Jx&uH=TCCyoQ8yRnG%TK) zJ$k9I_uy&r0S9qQ6-4S$u`rT7coy`3-@0B_Qa1dPOwe`OV2K*AVYhRrP7vhtaWmpO z)AW2T)i^=+oq3tDAlDdfS@gK*b*jRjmg9xMq|FD%eeGf@odtSb`hEE`odcC)RF}to20@!?(S$L z=_zhxTt2Gbkq|zcr;3OfO=Kw-qIj#yKlDzydGzf2OstsVe1P6=>q^=J62T({h`k(w z6C4iWz=45wD8(Mq!fQA21JDe9fs29jn&W8>mL9y!o{VU$8;GQOPdTeGIE$ZCbR z0<^W`71$as`}qd!XW;Q}0OmsgoRYw~0RRNrtVy14TvA-TfpOnX9uyF3B(Q;CHY(MG zz=v~mFyc`oW-E_huCnV)_E9vdERu&n@P-xqqpX%0B$=QC7nh6B8* zn=lm|o_q)=F92$ggFruNVI>M@d?0TS`5uUBBM=SY<`EY&NXn2EcZWrVgc1RK;Q4|X z4-vBwQOs!KQUTy_VCT^QWL!rSp~T#MH(>;yLt+ED?b~Te4`){q+V=U5xl|a5!}ls? zW@f}22i6Wm5Cc64Y_veN!4bs#sim9(TcxA;(>e+vmoP+GLCM8)1(cMO5RDe(=OcDV zwBKOKSU`N}1EBJI!JtXyK8u%MfBGDb%p%rnps1#RUiUnh3X+fLA6SRe6o9?N2qcp} zXdW%w>Gr-I8C8`Sv8d5ChirRwacn|6A} z&wX=H%u|0Uor4XgD@q)ysh18EmFJtc4Xv4>u?Np=X4!pBDpiWVqsFn1zn zC{EdObha+zp}<{Ybwd)i0jbQ88~6P7e$j$@amSSHq??L70;x`-sZEk_N(>zp{pKnPhHebe)JPb| zfrUZF1vCzMiOive#bg^`dZM2iQ-qC@e5tq}BDX#huU+yx4`Ew-}j_sAYfn zoK$r9lL*7)Eb&sO6p}xCaH+n0Z!CL5R>p&edcwJta0$9#{t%HCkk=Tx&_bPe@8KqJDxbox%02Ox9DwsZ`*T zTic@O!7m+^m>3Eb0(uS_;P8#@?LPIll*pNqA1hLD<`)N8$KP{v0Wfo+9D zfN~84Dp^ZQ3#ciLFr7xFrs6;*CJVV50za-_sGj_{=FcN1CvFhkS}xLBTU!&LW7azB z+<>ne@qayWzqnkpsQ9V2g`R4fdh1l%u<-nzRX0{cnWFH!zde0MMtcNW>KoTNT;Ex1 zJ%9Q6(!P^F$#U27)(}&+BtNy*_v6~5P)>D{&o!%)KP0KwYyO1&Z87yfenyRY?OC9i zHU7!U_w}Yg)y8)t*PXeVyUzH?VotEWEI%Yd{r>Te?$bTV2k$Vb?c_~PCn_xY zMLkso_SYePDaHk{6MbrelBJCUFw0Nhym)XQ`%7tt4mKn%6%MHT$ST~t5%Rqi6!X`~ z$>AX7r|F4xGGe7N2FME7P5>6PQh9zeKpAbGRI664c2kHG_Hd>K}C={`G z$@N6S0-_%4>UNdDSrPwUe@oY{pDFifd{a}Y5g3OZ)Ht@jyc`NL_B1$310LRNcyZwh z2XtgpmT}6){v0(lR76C?0q`z_T_V6n9ULCg^7E722)PK77Uqj!ICN}Uq^6pk45uxA z14jV8mHIJkFN6$YTVzVDXAu8@3wMMvMyo7iK7^Ab!otF@V`2i|z9j&MYa;}QjUF_S zJB*AkYHLLhk0Kn;4)S1{#=~hsO-KKYSqt!Fw%uU2^Y0aPB=1}4%+S*~@(=h}h>q~L55!G_u@*611wEjC( zj~WVY6}vF6&1LzcG;D%P3|6GJastytA$Ui#6g>El@{@AQjMWV16J!Wl&g1HO=zhDRH|=a@B%E!j2CH9G%PG(0^Pt75&Gde3RB_0Jd92=`r>s$LiVrA zD8z;qK;xej9gQz6ViFp$we@UYNA(T?SM)lN+{&2ZDC8_0oCoxyl`wXmVAHK>6J{tV zM4`a`r=npM&}ljYP7$M1Fy)tZwM=S2Mcsf(3(i3TKiqrkIbbj;EYi*>9%-F0cX>!CbhqpUV8yIhjqU~}j zBQu*|=xV61)C)cP6ST9tYvMBfnVDv7eZ64s0PTWt zmVD{S4=Z{|FgPRz<(@>$yJo~?Yh8GFc=(~L>`O^Hw4lcOfQYu;;HF1rY^<%p(0Vzz zd0KQbeitSNCa><5&i|cwn3Co)>ID^6AM_F_u}m5Mx@})t*bDlsqI;{MW!yEM46Ew5 z;L!E_m!+e;UhG=I;bLR`L#76=Mb?aep-_&YOPq`E?$qr1anJu9HEep!op<{aH}x;B zQVw+%{o_0RV!5M6Y8m+YbN5~<-`A-JLK8Q% z88<|-r?0OOMwkx;1qJe7MGj|5NQB)u$jHgzt5tgQHu1Oa{8{-v4SoSk z$zw(EJUNXH4aHS<&N&*`*yQz;Wo2E3n7_lq(gdDi`0k*{CCfRL=#kzz0hPRacCvbU z)X-lgz&^nJGdq-%MY9A0?A(DWOwG_7;A>@GfwX|AZzSLip`f5p@%HHevDDTFR9H$; zvC6TrF$MX9`MJ3%kke4c_#LY+Z~CsB2`efp0)XOPRb9n+t^5ufzKkM5hBciL1FcQh zNi{2K)=fA?sVbvvwji@={f-W;k65}ABlLr;eonw3a0HwL#21)xQ7CL|Y|0Fkt&PWe z9{J_xC%*rb^#5<%s{^=KX;eiHrEi;Eknu^*$?00JlYd1bAHg^>?aU;dlT?P)?-l8+nx2rcvLHJ#^diyC+1R&4qm8l_(s z-sj$?*SvYdIW9j*_+kQXMz#_GJqL%vmU|ab$ax<07ry9t zvmrfu+aIy2*0f5!SXEXr+%|rrfql<@e@zESJ;saWaHt2SVoAEFUWeolp-!JiJ9}b| zC!e^#E?>odl^^kKaFF=<^XL4}ckY104?J9Os!$!A0qCy@$@X9x0RsoY(=&+BXZ*UV zf=6fBo)o?;?;eA=wFG&MSk_e!;~*5j)4ULVK=^*#<3q@c`|Ip}i)%-;V(^M==_v3b zd_VG7uDW?%VQWLt@ieCw7`+QiOO@E(vsk|Qy)#Fb6mmyOdrRWi-Onk{%GT_Bv&lFU zZjH;f|I&4ThtYcWy;YC%)XkdB_ z4-d!141n)5kc5+kh!_iW+?MdLv$*p6qZMzrPyDpP=7%4?oQDZYzNubC@SD#of|z*@C_SYGTYU z-X7UX8;uh!xLpwp_6|HRI_}{ggstGvOJHXdgsJvLhow-FTg6~CFWr*Bb;?1H;`<)8 z;YxTqKX@noU3Q*b%TK36euqMoUhVpa$2AF7Jyoxgo6)r|TmCP~-UJ-$cKsT@jorx9 zMCPGXDpAB^o|+{|rDPUDqRd0)sWLs1R3xQIg-WK(iAsi$G7FjKP%^yh((~+Z|Bvtc zj`w{#+U>oI+i$q8>pahOuC>l*33nn~!jh=&6W8P8ov)u%(>A;~IP(0}E5?vgKb?(% zdg4KU|SwTVJ{%%?d^2_ZE*PZ)O?rZJeq@tn{G;=%_v{|T6SV8{TyKkRgTwI(k zuaZ)OifNO!o$Cfe$p4pWtdWrso7)fMQJ~`8M!vbwXc_W%Zf_&+zty}qPBs5@?JKtL z*y9WHTilr=RaQxNrbM1gQWt8bF)@7>$sWgAwu8on+v41n#5JCIw1Rf~EOnK0*riHu zoR*;8u*RFaniczCB6HC(IjwwmQW`6*_gPAXi}$*eF)Z*N4!m;et2U?Oi&eKr zwMr=mBoY%OZhG;Ve9g5DEVUF~`1$gg@-AdX-O;j0!k-RmUcY|b1VRHP#pLo*bXG}y zrh3f0`2AxUesfqWQBFLL2io^AA$!&3yaG?%xZZ5fnP+e1Iah=QzVm5+wV>wuR>#2L z!;mVkv~`sFeqs32VD}K~2qev==r7WsmPHgp)0iFUB@JGKo;btp>PERv0~8rxXi|#6 zgjm(K;b8Z!$22CWIQRvukg9+I_+9^~AJw_?<4037Gk=nmSXfxw8Jsu~hAUDPk2Qm! z29`UCpL_c^fr5S7-pbO_3_sYW8L!|QnA})W#ox*!{G4{=!{hYwKW6o}s zz+%7V&Xs|ZvUTM>#t%C3QzY_N1X6rQFXmL!3w!Mz<5>7$u_dwR;dW}RAJ&0{DKAW`ku^M3Vx`)F~{R34_rG}Q3yu)L!E zHYzSIq@%+G9V%Nb{)TCTaR3$21#{0VO@XD*CASY)n4m+DjZ^SN?1VXX<6}Akt8a10ufCXgJ{4Rjbu(s~s zJ;A#wv`^L1oo?Zm!QdtF1Bo<=3HA?a#J1nblJOCO= zoqm7Sh_B&pKk$EIkQlSu5{>x%etdX*co8nY=r0-o_XErwqJGwQx;y5jsx6zh;cX4= zKlPcos|40t7f=_9MNkwS9i8)bMk^~Tu?H97sn)cw8Hb8gR*wt=#RR4u6kqTU387oO z8WA=CRSqR;_1n)cKNUPh5Bqy2ge%XFl>_Pl^_j+ZXg>GieD1OGcbQK=X2M}GHYjp( zV#49u8z##0#^hn`z_#Sa$3pMi5#F(YCfFRvD&mo>?Cg6i@7s+Z0%&z%5g{;MwHVs< z>#ee98yXwU38Bd1QFK$`wk9v5ZW`ECQDYkifhIxKk#v5jY2e+zNjU5NkN4Cvb*?nZ zu1kJ=f2zEj_cCLEZIWEA@v*Nbts6>ScNtPq8~I*p7ckU4yk58OgZgDdj`Hf>RBtW) z_AC4$7Akhje|*)?FyYWTC~YjJ(NurYvM=;TsH=I(%E}UA!G3Qd$c@`(91P41!izAV2B?))*R2rVyqUNI z!3~WLGAvagRkVB&Y&yPwe_O8~b!dXF2t`~+XD1EbDBKcNjg5^r^hUi%PlwJz1Xx%I zSooka(Ewk81eF+W?%1@5w)V#E;?WUq$ZNn@WElJ9<%Mn&lzhGc0dxdUL#hTK2Jkd( zR8NZ_!2<2KZQBMnF9EO@V1HJG(iS2+%AP%Yq@|@v4uMT(ZEqhO9nDD?v`^D$P^#AQs5M$QF{dvI&L1GlDBVznb?#du)~F@LVF3m?OZVkfpw1YiT)rhfpj4%#o6x^0Tig&u4n8 zRTcwPg^CD^A1I_b1btA}6azE^(V5oO)iu*k=IoE|e{!2yMh21kz1mzI`B4@v)n{7S zl$$>CE$TmeZT7xWU(k08x8v4+;d{D|N^Fu}JzKBzCeCxjMke@`;a&s1UK5!ewa0({ zwlQN6bAPnWK6`Y=E-;wuk$1}leUL13F3tz5R5XXpcT4FGyaIdS7^)tUtF71sM%MIStPKzJ@B{#CBC zG5}hl4%Q9GyMUo8hc-q*B&m70E zW6(Ws%>PW-;}_?~_ngXeVTF^ac)7i$WiYm!&HN|;1*V@~%9Il)A6DJ_xE2K@s#vK0 z{h;N+`zW9(1Za$eO6EtVeC`rlmy^^$$W!O|G#aH;f~|j~XBA)F6sA zU%TU_fG=59*HC|=>?d|h!rQj7gS}M>cY>?4Yf&73PBUg_+vDh04|+aYDZU8$Y9i4c zSZc_dFMS#Z^{E8La-V6Cz?&><$fAd-Ti)&9e{=GjX`}UE`r9R3ma1m0MvKEob!$ikQT9KhHYe(&8UjFFvGnE$D#b>HWQ% z9&r@ceh&SscQfGWLmSRhDrmWaPP4j=_6T$Dxb=WTwL|4$SwYtcIp;@FIio%95zOAr z&D^G~?@g({`jsgiW4GPOS2Q&S7oU5we)*5nX;y|BI~FcH;#iyuuO3oeJ7D3z{=(|t zwu`xxqK}2ClHUH|x(ezQb?e2%7{Q7k7&KKpZ{*g=K!mm4N>?5<)o};vgnZ~JsjOV? z;^H#)@z)8Vs zdA&6>Kv2sJiuo9OJ0;pxTb@bEkX*F!iL{jBb@OT>k5YZHiIsI$lvXIqR9!FE0@ zCwr6ii$1q{WT`r8`{etd=MLU391gy%-QCf-$ZU5rzOT4mpZ(DyD=mdO_~v?^IKgLv zl*6;_zUcG#f24h%dgJQAsh{B)s(lj1DSG?2ZqS*E?2vn`aiIIei0FEobOf|tMf(9!DaZdl_$#3vM=$rlvhZ_Vt z@r8z`PK`lH;FMDc742am^`H?(5%Ld5KT^^WPqn#Z*AqNGW}BiSANWnaXg*@)F>lzg z0Y#*Lc~@~w4F|v&n;pvt7Tx`B&$}6!bf6(=ZLWL16pE#?s#AP#zF2#4;XRAk?n|kE zi>H=VY<+AcHZ(P*@8-XIr5n zbi%BrwccFaxLxI6F2E@tgP?Kg(34|H4+Z`TnqpR??ljmLtm2}jbE4Ysqory4Td6N& zRSw*a27+8NnL>s4)HYG2JhK0)8lF6|HjrYG+HG{BHCL%Hg<<1U4&evs0$&*nKWH3S z0wVtTN9oUXFJyedkLXX75Y_t)q)R1AAQgqWd)K6mjBC0h1FbhK!o>c-##0baT-IoyNA zr6sDKl|OisuKGXD?{ICct;RdJdpY{JUL<35#ABb8>H-Fz0TLcLsQ3{bWd(c9|2(=+ z0f|3V-+YeTV>VbW>e+LOjqXaiW30l?X9L+=8+`-#2ltqk&mQJ!4hN7~o)z9WFlQt% z-`P`J*cD9SI~(pW@tcYN74KkGslIFCF>jmF&rKYHTLmvg>aCg|S^kV>+3ozE;Oe&X zb+r{0U0MZY21na$Cw`D#dSRiDF?+yL$7|`2my>UniBN?P6`&}Xp5pk=1!8U6=!8?3 zBI$qr_`%Jr`Tu+?`Cs?Sb%gx?=U*uW({e7Mi2wPcUuspTP@v*8I`JTg!z`%1QDcb; z@y{PG?>08l2?a)tFtf7m`tNg-j#vG97#bEve5m2MS!DU5b96Lv=^3UcQc)t5!@iJ# z0x=04+N!f|noAEOcb9_5(k+Bmod#sMef#sY5H)ITG}vifaEjJMm+y_4l%@zLGQ{5= z`b*!_Ev2^fO^W0%&!}rM@bdENpExl!C0TJ~;OBT3=qPI7h0S_w42-gm+MgnK;rnt* zQA-OSl1=}hAcik?CmVM!{m_?x9@n8jkPvRkJ3mef8Lx2f4B{Gt{lN_RxW{NL5u;4B z_kQE!`YPeqB(x$7_IWjp8vQajTmgYme9_#UKFPCXX;n;gC#~PIbe)>LU|CQo=!k$X zGul*&m%ASbKOlM@bMNBAIp$22H z5(HI(p_mB>9=IL-EAU$(i3^mI6pN{2a7j#uBb8WDvNqZ`z^HwZ8^DvB50xR3AVQ?D ztWIP_aui;EAVv37;YGrFhsrgY3NVjw6&ZS$by7lOMBayR0kl!&f5&?NWZPYNg+f>8xOl8+*^Pb#^ z0$0+ALKm46eJe@Pwn2YvIN zQwvyIq+o%-gkUd(#{~r!?4iP%noc4U11WS35E$67&{06#?1VK^35pQxbb>K(ad836 zJk;YF4sRh^P)rFu5~T@2ECWMA2ssR}kmSytZ(;f(<@ik#A`0KqPV4y_nGFfHa9U-7 zyhz={Lmr$)oCc;Stk7gLN18e|H3gsgflt|gHlft+643;ph$tp zi56P$BIHGv=jObE%qJ%&f%!(_wWChocUBT+uC&+

=_e%wr{k)S(8-KGOSO$VeY4 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 Uy`x9=a~Q}2p00i_>zopr0IYe{BLDyZ literal 0 HcmV?d00001 diff --git a/docs/source/_static/image/dataset.png b/docs/source/_static/image/dataset.png deleted file mode 100644 index a0a5c6690d8ddf08611e2ca5cddb5ea405b6eae6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136354 zcmbTe2{@MP+b;eZDn*6{B%)LrO_}FPnna@{Gnq2aGo_+TNiAfmh)PK2DN2Ttp(Int zJcUq(O#8f9zrDYGe8+$9ef-~Jt=6hu@B2LWbKTc7iHd*zCyor#tGC{2+Zg~CHQu6XdgbJWjP7x(jj3Kz#340VnlqrSmRqqotJ zk2WCW2{!Z3~z5DrxAFw8E^P{@7ZX@*#S~`|P ztUNdF25t|v5RUjX8vI?d>Mn(f+ULXUjC1$f?oTr@6OspfgxjZPJd1Ybw^|-mRP3gf zSh#4LC$H+UZyR3wBa{vvu^VZw0?faA~V0eZEtM!t1>DvO4rM7Noz6}5fkH(UqefN zP-XNh<9u)QqNSy!iu}mPNKStK=2?;U>=~h@zkSN1pr}|q_%+DuXrI^N!#;Jv^=6`3 zu^e;~hp1TaGJT#|?RB)g$k*4mE;unUF|9Q%H7>bJom_9#ZaQX`kUF!rnbDkqc5uV!S3JN|fq&g+@xi=@3cYeNfOiYZkt7~UZ z&&}*?acymFm0(#}*+B0{X=$J7RzBpuOs?*rx1V2z^&lOkoNMvVpFc{8UKSP>{;_#^ zQiDT7&tJZbPe}>FZ}zd$t0mR5@2{QXB$pMfHBs%##KaWm)85junVXy2-NR!w9bGYg zqobqa?(Y6;V&cG-+3D#Y!^45u+1V^~BHS0O2PN@^@pfmKdA4o4)l0u1m9S{@y<@b0 zF7^5I$9Z|p1Bn?K_vhx^Pnu?v29+3&-O+7CBBh>>;sl5VU@O-)UQZ)$Jn z_VV(Yo0}uwbcm{a7gc_1iXawRks{dSa5BNqPr+Z5@^BSJR8;i*g$rjcT)5d}p78qh zz0OX3Ms7J}8yg`92M3k%@reoEUAz4FwepzR*^lY!@*F&Pu#D}@@^eW@xb^YliLUm` zM(Gi+UkfLCeER67zOIo)e9)e~d!Ijh_UO?gZ<!i_|}h=m69&w+slKw`3@Wi9Ur%)T)%$Z&)=VS=gwPi zeOBd9?;&v@<%=719Ql6y#ECVtQ=_Yj>*_-L`%P9duq#+w3u@&%?%1`fB3!N_ZN!_G zK`=sj4GsCBO)IEy$LbTz#X@UOJ2;49okICk&#I}FjdhiOsHk`pA5WX#8kCwUAl_=f z{(y1y`^w77`Y=5IZC^~eB&@w3?7helM~i^zZ%F0Fs%#e#x&Pyb?sXrZ=mdJ(eLG*0 zM;dQ;1|jq1xa1-4-g8uvN>ukBJUDaq>^odNzN6SOyK7{mI>WRs=g+sxs~H&V9cD<3 zj(a)&Y<%0kxZo7TEzhXXkfL=vFz~6{)QA#K?bDNN2%QZpNqn7MgM=XU_}pPi`FVw) zGn6Aoj>JEG8WJ1JR4m${S)%Io_gtRCP;ckQBe>&u(ZIk!^R|MB3rT8c&YZc1jb%_C z)N^NV3eUcMpThYoYHFCQtgLQgS++$7AQD1U5@zfbSp7?P)9&Ku@0}j&ijR*!t*srD zn8>FpGe*mMG|1keiCpiOl?c^>zk^AJSFUv6hJJOFY!cL^wB|YRZr}bsd{2dtaB*v^ z7TK`J_#Ppr#IH|l8Ezb&WaXv$`0*q8bo_{UOI9EPZf<^lrpn6maQhn%Ev+oFkD_Rj z>Oa1|QrU=zqF|*Br+;<)?*Cj8zUNtH2)W!ZHYC>!Y=7uWS-W=a*KgnS#UcZlfAoKj zGjGk?m{fm4U7fu~c8XKh^|$r%729t4=_tKd?WJzXt+fj0*ah|9)4BfcKd7!AUMF%8 zyLLH7B$zI;;em&#OgK=PnVCI9PQf-`&!_UrqDAbb+TM18Tfn|7a;H3`ipRvRXuktypNsT>}w?G-{HXv2ngVl$A15|*AP)ZM|&H=rEHnPj|k@HFK?z)9i=(1rbaf$<5+{30VaJGFa=Q*Pe8xq*$%KrHh4^XG5% z)0RJJ6{)-NG*`{d;~zhMtG+!4=@4sjT2qsuxNxd%sB;b3(A{VK0|T!VdsCsF-;0PC z$S|><;8D;cms0+ie1MOwyUSG*lSin7gM((Dj1ELZM1(8v?Ck99uS*P0s@yDD-5P3hvB+<$Nh#1>Dg_EvY->_dQxkFZ4H7 z-hJHc>eV#$ed@_-2Ch6yxr@t(1aWkRV8M82T1i zQE`m2hMxX;Qc|5C%3cWL39^q?b>-L%eHEofIGUQ9zb`NM-Fo;|PM$JZs$2#Mr91!J zzpsmRNKGvVq-kvI@BB#CNp>l09&sKd5Qo+O=94GeDEj*P)^>GX3ro+co%hJzJ$s^* zXidy`@MS?qWDg=*PfxH=1r1z#@!|y~5Y3wMKfdD|r)!|h}2yjFup0AlJ`6zkCn2VE3N8;5P>tZ?TabQKlUl_T#vu~ z`8_xECOz9(Ev@0fd*r&RZyUTfxmrh!`#)B`R8xGU926 zJBQuTDu)hH+x33xC}q>JcR;9rseUNH_>zUx94`NFE7W1}IS@66`fOSV(2*uZ+5CV(4wJ$Rzulnbm&<^83XwvQ_c2h+n#aVE~r% z^mHeE**G{(;12Zk^iudqtm4&t4DsN1OgeY6`RXQN!(&N$#ZQj-FD)YpQoobSQS8{+ z+goPkIk8#WrDx;hXnXJnL*W={$Di!}to!vpKRNPnXa7Fz&r3y~veHf?@vmMnFW2HY zrNGWxn=MW1qWb~d&6_iBM@u`|{_a2XA}Ptn&MpoZQSs=}1A6ahv4;ZYr$*arP6`xu z+Xnz)_kN0?QJ5NOb?FKe=kMpNy0ni~%)C(n*l%WL1~EhcSzDp>VPD28e}4}c7pUa7 zR!_Bish&N%&c($A#gPSI8Q3C1!e$%rk3;h!9`5!72kJ~)BCXz-%EU-J@sEv-89N_Y z+`o0}wSWMIl}uYcU0*@3Ayb5^pJmy4!qSr8D1DQvs_LObhuDF0&Lt@8Aq^YLcp4e) zj1;%(_2u?_R8YW)O+eluWxuN~;Z)qkRPE#}x1VX{yLRvHFqWU)lx|$pTYQb?<>l<4 zmoN7LVUJBr#OV}yehL@z(JQ%?IiRupG^Ey$%C>E|qNL=oUh#^G%F3?nqWS$jJv}Nd z!QArIz^hF;HoS{}bQT@T3^uT`zIGndM?R0rYB3i(aq{Hu{rj)o?m%YVRGc$Y?FRI}C_lhyjovUv#akq7TudlD)rDo z!E$#J6Z>4W+Gl_<3dYJd$0sIcH;yE+^~C{n=4z|NI$hNVn}dk@#5>6*413YIejs1;bIoeE0*De zUq!G#=|w^u69j+E0W*i(y{oJISj4vbSjwBfX6OQT2?!XM2XXyu&h!UPo=P14k=0~g zfNOaWk_$*vaMkX}`W*wi^38 zl-kwTXPl9#k-UBT_Pb$Wz5vE3(}w-EkEf@ntvlbZ7PD$oMzkRKy8*`62g{7pQIJm_ zqs#&xwRucwQ7m(NSXZv)p#5>aeMTeG>;Vfk34PNq+kxC~mFf#=hXNC;C2N%4-Fi5{ zPI{lto2dKuH;_gsT+T!4^XJbfeYkoO0cuGPROM!9LqEK)5^w)*#)WeKvnk!z+nbVb zDpCnCZwkbM|CoQzVZp9g#qF+OZoUs)Bb$5cQfAmDzjCQU@KfKU>KhU8n(=3$o*NG< z>c(cZcJ+LBWA%1EzKO{eD}zjp%d$!u8XR~8G!LbUjH{yte$Ulk)FCL~(i?Y4%BD@5 z$Qt9jl>*qQivQVvneuCFOeia}T=LOCVNGQvjqb<$yOnBxFmrR)T-5O>{rF(7P-GQ( zo>x-~zd0|2s>*FeY!(+Q#CS|Lv2snHOH}DW6zby%x=*z_DTfPQ>nQQ9OVyEoWLmh! zx2@1E+ts75(uU8=&~Upz!7pmS_%ge$vOr#b{vebN{q&~B0+;l2%y;kKH@rc?%FXqP z7j5O?DR%b!*_fsYjEBdF-uCT_3rXyvW*g8X?H@d*s>&xUd=KGS{X9-yOza;-O`F%E z2MX=6M6=!ExSA_7D zkEX$)4<4)#Np28y82qX>wdsISz*tUJ)@0Px!n<~;`CT^7&dx~|iOLa&u$Z@lf)WxE z%AQ;3>u;j~F*+qVjYTT&9LXEv|2sKMUyP-z|5ll&8+3K3tuX%_-H+OAE1jLn;R^Ql zA}E_GH6CN7YzlQ1{(Q0SUv|@4`vn9Tj(pFh08_j+s(M)WP1tGVyV~evtnigJa&mGM zPD$JNz>7PV!eP8Caj8sHJpC%U*etM)hD-Q9N|KD^Uv>GW56#VKAcTx^}0Tj&SHY|GYw(&&HF zDg9D9Ws7*{oqpBRX%Z>8CdbOzSprN!Pxpt={W0_m4BG_-YfUd-xUg}>iWQvFjsZV^ zn!j0Gn5BR5=TJ4vh7Iq(K0k+xiU%ok;|47ji(Sl|-Dl-m{U62GZrr$$(JUq{z1d_9 z>qu^2j9C4ky2%vUYzg1JGVi5gOO{g7`l9FG)enmF9&C0vi_0OO~*<=uh3fBX|L;#V55ah{{OUtQOY~?k3 zd2*yRN$|&pAk!-{GBO&`ti9*AZrz%^lB)LW8E|S#l@LX0$BrE;HQD7?-M98l*Qa_O zId+Wh>bIAq6T5z$dd-?O>ZsVY8|>egmI?voCw8Ivi@BJ5;lhQ!`Vq}QZuu?fMCJbW z^OE-T$dMJtk00N@V+YkKsuRk}44_ItCcQIhaLHmFvzP{ukO|-T}-#>QtGXW zJc`XwD{|#-S50E9XI|AGB_*Ypsmz!pG-PD~n@T{&kk)e*)0Xbq$Ao67((iB9>7Nx?TJdek=LidEyE->Ne=mc{kuZKO?}X>(!0^>ldmg=f`Lcc( z#Dbtyzg7OQ7`B*0v&&q~MYre@&{Ft9AR5ZEq$C5}%}br4m`|US(Iq?2|7jqA#@fz~ zS3-i_#5EA0Bfz)3XQoC1>HN__A9fKFW(rshAO#eaPv4R+gUBtIsFr5nl(>tnDRnCS zyoSaJWQ-c;xi4SNcBOUM@h)Glaj)Z~7C>FRtmHdy0xZ;Deq&i_pLJ6?Qefm^NQh0D zhia6>=fMnx%*;&l_BUMKjg9IfIbA_&(*4P5LoN+2kNZH;?A*K8A1lsUGc!HSym@ow z+|0x#Id=&PVu@4EoxS+E&m30I_@&)wTjAWWl~)%sxMb>zkQ#)|#!Htjfg3ubqci$D z3f;{`ZI>McnL4$(@YRZBo<(7$pj{W8Y|XZ6zf_ayG^$Mj%LT}M)J^O9668GQn?T6u z_bWE6l-P{#wX{67xbITBQPn2tRJ1cKgOj*x1FA;Uf!Hh$4UOid>u!8~g`S>`-|+4A z6?m?hc|fT8@BLWHMuX;Wt=()CKs`N}lv=Ichm=3v=`T=GQIWH~ZWu}M1}esD>yASx zt*ZI$6(PKV<$%%NuU-j0dGdq;cq2D#!sIRMHp#yABQ8_)!QJJn+`P0MZCGwnEq=?q zdJ(-g1x;};8X*%=hQ=(5&6~x{P5!WZdU}?1&CgC{H_M5OhfN`#12QF~!Ysrt=f@f# zi7e%3v-%fmQ|Lks0e6-52i}%F|IPP-LFzp|Af$NMH9${PE-wnq(br zkyH8iqoR~;ZC?xh?J$257Z)hXfE;6CKOSoa2w2wDdzI}|j4V>qs(<*k1~a^0m4xsf zoi5>Cpok==Ti2Zu&wMVnyhA^85YWm0RFr06cqD(+2)N;XSl9$=yv>hds+7V>4FD7+ zJlcGxQRI^qfWrlah0kBSur=dex9$)c3Zza$L&IO#ugq+0N3l)7pBWPPfe;fH7svAj zo3M5Ne$t7T1#$T!E`E(xFT8tq2!In*@!R(H*uAL*eoB7GhDH_fP8rK;vE&5<1$KudDaYaFQjnLc*RFk9P!I)* zD=uADO6uY2^*bdBm8yIKsv^a^Iy$a}g|VSf34P}Uzjagj65E?^K;Tg0dyPBo;}mW11hsu038^>A^OpT8@G#yZugktLh_cG z|6QjXA$(KLr)ebrY{^b7jheW2?V8MFqk;QOPZ;5V zLqkizm^v@cPX+Sm);>D;FXp?maW93mM^-@aD`HDPZvYqE1BiHl)`}4K<=kH12Zw|J zv_}f3v=c2L(8w&#P3+Sy2nXN$tAt6O{F`CYlBmj!%xbI|dKe@-?i_uGRK1+jh$djp z<>a_z&xMKDb07@Sxk5SkkzONr)uCvMtn05KoyBJMgFk-ZbGn9ys{l;}Bdg}{r)HnO zRaFH_j~0YtTWg(8Ud~?8flG}*CAfR%;mn4{LN}+xwKVv<>DE!tqd-OAO*d@V08WOE zk}&(^#Rd{C>;PNC?q7L94p5KB?hE} zq)jI+@)%J@9Kv<%szgB*-?u87{|7`@R`^2Sp&Awrj*r-KB=I)A@sK_m#0e0;24M`o ze*gjiph&JFu(>E2v!|0MJM{q@6)$Va`IWP}hEK)fQ)O#9tbU z8`9Hm0fBO?TW#%WWVEtB4LYj8kNNdW65_Tmm%@J8 zU|Fe97d43N`tz5bw`SYAb?cBmb$|VMyHEX9aeWWr4~_x2mEPG1kS1)?sYp~H;4Cy8 zP@XzFIv%fR1|;+BUdGN>-s3J%pC6uyQ_9R)FDow}+ZXHgrfwh4@~xl)K1^7NiYrR^ zx@yi?*}x@Mj)i4rjIfm3Y&V@{=Lri13xah*tPgX7-zt*tLX|ly@`S ztvmw4y=Z>eYNkIf_*Kg=Rt#wT{Kc>x%Ry%mPijrKaDt!$|0+NfXBQWQ_`xF2dFS2; zV>)_zJBLfl@BE^)Wr-$Ntp3EZa^I!bH(K+Z0BZyKfXCk5q!%^L*AzjO=qAlnQYDZY zULg}pA0Hom|Ni~E5fLt*gS?P=&s@G-d5vaORMuXG<+rhf93bTBwl6BA#YW(1*^XeB zdd{}AkIzli>ojKDnD7>6|aHDb0LHEXA;9#+K#KxHGWGm7|z2HyUit2>^^U;9p>y}~uq<`rl zMSfM%C&phuhhaRY^^hXmNg&A~Vw+ z`u_da@hSZ)Wpiif@IBb3k>r~zMTEqbKVuwCU_tNG@ReR&q!Ysk(0I-F>Mwv7Kp zZ>Ml%6#&>4Z_p>}H*IoJ85T&KIz<-b$)Dp!|M3DSE@frJ+&(@&UjRxPYaq4!mxr4y zvnQI2W2u3_&C%>z|qA3Ci#F>+s`Nz8x{M>9D)Arpp{^Y$CJqZRuS6G*;`O>cnyi4?N zJQpYAOYE&J-ZK+@2MjBi6Z=S|lCaSB0)HLAzDRWG?)wx`*U8Bw zH~nbLd1kx^O-|IgK*#*(?ry^@ix#D2N(^fHyONSk=$-2lRjq8Y7cW~CmZIYs${22S zYVX0nD}IcXa~p%cH$hHAxmEjr<8imNF z+@90dAm@BLP8;Ut~wb(_28Hg zY=^Y9W9lXe4VIq%p}N`#1d7bx{y2P4=aa8Lurd_@5@>)@>n2g|UVF?sW?8kXxVy{X zY4dV{FKva*V{?|S%8|uHcCH<#wU%&o(M=!u0 zomc2K)tfhz0%~y!dg9l1eJW?qo&}86`*3fE%f;lm(| z)?n|V{t(vyR;8k<>M2-S-B~-H6o*7Hp_j+7MDu_NY)260-g~(9wNpNBpFylxtZ?u5L@sZ?UKsLA0v^| z($W&O6I9d5!-r`gLmS||cwBPRKWL!bMJt`&cRB0Rbiww6xI+7quKzsJu%+XzJPz zsvv93j^s0mXL|`e*a&tJ`AHp=B${WWqR6Kw!#W2Bj-NkI_&szqn`e(IDQVX}BYPX{ zN-3EBD7lk(z(!c20-$Yx%U3QFM(E_g-MDLXbo96Tgy2i3(Kd@WZIjOJ_wL;bVCF&d(GKoWr2*=VI?|04 ziW})7>R((y|8NKY%UXWo|HwNEDG^rc6T2k%#0}`q>_o%BdAq)9i!^O&5r=WOvMAQw6)XmB6E!~v64V}Icc6*p!-C=^ zrul%>J-@4_ihzlJ_O9*j6Kf%7STu{nG=XHhjh9!z_5#;oMa3Iq(*R`j_~fV9$tttI zhZ=%W()?`~0Kj>Fw)Ja4HdezSJ>*n?)GQQID`!=HYGORzH>((J`|`R zp&=m$QIgr%EleH&fj?4B|vp+9ULX;S(@V2&k_Eqh8p9LJ`W?acKPL5s@Vb_MWfbz$LM1 zJ9o&Wo+XH-`^yp~wJo|WMJvDBbP;u71(k&eq6?6_3MKTz$B&&@+jy_V1%}OH0f3ob zio6#6rHWiFEmOvFW;RGC!vjYG6Z}%5Xkha2y0D- z0GbZ|__f=VC8}Jt>s*>ac~-wj5vPdGry29nFoRf7x#)W&fcpKVr`?uW%LZcKYh?Yu z(x*`+ORBbCPRvrO82AnGA9sb)=zsorIdM#ZdzwM_1jpAC^hmhoxb$X2;m<{0UU)|D zZ>(Nl?OeVvW34=aF5`28%i!U|hp_}taIIQHCMj1NQu;DfC!U*8pjaO}aY7%9j7mV4 zR6qE;pr~l^($Sm@vDk&4M>CmOPtyx${I21BtOvcr!Wvxa$C7mB@7%r1{O#qX!^rR; zIglXO>3Lv*T+hZ<4nG`!{__XqsVHj1kuE|}N>)|1#y`)?xNmf{21HOrl%yRVdJj}W z9&l=4Tz@*|voN?%REZNRG`V`U*JJ+j{Pvpo8LmOc{8s0JE0wH7bBLRe-@g3@zy!;d zEh@NXLK(MvEy^ZcOqJ^z6DYg-y-jl_2A(?`e3TvN#CtI@Dqwp-Y;G42V2Z3#($m|D zHya^`e~J_j#*PlFU-F}TVO;|0@paM-N6hL6oII&%Xox_Ho|E<=>v(If9UtL`pFQ&f z4pB?dG~GwtvW1g=Tq9ZSlhMSbqbuMu;>gtAXEF0KTE^vz^Ur5H(7u7)Tb19c0y&0B z<`;!#C4)pNhl~t=O)6ED&J@_Xzf+@LW%FF4Y_)@(m24mXANWxXts5F-WxH)3dnLol zaV$~XWxQK#bO7Yhr*Pu|RDbjrcykOK1qH&iNyCIVlLa_( zw=$2U7cT-bsZiNkQXE9Gu`|*~?7(b28c3Pmo03EA9Us5lz`ocZSk|$b`4NCu^q#`s z4~s@n1Eq%+f~LOK)TEYf``VY3lQW$$qzJ+9zmTHfG#jz)T6(@O!GE?TF_UHBH#`xo zz18zN3*%uA*Uxdz{Ymw@((&$Q0o;Fyt$c!lckytkPJ<;C*;sv(L(V-XZKN3WW*+S(|9?o_9S_@gXYG_mZiuCCG--3M;m zyt!$J4Z2;d&F}X_!)dSqavm0YP>Zf#J;!=0J?lO=gi`7~Q2Tk60t7wfTRoSqI&4`e z*;f2L*KQN6bEdK45z0$)ASz4YjSKVBk|P>50F>wylSj}{z6FM*^w%eao%6MvC>M(y zj;i72XE73Vx(o>K-5*4XyiERQ$r#zH8Nk+dvI*t+*If$UTdEWn2cpo+rk1&W;xG0X`$hfnMR&)opyGM-V+o<@~%)C-uSeso15)en1jFVrbZH8Vgh> z1rRGPvkeJg^QT3!$LoTJj^BS2q|lgNUOzS1#htC*A=x34c0tS~nJ+qZ84d1y|> z@!VqAWLrI?F+DT0DOlX9jX+NnFz{yO;bjBL{M{b@u-O8~jlP);LN1T-{A-#3MH2T5 zdCt(t=rtgb!SAwYr#91oZ6-_*;CN9z&>DcJ0H9h)q`8|np~MrnLyJ|>4kRG&t0}p+ zU6b5rZgCPX&&}!6lt{=O0MV8vgUkU^U%l>P-nmyyY_aEmfxm_A)Ah`0!&EFFrRkge^`jE^wz7PICA% za&1FmVv?VGRq9DaMMe4`1`d&Dr^;n@#J^knuy_-W7w)jkMm~6qk)az+TXJl=h#U-_ z8{W2us`B$0NAkhsC8)+$!xTfA289$ZY1b>ciVDkn!`Jtmx_Sj{iUBi!u`y1%yU$%} zfp|3e-}%n8VB!LV1OgR9D)FlHW=K5{O+G*hCHx#Bs-r7d-qN8RDb04Ya4KtPW{H5| zzg0a|rG}M@50o;XvlfC@c)vm0Ef6k?yI7lp2dN%LOJ(T{;jnn0(t+36u#NbuQoOiacgbkN-n-%DQn^}~lr*mKTV%<_8$ zwl}ESGL6-tJ5Skitp1@Z9N|3pJh2hh11Nw50i*vz3qZ6gbdpBsktxd2QV)NnAXK+y%`BXhMQ(1dvVr$l7}!?%cDlKxltIYmH%@PY^CJ

VPEPJty*C7I&uZ3N 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 diff --git a/docs/source/_static/image/environment_tcp_ip.png b/docs/source/_static/image/environment_tcp_ip.png new file mode 100644 index 0000000000000000000000000000000000000000..9fe2d2f5fcb4b34678ed9cfe1fee54e1a1a6630f GIT binary patch literal 224014 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 z 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 z zL7(9BebJbd6f8yTX8q6SU^q!gN!z!a=u{y2Ev?YmNW8;pD(-D-sv;;07It*7LE{Eq z3_@2=&%_+Z+Vh6(ZRqFalI0cN$%<(m*HiBQ!mhvjKkW9rae8u^&V@*LHg(0+p;k>E zZ!+gw%IWFbR-dBV-F`alL7>sZfB$`Q%G@nw*=@F$6&MCpc}$G()4;TJwDFp95fGc} zzP5rVkLITuBFruCmISZTr@Er+OW)<`(~gLerlXy8lxdHR?)V#r?j)PJvF%isF0qx` zRCrc9>%Thd?(?)Tn%TiO>Y5+>Z7!3tNNHcU{YkyL4QtD3>(OIVfft1?Cej9*&1sEY zWV7C{l7`Bdv4Vd%1;KZ2g`*zj6y%B8>t8bcI+#wdxm`6(K4zRgRq;1t>xOkT(LRZ)(jTPXsbL#kC7)x-ukzE?A_6 zQo)roL99NmK|XsiW7bIWpxAV8wpKEbVrzy@j-d-74n;nJ zbFC&EHyV;CRJJwLt>{vE*3n3_bJ04~HLiIdc#`T9KDA%Xu0&o^gcl{`B4OcMqeLS}4 zhClPXCCgreL~pM3>^z7*l}zfq7Ylh(7~brpq@*B26~Z>D(Sz2&hmbUA0m4R4di;kVB&aEQ z?b>@dmp2UZe|GHm&Z3H~#EpQT_3+BcYtv{e)&mi`(aW^T6ToUbIHbE5pNgpiAHfA9 z?Dx~c$1P0>=V4LX6IDbve4;ae+?_^Q<@AqWRdRdKkNTSQgD1>mg zA)Y?HT3z3rq%7_H7Cm;w+M#fR^2P(QyXJK!8$rEm#@NCT9H~!woh*0K$zlaG zk8Gw$4$48`2$$?h)xX=yi8+ByJBNltrwW6Z@vXq#j)MTDR@Da(Xf9SS^pJiH8e@!z zo}2vr1pz(L`GJd+u}#Y&da9!8w&3(kvctNxISP+TC{U#I>@MRx0Z0egw_Gx=c{3n@ zQsdjJQ|r;{AObL-uguL!izY^|Q{NC7m1|`&zv**t@$5(trgUhKLN7D``3Kr;>E`4A zd0R72&#aG-|RWv)> zg4moOr33bwG>1|v8O0GE-Mvh*n1&7{GQXv?wZTs24gZ`7lrJmUK?7gpCRN_;W_Ct2 zKM~v<&4tfVgs8xk@32(ig-8%=@=9@gIhajNiueJYmkxGy%m5oDCB;K5>uk?AKnjT)`24$oU z6a-*LiK+GSe6Rx!7uR7q#myO8@c^Z9cO81EWbd8m4YdNfIL0zn4ssf)R^sv}ELicX?j(-=A17Qyq+3#e7yVYcL|I(4^UcMJXd`dbp7pLZ{sQw6A{IHtHuPBLrCQerYzUxApgGj z_O!jatezf@*xiLG$Vz_mU^SqX>GM|cZe3`$er%?V_eAJT97?>r?kaj<4 zOLjhK9V_C3K(|{SGsMXJ=p~(SZ*K2*r?(y}jA_?>r)4t1Ax#=;^36_Zh98>ta!Hwm z)k)THuzGB;rS(R-5)9CI;{ma4b9GE}FgX5vlbWVs$Tl+y)6Md#Tg&mhI28X~`)RNI zYT3^6^QJnddEnk|e3&-H~{IjW>HhqCc)3F-lOzJFdXApX8J*cfN zN}OpWe`)blr0ERa1W3BU4AJYVx!Gk0u=+EkgGMs?L1rBIX#(u$_ErmT#-^sU*xWWm zn}GNs8J`UqP@PIsVgf(r?3mJqtzHw2Hha3>oVKHvTfy+j`0YeUWc_lOajnL%-Jp;l z0N&(}Nx2~;4YqMk69r?KCSw%+eK z`%I_(K<{gJr*^D|K5&>Rc^@+$NREe>mF5&yUC`SUZ?)X@)Q;l1lRb0_u0{WGg&sN( z zaWlj(da$`JdPdm=Ny9PK3~$wPC#9SiD^lKGjJdBKK8*3|2f_+G$_~J+3Z%UNG0GS+ zs>qWPd4Yk2vgQk5ISx<8pSyW%f0Zj<4cIB|K%X>p=2k{*r4T^?UrXPIMW;`xllAZ6 z>OzjwdoE^XW=rwPV!wL<3&mx>AOfV}_J%wjva|I3{F9t~rad@QHrc}FRW225$c#%l z;dv%ARSK#q4k*dV6lK1?FXrq>se~Dgmf9p=D<}jo^s7KdG7ejc{V9l^0%5TST}4oc zm0>kZ&~6Hh$8b6)qEsNBhNxivXM!gaCAad6;(X&C>X6PX$$%x7Le5lD48l6nvRV_( zxX+oschdwbXe-xRZ{NOE#MrUq{l7Dd`hls-v0#!XdM7*`JJUDOCPOk6LH;rZL=K9I z*t~53NQ3bCq5jK#bjv}2)A!B`z+X>#dLt<&84DrEK>=ZO$Oly=dsF4~d2j1g@QEOh zAhv;jP>ZCud0CT~pI*HtdOFbd*wDv6?$0V<2d}8}{Yp%$<%~wNoC=^66*hPuZ2=Y; zljw@j=&}c`^#@tyq#=#(_|)rT+X4<3TexzptgIl^KOD1jN=i=tWpi^gsd_0^Q@%=jk za~M(8ZHcR19D~hF6($S@qn$nVkp6v9=XSC)iT`FuY&2UJ9T`+<0rTP3Oj@|$$;9M3 z769|$xA?zEu{2o`(E}Y5153%zAO{+@CDI&V@3k?y$cY z_}KVDlN2>rv=4W!%ITt79K33KD1$sRvjCEI94&9wk5U38`rv3Q^>DL)cOYQs{Qm`8 zDa>nU4^PIFuOTHGfKOa#a6VBXG`it^$7-6nW&& z+_XUR;|Vfc8XVULgLB+wuF&u$o@ucg10dy3)(+OPCF;#;w75hZl4k8y4IIXUn=2QI zRUh22VK-HVxV&umwVeBZEC49_e&m#)ah$~PJc-jd&gWJooBhAbo~mxA=)e3NCxC-P zXne?y*N0y2)?*<6pCF0fo*LmPJ3H|L>t3{m7JiV{ z1Q!R=shE0d=sMK3g#am*kAjGZNNWG||8|YWfA}?1)AEKeyeR-&=6U*gIU0Al!C6*z zYEOT$|6WJk3Fmylm4ofk%fN%0Ki{4P=9|Fyy+G%sB+#+N#3ToI66=u#(gRYcQKW?^ ztLi^|I`^A6OesTz{P2+K$&Cn*c#X*EpwsH?kQzSK*kEL0l1?yhe)k_Vl#*OYB2b4_ zAhhs@HM?do0mX?5pE>*fR{1{)ku-HE7{G1DfYbmuA6G?XWo%;NWPvpU6O-xYXf{|J zNV_hcFN>?ZkAhPdj1HsjQ-i?VZ<6L1_&^G!q-5L#z{ZMJf#5R6|FkeTsA-hKVADRr zr6e_v`Fl0_-AW0_8t~GV?l=+w$!wpiEp*V#(Cv-!kI&1+4DtjiD6nE<@O?6B+W&Po zoIJdIu~oFE{uE}NEwz0z+8twW4%3MK4+%G`o&7lPJc)?#FVKz|(wPm<-gXc`kSLW= z1c&;(-7Byt$@4PkXi?N5-Lv+s`jz58krdUknZt~g*fBwc^Mk@A5F_3$K0B$IE-otS z%8eG&aNMOyn6T|;l_x^O70PF%Je}j;6t!9})hb5*H?i<%hSrDWZ(Uh0Es|i zvZTN6YTH`6=J>Q(3;}F*EO+!XdJ#5Cq&wpxxHAu=6A;s%r>5#stS>Z%uEu!${Qc?s zhWqh66jzZYY;4$i02HC}Z%lI2NwrYN19g0%0Aho#hyQ=Y4irh?WXW3k;4}J5#lC=L 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

1r>KdXJ-_a67KfTv99X^2y8@H4;`Dr&-+!? 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

    R_;5u3i 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[jyj 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 ycMpPrAvSq4sV7zVCf! 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≺vD;IIPjJP7anjx;N!A^z1u*02m=u={Q52zZ7&Jj-MyBXLI(Fri+Dn&gK>htY$bK{%2CwiVM&D1N3E{ zYJlI8-IcvsCqYypb_;#RTJEM=ikDQrqP7ngd+0!1Jm8gMRoLs{%An$a-smLgbn0|; zeVde$GF~}qOilek>Fw*`-pbhfe9yo$OG#PDX*rkxqNz(G34-^gcrHqhD7JBd9N-{W zw`=X>=nUm$ENe#apPrm`V{I!+xd)H3j6u|N^Xml-zai{ zGkxsqIH-}OC;-Q>u&}nawv{yHlQkMNopP4`sbsEP3AQsEO=?`342paZ-?tkjurl@C z=Tm?&)Ya${9UWb8SQy0Gq|AV*BG|5DqqCPFHsM>r*FX$vHZI7t`t((|jhCohkKNVb zAW5HfRbREtk1Cp0rYWr21puGKG?)al}3qiUYR#(dUd0zDn?o%XfcSZDTwITF?TT|9%x&2ub&E6ihrj}M?xr2C?{%W4qK#}{Qt*~*HZU+) z3AdAgm|Wg2Sz9?bXv^gvXO$TyElI%(smlvbI~Syplll+bCTTSR*|o)|2xI=5G?t!z zV(ES0#PD#&$++XLhgEto4o=_{=L>cGJ(5XK9rZpN9dg__Aa!zaDF#=zg<9!^-eSi6 zzE%qjLE2@0FW4XjpwpR?U;jcd6l0-EwfI&=rP*W4aR7j7Q*3#X#JzBu@)zu=;re@D z+mho3khT+}SH{KDPaTb}?z`XQa8ZFMV-|p`$>^Q0%S>2MMVNoIX>UIrnv*0-g$LpZG++NfMG&l zxE!p3C^2$;e4?oAVbc6|JT?I>VlXE!%Ng#A)!Hprf6gbgXB`I(g|iXId&z%79ST9A zC032C%HZ$$^7#^^$C629PgrD?<4Ov?_YEfs_L%(2BIa-keU094G!=R{{l={cM0oH8 z$71#bf)oF8A0ZOg#iu7c=eL87xD=9cW`$*$axI?6@$Lr;PhpjotHy~q^ym*}Af20b z7N8(L4r;+uEmzf@?j|+Hy!(dBTGVwOsc#mmbmStge_;Gw1A4>>D1&)76s6eZIfY!a zsL2%nLUeB@M10r`Tg;Lwr!6%!8dao2;^iX5o&+ino^zStfOnz(Ptjzn%uHpwnH#oK z%wv-f2M(V5^{2iTO*!V_Z~E^j9YAucF3uJmRRS&eVga7)(}Ryq;<&WQDb3vH*~eJ3 zNervBPf3?x!AQH*@roejt&_)|t&u{3+wRy{#*bmqlrwmQoBnstF31|aeWM=f?2J5r zl4Q0YIuMA`V1f5whIjd-Hdb*jdE4yocl#XTShiJH1z$JxpNED1Hyr+aS+rkpa#HHR z_u_YwVZ#CG-{+B9Prv?OPrc)ppPT6_36hN`tZ?89DSlPX{P)SVkOa}f9V{i8QLUlJ z$ElQ>)QkL6Rkh|7Pw@Xfa@_N05#0x=*0MnG?=A?ttt^Y-C`|Nc!ROyB>0J|v`n1V8^b>O&HJ`~TnO{}0;#Yo!rJ5&+}&k~FWg z>AIvB|5{tEPNrSqL^BnJU0U+@zxL8Hsv0`-jw^MrF701WXr1@=_O7|3AeDXl@k4m8 v3p}G`4BP_9Bm<1qCkclD)c&7Wt@(|Q%wbds4v$R1{f{KY|HD! zTn`?#NMR0o{m((N&SoaARu1+|npSpZ4{Y7+d6)o#O!f}$JWTw2{QOJ+VR1nbaRK26 zcE3{}JYagD`2L-yXX@^pZk*Y)*Zldto(-`@PbRdaApPxw$8XzS$dMX{V<~=q@iOoS z){B?Dl@#hz&LMk;-&&%j0}kk$kH1L=$`C2BymV?G0)f!51a$IQ*~D>&-t}(L`imbA z-n~E=y_eH6OEW?Ex@%cT-4d~NQa}6OxO`~Qvhak7<-g;2E+a$f{eMRZHa6&gUpRBW z-X#6+q*$AG?~=m*I}Gsg>i;)kRZnf`<^N_(1_kl_?<#eaOm*bM#mTbM6y5dKamSbB3K$Ec}xQ?bLYw+W^SY~4<*ql)v~Ro8~CK^b$Ru2 zuLED$A$`;?Nz*N`9D6`)yPl2qNzMz}FsGLj;>w>*3?5c9>4dR=EaG&k&M3o{`>dAu zQts&NAk9U`ro7WMO0lJA`gU4O{&S+}hU3861J1cx+y!qPc%r`h{c2@V*!SK@bb zvLT1rZFfKab5}QJ9kAX~bx)z2h|3k{s@2ui5baXKqxFmMyJd7|Pc zTr$0x95Ym9rQMUqQ4bjjvjxZpOaUR5e*&tY)Pzz&<&EiA%cZj>U9SGNQV8@;>Zo**2LuV{FUT1>p0+AgP`CQ(soQu1vv z!yhE5#26%EKRaY5l~GVsSy2~u53b3+XybYC z2=`KmhU_czZIJ6db*s3eu;eG)EiXx9Q+tHqkk27?5ohw?x@O!{pk!v=t3Ec3?djC5 zFCRgwc{@+zwxZx74*W*D6*c~UY!AK%U#51urrwCQYmr5L+~86>@W&`G>r zN6TWRMp7m&P9GLTPVA7?R*pCyEi4=$uRp5)8}DaE<=$N-k$zShO9{bW7KGpFDWndd+ya z6vlX*Of0=PF!zS*%;7itS*A`I>;RCg?9M!eI7|0Wk>SVu-W|n%^MZko|H$G=wVl|T zH!Hil@$SAs(+m}Lp2+pxaCDq@qlEZ+N1;e+da9$lcMn`dJ~chfSSG_2OqX6^)l5M! zuI(kP9WWuEO2=9CcU&!7X&@z9R$z^&_&PaE8A|Vmua^ z=g;0e_GuUbNS--L0Eosdz+Nfk&@ITop8kR3_1T-*W?%T58V488+QFN{=S22%D~A)3 z+re)%D`d~k&Zbc)urPcn^_r~o8%az0?DRZzB5wjv>abbRIP=OWqr*i$ndcZ1S0GxEyVHLWGc z>u5Q@%luM&sDiLh(!&{b@J7gF3_N-k!GaBHQU$7K_h!b?$g(o3zf2O!uX4O-NX^cE z%dsFF!raH-up*eXfp-aJ7*Fs6Xn7Nk8v%mK^VN$Ex`Y)NJ7a(VO*cZI!PcOh{Uq1( zN?-3{z$%J1ZiXk2r^cD~Q8A;D&EwQ>`Mx7T%=sQZ01}&bTKK`&QQhV`k!o7|(;R)DgeiVU=RP z3#`}C*w**_~H|DP3k zTc-yi1LFzKRA+AvOr=Y0v<6?_>>u1%_-)kOOU5ul;N~t*GrQiarqj!XQ`&IFDue@S zPS+^^7_bY4D9L8W6FX_|JqGtKG7;?gTn;udN{jT3(iZ&s_(n@S)N)f&hYSPe3|vOF zPSKQNjy6}WkoGM#8K6Db+xq_wyHV^sg29HCldZ=A+J;!(1&*(o;Qo& zi9-DF^pbNOGInlbLMSsmoou1Wp-&^}P^F)3Bwbq?2!HMn6sO1x9LRvGX5-};Bn#g7 z?w}Vo0&+Pz%krGT*jn^Kr8g1@k2XfgPNZ(<`0#CThf1_Qd_z%;l3Xejye&M2lr4$k z$Z}>0L8({txg%srfI6}!8jfS$6?EQ=VG$zgD3gtaJNFUlib4__i80}{$t4KU#_@I? z-MT;FMg(U~gE+!AZ~Yj#TLN0N?3|RtlMO2<1=*}S7T!=d_^g$=;I8snb?O1L_>JTM2hHbF2TkV>&*yy+9{aOv`%NBo=gGYM4Y#_Kk=Ph2a(tWnFE?*?xXWTb3a+;E zx?FzR-woF48uU84c}6UK3F{)3TH4&Cu*mHaL`VYxT0R4 zn$@CawY?VAG6FsIE~&m=iv_-|H;P4VLz?&0s>U~}r7YAPM?&d^JDhS7iIHVzM^0he z+#7#>M7MA#TQ8uWAvjZ0X*hF7&^c;Wc~j2rkYWT;luVQ;+dBUg^^B0xJuk5;0GcSU20as^e9IN(m8Ofjb`*~7wIo%)4ZM^x3`b> zo9<R@#btdzBa@z1zFd z4o+t$a0&0jr@ZM>%d7pIpHv4eQ-thB-$t>&?bg4BDz}A++Ox0jeG6FuWGxWW;fxj) z)gUPtJ9P~Jsy3={;x4t$RsKv^VdzM}75(h$ishANW|cM^^k@8JtDG%+8r#aM?B;@= zr-n_-zK9&n99@xbnpI}59T-3J1a0g&pN<69yK*5DI|bh^3~hkvtKSnP*!xmjfKN?Q zIfXSdtn z!G?1|Nuyb88`;xIwWnPPO18LsT`mGzzJv!zM@fR>jES5&JJ})mc@D8Y?8pF@=xHSdS|*>n-jP(nL5S)nPCzwta_yfDy|R-NE}{`uA=l!HBBCYk>G`}I?j5$XuY z8?Y(SaGyOmv&u?iWynuEY0J+J!I`m4a+NjFz9dA{MFHi$ktg2uOGjY^ub4cKQ!4i|)plRtC_)>NyIYdqgvG9*-^3gNUiQFQ!tcB;89=c-d}K~!L$Xe!qBplx z)@w68J)&H&84#Ft*d1H_erA?mj6GvVJwu%mMoY%|*byomj~GAxQ7f6#0_pC}jj0p$MGnJR4VxgX-Ei$j&!eDzci9EO zljbn$!=@)>E`^Nb6TkP6JQ+cPj&l@N_TxhZ3MC}YVtI#kDV=MJU-C8s$+8)52~WO_!_+VA-IqeXei3moFk zWt38s17A6^>up0*mfgh3ZK5Cg#}LDkuYAS)2v<%1Pj+oGd_LFyZ3RhK9&ZNfbIctckU&$R!XOA%AUZV0L5qDlb3)epM zBA0_t_MjA}4A8>6a^2)}u#5`KHzW9@QbM#N({Ai8Fk*~*!ZHCXr~Ov`Z6u7`oG`&c z*3INgs&2KJ;*%~mBSzWFAP%2>$}&CAz>rfAI1Ka3F=5O6D8JzW!dHw1Kc(pR}H#Wtt=fzwuxbMd9Ql(;#Nza|H7{ z#cI(Vu{9ZO{jJCdH`uD$Hy)*WOkn5W*+d{Jp!uyF6;SKrfPQ;1k(<%8aXx-c4yh8; zq!$*;Vd4k=B0T8IVMKCRgb_x7OP5H5TQBDK_1IRZKM@vxl_e3j^_*>507UOawU2F6 zPjl1bGB5v{4mFdd6}Qcz=jey7D0JE24lla!X0XkZm*d2+w>SUFq$TX`NiaSe+3Ds7}H1jv;q=BGkHdQ-6?c=o``{Ft5!lSVOsg2M~>#T zVj{D!wG{v#w~4>0W&Phf!DVIDa0VL!ABq#n-EqD-PRDb<(8Z*&13yBS!)(nu_q%8A z_Z6_|=gaH3qs1d>+rEc{=B?I?x9AVu-L&Z-~0;Zc=a3MKeQdy>UER8<)h^K7A zRs))Vu6`nPBo83c!`P2XpzdiW=`~>`XWLoGl*3nMl56OqWah!9$i@kr?CSKHuS@g- zeTAgoA7?@;I??73;Xg;TTA+ffh-b72j;Sbs>-_L4joExIll4@=)WwBX$9PWvURFAY zbDboEaNKaKo`JIns%yFeDf)u@8(KZOloGC8f&i%{aH1dznOly8BEALNv>)4pVj8fq zw%&FYlov+grFNGBy1pQngUGk#whNeMB0Tn+2Bq(+?h_X-wC_1Fyj&YjgVC>=mHsVL z3|JBKj+;%n=iJo|A#2Yt%FW8k^|{y0A=pszHl$)jyC^ZK5=C*rJ?!BMQYDZacnlG& z^v&ACDkioPU|6ZHABYL=3bPCATqSg?D|po&U*#&Lt-FTgRGG=DDUAMVuKi8uv6;62 z38T3O%?Le*g+_qdOrcAPGVQ!!R57%QE-W#LhcHw%@O>7Kh8e7P!xojT3k+3i*={S# zQ%+CAenN(96!u!lv_Ms9hMMW|PC18l*dnYz0G|VasbFHqQ+jV>yTzE!Ct@zzHiU9C{UzaA+j(Im>#NYhyGt!Yi0+2={K+Kk?b1x1 zSf4hR9h$i&ul{CNB1@r-*9zSML?s>Vd!;?E5txyv^}{u1UHm97se4${d|Yu8bC167 zjJe-4%1Kwg<2Sm)Y}$V_daX zV8Sch*1^4~#|^VmQ)ld4g=mS~i5LpqiRQT!ty)b`LR%rZr4HDgLLxi3Dc?jrKq#=y zvqzE^50_9I%1&b89nvn}{F zcbpN{(d9z!{H*TW<@z~}kwh)xZKTYVt`2vsI~8D=1;=JZ6uGa1V`%FW)lofz5%4t4VEKEiNaNnwS@b__3T&dgBRC?N6usuK;ktOTk!>{&U_WhO2~SZ14R>j zqBEuKV3gV(*fucDH$Z>(7i_j&E0UfB$B{^*zm=kW6Hxs?WB8ogMPNKJt4a`}3HD$IMX4<21!k^a{i%(a`&g-gT`TBnJq8m#H25Mw& zY^gbh`u2Co;}Tg;0R0D7OT2flTaE_rUrL`B-pBhK_!4c*8XX!v#K>0v);-6MitO~G z=NofcJX_aKuz3+u%N-SA^iAG9M}-y!sm&%{UajU$Id;|U)b9FtJ1qSPv@@kqG+_Zq z(MD95AW7p97@rA1$jZ3RNhnaxvdhr^q{2p#3smrv!|ug*nSgo)>6;{BY+x)o7y0Fs zv8^p8pK;#}~b;@hVsGr!UqOl{v;c~eHxL{kcv9__I` z@yxHXh4l*&yi!fDduBl&*}<@O@_l+ap!*I4n{8Wa?cbauyg6nc=FNmHYu(2VQC+MP z*x0nKhx{&@x{4U@0Kh3(5`URxIp=%lpFWevqb|fEu(J8ZjLpm3*7#`&G%BB{VqX_& zrQo-;_`6$nNp=+ySFMl&Nryx!8)TJ%$Mr|oT%Maq0pb&oDe+qMrXNA$V`g6g(U^*3 zfUBdZvq5Y$m@!+gQJ#xRM!y$9?R93HAlStQDz;}3Ab-ewo&?GjZOVHwEyf! z?`W z)XNW1k;I5zUn66XY#fAZtJX=pG57NO*Z?Y@6Rxy-G@RQXTz6_?+|LK^+2sI3<{d5m zVz`xESQgEPz!O>TKKQ0Eh+(n6_FHM0ITafLh5D1xlLrHB^Izy68`WzT2$hcX6;-%> z8O>cnXZJo#dM=wn(j7Q7MY+|)Ov?UkbuVWta5zy3nclQvj7|5LqGfjck z)g-b1-O@gRv&w|G4?Tk@p)gW_u~Lt7#Cf2zH#|JkF@1WeNrJ2>D^f#d%CsJwV&2_h zCXXuFx=NQ4KkK0Yh?_>~=r8&XR{pltgX^sX*jeZZ9~E&h2B}}V%|G>}UfLs^z2*_wF9ZTiG8@2-C)Z2i#ZtBdR_Fsc45Mi4eQ zNMVXsS)_j^tWaiyLJLWc|k930mK)6jl$lXg%2RD`5oe3ELv4tFD>624edQubm6 zdR5VR*K}D|;Iw4!NKQO9#Wk?^(Ng(>`fvpbFm*9Y$#jNx$`j@0&yCCoHv00m+!ati zim7lDFAUQ_-i?)6vsl3YRth%ondxPpJ56Wx{K{M|KFzKIp9mgfH>j1wz)?#V0O8u3wHxFD;0{kjd14>a*AuoW}EuDfKfOLusF$R|rU7To@zzV#zmMx_;w>vsW_owO!#p)&m1tOD|AaOH0 zS}J(7T6QVP7bI|CzOe{%>=hKH^L z&I3z`MJ-%NzNjBkb$RLR*wg=!W*@o!ofr1!<=ol&tg+&w!9oK`xB5>T6~FTn_&UkW zFTW4?ZjG#5;!>ghI=``h+BlW<-3GQtaqP3Op8arC!lkIp>hP|bu7)acplol%CBfd3 zTLr;uYh6>ZDm)4+D1guxHrdXLPF;n6<;z$94OXeQOD;Tu_J>HdO;C|fEZTNwRmKkt z`5;@{J_BDig>_Ueucw0nlFO?&0Lu2oug#(Mp&hbfUDxToB7>K!M@mv+dg$Y}BX63z*&MA-AbcS7 zoc;T$n8H4?mf88jyHT8Efn*DEmEz3iyLY6;WWEWJ^6zx^T)~k(UXeyhUmx5(@mo5$ zCtHtfI+&DJ#l()Z+DS{;KW+$d?vZk>8-2BktCDJ zpX71rnSI(LhX-~Sh=87z{r+UqlOsefhi3%+URZW`m$|mzywu4hx!EC1=z&0psr1UUt^{&x4ji&?WB+p%xR(b+C1bGFn5LBtZ(w|W#`zW z<*!_49Giy{II0=#@dw812PQl0oxnF4D=Cp2MrcC%9mSWaob=Gm-iyAV8QRmlboCX`)E#H-*zMKpbzZu z+fPI*NC((f+_Y5;)yHFA?_dLg8We#u?qg)HBiJXn`Q)&N$-Xhyi*?SmPxx#}5ih2!yusV zh4auIW^Jq3(yzby$g@gK_!``Y+p@2(VGO^VaTu|vEMDE@-XA)MO8Rw7cXQs|Nna7# z&wDZMoa{AVbRoot`)qQI=*)28N@qGM$d#m&GX8YuSJ*!WT38GN`gmG)z7f*1*3CIE zsH)2|X|%(p09zJ-4+)>xCH2$H1PRz0z@?5o`CAdWK}Civ>Jg~$oY{I$i>#5XOOS{iZ ze4+@SlM7`YwC1EYH)F^m-D`{5Y0&O6&jh6vd~uaQDp3%HpRy7&W!T(y!j12vI9Xjp zJLOKffziY*1TJetF23pCs$W#vceSo(E7^TDi`^Hg{I*}M`2#C^fbL@w-i`&(8eN13 zO9*tnBdSg%oxz7RWL7$sZyhlcB|R9R-DVjHJ26b~(l;yH5>p`(N6j1CV)Wp3H*qMC zPxK8A5SiiqH&c^dL?0@VRLlkKDJS>)q%>b|PN1Dg@x4vbq7(0x%@jB9YrMSF{>j+P zbMSaFAm46egL0@oq=3Hk=;y)Lqtp_ySJa!&+wmaJ0M+ev{J@ztU1!X-rXSr~8mgI&#LwuDfO5g6@!x6OTGT!|KXpU|25H zDxrWvRjbTa9XqjMBwA>n*#|SSA@|`SW9Qj?bw2exY14a{Wpw*QCIS>+yNn`25;6!t1|etZLj$<_}+vy z`kkhtM_HrOB^NhOZ4!H5_Krt1+msEH*%47JAIiG)#Uwmw>mqk{jiMt8E6ALWK|KNY zq1F^+=QWh{yc?z7nv9n1Dn3Pf6X-w-e!3#a+coP#?O<|W2`u`XdzF(UavoE2#zpgk&r<<~R8vjoU;AG=U8bJh z0CFv;B&4mTig88S!+t~##;=(#Hn&=iQ&}3>o*`g_C7M7$Lhds3Ptu^KCTi$FX=a1Q;bwjS!EyoQ@;FSSI_%mC4nY`^rw{F zh(X!KFzdEVJg%6EnYk9%6}ADdgoBt?F-Nx00uc)+a9&u9eZS#+JUYer#2q|_Q`!PS*Jp*T6*ndm$0LQhNp7&>R7sU z?vDR-U8n{{Mj5GOKd0lo)BTM26TvH+FadT;3DjGgkx5`uutdaBV+gDS?niCx&>0gd z6hgw+xr-%((3a1s1K}twUao}QNZ+5|cXTaWQX1WNN^fl3-<3M;q%}-2D`FZUVxRwO z%r3|QJ}^jox8F+NaHmUOQ2JjyY3W}(!jCp;F>Np$J?92Z4hhcLzx6g5CAnx;4KDJq zAwlmVVEQRsTTj->p#%c(5f`F&V?4p_{kqdSogk zAHMb#lCKxd2<#; z3K*wzgh8t&Irw7qQern%6S$^a0NZhaFCWf_!eo-$U?5y9E6V9L;ZUW>nYWegqh$6W ziQQS9Kmk*i0oUr7Dy5l{+%hYS&U=nGayoRWnNNXkENhEyT`PlRpuvY@Csz9ATb*h} zzs-xJ1lTo$>Bn~UGzjBZ*7stpeyvpZ7Q-0~Vr7bi-OEL)G5&``UHpq#H)0?=C4za`met)&v6j<=`{%^c zcWlHAkiKDbm>`vd=$kj$==AEnaAtLKw!yBC4}X+5I2TLwN$#h2tv*1)$m6M}2qR)T zcY}(*E16@+QYJ@dilC*RUe3otH+Wl$RWgS&+nSEaV|K1GOzy*Ngti{}GS%qHoo%N# zJ%xFF5#|<~z5WjO!9P1QS0*BU?a-*|<;a`u>>iZApM&uMY?jZct5s$)JTAX0JZ%o! z1Dq(Cwmf{(zh)+B;v%zPxXe}qaBuT&%B~E|!GEv*CaaLx(JT~SU-=r3hxceMajJ-~ znN>J!OW333r?Rqw7Z!ayTJBCCc#CXLi7ezo3mQ9(Q!aBg&mX1M2ha~?+(=O~Q_B)WnKgRl3zMVl4Y7WsL}Hf6K6ItxB1 zygR5K1iE*sC2gIefYz*pVmjV#$%b7yJ9Sf8?~Vsn-wx75m1~-PsUxhT7%SHkN|n5f zf$b^D(7mSgC?8M#o$4YwZBZ|}xM++(#TXtPUqnR~|Kd^igfy!>w7^RzR52|0ULW1R zF9C&c6HF?;AXTm>eh(=b>g@o9NIbT0m~+0rlTS@$KreUT-yMnE*)(6d_{SjWYJhW? z=Jp>}J3s$nTC!2G{pKn)VrHjTPY2T_7Euc~pk{Rc!piWg^Ey$l4(lc?Qz8)p=9Jm! zc)c+e5?Q=zsQWtwpgvL!J@!$!k~^mWZla+dD}C_D>-D85Zc~};+9$mHvsX~$G_{V< zt>~o9=MCX{PSl}01wSPvFpK9Aif8CtEY$^D#C~?!_@2$@+5^^fjvNzdQIM3(D0J=n~zL zP%Q0%*S$|sIhiHTTRn7GKnLGa`{ zFLR3}Ek-jNtWKdr<_rWW8~_ROxp}g*T2$z!&!7cdty+~$T^HQUuC_k1_Gi717Dlhl zOc~y%%N7C2#1?XBv`6`|zZa_}D2u;MXERy4+t*c`S&o_mWDpi8N5{7LRDP=9kW@sV*hD~Cy%m^7EC!Kw!M{!Dt)SxGqxvEjpXDqC=COJuBi2Im)L7v@ zQchJB=j zegAD3|bj9F*jby-gMu~H_VNAs8zJecO9R+v|}u*X1YoYW;fuPR4xb_@JSU??v1}n zHjU=a$>-p7Z8s$bEB#Cx(#UZi*t21q$jzMV{B0#Zff5e&%r?_{BZF%@6EG<7{fcC& zTJk-9=kOZSt4={pzdZZM?=fBKt9J3SLzv_+74$n@LsK9;eUZ-F8ZtR#$GdsWJ)kH2 z+}1}PPx85mhh%n_B6D2kX{-u^IU~!gL6%zHU-Xc9RdxX$n~z6%q|b%apR1S9^fF0I zlYCLiNiz>7?7GKQ%wbf*TF->iOi;Nr_P)*QAK^j$ZQp2MF`3JLU^Ctck{kWWP$T-OaLl~+@533jeD_9W z6V|A`DE3+JMysuEE(4(Ju>BaNHcgF@uDnqqR06vQ-e^c_2~4fED_(NpDFxCTrm21B+ooj zd4F-+*H)-YC#=|9?I1AF@iNHq?z`tP_e|WvuNF^PEA67e!CKqgT^0KFw+(ZoWjb_D z9gj7h^QUPmTevi)Ju}UCO<%r165N$2)I=-@Qr(;kv9v_~;Gs_832+cir75tMiE=DI zD@su~U=2kVRZCSxpXTcooCoH_ypNE$f1S->ett*%Xav10KxgF>L%c#zWRmC8u2E&f z28?up{w~t#qk`!?J3BkH4|+;yw=rFkMB)Q!=nTU-Xbsbpn#6&x>v&;Q8f;@Yr*}`| zUfC%L{4J`KJ(s&c=6=U!6UDffbv91|TnLV_XQ6DgqYrp6xM z#=O2m(h3Km1#P$uf2#IasZb|Q@WMy3%9CT$;>YGRwYDP*#QJocB2eAfw^3F~S%gK2SE)o5ywQ?@z2k>HEdAeYyhTCT9Qle??!EL({kk^wjlRQwz9lbB zu!J^K!B6x|qv%(8$EsDCmnxNdAO0z`XWdzk+H>!H0vg<%pyD%=v(=6_Yi&62Pj6Ce z?N>ye9C0$FiD~VX9r52bsMl$#OkCoM{c-W?Mh-bnQYX1Gq`)x>G!i!9xM?_;iKPBYh%T3)(qK&&(S6hKD9tB z#`q)7E?9X6W304F^Oe8wqh7hjPC0DqAtCN>fjU3g9ZzblV}7P*GnKw(kKk~?t$aT^ z+?765b<$)p`dDVuqtyhc6ZQ4+6M)`TN%juwypN2s01Wd42v2|B_H9`)@fDv|S9T8q z_T(nFb>EN9iN~&5U0nvC+mSg zL#V@qG1mN8Ws-dR(5Q%wwN_DmP(J#xx*Qy-rmLh{@B#%5?~pxdcMqPsr#E+wt8EI+ zKYP8x$R}b!O=jt<(6u>Qr8xsDWsYR#AKWWI-W0Jod@PD2cWk9L?Z8#B&)2Q~;vM*7(9%C1#zZDJ+he`SRa>2_=BK7bk>($HxT6C&872(vBOBz3bL-na3|(V@HU9`dhEulKe$avpAxeqrQrf(*>!6I+48p5c2K$^uhbMd*fSR z^{sG&Tm)~spJsxZ!)C;Jms&nVzZ!g;U_3fa~P!%0vl=7e(IP*;2PoeiBdLECoL96pw+&<@b`6dzfB4+>ok zx`GvKt9z*cs}1b+&R!8QiYOO=*WDiLXJJ1cO_zHP8>AXLO|4IV=Bu=;MZMxg)ul{q zeqp&KF~+kDPb_h0C8u6_ZOPXm7#h`S;^6m8Lm792c;wG{Z|>CEQ`k!tD$DBtK%x~; zgOWrR9!wu8WEHwPBsFzCrswhdW8j2Y9Q2S_3HL8L3;MBu*>(Gh{!{XpyN9R{ec#C0 zyPYXJ`(0;LW=x$pU?CS>=iL49@@9q;s4SIJE$i9vGoKHfFxM^aT;6Rmp5W+J_sziG zr6c|ycV*v?7U%v#(6L+*eZ0+L*NQpIT9)#=#UawVz66I}Hc6u${Q)gi9p1rOk8`%u z`392FD#@m6R*2)d((asJQq#gMcRKz3{ch7_g;}@P+Ier^UUbvO)p5x%Wj~ZA;DKT| zeH1l4{|9$+JjvH5Op;3Xafal~p)v$ea?*}r0tH+qlZL?kk~7fw_}b2u&un#aei-k$ zSm-brp+dkc=g*%z^Wqi@6?SfS*2SmXqP&7~zr+S5-B<<#1P%17(1LQ;e0m;P9j&b$ zJ(ui6qAlV$q|bpNw`P0CDW3)}H=7U`$4XMlb0Zg{IkxoHG~de4LbgN{u8}SME59{z zKc$JgT~&R4QZMedJ(fQzEd~V2Rf(Ek?YivG-^Pa_eEWhM7#;5`R6Va&pP{#=>yi0Gqjb6J$n&rVH3|!b4GKf?j8Ja+EHd^frZxSLkzF1#QP3b&FZfo zyE0hAz4Kh7K7JrMR}*h-z*&gD5Pu@L`B<5F9M$oqVY6Gwvgut4R;lNvS9MsP_G2TKJyh@Vfe3I@4S!KiH`Rr! zmlh>z`#k4BOx4Bpy>zJI3>bV7GHZW~8z~?b=jN)w!A9-dxyoCejg+%h6zfzWJ z?q9JW78B*nFV=0<__ap;1@}(@au66jZedya4jXIAmrmPI*0GqPC$d+qiU|~W9*vFz z&r4k^?ay5X3`#-57Ou`1kFrbeiKt3vSw3F-|JpGBHx#!|c777M{u7ECx&EZb^A!d> zFh=Rsr8E=-a;?JPd&NV{AhCFO7ytoaPcAmTik=Y8s9sPI4tg7Uc&$@f!^mE4Y)L`6 z`rt^FY3(CYvD6dt+xOKpVGJ*y5Rl68;)Eh!cbPB5<6SPZucx&6Wm+Po#6HE91xDlf8(ul$;y%X>oR_>R7eg?ImN%I_BKWXuAwA zdL_KPT|Tgqs`I0LjTPbLvRLjZhzEY`>Sg-#1Z!sP%W2e$Iks=C@u0;%n4ZI1x1dsD zE8BNyGDxrl*#xBYuT{BqPlU#Vcqcjpy`bm>iRRLsJjs2u`=KAv5J?G|OLY0?r#vv`DM- z`EYO;QXUW|WePItg-y_sd@PzSJqTSF!~L+smhkB1A!yeajn?qHxb^RvYd|<(?;(!I z9qt&6Zh9OlJdQY==cb$hoi7{U*26 z{e|8B{AK*sG`rhqh>U%x_KsAB7Wbz0a~bZuAJ~@RB|$w8qbQfZw0*gL_;hvG9U1ohLpHk;Rzfz zEH`#x9?rBMj{kZ;(B_4+B_~AVZJg41+z6IZd@E-tKl|%oxL-749!X&_w0bcuyW1Tm z7@51;5pOk&Ob`gv(lW4Oda%?O4okw>czGuZAmgzO21NGbE_IFieSRePO9D;}u<+qsDvGPijPUaYJ3> z39XBi&o5i;cgVWZ*F0l}Sq!7+;kY~Z`Lg$)V17F_%>tnZ_kT8!xI-Jq^lvHR$oq!DCkw$!3 z49I&f1br#39yEjWOd!$BJ!8Ef;UiQZPQlWA1)&iT1Z?ET32l@?Wy1M@qh;NjdvEL2XH=LVWmFD7r`fYLU7kg0hnE1vZR!vQ)1Gis zQbxH2eTa?81~fgg6(m@s^$frO^GUZS5nj3jJw0JLnneWKBOngBKgh3HegZX=wC?$a z3lPf%2FFM@E#fY&2BAOIne|@;Gh7b~3KP5pZkWojB8``&AubzwUuUA?nCVt`7j2|lDjcp8$R8mzZ=09nH zIrsusY~AMH2{YoBnuxStc%l#jgP^XbXQ$NJ9F#{TYLQt|faXNz{dC|O4LOt63SS(-4M4m`V`1v{o8wd?BXaCU1$WvnYaP)P>O@m>I zxE6Xbq$H-#U<(s%mIkwUYlK1JYvyRy5U}^c=k1w2!X+SuLqtH#((YQy_@G$ z-0U5uLu%Y;VB-0}H8n~7!fAGOnt4C5nZPlM6yzt=m)vnD`{*xhB@J1Lcon|`cyNhZ|BIun4PB&%J&NWMd>dktI+a~js(o$@V;2@>;(YDGc1J#5 z@8?i|LJY}|FMZYgQ=bN~YF`mTHscxPq!0-v9r0E>15Zz+KCvxhN+rv8737LB^lGTA zEEeDIs`&Z`@;|-zcqFj*3U1=!BP|!zm@X|{=MX={dha1;5g#aKB0IK zPbHn!_oZQ0H6~w!e@gY+gO|z=30@jtP5F-I7jqK$WnvMns#PI7qZU-fTsN36Kp64m z)q)1?{(1GPJ3C0HZxxfwohK&c{$glV1x~TvQOVWj^E|GyLGCeIDH!XEpse6v26j#y z`LblPiLV#P0k`Bh7|@zQnMAPd7nzfjH~qm3!O+4p2ZmHbTa_?(`aPF74o=*~7dHJDW3O+lfaJ^t_8^|zFu%jpgWXdout?c$OpkzIDUh^yT*%=cVHVw zzQXzpvsqRKPSTi3%&~X)g&_U+SK@hLX4@KpW*K5beB9bxM<&p_rw1vSYwkHXU2B?E zgN9Hi1h4#(6FzDrYn(*ePWfm9(Z8HOe6}sg4NKuiGvD=x)@&K>^-_s>t%lYB9#Q3q z>y(AIfeN4JS%W0^V|&vLZeqQ@#LHBnRq1k+(bgPmV1E3Qu?Ke`(xpFpuV$f-IdnIS zaJiJI;ZhU@UbX5D9sADd9g(>7YTf=Q$Eo^L(-XVHzRNkJe}f5E9rZ-e2FoT^2S2 z5OOT8ne6wdRj&F*9yR^$>X*^*+MHu@zrDRsLNjUI{rcZPe!Nk>OJnvpsYq?-0(2D~B)YC~0HQuN11Mixt6Qujn~sAdV9KtulE1Vz~Yp zO|aPbW*LF~#rs)oSg>8bH)YZ{s?%mClr!t+OZ0870p2{z2zJWaML*Y>L7p#iiTSI< zDLE>8C~kDIWeb1u+EYt#G+B|PkLJ;h8fXm^#NL;DH63ZZV5c(XcB#l@ZjR1qEcIwAh#!#r^eA<3)Gtu{1*Uqb9|PM&ffGpom*};mpQKyMHmowS^0jwoX)K zV$!YGvjjR+42-G6ORv$F-)in!P3aVT`>K+i_=)+)Cl95{ubC0eJmiG8e~DNSnl#Bm zL(GBS;WhH|gs$&chY+sasSZm_@b!R$g0vG(G-P5kg9p)4a#HfA==1xGBVj9agck3o z!d^~`<9Vy)!^p53bSE$olh#VyTl3 W57ObTRY)Zu(di07eRt8Del!xv?kA;Y^1A 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

    ^+SKYYYQ+KMIG6}H{MDFVAmod79kwRS;=ZM&tA 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

    q};Y|y;C#c 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


    }j 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

    >pH^&P%#+wVtk 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≧lx2$ zLk4GYJs%nBMAQ1-f-u-yQ}BRPtwb!4nxBSTufp;v8FDa)kZCiWbUw==l)Fn`~Z zbYZloWN`yA57dh=tJHIArP5yd70RQfrlyu6fBN;SO3w{r@M_p4B#e|L4l3dk5-MFW zYT#dZ9Y_!K`t>VW5QIv91ou}1ZwdD8rHY(fNW=OEo~u=4k8D_2S*^dlJ#&$Q0{o@F z`NO3P0E7cp?#3I^r;pof_&+b~_?P_`B#ZPgws~GLam7e0=?x z|6G%D=_GX+O;;?}PzEHL^vTo1d$`78Hn^`Es0i}Uf&*7VX-+}^`2hHDldR1D{pm>a z>)$4E>_7kc4pB&>fdrZV!oNiK7am? z+whPpOxhBmy&On(^Y248yp?ba4vCFYKfWSA|8wURA8n|;2U+OfFELUeokRamn>}96 z|Nip-)r$c$^HIXo6K?fBsPh|-12%dOz?<$$PK+vBC=BvgC~g(Ow+0g?RDm8Y@1XO# zRFDocWJ|jcMNE(J*AOdHjEo!@S)qnRO-$Q=J=!#m6w1HZGb*T?8qyj z0^xXn5?>;AXQb}%`LluC`y@*2xRGB2SL++9td{#v$3^4joe##s{C9LmCl(Hz5L0YM zzjfQt3-|`3p0%}84u3!NRJi*mUpM*zyEfD&!6PCj(`1MMvUMeECi!;vD{R{xr5p^l zC3{ccyZbhW)ZRC1G1CF(5U2nBc++_q3fQZWU(Qw_*G3bzWLWMCP6;io@!xCy0;5@k z8X4+LDck(L*TY-q4Nj)m$Qpmb??%&T71-cn-K+I^u9E`Q<95}Noc$&lE=DV-c_58h zKZn^0 zhEM^cDA$w*)yF&q*z&?2(?Pd!D})HA<`AW^V5hm}3z=}u9xf1s7nAat)dtBt+S!@| z-Ac=bq7Jzj=IL^)Z2wxb>YZBA?1)iG7YlN$Mhe_iv8b>uhERJyRJV&YYydzPo#EkX zX{fxwDj^0CRZj**E{`7seKAs>S!@XKwJ&0f^NV1{XzRPLSR}a1MWsnTAZ~wgLbxl$ zv$nm!6t>JG{CfiLp#HvZnkOe=WR@Ovguyr0{d_tfK?e7MU}OBb=77BCpr&4pV!2GK zLT8a>?2(18>ZMB$*&`FY^wx)*n|vDW+6f`T88e%vk<{!VBOzqNPD1ZUW*{=k-b zs}AqwG+nvK^PmJpFdhHBmdg8LQW8nLx}(Lq#T1>96gJ_Kgw{lI5*8#qULYPB6#qO1 z;0E~jItS~C!QP&W;s<}C*PId4?S9w4B^Y`3{PVi<`RDGa-w-~0#hSD{1mTB`4$J~k^pr`3b2RFGAFo@c(-t;2OXpg>JJ?a7BZa4gbi zB!+RNw6U%)DTA6jX^yf=1I&+rB;2-n`ZI|q7HP@I-}}!F?j_xz&#W#+{9v>FyI9GY zylYPXXpGd%tNB*c{lgKG{BXY5=L){AXK=N5SKp9_Tiq{VD$xPJ0C`Lt@9wzBpJ2*IgI}Mbjo%zhVDqbwHWQ>J3`>=p@_HKw{yJMn z%0Bd<8d*1CU6b5mPZV~3D~>SKny^3ssO4v(7U6$5UZk$qSH2gDUp-7aF$v7UN;!+R z=V(BkU8ELO46$POxl-Vv#kK&BD1T1Q^=M%lp#6(}w$jfFuwo7&hxdl~zmHS9@Kx@5 zR{A&k8Nu6yI#U+8Ynw#FKq?xF<`m1?j~DTCulH6dru^qkkc{7BqFRG z9~;(w+CX;{uvMeK9o9bt<1v#s{{85?Ue((hsBUe&hilMXW^7NS`C0 zFtW%U#jF?+Isz8z8rH(_v-&HiHp|RFA8)HiiEDa)CVWJOU*ZO^B}jXmPWKHjS~@@C;h z{#*AB`U1i~P&1f<|IAWuuzAS*Mc|rY$B5A`I1p3&vAG5-IZ^S8(Df1&nOM zP%~dlkO0g`6f+k?LXpKb({kk+!9+BOQr~OoWBg>0yE<|hJuYvo#0HVuie(>optrf zs;OUSL#kAI>cyo+9(AkPJPZ8@8AB=1S(ivy%KGrDLFxcR7h=h__pJPl4hbbSa^C~ z!O0h9V5ZqS=M7@rX_Zzpo1^>}p|oLeyNTSMb{kjFDM1$Qi9OG_@r*O+(G@8sH2J`b zg13ues=}f4B%UXy#@(XNELOOcpI;_z-TzHmm%;MgIJb@Fw(MLwYZLG-v(3oL8XHqo~vm%)ZnISEVNod^8o0?n#C6tKk6* z=w~)9U0JJy3-^1MA+yjk9H2y-UBq8jQ8z83Gn3PdkOoICTPiCe(L7>Vt+b#34K{2@ zPu(N32cA0)*@$`~77<&dTRh^=n&)8eu)|%)vQ}(IGw8I}sbL*YPyr$OZ8FGbUi~gAUBLo?;=cof$F{pt)f#WaT6sV))qeMHGH_RRC9_8AP{A%=k5Yg`u*x zOge#6EP9BZMKL6F1fXm9U0y4_haw&H-cw+O5bW%TeUUXO0@HSvT$bO&&IR|<_DvA101nMbY~?`SYlXICW1g$MP3cY&D#~N zfFnj8A~l)Zcaen5k}e-hAv%Fhck4i!zw!8Kij%P0rS&EJ3bhf#squ%)CgJ2AwuZ}|%ILRo&Eunse_1&|fUncb{(4fw!-)q4`@DI5VKXG9m z7F8h^3yQcsiAllP!zhsj)yLCOwyECpAzl{+y$(Ab0O*a$1IsjA1$ql`J?~K-d3nZR z_YR@_jDve-pB+G$1r&HWYW%iR?{a4;{#n4b_EZvT=T+;|Lva zc^4p~S?E4Gyyr$9R1PJ1^8$tzZ$Axx0|zGY37pUww}!3o3XiX{8C98z+|~MR5w~9& zD|{JYDqM9jqBObdX4bYO4p+XivU>2#wH5$!M^nsN(W=4=rufMPjfeE)U~zAKnHUhY z0Qa%SU4L=8O(Ikd`3eOca;Z!!F>MOQIxxaBiU1@>ek; zGF_D>gRZ&=izW8z45|;==7+HoYshvnVcRhwj|PmpKmx5be@OC_!I}k+fly3*(A-?rQJtDf<@J)NG}Iaa1la}P5BdI zhS&Ux|KSl;Tx4)Gf2W}1UB?S|!Hhs{5f~^RUfO5`Ix8RQzaLhPaq^iOPY1|M5_^bE&G>ukx}U!W>CAnRqa= zO`s6hjOxjgACmTBx&*pMMT;G>w{T0s>QqC3p5ApQtOK25{G}()F*Nr`_~obTg{fs< zMR%x-1+1Sd{D!Qx*e`rASBu(or<3BZu+x42e;*ye(6G*8kkIA z`%Csc8}}w1E7T~p3r4E5!$#A>ZiGihXn-VWF5>1|u}yzSvoVRzbGPt`oXx0{&Q^yN z`x}9j#BHcXVVrH8`ZfH_fm{EzytbW9An7iG%P z9PARB4k-4;|7DtL^e+y7-X3_W1(c+Wsi*M=qrH#2joN2XMH!`##;G!W@H1IF2T?O~ z$;Tep!oZJq<5~#a>Zu0t>I@hK<4D)JbJPNboca9~F!U;+)=snX6eDh;TAXgPN>X#= zcmFBcU!%RiZ4gke_eaE=5(pWHp3=2e6_@wI*Zv_!L)`M z^At?_TVf!#_|Jt&q>=E`_X6BGP%4}c)x9Lc#txFppf$yJ6Wb%fA*SMinM)z~SG%*#tkK_6R@$>xwoC>E_<5Cp_~bFvo=SL(1c4oR zd8Ep%U72TT!wj*&LwT5<6o9>s06po4v6*55s(w?{K!JnHWRUz+%OY0YD7zRL!i0AUcxd+)3l~&; zh3uxMWH43*WGE#t*DYh@AUvbD`} zM-)Zh*PI{^>`qhvR!1j0c_#i0D)GdXfJFwgY`#m4hzg0fWcJ8@L?IUy2Lep9)V-QB1V`t59aUqJn`K2j7&u1udM3SFqCo*5|oGhPD2rfCEbJz-iIn zA?^;OXN?Inaa7%!3nb}|BhnGl2f#BGXlN)szy1-xh zRWbe+{j^B{MPfiib@oS~zUk6#EH-kOoKuIVBcXlNH*9+IaZ8r!<XYImQ!S{Rh7ob(z39`;3;Tea2>ARif&rDFNPjz&Eyac>hTetmhV|;+XqD~rSYZKOU~K6o65omUo=F!8 z>Ll76o~T3^_*L0ix>z3!^mZ&MgBjgNA!LiWCgMtf?VlnSK(x&TUY$FG=uMn4!;})Svz(oE((8OMR{YKH)ApZRN(?H-GB1P zbm&Uq$`S*}axvRetKyU9Qi1YO_B!Ry8fO}mxeoRt{301cY~fT z?dXTxTKQsU*_|tFtdM{S&6FQk`{3V~e+{0PYT@YPG(zL8;ENb&pzX@?#X8aFQ zb|z87H8jW>-fkUjK+-45$+`IS19hm|j||R0Qh8aJyc{7Jou!UpN!80-s*RkDOr5Aay4e=#GbX56zVVyA`@E6rb~jhwWOO?y74i%bj` z{C=3<8yq?Cc!PJsaGv(!%1CfhOR!j-gC$bmu%Vvc7LaMgE$m5B;8UeJk{I=~V;|5T z1N!hf*AYTYMQ}{nI{?s3ayXp`iQr`R^yUITacz4KPuL{XUJ(GZ zkWmzzb+Kuy`|tP!8qM&B+*<;W7}4k1XODyLiCpZjw4kxE4?6f=M?;#UqR~5zJy!7-s>!Q(NMTUj1J#2{yqI(3>0fF++}8$_LeH5sgX!uB||FSQ*Ja;0=> zaQL@4oTpN>B0YWdGqb~0p--j>nk0aSjk|jYzR>q&{NbNFy?=ZEMrM|;d9|QuXrz-4 zlH6pXDjiat9r~O8M`GCThY23Fd9`Lu+qJx^@kuu%GE6ROI>S=y&#KRrZJ3y@vekGN z%IAP%s`25F$EUDcGDB>g=2)LA)DWTt>;Nq&=ArVl13=ivYz#5uRuYVc$HFN3Kjh=v z>uF#dnemDR3 z@p)!u&4*dD=F6P5Za#3%ecwBOab4Hmn}EU?wyVjug(H#|tbG<_g;%>8+VhPVZ~bI$ zi%r;6Az3k2-7}deTH#Lux;Yb1%LxSOF*SZg52jAJfmN zD@C?B+dq@eeLHt0>>7HO;j~tUc0m~n|D&mRA-{;H_uP`g;r!IJQGjK9^Ems_xjgVW zYPIt@6a%F%t>*^!mF+fxwXMse{uRjRa7(|t)RPxFjP(bFo#n<-QZ>jAPS62X(sk4C z&i-bBdJ>i?x;Ir~hF5L6jl$aYEv>9#!@Q)pQQfVqOr}=bcf(d&UyuHM6SGS#?e_m0 z0r&opD$wLn`Lw75)`{W0^97EhFoez!47{dCyvWK=CTBvgPKkA&jl$0Pm-*|96vQ7* z`RcrFHE)L~T220q<`>AqdXj|)%l)y%I(EBU+Bd(gof=OYc}2Eh-Y)qUfLwNqFFatv+Ax9I+8j6LpQ~!3XOUM0ONfCM6A;<}-qO+3$R-Hs(eWR9b*ts6TGQ6-SL4Sm ztO*ENf(f3T(Map|-7P2!a-8L874K;o6MvJCumry{4q!zAH=mP()C|Cr$&;HXHLXR) z_rSvbU&X@b1(Y|wxp{hcNfi@ic-3x0DQfbOF&a6nYU4v?%!M{1=udmR>>sG7zwci= zOH~lOFSfGUud2YC`$9SOtpcRV;54@0EnvwW@3JkFuw!fJPa~gEQ_d$sm#uAr$nA@f zV`2SQ13dEpXv`WQ;&;G>Rcu!>kbl`;Mt<6+iJxRC(Ll-6S|vDpb~Jnk=#)q7T`E5TBVCYL z`p@WRiS8fVouxb87kV82UHEnlf1&$7hzWza!NBjDi)n~Ro%i05C*T%p( z$JRW-y?H&P$IBYp38(R|%~9x|u5avhL%gyF*$>{(+FyFdW zJ_b>i+Si^OjbzzzRv_H~Th*EoczGRC11cCn|362dfSaHH0$NZCKm&{c70>@js5lVL z(kaOQ(}0hE7()J!@~=+OJT3Xe&42M9&{o2LYW2T99g094W#-&p_$E2vIcF~{t5ze- zIGyziZ8NO&MfL{)(-@^R82-}EGs}boe$D^735D|Ure%ojynsz;m)OfkJu2?PLP3;G zznh;);vbzf{AFv+;1d1Zbk=WH6)-xtKKxx z%FCWLq$MFWVLbU2=YMOGsMhdn|KorW2ly-d#hrg*x_DQ|`mCcBn~{@~vt#^UAoMI) zSkLjJ_tko`x{AsQrZ%nOh%pCo%Yc1@uDraw*GdEff;#=b%8CHNo|;Cv9@B4L)9skq z{fP`=Z#6wVA-(*6V`2mdx1PiR5g~T5{^aw5!on?X^?y-E#}W?Yxh2MH$76T0e@|54 zdHns)pW^TTrnZ|s*VfklQAGOhOQg7tr*B_yaw@-0lSc~5UL48abzyk--(MpDk=|Rg zc4x48=eSqJ4Z&^^o}} zZ`*Oqhl+plnt+98kmp--E>Pib{4NCz3Ru_+_dFFSVe3Bb>(tfNS)K1qf8+S@30nEzUf~HMsAE}6ix5y;T8$RS7nhe$ zRGSUp6A=+@RsXB&Rv=JL|NH!(6_o$KLSg^E!;&9mkfTKtu+shfb>`x7Wo5S*TC3T3 z=Wzk@FL`buVF=okNZa3L1VY}++#DjWb9{&SS6c*c7coUv0j`@UW*~{SXR7pDhJa40 z6K8*96sD=CMQhFusU17(Wc?p!sF(WY70ji)6#Sn!*j6gYT-Ix6F>}YJ`SL-l@+AF1 zh4#I+Q&oTDMLMGJrc4^)S~Mznf^azj6hn_C_N5*RQiEOoda(<)jsEi6_bPU(H9({J zenrPAk9gN|5e)YxOC59h$5}TIM4Owuus`cJT%PoIh?}WuVk-wWgcN3 ziAjaDJevZAzXn<>r$uhuyFpz+6q`STe58>=waaoF2O29k`8j*d$LmxXwsV}Oo6L7; z59OuPMf6w@0($2x|8?tff`%k4cJ zg?Wa!U@3GwWFI=VO*R?XfznsE!gO(jOy+u+c6d%zo5GrL^1*|4GV)F3vJMX1rSDZpQC%O@EpVC@kCm)*13!vCzMC;Kou}S@&%i8%;FqUQwJQ@$Np$sB? zeZB5}EI4l*ZEViciEM0W!&)clIbW9?v{;t=wQYU*z{Un~Dnm)nhG>vi!-uJH>*igG zL;2%vB#ca#9^X40p5ASyYt?9fb2Bibp|ju}w57%5gukfpL^9_H1*)hLsisb?u5oHv zU4HbLP~MM(5qa$8F~&Vg$};F}w$ zX>nn(B(}ag!J-j9Xath7-8y55Mm5#(T-3=(;k%VG_s1Wd-{(!Q@H6!O#B@-GyPdq# zsoHalTefP{coNWozEg2g#==(^5+^c!gKVh_`5Mm}qp*p{&GCmMM#Z@L>noUxNoksn zR1KlX`PYmE2tQoU(>P#mzTc!td1LEFOEOWvCry@}LP$piXYB1AtH^xY+|c4xbePEj z&*VSSy0zc~gP|rEwsrLmltu@IZNXs%#s4nJv&l$bP>2Wr^8+EF;=i|K zm+t0Yo^N3o8l zIHst-+J08DPyeQ@c1SSeI=SckoyuQh_C?P8eOu=4N)wkwJwvXGbLM7S=1u3Vzdgsi z4kw2e5leMG4eY2(D?a?!HlJCo+ry2nQQlaFd#5|4!+j}L;&E@K-)_;=~7WYH;6Ekg(v!==uJP)Z2b1&V8CrkD%Lp zCTlC|uI!_9P=tv;P58;ubXS;&_;hEOsib3{Y|(8bD^x>SUwGreCFq!f`hzXCr1(rd zPCTdN<|lcE>ahKg+8!;~B$P9VhGw%vOF68H1_$2Ur00g$KeBWV0U0wg+|txBlF@eb zc+_LP1LwGNAuCljD*|OpoASG9t&jjqh2xaED=*`$(@pP1bP2%=liI*nI5=7QcQ!en zuI`62v6^2CL=!|5Dn=84(#NNyY8{_9ERcN!FN%NVVB?1{0|Gh20dCDXYMflqHp?06g-h-)ZxFlitxiJdG?Yd)9h052zFE8apHG5 zoTyvx<|ImI8~ffOk_z=kigO!*+J*l__#`PlAVEW8u~Zp6a9t`^ko?wVRF$SWG&LU< zXLt99bCo0y$xM)PwqDGqvcU`sC!R-6Pg?ZkOeDE%%N*1w=yA|X56TTlXvTmCrYDNw zvPrm|d2HUymRE*Fe_PPmPgqJ|3xx&JcXlQf<;eTx250z^+YN=^D z==BVN=UhiUHg8HqQWVsub@FWxH4SM7IqvteSt~~_cb82rOB&LYfakf)6uCR13>MDD zoZO#SUBU|=@>FtDiVMp-*I!2&G`LiF7{`O>V2(M)5On8)S)=^$K7ZyUETgpLovo-h zY!ELzys&*Q!;z`Dr0+q&GzvR3bk>_#oI71SiecHgpIOh^8D@(6O?^(BTWLW=(yl?T z$ocV!({ql|1c;x=UlG$d`#yeQNj1CiZJ!WSJANLIO_I!+Q;RmI)A@p<$|2B3j?H|X z(9t+P+?HLYMZ1=z%=xkBUibZ{y_SSlWFG5UQ)AOb$I<|&?HRHXWQ+Y)yxxe$NUy;q zE(pm)I5)~SgqMLZ!ty9j#m?rPa5+cgFK~A#h6(5~3cQG|zWV)V4X$)FIq+3BU_@4K z?VCWUpTQj058#7?Bd}VXm%>$%qv;t4wX3AX>v8c{oJ;gTd%!+FmV}lNhc%Lb?CZW( zrrWPTL>d}W%Dtq=HnFIb78hgT8)P&Z>}Pdc3V1ndIz{;jFG}3h2I=N4{|PhE&7 zG)LRg`I1#D7MpDRmK@^Z-C=313zj$2Q7eefP#mI{3*T|WzzJl}>>utgwOfwnCD5T9 zhefAO9&9E#j^2Oz1fA;3ao|RdI%F=rA&77rwu?bxi9A8XbvCl-SRL*q$zWX-4=JSe zI38wF&z>4Iy8?E~_7`%(f|?mt?h^kF%~RR0-n!OV9yxa*0*eCqkg@t(pu;3S4?$uS zuRe>Ux0^8aB41%&yBUbMki=09Rdt5%1#PO35`v|e->jF-mGu!pcYh|N*zS0n+N%Y7 ztwGnjl8|P8b_F>m1yuz`(WsS}aG_O&D_dGDs!b3QqImW{9KS%u}4$8Q4(GsMNklcrdg zza&~H@pB{G4BNB63{n}#3`xU)tK{M|??6pfk7)ASAlgj}HbsYpZ|<@;g}-i6a>L~} zBUm9MRAE}$-GqP%J&qtWSM_m)c=`Nfng~{;A?PSh2=TUrvJT02Wdj5FLk_~BQOmBF ziUC_d6ZdA};N*jI&;OaNo7>rUHF=agO5~W`R&+>I#hH9VsgYRo>$dh~M{xTL`zhKGLb3ON-Gj-5~8 z(p;i!LCCh;IonsLE)F~jBC_c>m1km1{R;^*L19iyra&+a{?_nYVp9_4!>8xrSoEx?}^H}~2E|P`xWcY|& zWaIogygro06BvA3EG1S9t>4N?lrjzm4$fJWz6ON&tjEn|QbRBH4b>XqYHXfz*FFI% z=jF{b%bi!uxEJ)$SKc?~yM7mxxy~Femt0$?S*^=rVqt!N3HDk3@)|MbtZPWVPtoKq z(hQ6Ia}{Ff4Zg>j=+zF2V1>F-Pq_o~sh*}F)v=yTcj()dl{bFWq+j<@lsp71-}Dl* zV13t~J%8R@FuVmx6OS}f#Qu5h@xjlP&o6~MZ0ybc1B-JizQbTzC@~fL?e-eKH+HJ! z7N-U|Cs%IQk&!j-YrTH=Q>F0q>?rV2wIc>QXkDCwATG!SVf(a4| zX7J^}Hu~jzYutN&Z?rWoyR!$Xg6O{OUAN29!oi;>5qiWXJvi$g&2v~tTW-# zee+_vdpUmy()o3PWm=Qjt5$djSa$x2(?Z@PR@OW533vJYqD$=Aep%)9m6M1;3Td$o z=HIslRUuM%F-k~8d97p*{OGwd_8|?0bx(2Phdd)$@V}R-KQWQ$Q$?I|WTJ{{pNlKc z@KvRSdp$T1@Q&VPYbx9}wrZu)y{?L+jF|v#|6ZwLD0O8C2Zx_O$e1YCn3#s9dgil~ z9SEQB$9c9|bJb>f-JgsqevyWo%XnsT=5nUl;A+Et8IDRsV*=j4%~b4`5oNg=DWUKZ z7xwIg@Zytufwg^arak?YSevqCV1q0G&$6u<)&Dyth%Wbw{&RA<)VAQ zBt(u4|Mi;$znO28AB1A#7@o3K>3^J$;Tz9N_(My4&BhBgK0cSDM$W>#HxSIU8F$g7 z+c|fb*3<}LHVcE9kWy+f)xJ!t@x{hcpGfg3I}Y@N_}kKKNQ6*NFA}vK*eew`DUU7$ zqPxXRsjCDIHTD$lZ*gDU5<5JVIcSuJP+Xq>upF5^>G-;S44bMq#e#d@Ui9>LT08fR zR)}0EK?Em+M}*XhIuJ$Eqw9x7G&qD5GBy%iQK7!{oh793aMhP}y3G{Jeiyav!DEFy zV_-s_tMoeu=|f7QNYHaJt=J5m9&ETMsz|`ny5w!osk*@sK}BulJaX zehO-$J5{y)-m`i5=*(;Y(o(pi*!*r9R)tBpYa z}DYHwOhB}10ukAJ5S;6yrxFwi?(%~%d{ zMLa3NHK}^ZhDNg7`Pgcz^xbFYAJ=&6jWplWEi~%{f0=mQ>>Jwp*RK}iz2D65gW!wy zH_9$7KJ)XA&@GR-RPr!;>#pSD?5;i)#daTacU_T_N317ET4_&4kG_Any250- z+_W7XuNuNG$Ms{gQK#9Mhn8O7?e#woT$UpPS&FL&`?_9l1$_*`a3FS^Y;QZ;TB@%P z{+j~CuK9z)utDZlNIkQY@?cz?dEX9YqfdLsVC&0lTk44m&_njRYQuh{6$P?{F2MWWXdr^;|q2s_H;1w33-+f9pr8I*bb}{u6_?W@pUHc9)ELFF=4zPliwlaFA7I9waS3$)`J7~RE z9fls^28;~f1L@<~hci+zW$eOtR!fu4*or#VF!J)Kbo6#P=+J0@$kJ(vvQUUpBx4kb z(=YmPIO?S(4;!LHa25M2U=iYer5pV9s&{f5I zF#8H=9Et=ZYA7u6&tNA=EmPI}!LhG||E@s^&{S)z|$VjpE#iKtb}JyEP~$NqBX>v*x{Q7Y>FG>+nGc;%)b?8!>mAVyxu z6VkaCL78EKy-zMz0;?jXl=WgWO(}S_1C082ermM{`wRHY^-wE8y6|{=B$o=NP=vTd zNMt{nX6M)upLr$A)IETn!)0o2K2~1fp{5q5=7S5QJfKI{^W$;CxKHQAF|hCTcfS*! zTRvQlt%CQ1ys*O0kSpmMGNcgqO#5SQQn!9n@m{`b(Oe<4qVuX#AB}O&H2mnVxVaj! zpP(nX!C<4QQw|<+(+Tl(_}kK{tlIg9j-@Tat^Y;Nyd@{UnzUlbzZ=b zz3{w&wHsKyv1h60;?zj#*x4dHd^$=+wZMliN|s3@Q_t;Q^m%dY(=HOcQPvzH72CIn z5ZH1k`zup0*H;D(p#*fgYSx&M-*lZCvwt&k5)^-4J;`^$**3fBuV{)@(6h3v-^pCw z$tdMoWiD8hrwkk79mc2AwVn}ltW&=A;VpX7p21Cc`0_SG-SCf7px;#8OVzKoB*&&J zzi}LZ2vow6n+bD;fb! zz8_H*V)i`}n?|Bys=dh9t7*C$=)aSIv1^POvgGDlyz}~_HAhMmGr1|(<8b)BX|y_Ze1>>810?mQgr`!GL7CfD*MiFMed(W|}v{?M2>^R+96PyR*t z#?Pu8WwXXE2ue(;OL;jC`-UE_eFaH!^fV6jYtQ{oKd4|+v&5oVqKK;#`RTG#_RsR}pL^nGWQr(5?wsyf~sR#+NkpR086sCrT8sMht zz+vrjU+&I!TUuzVwEq;uSty$?Faz+$>-hAPgZf|7_3PI8 zBmW3{y1IWg8gim5)j$foeOEjC8tV82r3#aMY*ZKeC<7`{O+$~=s>3UWRiZEjUGgAS z5izktwp+@_n5Mc3t6exF$k%KJRf{QL{C)1x`R#U>_@+ORoYdzzL{hj%yWXDJ__Fkw zQ?iWXVv2anYl}!noV&F(wHCK&$Ww*Lc5D%SAt~ud)5)>bhQ8XvdBJ6p-Te?l1@ah> z5ALM~ zr&3Xb4i<8lCffLKUbsXZ`zcn5w^Z<=9X3gL>@#KVy^HFv-+#ZCU04sY_)vmIEaI>a zT_IIDs+Z-Gw~KTz#g=|M6-PL~#ZfP$k^0Q5{i;_vhXinPsHi`$wYO8|UmYjo5Jr0{ zf0z>eb(mDPKm~^&Ja;^KD35EOSz+@tq0hOpIZx$Ma0TowJ-qfGXHAWI*9?G?6XP~m zB#G|#K6Bd}$xF}wU=GnA*}p#+uyp{pCg;aChOI!(LMrc!<&5B!cXLvNu9e7#W`f{D z4PQz93ek$nU$_v}L)QoDLZ-`c-c8YELpbji#UXn1t5cO;)8?>6BT%7^6m(u5E-K;A z;MLJjD!F|9;Dcix0}xPe(L{S^;KMI+Z^=@Ytom zoMLVX-HZI@}Pyv>daPdbiDUb)nOt}BPDzyrS!{ms>!2% z#KhY{o50UquccOSOf{Bw(+s7--~ATj;C3S%U=7neXArl|1`q0+9LK$BD?A*t59|Hd zsw4~WOFfKriob) znL8{7oo$v!p2u1rYOE%@XdXih(2GH*qkM_NQ$I9hB?Kq?tWl;=*?B$mSK->4G^k}}+>6IOF066P?QW3Vgf}&@i^kSGjSGiO#JM>rta&H} z9N+PYMq?gz17v|}%tm0xdtolW%db@djaO1C#V7rKC+SX@GhL3R^CeyhpqT;fvP)c# zS0hD!^P`Bj1H{IOcKe{`-$vgXA^Uk9;Tv2Y&A1TG0OjwK=W#w@tTAyNGW!)$w7CJqQL@6PBO`gIctZ;B2; zBAbY$e-j$f36JOny-&$*kQ$1$S!WzeyX0DDX@zz|-x5?kqB_5Y2Yyx5)h;71lE1#z zqe|nGMET`zP}1?REidrx?+uS|zWP92*(@H(T)f?(pH1Ic(@^eAzZCt+CyS({Sut|53WypP5jHvvcR_F%8>y zpGCji)un|`*1Cs(X+Xp|+>Oaq=g~U45=kHmX<+Czs%te??)17lKmdNh{X|@C_ zzW)!+-*&?wu*r3DEpj|;GqF5;?Q6g}cd-HTP20*!ol%Y|0_)1;agbhI8bB8VnauURIwzA6HkszAY{TurraSqxOHWq&?oJnDHe&ikY|O=(q^}f%x%~~2A%;=;D)#p3 zaWaNJZ^Wj8VTXxS+bgn@coL*d35$qF$Svkh6XM8UyCi3a$TrT4+Mr37nn|u#=Ds-5 zrBah%=nJdRNGHMhWAQLkMRgFcCQ9}0Z>A*UN!dRw0QPabXZUBA$HyJ& zopJ)OF=g}`9OyiUmMzE0n;UD6xk63rmfwfct{{GJztJ-LQ>2kw_&zy!Uthi8P`Gtc z3xHbZap`^%e*r}NvYxUg6sZI8CQfYHu8DOT`^#!@<_W3pks)4LEr!0sVaI@l>t<9> zRXj?4B~6b;W_BC!oGoiT-F=a04@uY8l7_=0<=2L9*6VVKx zZhLtkh=kK;8$^}Ky4YSS;hvE&kaxKr3?nq${ei5AdVJiCtQdJf_OIi)T{DD|Ug%*r z%hr{ZBL5}ogDsvw(FTIW-&MEADFytfXoxXZutTiwFAll8*hrOzhF2Yd%|V-iE{%PVPiE1Kl@RfiK#r#Ku)0ceZV%Kx2pkEx`~ zX4B<(=1PyesF?GH54H9NcI%w8S`4=-9=c-II&|rGzR#54T@ZbIL4aV58PJ`$;m_`S zop{Z3--c4f!FgSA(x?&p?H4~qd5gXJAF&5cv{a|+orGJPt*zAO7@8iD!{4uycIosm zG_5dxwpBCh*>kjOU(ZR;_v|xVO_ti*Rq{7TUuq*5m50O}*qxE=2%2VlRcVGT1lxW{ zSdTJIlr(WNTmNLK@3!8c(ZUPI?fP+9ugw0-p0^hZ9j2K!3{fvX`Li;FlUn9QGKeS} z<%q~$yN097+`HE)g-=K7&J4E2zN7E1ppV_I`7Y-_{rB!7Nj;}PUo+xvC7Ro(##Tgw z_9u$>nzl_L6xek~d41;Fy(OmQ^V3SB+m^T|B}}pR8OaSyPQ^Oz^Pag2JNR#!*~c$4 z#s!fFO0q-Q3erePpEv|vNr@j+(rvv@;M*pDy|Z0IH<1QM)dmi{@S4(DW=40ffZ&K$zoe zPAkFXT7kA>g7rglixVfdLt4@4d(;uu_9Qq;0c1Ecg|~wVLGKEyaX(%%dzlL!Q?C z9xo$e@F`@EFM4Joa*wMC=DB_avmGyb0hUr6@zok?a&3w<`LD^@zWlPMN9QCv4Q?c! z@~nJrtqM~uE@sB1%3Tue<5PW}lP@)pbyu+&hL=yzyo3bpJ{3BYC8uGWBjv}VQP_zL{Lc&=mKJ2vGws* zwrx!ub``8g_j4)%*fcZ^X&%c;YX{#@{D*`TzvXjdD^8?UrE~x3TgNfYv>*2#ht~0* z9kt_$gX+R+Boe(E$V%<2dK&uvv@wO+H5aq)VH(NQskmob7d=g36%~_0QJ&h-v$}T^ zK%R4*ePU9BJ(UI~`1{YaGp01@wjpEsm4EV06(0ZD$Xzmq^301dw~tmJaC&^#IK{v?qW-cO7Bl7b*2KbPisVVp+jvm3eoPWJby zWH=AEbNt(OGL@`FgJ#{_w{KO^|B~u{o}G%8J(Hg@)ChUJBTjo;On_bt5JaE0KQzhX zYVL~w5Kh(K9})BC4dQ87W4erQ*s3OV{F#RWxtCz%0BmGB1P;=_4UmTN=bn?p4-(S0QKn4Y0B#F>iHy37*iU&kH>t_ma{RzC7F zl=P?%y>-U6)fbwz6otz#CIc{1SG!AH%#^A|1W0C<>Mt$RVjLJ39&GhrB<=A9@KLeP zB|QsjjL%NNp)^*TO8|)s<_Xu)sMBw)`?Chd{b{^Uo1E28Gw{L;5~~pbC*G->N&**f z7#Gh_9>M+ zuj$Tr-ae$Q8N)-iBJv}Hstql&J}mAejpC*%=Sga{S|%swbz{hTI?z$eLxUJ|mp%9J zt@JoJFr(@A{FGG85UJ|8zZJ{*BvzkgYCAjsD93mGU75u*39eTU87kw`u$!Gw23dns z69Y#A0x{8A+*8;xM3@@saaLw+9l(w)cXqlSZq84$78DnxWWz+|Tj539sU;w-xe)S- z^yW_vEahn!_eouC06WU3U(Oz*g$s}=>!fJLMkizcrkd^U-=Gr&;<3u@lo_=aM&6?C z^ZmroXq_SX9J@lmJtl!1s&ccriHlQG8WQZ)|2U%H9LvjLEB#;ZE5{JB>-Bx$NoJrC zUmaWfCTp?vjr(B5iThg7>d8{OYVQHpUac|SU5(y z9~s9I@~fz*p{f1B2xI+*sG)Wlkb1{EjU{jaS`O@kaai3KQWDDmp84SagDLy%)bX8X zO0^l08Yw2!q&&20CH(w+9pMqzMfW~8(FXW`vkY_alMw#lQ{jiwN{sezdGn*L%5qQI zN{YgOGybY%w>yXEwO^=CiixFZ(6=%rEr*Vd8!6-v9r3I+gW9{N>duY<$x?T5q534N zQfd)|mA!aYMQ{NCs)hK=A>4cCkNlDSoQ+k#NJmp0LLE1=5`@r$JfqK8S(i|)k#AYk zn&>q?S#s)k-yvC|JE$zC%X9*&lk#vSlIQ=I=Wm)$@C_AZve1^V>dU%^zb@|G3BvHW za4pUU%t`w~;eeO2HL!a149wYM=(K9^tRnf{u%!cv>fcilF@W$czt#y=@ZBP0$l+4O zCFg!-;uHwKf+(}=C7Eyr=rr2fQh$Y39uXbLn+2hO7=Gj&iTrnUDh(tqhmn#tKnNl3 z+jT;UT>oyEpb#Ga^|p>En4$dk9NmdBLa_MMFI!oG;hccQ!V9_$N#YKuhm%~4fi0&{ zm6d!pXuWwxDV!c-D@doicXv-$X6LbKjpJ9jiGsWEKyIi%>Pv|igy`s!UjGyyYQpx| zHx2Z=PreG{-DRWk-;3BdXF{C{4zDaEnuLwKc-t~5DKRcG*5=@Y?nJS_+EnPz=c^22 zOcFjnqfZCkbPr;l?&!3^ciwi_2ecq?rGjphPrW7=*rDmsDCNYvR6)ZO!?-|#(@1*5 zp!X{e`!tE2Jt`_xk69usOps7~_jM)N-dYy$Ur;l-XZKG%Ai6$2AmXL$7oeU?dQY1| zT#9}_i^;6yt+}F=hC11*or`LXz{FQ^ z+{Cb7Cy`ILWcPiubVcN^FDM}l_pjWUA1{N|2 z<(;KBdtErdjJ6V{u20!L4y8%W+-xeT==xAaZQ#gD3tvAheR$)KJ38Y+2X}ksoe+Go zPKLO^SwJ(n+dLAItnWC<@x|wESlLe444xIG{2n0TMfNLg z_2uG8cOp-r)g-;xhIlC8X9?&AFjpn-JX`^@ng_?_y+H<4F)8|iE2U1h>S>TSx$@&N zQdk+d_Q?IhM^JuMj7&*!poR(+S;n<}_R={b=e_P<*^zNo`K8^1P>CuW2rIQ@p#s0| zYykS1e2^D5FyfCi#Uojuy1mLNEChjWu92!`yYAZ2{uzNity{3`9#Y4Q!941@kV;*Q z=IWAdxR%JD$f_Fs`}6Y;xk$kTDQ%xteVRLz{oUk+TF01Y{P5}Rm0j%G7f;KoRq_y!s~LZU9Byj`6VPp z0up+a!6ZRx8akvW6+J-+8+xxiq4*4w2M8a;(3Zxhw^OD^y>Q^F`%B{INB!!P{RTB= z7XsVzkSJC7-(a^j;ygU3?_z;*^f^Z=4F_#ENLrrGE94b_agvK#cOrn)F1Hr=wYpu^p!9@>( zB+mJKAKk6!7ABjKv=WJ<)?d$w+G@G103F@0@hP*AD9iV?I(Du!3|aNLcmVf816g}- zgMfegA1u`9}B*u7L;im*cXZ3wcQOtVZa3)ktF)QBQ*n1UbY2zc=Nx zDwmxY$0YBCO2}IzuJ+yJml$(TGrGld7_@KY(r$S@HG#9i;w9#HeXwz-QoHwHnKD5ae?zUawNW!6ZUlOQ&(y+6DyOeL11p0qb(Z2O71b?PKQTN68$e92 zd)u!I;NEq$(dE)`=KfGm`5?#%x20m^SSuq)#UU(2Jh0*#5e{W7#dodaJFn`V&S|v( z#)jbD*Y)B#+|bHN^sDc9kLZj3$Awn{%Dk_PxFyDf7%(pz$%eKzF(JhQTeQLM-RSeyE=Tf=Rrb`H;a8+hFy>;ZaFn-Q6 zIeEBtS;EZ(kLr8JhM(6}X;_uuLNg}nq`et=P_-Laf99k(dTiMRZ12-|e;cFSYj+EL zR%>Ww*;~LO2RV(3*hzFbw!L=Q5-#F{3aSdcDBg5=GBiQZx z?~RMcNPWKe50#rJBVHc2nQZM{9q#RKgw79atg@5fNvOv=7vZzunK*il8^c2ON;b=N z>MXvNjKW<`cP4c0{I>e=mao;C^MEYjd6Uw|WSL67*9geT?BbG&<6hqGyctzF1o^Ct zm^#+OBf6MNwHVBeuM)oR(tN9089Dy^O@!g5v6&i(8yb2gR)Iw3^G&Xq*23sV9Jr=2 z>qpHETAFT*!HHI4c|hM{w|=uz%UhOmX?De1>lNbkR+Aza^A;ofsQ0z@&3yIQ&BLKg z_6W^OARjPcAzWT|dM}ArxFGDb&)YCa^o4S~A4}OgJ6j-*=`7NhtLu{;%u}{Uz{JqTY0BXj zQWTX|Y44`(OPD<()CVLK*l%WCt^rdG#&%YVdIV68KJoB)K9&V_lSsoAeK`LL+im@L z&kPtrZI9bl6WpU!PEM;9_Dr4;;o}Q)IyYj3%@yeND5GRiFPuKb92$x zQIl@2E5i<0xvgnLT6ZgJo1@MQ{oqZ|N+A{T$ebpu>ITW31(bs}24 z0osR(LIYiNnd6@@^5(cnWgu5uiVym6l<7J89R3-AqaTJdV`EZD3axFV8YF`l9YQRt zm4W5>fvm8s(!HePptB1a4cO9m>UWxpnPC8Fo>7};w6hZ1*u2@}iuH3^k+Bb`T2T`4 zxrw>+QveXI#(_Za9X)d%{8GX#uFxyHPnCZ>%*9a@%5nFnWa>>5HBBb! zh3OMSzyzFY+tr1A(MJtX7BL7vJI4LO`pDKTMfnz5*36ZhFC#Kk{iv039Ta1h0Q6~2 ziyKiR5KnVHocIC|K?qiy26S;P^JWvi&wNqNzKSguc(Y;`fz!o(ZqA_cIX97K8c26Q zDXx6>MUP%0y`n+6dj0fFU~sw(*}|Ia&D|J%`G|Ax4!MTQY~MzUG0Z?Uz)pu7Ypm~z z?vcAlufTh_z*7AM91v@hw4JKkDhTx|y)LM;a~$&k?Jr_K@A+)Ktf^H9@cB6{1dlb4 zH&CmuTRpG9UiQ~S-+wKF8Q@;NwV=ox|NM_^Agw4Bt>CKpD_HApC@|&|#@|e|omFjhZMK6FKoQz0Dx3EQIc|HcM?P>lE0d)$wUnj+sTG z@&1oKzGYn+REa}YaF2@#ZVzRs4=54>(u`klu#Hd!I=j0T@f2FBu86y_iu5;!vPTU_ zq*Z=N6qpHQb5b<5C>`c3-`MbbDwbWU9)Y+%$tfy;5pceu*iMl10)X(Hd2#N>p6@X1 zWy5=;kDdjy2vH~hRsw3-xn6VdVgw-J*n9O}INeuUFLrIna3ze%3BoEunjnUNQ*ab4 zjxfe3=vYt>Z-}3~JkKIwxo!2-RI#1^TTt&XTd7>M#HWe|#*2jzR7y%vpFD%!pe+lA6V(LnUf1<-my+h)#I@s<8c2xF|>81>vl1ch(yt_GrZq z{$j4^?UlXqVC4C|vpMzGyA5D#7YBl&QoTQn^s=aYgy+g;ey>NZaDkwhn#p$ix)lH> z$Hh8>QGa@Sfx*a~SV)4Xkf%+K0s36O<7AWpVdUdaE#7@3Ai|5NCn}7NV$XGJIw@kZs>*a5uU7>4Zi4*%K^x`o;F2 zH7kYV9R$a1?}kYq*VE-K*izgiSY5z0tI zl2x*zj8sISvbPYjw~**=hm;*z8QG%9$VeF#Wm8te7E+|l-*Kw@{r&vDpU?06dwj1y z?mJx9^*UeY`5eddcpm5LOriy&p&Jk;z_5YtmM)!0tKYgVajC9Dmvi`~dDc&xZ}QJf zem!~l<_)T}L;BA-SvGxq!cvd#x2zG;Ydpa^0hKF&9dr^beoSrVZy7j|5su_1YOZh zFu_wGWHzvD+A6KBVK?*q$dT54LOBIu&iUBYAq6Jh%z0_QS(YEIuKsS!l9SuIV+ALk zC#I~Pc+NYKNZD|zO#Rj#NrNPloVFRJH!rCrKLqF+uHT@OnOFHTxn51*vc>alS+x^=ymR+M+@}Hbk(v4IVk$w ze%W-K`3AjM>tm(sH)bB@Ra%-%;Ohi6a@wt%4AMW1?{4E5q`RWifX!~Fbj(GTJ)MvD z($8Ajg!Udg5Vmj636itj<`<*vp6ymh?BbpT7fw(d3G23eU~oi2oWiwz#Km=a($NQp z7h;@jimda7st*)Wu0EV-FD~*=3%qfo)+%Miu=UaFF^0L6a}9UbvDSYidkPON?c>ta zN$z4(4dh#zkk@C{u*m8;?oMl5_T-SG*2BY9hmQ!#;l6J9^UjEM^?eAHc(d#Fi#-E% z8#!&P&$4lBAJ$VSKE*WcU`hWRW1}+LRDEmOJkgJSA6Cgaf~R)#Ix^$JPL>rrF`vJB z?YQ@IZ)b?Gyyx6;MV&u(|Jy?=a-&_3+aJ+4_Wfn-ohc^Ey?tL&0te4);TUa^PU^~4 z@QMe^Hdp0*#=^n_Pc7nGmt9jJGwjbE>9EW<@7`nobeGx_?CUqgKRePhcsWy&tdzJv_k%5U%G`V6VhwZ4cZy%h zCq6iJGiN)Ni-GOX(j7bAxP6AHs5Fx(BtPx7 z!9cMI>dQB-Itdu~%ojmvyC2%Y8^ub=OPJ4+_@kIN;?mio=A+cC6-@idGG?r$*zIGl@UagUrW#jKRXr&6Q3;4;wzJU(m@@H|B*C{;@iw7;wpCzs& zUyd-~TuzBr#g|5n>7Mm8Vv(J_XUJF2Z8&V*{xqs4G^OQ3L2}};St&bfQO_G?57|}s z==hG(cMEUF?)9Mnrhv2}uPM7N*tlCW{NLBO9WGyh`cz9v4(veB0|9yi?{89j;-U4Tg&R_E+?KYe?$H4 z2la*dyr(y7JVeB;!lj<(MP$4jVq)3ki=aO7`TalGk=$xG&#-vluI=wjV`IL7GV3?3 ztl8|sC=r^a5|^~b(n`dmA!8htm!Vg5C-fFGJcX5NW&>Xqew<~{u8*^+#%5=1eBBf< z%F)lsGu<4+0&ACuy;yZ?M2p|_!POfzx3~DrIfjIX(>^u2Fmbd^sbKOo2I@U-f5_4M za1VW$m(`w>WwP8WdTB=AVGPC9^>jg&iKo*qxh4G^$Wk$Axqlja+h>uJs}c9kPju32 zD)0R{EH&*hJK6cOKAN`V*>$@+M|S8a9H{TgoG;9L*cY3|W_j+({(J2s$S>MuCYu=r z=F2g?H-$8x6qB1C?YykDj(uqMIriLd9&K=52=-dOPki(poaP!8$u!}jHVlF#VjMC7 z*lexqE~I=!rLITqHUjJ;F=sJ8Cn_FnHXfm8uOfSPJ$a6ptpJdEWH%&dmdqvT$sIEG#O!HKeI9MsDh;D{ugAdZ4d@n zZft3>e&=VgNB5~*_!XS}aP)Gpcfe7v#;KQCd4AFLbJuy_MR@}y-dXc{h~*zUcjrn{}Pnnq!J$B*fd z9RiXxl=YnKH-5$8a8_NHjg2PTH*b~H9zSB5ouO~5VS4%WtD6ce@y83!f4O3mf;-lX z*vNE=a@Eb~PHo6aimb2PCB!imZplr!>six`!A{H`qE3+~=rjmNqc4?kFK5;j0cw*jeg~#l1?bNn3&dtsJ)*og6`O>&@<-~PP zw@HT^`mAK}g<|LpdP==#A31)JKe*qN^|s999kKvNL_QzmkfLnIhQ^p9+Z`v@-}w8g zS;?)Bf9l_nPxvptJx0$ch>gmEiSAcvm;Qwx0JW}SpX2{}Jvd}iaTW5&s}A)4`Srld zE3_Ln+@&=+F7)pk2P*#j>J!cX_O1>8z8ba)eUgJd=b!e|{{IfEaPQKuLzX-+9i`vK&v{vM9UzU*A;7gYu>8#Zm5H2~U3V{r|oM6}f}YF$z9^{(SYib#9aA z{|3a*#3sJ&?#SzvJZIKSQkf=h`(Y#J>Zcn~tnw*|O!W z)XQ^6j4Q6{%R4&@$6WZXC?X=VA0b5LQ2fb{?=51Fy0|<^xGv|$b@v#>;VKy>a9Kz0 zS=xNNuHnHm>m>pz{aC(qdyTu|3fDI3-Magx=IvYa+^fd|Pp7`jF|B5oTATa0d+7#o zi%+Gze(v{NeMIFfOd9)=8}68rYkPZ_BwUy6b@^4Ysjz@pAZ)NuR21i@;jKyO>9loq zb+@-rHQa#_I&HqD(M2Ynme#r6@&rv7s;sT%2xRGr|(ZP1=)TxMlXK9!PbXQ)L z+t&HiuYIg z=_{Y`{OkM26OUu(ZECuM|BSvyGvl6~gR}GH*jP^a0M@Iof_IsAKl3mjScZq)U4Ghg zvE%cIV$$hfGc((_ZCfU6@%ClyJ>hMkRwIQiR-!Q;{hNFH`)LBTcs8EL;5V#aZ#=21 zl*AAyv#@`7WaO%;bARpg9f1c;9M|T5pBc&i^6k@$6&DwZ$4hDNoiKl~;k~Ato7lq7 zr)ALpW*Y`{*q3!(+OeH=GmN!HUWAn{GBWZ`RFt29UcNC7s{S?i;~n)>WM*b&4cz}a z=fxYlV$Lr=dh}?8ce+-#{D~9(gA?{;^J0<*4g?qu3^pV@F!MXI{kf{ojM{ZBy`SSf zfkyMgTED)%pm!W<+$kb1emGMzAt7OfhK9!ei*x%8y+%b3PP}9+aO!(~$3H$LWwol* z)W&rjl2>MCJkRV|8>%GAHLma{RQ0ar4W)5)b**{z>bcM2tkJ=r?WXwm=5HgZEmk68 zV%vA^qF$V7EBl6L4#p=Xt@0Jy`~&83qn|ZIX=k^`07r0k9#;HR?#{h?*Aw5MJ}8C< z%amWCZCF19kq)^zIySNP-l^f?;csna3mV#Izl@FDR@mZkL|yrmn%d<|y{ETg+CF@+ z+Su9Y^acmGDdxN?m@#e3GH8EvW<`k7+1S8L5JW%T$pp^LU5E<&@+DA49?{D|FaD%) zbIPgJG&H%tz5Y6tgdcA4*<|DKUq3tSZNGbl?=h;HnHsq&S9RmY>bY;%Wj0Fnx96Dd z_xhPJ*dEHK^~inPupvj(y0yHcql5bk9AqQ12y2{#bN^OJNmk$diiFhEHCkEvht8ad zA9S3Z9#?>E!q&|ureZvYcgNElNoa_}ybQdJet`!~)^~N$$#&ct!Dy>-F!l<{v17-| zzxTbF7ll|DVdR=Xdl%Dqd_p##fTe|U;FJBt>y$-!RH#(b*>x`EzmJVuLS@AOS zYx~BlZj2I$sNe4hT2&Tm%0c93I}ss}e2a zBh9TGDU#AtQf6KoaS7j?@I~*b9g{2ltJ+%F)|G}Yo_eVpHVh$vTlyxe#@?QsbV>Na zBB)<3v-n$8L{xN5we-R-Q+OeDOiYZq(VpGAUkxtcO;@g6`&=^J&3HR{zoXo-0t%VG zyV21CT%JYPU)!o@{|S!3D5ziHdzqS^CUDfC`#^|SLIgZ?^Qkvk1qJ&LPBp7NpzPeS zV})AsiRX{sN%OJu2@1-eJ)89Eqj}t<*3*^L)WYxO1pDTG|2V(!dqC*o+lxwG=+2{mEiH`|+X8k4^t5N&zl zo^C!gJm+mqJ`P_e|;k{_~l}; zYgA$Wv`2JOLDqJ4@cRTHso2iafODl z>ODK3W9U8AqEzkV>YAs^v4fvKLoT)*ckEnIX}ovv=B}9f<9kZXvK$;2<~#(9O7-av ze)A36W0ck7v)FYZqpYk9QB`wV+&}>+;<2ZZMO~yQ^P2GZ!`IUgwUjrr`kMP+p{+0X zoE(fFi1jhFFPRGSP#T4wY-D2680BZNuBfQsXAorBW2k=W)Ln^lp9cq1$VukeB|jr? z-MYmUs#4eW=*;ETOg#&sR-}yPLg#^^(mc;suV3eC+qGpyw>&&l>D2xk}L z)c31bjypQ$Jb3V65>__U`zrW#U0rB;db;jYyG+9p&vUgALf@yRM8{)8LJ~=Kt{9nL zm>s|6?;pZ-R9@by;>wzS+lMOAjkv1oVEr~&rs1kptDGDi?*;}2O1h6_bl4Ru&(BV0 zcTg{HzWdzxk?YdoR*@U1si^}t3(F(!>SAU+=AU1t4h;)4&+0zKcIM0(Obm?CZ**Z{ z!L#?S!X&`Xy*3O;7uZ6##51q5^s&Xeu&5}#Ow&iyc_rd!zJIXJzQ=i=Zr!?81VZ}g z1MVt(ni=&A^RuDB!3lxVGsmQ*rTb0a-j_%~MC94MTanD8qT04Gh$WV1W~$A|b*xiD;=qCQ!`Igp&HwBWHF6LeTd_Ln`PxKrhdkI z$Q?Wf59(t)>e|{7NB`mB=9b69j;DT^`4;W=d9ZXomxiO8o2I2@N@dY^Z%ga6xe^*5 zAD{GDcx!s&zV1txF7@AHtW!1^SJqZLc`^+9c_{@oj_WMl!8>NoStyDs0BsrT^7lP5}n)F*{&9hR{wo;-P{(n&={B_L2>_wG}g znqdqK3{T8nG5Fac4(56KKR2%DT>Pa_GWY!rvy>9oQ0eTLnuCKwfKi!HL1E$0J&TwI zn45jpC*DwQLx3&Qm|I`Je$7Bt!^h8m;%S$g9?onmU6`@<`)&ptR*YPbg;}Vv6dA^k zO=XMo_ReJyXvE0DoKG1W$89YZhn=$@q@)}}nyLRe+L3pziQCUMRP@>O#6$x!&D2CS zr(TA5*NYSzv1=viBPDB8Ro~H=l8|wFX#NhcC@&AsvC7Iy1XvAE&!R9v1FP<~x;o`| zQq%Vl6}P263F!dnvlbl6_nNlDs-7??cFX)GiGaf@eW8Gi{P*wQyWu*V7rtHEw{KtV zERgC$72z1l91FbmZb5Oe9)Gy%KOxhD34xjik^$Ss3I}48o)#Asy?*s7wZd8_okdp- z<4>vgN#ghMS!50Dd2FGI4SoyR%zF$o5be{&{rzs;a`{-kjAOrpET2|Zv+%ovrYde` zXU`5{Muo>a;tx|xE&Te@Z@8E0@}a}m);3IhsV2{URy=+2Vksi6OPhgv%P%SVEeExq zc6+8LHU{6zeZ{OuvwHO-ZNZKc^p-Xu+2o1XC$;*Wj9xItP^rKcnIi~mJ z-7;TSE`48l6VF@E6HDnno;dp)0nTVhnwCYd8JUSUV-a|~1-O~UTiwb8KJuZ!(KM+8 z^M6SB{{Eb*FRQ4ih7r75l8%Rm+_@8n=XjGSf!Lez$R2A^{ zv0l#3&X&K;eok7a0Czxq+_nRp-NnvlMuvtl7iWfcNg=2*1?}szh>XBG>+%~FxumvE z5EKSrhbONGH8wTv?pay(}9lA|Bs+d8>cHO+)V8JLlR# zOwPP6QWpdQJ?oe5mk8RKS$k%^fB*WW9i3;ocN6buuv%DPpz2lEJ_}CA=rzrp#6d#r zw_4arTIdi+p|7uB`)1v`bxzLCYO=DjNvgXd^ov{+jl8EqD~kjTinO@|Q4D31fZ)yg zAP4bSd-5oW<$L`SfrME_twK-=CV_#lhF$dA_QekF-Mwh0qC<80`Gp53I~cD4`CH=Q z?93ybot@34-d;djDkwDD;6{4sLYJ<|(X-%IDXwQkSo!pg+=m*I8s{(n^Uvfa;buD9O2Y-l)jJd8g_n}wNK^W1niDjvTp zeyIvdzk@9MEH>P`FXPkw`AD)@QZv{t%MIyIpFYJ06YA?v{&;2fKKO84>YpP6WDUNy z=e+2QGvF=BAAJAVB54+}skM;5jd)uK0~_KTU}Q3j;rT-n5)wfRdb(e;edet*8ED2! zXYHatTdk(^wy!YWs~)*_;^$8>AB%hU?(y>SrhPkU2hdn46@oOGZr@YdQnrB-@ZjfB zhGpuz1q76VCGyT!Jb&}%I0DwWkhYF|+bCEme=Etuf`Wpca}(793+q`nZXASFTYum6 zB|18qPb6j$rIgF)2hsG|@K_GB$ZKp23??}jt8a5Kt9nJ)yw9yPDV%fEkU+XPst=~? zRNxjVemf_uTif}AWNj%nw$o!>GoSr%!q?PDTXv(9_7p@2M!Wg=-aPx-*Ox>}RTS>c zr2Nl89K40Y1>W;Gid;)B5I-}zDWM?+FPd2{F0Mg%waDTNDcbVI#8-U?mzV|Br zaKe4VeUq+Sxk7XE3QFRF54py!ckkUxwJ-f$Wzxmr-WiW{c|3?fUJw@}$fx4=GUva= ziP?Tg1VR!&e^6ud0k@3)cd_<{w}F6eGVa>AVM9D>#5~IeIZQBtRBodm&N|uvRU|fg zCS0bXJ8ff=k=Ur449cf|a;T|qY)lY?1uoSU$#aKGfrSb9!XN9;J3ASu{(;_NYPdyo z3%wO!mK_RdeBn#WlYrlUC@X5&ZJo`E@p!Mm>IpF$h*ixNLh$ z4H_C6zE4kUsi@q}(l0b|oC7jw!C?oi)jFkZZ}V+CTqZsr#n}0F?NR`@DqNPY2+$f( zO+D{dG+T#-FK;8}3w&&ly*a=yYjPW)-))e{tcrh2m+9r2FEmEucecVq#*&{7tX% z5-n5qN3)!@ThxUmG?0S7i9i&lP+L$ZgFvOKUdg9q^Nb+QN@gk z3c0#inX*^*?Qic1D+KRS&2mi0-*))gV5pX1&i?l}1fq$}{@EEv6K#F{3{v1QvRYVJ zNP#Y56|>RKG+DcLZS$2ijQp(xTcByz0vHt<9i0iM)w6k5SC`phO_@6MH!SvzjHDwD zJl9uz5HFznq-Oh?kNy40)mywWDlP)1M(#U%tFj1VXyrL$M$bCieME;|X6{i{Ag6tf zps?^`x!kjz1&*XP8JUx$uu=)@`%>XxkKUo7R1|aRs95Bk$G+MI28l9^MIc# zDzl=gR=v!zo}}X}T^<9`%uPDM!NGZFUQnT!OFm`tq{?`&DsDghWK7G(A-1 z6BpN#PdEksg20a=*O6oxENnIY6Em z5ZEGCDbFWGE<=s7<7;KTH~BYn9TLtlsic_)Vc6g2HCCvA`_4OaM7F8pna5-4#X096 zP9SVZ=aWtTO0wJJ;Hi?|{ZYdo^3oSj6Mm2+ht9O$MGUn3RB;tcl8C&1cErK%{N1~E zncuX8HdAFOpcu|iS-a0?VNNnsYegijIgT)^_eYT$Kr4!mYcnBSAtUKsUq&gX`|C%< zmnc(5AmaG@ii?5&|NClYgv#gd%hz-J62M>j@uVu3K80c_fBN+4jhi>8wi$ZWwZ`Kw zhegE}M%XuxX=W&bRN$>g_etyS}`9oq$d*+2BQS1E6nj-9`x@ zvJ(m=G8&+;9b};LLJqj<;Z$p`J)x9>g0>vMr}C$9Q7g_a58>H z$_<}A4kgaHt`u2=E^;`Gq-$E{3yLqdD;PXdxDm@+Knd}#^%F(8AM9isdIGi_RKDgf zD=UxsF$?6nZjufDkbBmT8I{NXVxuUNK(D9A7{xdKS{_gAjKdk5A-|sVyy>G*TkEIkMzX=#VYi~V+ zpUT%AJ#vI+`}PX*lfw&>Yk$58f032C`*-okhjuY6#c2HV|JSx6?*eq}-nZ}cJL%aB zVlWin==YcdPz{6tz(T=RZ<2+nyYV01h(dYv&(c~gUE$BQ<2LVO4ai^jXJRO4|J zunksMUgW|r2Rios^>|<1@--~_AZv=b6^Buhjum`a$EL92qkQh)Y%I@yv3<5%Q0{AY&Os}=p3zpu2! z?;|%XO$YwozORbkPSr zPA>uez{S~~ML~P}aVr0mh5onE>CMgR$`N}Hxp%pSXTCV6jxwlmB)iPc@b--x^`u7q z@nEzp5R;GN+G0x>uTeD`(%7f&POVESpjB zyUhJ^R6HFk?JaWtn{l31ixQZ&*R{3kD5u@VI#d7oXSw%8CCm43-|7XPb~YLdA(`_? zOB)7>xP8?Q;5eWO(6Vk_fRX4oIN;1=ld8taljHy~P*rKDp=j2w4S8;S(%d{5=9uR^ zphAj%5L9Y*c3ET3e!P_r%uKZbT6q&D5X?nN1waP1W5;e>@b=bFS5E{~8SO67!|h7? zEIgB*`*v6gY$4qYTY13sAQLQe!Ko+qJ%;1Ye%x-c<@A}$0RzyIsV80+A@pKcIzZ#x za@Szs-WAmAQb<8J#>U9lv~t5T*%wF+&1!OT|46+5UcQ3f63jMpN5_+Mq}Zh6kW^os z8<5e^)=nYpE)4G6qfG$f4?cf3Q9NCCalZHK*G^+otv-;gx&;pLG&D55D8oo|akxDv zp;9_|+NYYs9sHT}Y4A57JCjI1AzISaDri5O8YWiaePf}s1w6C3;?)Hp_!baGU^?_; zFMPjA$X&~Z?F8UhH9WW~P$4fYzZAGSE!jCZ_yh{gB6oi3?LDHCY#yf^C8ngRN@8Xj zL?8k3Xx+Z~yr1&OJz(+I0f%T|Wm{W(#Wi|P!=a1#TN9ROr=rE%`^2AF`LE@Q8%E`J z)}yir0iJ~7LTY}pi3yMY^zibsMjOBh`~i#v#dIY9+bW=$2k2);t!d{9jPMAz7ekKWuf=1+h|Xj5!y9sAP+&w01Bs*(rR*$R8iRzvu|r^>bpz4 zb}GUHiwQY5Dug(}Dq?x(=U5ks2Acz_Xkv9B1whzJMq$Ux$9EjG4!SxTutmrcP-AMH z=>)Bz28Q2rX6PXkD{ER}A{PPLWInsxdoqF7?@cQ!DK*~aI1myP^Z>kP4e|^4*Or>Q zJFL63b_xor;B3C2#NBGi=&NUbFrE21nvUSfgUjtwxnB>a&G1a@hdHXm#*0!Uu-nw8Ii5-8)w@rsyWvQ!^%WxEF00Nw}u3~pSv`r90mMvSxy4TnjbeKkh z;Vy2{n1W z0?KGkP@wN}tmdfgU;dSJ!D3E`pZkGx4MAmst<7!pCgfar*<9stJ_=#)`KdOgW5*t8 z$A*VL#4wYRlZV@kd|K30rIJ8GC=Ou=`uz$}^XA?2yA5V)mMd%0sk0yrv-$; zH3U6bkka@ne(gtba;DSh0FroF9mv@RZxgY2%bqU&D%-Fm4W+s&95P2cHY_ap$rBL* z0v?;!r8UhipiTGW>>Fl3S1{|%gfu`^3dw*XN^Yjx=)bpuzKBD1qY6~yjqVEqeI5}J zp~NT%EZjfxhAyDF#<-b?_~G z%vxGv6S|g&J&3<-2#8kW-6f>03!UX8xD z0U;1V5UuK#5-)cTPfuRJ5+qSwc<}e{-zUMitA8+ibg-#*H794RZppjXHa(?X_iaEQ z`;CDkMOCJ_bLUP;aFXB>Q(SI7^7^2rzAxwEPXtARp+lD)yW|MxdU00D}(8ak4 z1t>;pK=(6ppQr~yCrk-G4FX)n>*v=7RhjujLY%-ny3ogu9}`BBgf~fuVW2%oz!D&A)*a;I=H*oak$g8cHkLF;G+2!^V(kyV zG*p|K7Ze8;1g|o zWp%Z6OPU7B$Kgg7QwL(avMSm;p4w}{^3g%tf-2`Ex*h<}O|U z|J_I$og`v|*CX)L^XJ#yP#_RLdlSB=4LrVxefL4a^4AUPG8dexM>5}~s;W9ar0TOT zb}KtV02ZYIK|V>#(Bo+A#hKKLzx!CwsaKz&mP9~>1>EI=WJCqWx1uk62f>ZtrVTsf z2hpR&p;M0u07hS=Wq&Eect3*0K$VazjT}^KiY88!bxLdUG6Ofae6CcyO;kF0ACyl* z?;=;PS<`?PV*;%36^swYdJ^7J_t+lw#{wv?h*)l3Yh2Z;r?jN|!VhJVD^QT0juNva znHpjv#YUKhD^Od6%w-hyQc#)ozl(!f0d7F#9$e{X-&&=|UtDqZ>QzrHrwG{N!s6mL zxEj)816bCbkY?~LhMZQ``1xC`fBfmQYrh0Zqy@kkL1J%RL4Pm~Rdb1a-$zoXiO3N0 z21%`VbTqSUu5S;?erWRa_Vy;i@P}UQiWSj1rTx?{8ofMT{|S5))awz_=Os-M2vgHQ zq$!;^AremK1IF70LGw)y4k-j|`T0{5bQx4qys49yN|4($Hxjp zfoWa6C~^B%lstXI!)Zu8?}mHI%JdBk`u=p}N6>3lgOP{C#@gq}t)_HMr8~i>g`dG4u8_u!g1uI{%Ak_GWe2iMi|x}cbJ1&u zE>oRDo?<~FtJq)mdz~Qx2&Q2-&rV6)om3TcxW4-s|$2i4cZXKys09v#TGk&wB?`m3cjgUGY zETH9UVYdeOiO$YkCbES=Pr?LpcZy2kGZ}e|t{N zEdEuwuW)8^NUg6|7V_UkU?$x%ttM@{ ziRv!(NTNN`ROLAV)s`SVJx(}zZU5A54lSvjsjr9LBDeSJD533>F4mtLd-nZ!ZWh*} z9(<8fplIaXZ>8vF6m+!&35nuqtY!xg=h%x)B^%+S^3Y+;^N{$ zo4h>hItW9wM|WBkF{h>R=K)WXP9v;YujF>>Fzee%UsH5ql4jMD0fs~0_KFASNjEgK z3x$$A48MbHC|k})x#??IeSN*var2e|&*sT8gl>&e6~LFs-sew&c%m3}LZ$Xve=y&> z$pu5C@iwHiZ;^t=;Pcp6)}1@+uU@%giM|nvuOO11ftpAUJY6%h5juynBCK_EZ_`Jv zo^~rvP1I6ON=n*|dL4712KapRL873rh;F{kZbi+9dRjXV9O$?pR}X-XU>4Qmqw$J4 zb&^YB>sDC=)|{CIWl@(mytBViO=$x2WQtW`F*S+R7M)ekvP6V7ttCd>nr)Qbb8(i)jv#SPIz<0AV4cQIk}CSHl_Ibl0KpJ;=;V{ zV{_%qs?p}u;C1Z#ot;6>+8ypEC^XWP#mm)OEJ#|8@{qyQo9|r8vD}n;?^XJd3 zGIvim=^Q|BQ`5;QSLOcxZ3zw9UnWqxr=x+R`JuVXP-AM=j)rc^z|+%H%*B%Ah_5FN zww%s&%RDp6T45;?>E&lgFFa^_rsmEz-Li`%K|w(wP@SR}0&pShkdwQ+7Sdi6*E(wI zSsqGn?_^KM6FTq3<+4qL?l}BcMWL7^s&`mUE=?*K2}JV3uSY0n5N`LlySt-S+kN0b z@rQd1XP;_`XSSMQeEnu`4%15|LZg^j!8!K5$=!Oph)8p1oPeaHE&=KRw}qz+Cw}}8 zE~9Q*)D+jN!bqocelGAGcr}GSHCm1$uEXl6AD_uPwjKeF0ue;|by!g?iKAU;C%)8Y zPKR9uDXAv1{t&M}nc-ck%;9zTLU`T0$&ICkrfgkA)&T@D4AZEOKRhi6o>o8$=~nXzMR_F`I~%s61B*N9?T;6JI+=l#)o=R)J7$ z`@?j$Dl!L*pZIcL+PmmAWAuyb^dqlB`IxJlucZDQLUS*@bI-PIhtLBC6Pn&ofQDj! zYAf-XhTxqXmNv?py-dBs>uv%(kzO-+$k%_#UPzpYa)fkUGY{v` zCR;PX;`%OM!y1F)%io{OeWFcNQBg5;ObtXAB#pzdi*pZui+(do&OL(+OnSb*V-C6Y zy*x4OEM+D6lL%jHYcnR>iAa&7V{&h7gJs4OP8DU(myy`Dkmfql8l-;KJ`Cj`Qj{9T z@2awLndL!S9NKBCGaCSdlCYQi^C&iE5Bnbv5`tQEdA_S|n8tnlH~X zQAt4^CmJrpw|jT0!v$jXs7n-+N(@iX(OH{(`}jO45okb(@i3xAF=;1()yk?;bL#5s z6hXdKOj3P$;rpvy8m%r;IA;{{WWm%YLtgKv>m$l8RkeoXh_|}0fp>nKVcfXUEQ|k< zRSR!=2^Th6l`bxLF)=g0Hr^YNZEs})z#XI%l?|8d7?#${x53)o1*&R6CsOj6--sSz z5&?oxAI<;z637c}Bc|P|)9W$d6p43O)8B?yQjh5CV9ol6xAcWZ-!(}yJGIbGGzs$K zFJ8LT6@!dcvZ((0A@8^!%br%$1APE7=@ZLDq>;m>L?9yFxoa1nl#~H5yw-&a&#tZA z{NUq9V~_LaQvfH4z!yZFiHV82x;hsDYiR8E3&|Zm+>&i1L+AzT){IkNmLSYZ1$Hqg zbUKQt(EU`Zorz8{)=);=}d6|PdC9OxUp%|~RQ|HeY zRJQMs_pe9VX98adJTwTAa^s^jFPce~|MiO~M>`9h37tY{$IzG%^znt2!meL2u~Z6<$PK;>_Z00wp;76p+;--$rjEh@K!3dK&a zmS!TXdDt9tR6&7HQnIKb2UV*lyw+OdhbxHR505PZN&O7pM2Ya)#7kDzms>R^1G|A^nNo9z@GVHa5K>|IM77oTOp|n03Bt`{~ms zXmIP(G}1}u#QWE$>r6i#fv+Jx&j6@nE%OOR*Ns>xi2WvT>rc@1r2|uNPB)A+lctdXfRbMX7>C5*?m0d z+B%LjyshQDf3kl`R?kjYjW3%jJjeW9OeRq%qazQ6>)n=lyRvyV%yhh_=>bVeE2y3$ z#qDE|C7*asS*1VT2FYe7gcc74Kj~=%k2EK1#zv6H0Ag1$DfZlZhF;NA;JHpE$U|jz z94t-Crx({b-fS;bZ&+`h?B3|LY?E=5XtnWgw$$COy022~<5tu}wwhdF?zMaGr zL-?447RBrL?+Y6pNgX})^}QOI8c@arh+TfZRYpeU>a}ZDy)}0sV@lVs;RZuS5-=g} z0sVNPB82fdK|Bxog9vTVGtlS=$xds>C^9hlh}EQkB#Z<2pl2l|E$9;w*7mj8DbsnX zw5EJpJrWcMuLK$d5vnD%S>xR6cDC7K(tUC8UPDLMf^JhPVCfKo0b%ixb_k0PFa#xR zTblcE9dtiD(bOZ;hyAQP7#5jnfT`>2I7G~9);kXgX}971t5&WY#Bhci60W0U&`JL? z0wl0^lgQcE8%bQoDB2+OBh4eE4kAdy#(@&gsYYmiGD_VLQKuft%qy~q+o?lt1cQNy z?YJxQVXJWR2}la2plKvcUzAK_oB$kHg_aysg)X-aTcjAR%<5J3B5SK6-=14xV}tDp z1Y*~|K`S;8#ieea5Rz&G#)q7nOggPOhgVV_qY26beh@O1X0*F(Io8#GU1FI(bOil! zrwGzI8)MMeu zE#ByS7sbo1{HjtHwG~qxg=8Ixgr&G0?fkfZz_;@3+t=9X#|UmmfJi(Ev?m_fn`d69a`|s!fh~+4h|MvM8uUeN;0$xpS{DqD?!s@9d zK}Jw$=-H9=7?ZEOMbTKP%Mb(6Qb6aY#h}6L`(f*+e?}rNA0Fb$0#F==4T{~SDi5fc zgnZ-39|Z<&Q{g$ZrAJ(W3w$ zERb}Hh!0_)j5A*xV#IzS(#|#++3RbLNy@TDl4zve^UQc;Z~X}@<%#3rf*MHlV`YmM z^{x}W2F2|mN_x+sq;Tj~B5NKT^?O1TeQ1D#2F`KaPce3npr;~lB(FaTus{F)s~`FE z!~Y-!5)mdkdo!~{lp{6}ZlUh0!LA*m9D}^NU2-E7A;*>kW#%9wxL!dkwx8(R@rocq zp1-d@LJ<++Hlb0Wd4T_%kD%`wUL40 z0gxcHN7`|J#8P^eD1lKdqe*Hpa*|RmDDfU@nnzkFY}%k|g^Fuwh;C=FQI0Us5PngL zE7uYY&F24TXgHUgK=9sFg7Fz7+C=nH0 z`_5f@)D+53DqU2+42QG#W2t zTGbA5;iCipFF_8JW+{!(nv0V}v5WCucT zo88{t&dR|EG0SFQb9HSWg4mFoTyG~y1h7F1LlmiRg_c=L<1SMg!CvKI_s;LxS!)nu zO!={3caVxwfBZu5(roK!Dj?F^+e>EYJ8~6N1=kSpCbkml2iz874bW^G7#QG_kSKhc zBl6G@i9{3u`^NzF#~Gwr*-)TjZbhP}Xc<*KIB_KDRY2q;G1|(QDpw>XNT!C_F=KeO z`GND>Eh0j?r=rqTFJ8PDp1~&VLAYGDGk@LLPM|$mh+RUtohbT7WaM{WtE?ruPM++x z&MHFpP(I2#VCSHC&3-U(XzSSC$@%KhNm|>_O_v#$vHy|LMWZf8@I1XFCnO(pGefzH z4V2Az5Wtd4h;#ihj}wZ2hW6LCmmGrpg}i=VAzVRwDp2xn0N#ojGzn1`lHL<8&Xga> zaUj1@J`nG;Tu_NQn2O$WR5+?OI2EJ~fRmwU1(2>qg(FCVX5VEiBKN7o_jrgWJW_+6 z7HoZPQ61E~fUiugv$L}!ko;PIt;bgyAI3E_8F^0+d6*#A0mksz0ACHTO6V0k!ZY+)e+{!pdQplp>58`bd zz^nlBC)|h1u$(Tfh}u3aQUKc(S;L6p&bWtYs>Qp{zkt+G{27`~P%(9zL=UAN4HCeJ z#Iw-Ve2oSQsLX!X$=_HnG3=2*dNodaXAm~D-4jai{MDrJcdHQbt5nzYm-d0VQ7vf! zZ{|)FDzh|UD=sNJ1zsTS`v!!L|5hNQwpmKUbyxCe&6UV3ZT-96XJ_V?QHzE)Q*L;_YjO@gP_fGs>)P@0i3Pq2|V`YUI)~}C4 zk$!x}{yo`tEE`0Y@Fp=Nn@W=Y%clefzHaMF{;Kfo_I(FM)UL%#fB4tDm_xTtC2X z|5{2UJpxcY<@=tbp^A80T=l9BpQTII?Y8EI%SX1bRwspY6@}#LUH7@DC2--NjhptJ zz8Fo-yVWc)^Y98^^W8EBw0601)IXnUko>d9JGqR174YRZh`r@mPJL z$@}*GWO#f@i?dCH9cyF9K{hIioeS(HZVS2W%U0E?*=`L?7KK$GizLbPBU+{M!b4{1 zLq%shjW%v#I5YBy68HXwZh;)z!W5k%Go>y1pEp_|zmEGe_}sk9)%CkmgwD$*sPmbW zE&FClJ2PxLDPBW2+2Hp34`RJj&PWQGN-X`bv!DeZi%_wT`>&eyTn2ID5FFZ+m*Rk<$Cj zuHAurSRcE>kv)?h%Y66URv2%qnl8)CrT!-&;qtQ*?`D)Szt4LGt1mMaXOF($`#x7> z+1J!t!Wy6qLV|;TEatfne6rox`k~xNCrIFZknO8tc6&#LT{({rtjv|$x@`@MT>Eqw z?M690p5sGq@itE#j$bm=-CFXLKdGGh+Qt|#vm1pB&wy(nYkGD=& zw+G2bxd(<%PanKkkJ4|4j!=Sx^V>seYQmC|7xdb6^mKE6C%NB!>PV|8?ZoDkZ6wqy z#JC|qaP3FRbD?R_8;@-lm z>gNj^J*0GpfTWakNtbZwR=PpDk(N|x1QC#s2I=k&2?0gA8$`P6(A+tG@4dhGKX}ht zF4uy?Cua8SJ+t@oJlh}3eN=dQK$$LQlo1G$fUGYNNO{*ghWaXs?0H?C_yFl^A8c{I zEg6S^W24)nXeQJCp#39d;bw%=bXmNfClL#v+#;bAiNtxt;@^NU95Url?Qez4h z0FWI{=r?-pYP`|p9PKdaG8{QSDNz9E)6mhu49k608(r0k%4u@{df{lSJ2|jhF@lbP zj@+2`wtLnNIbG?tW{kev9ALpbmZd`*>47iM(6}yVHyP=qD#N> zpuK4Ge5$NfKp!3xE-&muA@Jw#ufRQl*_`QJpat>)WF z{FXpG@J%_UL~l}{qxksP66vxL z3kr&le^Uw?&tcey-LS~EOa}W`EQarSsC9FBl?7`6E#ObXnF~Qifp-(uZL+y96n`QY zPOcI_;kRg#Liq4K2#7CeYH8(ERcW|Cly?L~g85sx9DAxRS5-%cA8k|$TN=<~suZ@(VJP<3|@;QNw9F*@2u9bH3(r1fz z&v##Tri^j)ZM0%cRCP|4R*hh6xIAqVEtATJ2z0y58%0=q*we#@!OcTv{81ZgTmIMG z=by4ycH45!-}s<`M3yPnWBSOC;Wic#F)=>??ko!k^^O#YYJ5!M?4bUFB10e}3+1c* zcSBRrq_FpD8Ap1VM3j->Zn~3F&~*l0jp)4uG-j!uKx$y>)_LIhpDhj~{v_^%y*sot%JeZ}2 zG(9lnmIC{&{S#@Q2-)s&D?y3EUr>iGDCid^tV#d+*`1kdGrvn9BLn4Y6c0S%6dxe@ zq93w7o?jjE7+(YOCIoT3Kqbt$g0!rmzIM&oOAJM+MlYv7JUkQ?em6Lz>lhRZ{`{bD zP7qF@wQTqbU(@3I&egBzV#y0IjsO8=Ctr)zMMIm2kEO}R|M&vCP^?KMr!YJmwQqy; zA?7yF0l#c~_dHqLQFtArK9zxPN8#;TVUSIe*=129`qLd|k^nA}ll>j8c@_lb04sk- zYP1=Q)#EcW0ssu;yJlYJ0juG%mX!7K5QdvGf{4j#Qz=T(#6|Zr}J-4obz7hPo z|C#ToB)C3e9*<_N)1Pk6?CLD}93b6=3fCRvbJF`r%zV3pGAaxVc9v`o@`;8nXNhO4 zJC>Za{q3lxQFxAHg%U_VXe|3(%wy+RU_z9WV-f3w4dK`Lf!ZYX4jiVWI7l-S{cg$& z82*g|Tu<;tiZWxGpev%KrRB>oo~_9GHV!-IT;K|ro+Dy3vGwNyg}jF9xNdyXBBCNGrVP?-wM}7{hKPL+Sxj$HE z`jY^`fp-(xSS(Rhh+1;(cxAs7al*8JU*JKZAD%I3A<2XT-BMsq)tg4>K&EhKHZiF; zumI>Xf%6e`6c0aI1^D)n-SZreOo=N}-LJp@nSu+DHXLnSUsP1i!+Q6Eno)xf3Vod< z#UX8%)>x;zy#xauP98oiE;7C`**1sVqr%bz@$68jG_7S9vW7jky%MYE-8murmgJA& zQ@FsL@k<)Sog5E6!kMVlaGOF92C`o^GFm&xYXfYId# zqubc%J=$#K>?fRGYzWsEYXwMvk!|;vVN%fUAfORR?iE=d)dAh5BMSe7^@W+7F6Wq* zSA$)@T=)c~1l+b~V7`&;3sa=b?#q{q@lj>m5bjX1a)x+a>8Hz-qB}UY_!_AB253cq77I?^g7J33CvE5jlfh# zb+w;rfU+!Tod$djD{+`OA$y+;OZGT3Lu6>Ujo*MmG#>kO`ZbAXi30q+dRQ80&2hL4fq^05uB zQ8s!N(@)tP_Uh7&ZhslXR&!aa!+h@NpU&Kj{dlohkTAjhx$(Npq&e}y=X89_rVeNR z)Z}u-qrcI;-2Q4y#N7AgH(2^K+LDe1SJEZ`G4KdFe%9Gj5>#)uGD01)9;*4~05yM!#h8>F9jcbdNCnJ&eHUUm-MKkd z7Stqqc_Ga#&O(%Teh{x+$5ao$`ctXnrPn!@W!kJ7^iXpl0i($3! zwa?;038)$F=XZ^K?N;ZQVcg(cH44sq&rS4_z z2tO8N1)0SYA#WL?V*T!dQIce>+)2Rzo!)pQ;P#9fqwL2}pirwT69AkEfEIWY0tv&I=ch`Rs$O8piSR5-mx_odM8&BJ`DSW`Oi4{-@F;)zj>Q34sB zv$PmhK}s=tc~P9*G*u>H5#0e%g25)Y=PWpB!F9HeKRrZ*rf}#p{WB6)GvGV?`x4E; zR(2wNfkS^vhB+%%N%M2|FGM=tgf0H&66-m{CPD*a1OnhEx5c0>#G8m%?X2jqM9IsODq$Gz| zW_fLWZ4QWWT_8SPhr*p3seZ9)MB<-ZdIu(w_@~Aki*q-9BoEZ4MIM|?MuLLeAjm#F z?D)P!91eO}6je)#w#XD=$>=5r1BrjCxt&zky*BppzX>5i$(5$f!Qk6%%oQx%e~cZe zZ%XRwJk&3wp+s2lB@liw$U6+QP7y(tT?XI;I$*CIi;39nCr=;-I7nZniQ}h!h>Omk zYwja4dK%5?+9B)3VH_vyypO%O{u@Mo!@B#@i6Eh5%xBmIz~-PV-o3Zosm5sIynZA^!mNDeSU zcseW5u2M_0mGHMyIQH^i}&TVFOB z)jhgA5IDG*x~IlACX-cNzDrKXUn7G_g7TJ;Gph)<&cBsIUM3;o4|FZZ#_)F< zB226!Sc{`N{9e0*T0vNm&gM=bhBN6d1H;Tq6~FF1!pebZx+XZja&7XjkI zq*QrM8i)YtM~l^&`Twv0QWo+Z-Z*2N8QA{$2t%q&;_Y`5{#2Z}T6A}7v+@}d#G~60 z>N55avXP~+(EK<%O=|Hdw|U%dE4$rsd(2N!vKyACrpXe-_{XE_p(Uog1|#m}7?!@6 z>aFKA!uua2lq!x+#pDfm#-HmjdEIFhifbdcv;>~i>KnD8O_8DZ9x?dbMa zaPQ3fYeEL*Sr4p9xuI)OHkRiI_V>40%SIf&w>YCGq_BI0vO1NK3i5#p}sVa)@!KfIost#g-lRMggFP?bs#iEr)I$5Z@TW++w;3PbeVa3d}o$`flb-k(G9d%1-*}G zKwWusZY~l)H2g*`RG{nsHm7R;-4`d+6R-H-snU@&s*|Kg?gd zZM@F9Z**xXs5f@435h?OBU-+haP3lCU;|+%s4H6Xy8#4Xhl@n9l25Z-=qLl`8bVk? z2v?h*NfBhbv&O=1x5zHbClE33k>pz?TgB;#M)J`m2rD2s4`j^O&{mZJ60a^9d<6P+ zM6I?h6$jn*cBAXxooh)0@0ui#c<$LRJ*BV6$oqXChX;J++GVCwXcZTIajBq>mbQfU z#Mj6h_ss!fwbT1yYt4AJTVT&aw}gV%c1N4o(TE#jSoECdZJeSSm)vZK`AdjTyjA7- zE@UlGE04%%Nk&g)5U=G|&sFsw?cGN^$2kU%7ysp|Z?;ARXOkC6+$O-s*|eTGY2|8{ zFhp9MrM7NJ4YoR>NIh>q%$6dK3`eG9WJ``AYcLFzcD}{TYk3=}@qtDE14Bm>c9mXH zxyOD00V5%avfP2c&I?MyZ8sBd2coXmxu(fUA@D!SJ#^?w;e^3{Qqm1DYlh?@!rMMCZZue~MREI?q<+#scMoMnSZj-@HQ zAmnfz?^(WEbK`l>Xh>jX+3b{bD6#65demsccDemrK22<3Q(UVSwb%yRJq1sE_7i}uXT%~b-F*4{!BA%I@$ z);o{^mQy6a+)+{67mKs&PZcxVo=$I_|1}*#>MS>b#26cATe08=yp3x{On!JwbLv;7 z+Bru6E6KQ9+_ODb z@Dh{FkNomF>_9O|mBN(8?NI48o;gDB4sFY%zln2k2{`$1DMelOR2g!ZF`O|c`Y!7$ z8`ntImX99<u&IiTK@}ZBaYCTA4#1(BVX*1JXX54|&pAuL#SZ8y)!H`IDhtPi^cI__ELT2$YAdCrW! zTrGE>V*DXF_54F_Sv%QFH;&y}};J3rUuj-kWpaw>-IyVmz z>m3rePRC5fr`W&miRiQ>DtS`%E+yR~SZ~r8(FnW;;F+jIUy*J=XhIh8I)EA*Qvv1e zFgiZE94hz;;RQfK`9KpEgws{~EqJB_cXxM>N9Cwj0S#PAU!PnX$Zq?bZh&3^U59`6 zwU4!|Fq$$WqgG#fLFJQeTFNIP(@{#jk(h5G%*zPwSY5=f*S2eff72~tVjk(wmt;!$8w5}~pFD*Aa4;yl(m7)ekE=tKnN~GBL4kWG(zw5$%r~PTl ziAN~55t4ip>tap@;i({Dp?{m|7pFL-(&alfijn3`!92@G9}l6`;5t?`-DopHXCULc z3+s%@jDTrW6DPZVa+$AG_m+6C1*7M5T`s?MGCllkRwY;6i_VvUxYI#8=cL!%o{Y6& z-Jkj08@G-9vM$zvI|DK14p-f)aTkY-Yt?-9+@0y=#zPLP&lVAjv8uCs11x?ZM?g7zqJ|q~IMw2_wm_>6uSQdlZ_>Ax992epn%gW$J zO&G_%!DE)GbrS-`fCS4R33(xr=`qVMXn-v4g1Z!c-LmX;#(*i{ZVPd^ap!Vld9El^Pe}# zEEMYXo8BXL!_BYrSZCKNJ+dw?efToi9u=PZ7}4xMEN%3qfgUV+v2 zl61JUbe>ZV+B&i!aC04{;Z}m_{XUl){Q>;_8>)kN&*XHYR^tpd01Nr{2c0Lg0& zhL~GNLc18^@3WKFyrTRVdO94?sX3Y0kYF0H!NH_zB@xN=c?2;nw(IPe>e!px)C%+; zJip&)yC-8gRsC-8_q&w)93_Z7w3wf1U9}m1UJzY6kWx|nozoF8%16NPVBRBHwtEOp zizxr5?`DfVIJtK58!W2C`##DX3#ILYL)Y7GM;neJ*RrUTxc49gZrBm$tHkt0r(xK!?Um3i$H`OR5Ma=<>%;Ptx}f49*M(I?*LuR6D}P704f ze?1()vYlyiV^UP?L6kr?DKPH%6VqyRA-BKwcUg}=#@MK~PmV9@zFQwH# zSU$aEROE3av$D|9YMdmkOG<^SysM~h!-)2UXJ7WXemoEY^{6YeDMPN8K+|n2&#!5~ zc)%&fh5ULzE;J9Je|?eu-QbJuE65*P)4uL5anU*fghvmMV7o=f!8py~iP)RaMuLldRFEl>sTiSi7S`EQ9!3_a| zIqI$}^qvwA?P^ll;HF{A*BH;2NndX}JB2AzyoP{%Ql)`Ip^)~Tq7D7f z*L8ta0?<6sqh&*vo4^VK{Y#^*ZK1IH4{P6H60^TIey>wc5AC7oY2D5N5w9{2E@sPD z-O1YhPuSr{`22E04!^wok{#K53O$)HxVd;MI?%nq zl!ain<->)IvgT>=)427=lA0c&kJ&Dx?>iH+cx7JjxCA{`!Xc_wwhl`Xy*R-Hi?PoL`_8zf*0~6B-20auEK9U z)d7La>^q-Lyt(1884w)wD0q><^yJe>G+-t3c53yuD39jZ$*?)E&crtx*wI6oJ zXdp@%u**8}#I2#`>zlt)nL^?jbOi2|DeN=1Gob+t2G!1u&;*BPmo7JrO7##)-DGf} zDijCJaQXnC_Gsk;t~pT0DYuBS&I$R#2BojVcbWPeE*k|fXshX7OWUwNK)UPA?aCvG z#}jvNbz0pUDDCa-yFmkn5Wowj58&1OoquG%^m#WB!KD*`~Ud;Pkr9$xC?eZg4Wvo7Ho603=wLF8x{h`|`ezc0FmYpI8-xWs$S4`1P zF{Hft;JgEty3qF-*3}Rxr5AsmNeXCP!kl!`68!%J&KHYy-B5olT6o%yN25935!gU5 z5*oFv_DXA6O%Wu>QA?(|teV~r?6%7riFSM3+&0LK80u44*uU66c7yx3P#--=(3xe8 z>q%}pLW6S|_geolKT4~FNpg(IOg_i zu8Z@6<|b4LFDDir{R_`OzaVt@uDK`i?h0mv(r+=s*h@nvKF6dnWwbg5k{{2PGo|H9 zh(QEibMw22Ek4(Ws^V?<>PIv3rI7^Urns2ZvkGnPxnDH%Z1pd<|M<(fge!Tu$oM<; z{YhK)Z=Ceu$J~vj;rUdsL+^ax_z@L@CNxLT1sIUH;TzRh+AWnp z+?85IMQnHjeH}=igs2u!oZS(OZuJ7FGdV9JrA3ieHX61o4WC68Q&E__N;{XuZB1Qg zOjyvL2iC!@O8#pniX5WQUlo2zHgfBnLin5aT@feFt1#qtiM$LZe@n@|!Jt%BOJ}4= zan}cAC7fUxOg9!OL=y&ny5~<0jmEm&C^TLcDP7^fI#0OL`yf7?0~z~0h-qnuQFGnz z2t7NG{6bN8#m+Pz{D*Jcyy=Y$O5O%IS;uC7J9fLdSXkT z5-bP1CF*P_ro~Gw!uF}~-?>KT$7D>R(EJS(mI@FXH@56T|Te+p(%%;TG)i1gJ zMn+w?W(KDmuzYSNY_oqiHEJg_eq}fPW^W>a#^8~)EnoI=>CIYPkKP&!DLA@l6dgl? z=_%7xf$zbEgs1Q!oNyiA>|I?(BDIsg@n*CoS4~1Gdw(+Foo~p8`NO2TAa!vp=5*fI z6>HP-$M2-bIQIOXZ_}>bbuwYRBm;Y@{qvM)xYTn{tH5*q+4$t-Edg$6=xf|h^bv*A zKz1So$R=R#24sSELTRF8mrC(opQaAx;fM5Q_h$ffj2dz!da}I0-S`3*rNF51x^3tW z#E25NXBZ)l1g*QjrD8BC1g(al%e^x@X<`1?WLLA3e@cjX5n-GtOIu}W`pCodnhGQ7 z9HopJa&3{6Z)v|f4G*#O*PDvDM}Dx9@5>bo^29PG?#UsKTUd1&+0N5_2{Is1%(k*o z?WgoCsKpm9D>tFLynMQc*-R~Y&huV4T<9go-u`+5m=gZP`yD_taAi{XwWJ)AuPxzd zf0mkptZv?z_(Zh(sYe~irj2fS0U!^WPeqaz?&&CMulWj_j z-hA7bl|#+;3@}D)m-|MrggEdBa0xCI6 z9xcZXO~ZHJ`O-MN#Ek93(Ho8Y&B&hAw|i*8Psa4WQ|a^zp7+r07R!j$i&Eec?Wd1u z*8Twm8F-)vV9R&6x3@1!Jbm`-rcv3aR%bZzqy6CZ)Ukir$)EQP^R0TIJZi(wHy}HTI z2xbnM(tJjWiM0B+xO13{e0#p!mVUPucd1>mp^=bdKLPx-~jb`Lf1^7&o6lt79tM;;WaJ46kvg(%Z2Q+xxDKh!B0>JgEUUGAK3 z#O(yZLhO1z**l~2+>~a%wPRxZ^5NkW1)c;gl*JAaCOSRrAQ#EFH>jYZ*tVjN^UE^R~zYAnBEcM;Z@*P4Clw&4GSB9m7m!DYMLrOLpuP<$t&W$gFq zsh#7_{ASON20Ce}rJK&~THiRAPFyy7EHaV3o`(Is zY&A~Jh|{1qdE=G>>{`Ko5k2X}^B|;&t%1%i>);(MwR#dW#*ik%WU*0q)Iu@j+j+Sl z9&^*x7;R9FT`qfxRQLyS$9K#Y``w8N6;i+2tmyVJaKP33%*WYzXZdDssC@_&iy<_a zE(w%KqBhG4(W_d4XMqmQLjF=`Si(SriawC zc8dS|=qSu5>EZ&yi{M+-RExS%ENc^Yh8FcP$jO}GW3SYgmtI|YO|>NT0>r_%x&k54>B9owAFSZa0a`7E%YHdP z$_FA7rN(XK;G01AG9bq|l~1eIVLz4&Y|1H;k8{gWm@4nywc|=w%+S(5Yu=Re9=fxE zk?YP*vmS{VD9pZ{j_H)MA*x)5b@`D<%2%hoHA&VPBe~q^aV@El_IBne$4iV)Y9{2! z){s=5K5S?b^tMzq5qysYr9{un9_^Zg*tA?HW?rEQo|o4if8_Ce1TRbVXT?l}C(h5! zWsTY)I;dpKiRlLotkbH?7LJVC?N7c2%@rX*&m}Dt)%H_bGo7=DNpn?>r#{G=@Xt@G z+AoPcgcf&O8=^-_mi{ht@${4;q0<~X^f{KE$Ydis@xFVu9EkX;f`o%dF-?r@#~Mq& zH1UWvBa%x4Arb_n-F8$HX>1QHwwTm{L^aXF%8<}&#O5QeoD~+G?4+UZT}sJGCL5yK zG)E7jE)o5;5jvNaK7ISr8%jBR`}2)4 zbI$tKcNYc{ad<1wjaSaXlaj=lGNsO>Q zg@dh$U>buc6=OHr{Bnc$MZ1ZTsYKo!eew+t!_+MVugo;?lYOVZW>BxYckPW9vxr&+ z?FT>VbX!tfbI4}b9ZVn!W)40SeBRf~>u1-JC{6UHM^%ZKYI;(2aCSE0XX~qg%s#gx z|G{tT;=Nl}KIiufxD$Zi9mZUAc6YNJZl3NoO`eJT*ZWWUV_bTGIRuhQ|60HS-6tHN z#3AM2z{YA6Zln7TJD~6FEeOEa@8Q3!Xg$FwZ#fX|={>*e$>;FTu$jGqE?08vK#t@u z=#3^i?wCcdQAJAESW<*PksuOsY~P8$;yl-NLG)^q9D_OxKWb?T;B+oVjVdh zZY|z_2Is4-mpx2dV%7-iF6@6ja`f12m8>qVt8A65o16rarXq_iuY0zk{@FK*p53=a zFZexQ$=JvGE^8&bZQ8-l!4{Dkd}<>~_R;vu9h({FNmYL3tgiUVmhCrm? zkWi|libzc^C4%z8xI=pY(`E_>MB(p$mZSO(NT8rd#-DZP z-9T*5rlw$GM^Q$}O zM}PHJP3_TJjT&mdtUScfl$)kh62+Fh3oehpJnwr~-;TSoCr~6IohzdvJ~BJmV91Lg zZ#;GS+5#iHY^8@agnE-X{n>L#l2qx5B@O)i<0}yrD4!t@=T`=O{*>Pc_4I*?UV(?r z=>f+M(7Ye8%jC;6tjEo$AxNE9lyay<(v}%Fm%`#qNZfkQ#Kf)EED!!{*I!%JCQRZm zqZXxKI|z^uHiL2>L_dZd9>mNHnfv3X;pWCOlry-9j#Y>s;Fo2aoHc$5%6?=d9yBibv^$h zM?&yErJ;OF%dh58_iwW!Z(a7u%C>QP^?ix>89EVH zhIe<#IU_df-_8t@q+Pu+>D>3twNawlBY}Nvj-qN7x53QcI)hr69G0)KFYy>YNKgrb znmD}vCrH_af{l+Zyv6j$IaSN8^s&ljh@Crt?||}%{Ro*Ln2tAxZaVr2`$91>{1PQE!(a1$4qqO z1MpSi`XZHCJjL4KOQitpN=35U74~LPn|u7ndg_wuGT04X{gyHsL)2g7O0Ks5XUo8KKXxY&!M=gL~u}YN!#;k!(;VS6sF9 zK=FBLIAoS81E4i#B@hzZ)(uv3se0~bc~XutE;i-(2OSyohY&z{&e4&a>zrh@ejFj; z@bQxFw|lH;Migg+sdDk9?9ZJi5=i4tu=HV~6a+SJ#N^tcKUL0=^9VQ$VSNL!$$P6l zLp0& zH4n&c8uuvU7l%FJ7ioV8yf;|#9F9i_nkg!3i_F~_A)EfgXcX{n7xv0vrNdt*y6;I+ zDJv-=ME|Fq*^XKJPmp{)N%>U0Rh62ZnoK{(!Ou#ZBzkw4{I*VncHBE3k?CH@ukY_Z zdzs*E{*7DNzk+jHp(yoVwnTn+;f20_7DvIeXKk6kp-}qgGQ+6=>=|HZ#G|R9P|2^; zG)4BC5&!_lkk1yKgYq~LM~&LPOuBa7njpGSA%84@ydoPJWl1~3jY7@g^{+^4m_YK) zz$>qYS+ODG+~PE@Om`_9<1uiZ^RE^xGN<`z^`h;HY3(7$I6a=R7iq(nH+fd8iWv0q zdDo{hlr1jIioTkh0=tT%weqXxz)xi}&w9Ah6zd_p zEdj%$MvnY;C*N#;GyJu-;j5}cd&o#?Lt~~3Ue~mIC{Jr_j9!=9$*%ItQKVMMSoZ8>VrNKaQ?Z@YM=|8w{D zU|e%HsGlgiZsBYc#70Z+%wMY_M4GM!x3Qp2K>-tH`=bi~VC#D}efF%EGKuEni0=T^bN>C%6ITcj*?Qb&*uPzamysnq)EOrt9dWW=Qq%)6AxSj(} zaw`Ex+Oib%<*9h~rkF&~ThGgA&Z>tmpGKAzcJ!Iz!)QQ(ZRfFT4!r){k`YMFBtf?D zGw!y-FGd=uTI58gfaEiPDxrOfCg>e`sSyUm9I5$;60z;DUheKy0xYWpe%<+w!sxWm z%<^x62C!F{QGEo_7aEY4e`_%jFPA@A4BO_AnV)8Zhsm1{lnY zr1Lq+w+@L{N1$tLC6LiV8e|aokGZXT+n0<=Ck^$Zet&8YjTAkuN(C+`9623;N!@cS zHZ>dq96l^VM_mMuc9;&PS>jjU0}JxmnrRD|vmhBpA>Xtw&HAscGILy~i3Pl!LHf@+8{L3ix zoTyk)!P-(R=_5QPZDd?YNRy9(;wljAUk~5xmPMv_=EryKDh?8(R{YYE$|xixNkwE$R%m>>4onV{6vl>;1ay(M}A8f`FvFx#nIXKVy-&bZ&GL)}@tLtT%1! z8}6)3)ZjkM%*`0>PECx7C&)v0N?(UP|2oke3emW+P$HmZE zF@69jMOwF2<-*$4O3&X)m9q{uc6L&rWlhiLXpUY3+?=zP!_NW7PaeOi$x>2->S`jd zAeX9GJ&t14_-qm$M=9s0nuwbSQnf1}UmWYYZAkw+{e3^WtGWz8>1_ws4SWpc{mVS> z_QcW`V+Ayn^a;yV2e}~TxwKD%zkHXgYj_Tc+Al|`W9!{X0K5nXR91@%(LfQ)SBdrG zW-+K*5e+#@V6{B=mQlZIC!B$uNO0NyMug_kOBUgH{N6*)U+Yv56iF}d(arKShm6>I zWq}$LfcZesGApjf+tf1j$W>;p21UXL6kp7(rN}1>;qN5NGk0qNm~{@Y)=9bHwcD6n z8)2QvQ=G8aregd)iA&U)gT^P2ES1RbA5%HIKX+iit~=whb#BOR(o389saK~CN1k8H zZMa{oRzIRc_Xo80nMT)Kdmo^I)Cja+89s)~QUDf`QJ^l0Z_7<&?>}D1Q_G_)K z^kM1e2HZpD4_{8gV6Rrp2(k7wW{s526K@It7*+S=-@4 zDxj5(xx6`j&S+6v8zsDhuDuW+9}$r1@vNYJm~|E~g;8WSC)SZ;GpeQVB-0Y$Yv2K? z+`{s5Pj)7i;m+<Ue0_ta*eDn>rLwf23LrG$KYw^?RLkJ!Z);VDgxd(RHTkA9ubT6ujxIS#17$Qg zayy#3&SOpSh{3BOo|%9y=l%~CoD1-hvl(P9_~y;3un$+H0imFHY=%SM3S32&z+VxO z(O6Krw!FCbQfMeDh_%bVwdPy~JIwL`J{JG{A9}C6&|_dEZsmooj&6a(^`$YORbz21Ea-lU2oIBQ}>Timb31|)9Ctm5_l zy;=Q*Ck0DM?@6Uz?8r1o z!E=THdi>tt&9E@N$}nD;h{>CbvR=S+lJxOO8Qx2*Q(I&*18EDSZa7^+32fcOYmg%C z*(ztAAW*#kGzty%DU6oxZGG)vTir!~&|gyFlwi@XpLHlKD*MOqr~-I2bf6fW|A<0Q{_%M%IZSY6k}o9>wuEZ7dT798m{`6R`Dk@ay!s#8@e7;;wZv^P)C5&scjDB zV}xVs{(J?0L;t(|Wo`MGjr5CW6~^>*JDl;oFW5PpztycBvZ`4<+vZhn#=}4F22G^Qc%nF6 z(!3Teb+7vO|IvUN{~a0Bvd7Htjhv;3ztHQOs89X;2I!pBx(+xUHI>z1<-<|_IRJu_ zd?irY)4Iz+SXQIU&dgy2n0Fc`p!p+JbaZr%hr&3o%Q!Xlzv2H5XUgCQfBUs(@PH2RZ86ZJQ4B-8B~ZNof$ zWF(j(Fu(}$?7I)g@1bBhV`Bqj4{x@K&o{p)3p~>^(R6(AqUex#9_)X?nDn)evx^5Z z#ao#~4F26^fM39+%zsDxpOu0~7WCPnXG^SyKLq(Q*k=K0wm8n)fX-2xfG!it0| zpP|rPLSSZ4unj!@+;(7_shFggi+yG7uf|A#AMF)FT*l07f3BCwccZv&cvFfDoB=kr zM*=GR3Dw5(z$zVxF92#{G#VCA=+pS#vCu9Fy&Fi8f`HI2 z(7jT?1I)et@sW`rAQPQEPU|F)$|r`@u>`}xnoapXD3sEve8fsri^wW#M5Aw8x+DAx z4AgZ;f7$_)YG8@ppD~#E*qn$n`W9}NyBSs#fPFSTp+UZI)+blBmTlmFP*PUboibiv z#-G9v{xMws?fyPLe9D0`mpJTq%K=a6Lm#HYQ(5TiuTndZ@Niicu%52G+9F;(Tyttt zUP-x|*!qjGMDu^^Es7;Cc`wBg&kPcmtFQUIvMO`18B4l1#lgLsyxI_enW4i;n*prU zO_a?E$bR?p*p*)wcny`Uc$KYHrAtO>p0iT|rYeD~0+de_OyJL-zx=K0+zpOL%HS5= z{RE835qwoMqk*SLv(F6CPx`pFPXUF^QmPVZ8K{qr#0=6u$7<$^$N>iiePDL4g zz^ow`pr0lIQ+mG}VrKkHOKmX>td9g5Veocv^Vi_C0ItgMB?-pExvy?&2^6L$BCMf4 z7n#F?Iku)X0+=FNr^BrH(N3OWgmvuvk5woO`Q{LvlHQJ%^&7Tl!0F2kU@|Yy+r5rf zr8#pnW=!lb2k=-c8=`#O48Y^)(i+Yd0_w21B6^!_L?g5DZ+U3$ zhu#fIBc(@nsg4LGdd&>K+X+&BO-q+4QRQ!e3FZLiF5!3Dz&z)Mm1=S=5()F?Z0;Ce zm2`BZw6ovEqf3I%6rhxoM<;&agz~Wn?s+EvF5}`S0}uX$YXA{4I&iFeGvVK_SLgEh zDVPT`;DqRQawkF>zH@o8D_*olfycb2H-^w&z0_Zh8N2obyj7dYH(SpYAujAlQs%Oh zEYwq%5jK9j^%x%x_B{*iNuu%=-^tccPQ?sSQCBEshLt}5LFb6{qLCPXakJV_N{WjxgXZ(zcOxqL6fq@ncB;64vkvP?;KsBW0f?dt90;lH^T>--bx*V;Rk;8!8w z9%p2yX31{EtomRNXm*dy$_>m{u-z6n5|HhVReawwnt++Z{KEfaHi;9u(UsEQ=>>~n6fYbc zRgT9KksyG3Z`$y6#G8W}0{(}y$RL%ESyN(ZbIkITG9O9WM9&!2<;3tymhn~^fxykh zWnm3sVFUxL^h4h4$Di-bHA#rRkI@avG0*v8K}>DAd15rnt>+s(elrAm~=8Ay- z8MYh}6N4jqvsd%q)$Wf3Gkyry6Xj|=r4Odo0dG&x|MyLiG))dZBO9CY4jvev!bhSp z1ywQDm9`W8KYzvvBZF^|)8?yXa(JLY`4BBFEaWM{BjBUp5fg_1>BgdN8uFJsOuLOX zM6>md6rkgY-?apou+d9A5|U6LC1(zFMd9n{sX8dMg9c2&b94Gd((tjzecMI_dJlC1 yL4j{LAvW^x^3DG~e3A~&0IBf*|L{pZP}>A9<093GR{`xDh=PnNv|P&c{r>}iCj9aM diff --git a/docs/source/_static/image/overview_components.png b/docs/source/_static/image/overview_components.png new file mode 100644 index 0000000000000000000000000000000000000000..92c1654f237b425b1f1cdba5e15b6968fbb5dcd1 GIT binary patch literal 123487 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 KrSPt0;J*RJvd(G% literal 0 HcmV?d00001 diff --git a/docs/source/component/dataset.rst b/docs/source/component/dataset.rst index ea122a3e..ff5f6e9c 100644 --- a/docs/source/component/dataset.rst +++ b/docs/source/component/dataset.rst @@ -14,8 +14,8 @@ prediction data) and can be multiple for each data field in order not to exceed of loaded data. Each partition will have a unique name: ``___.npy``. -.. figure:: ../_static/image/dataset.png - :alt: dataset.png +.. figure:: ../_static/image/database.png + :alt: database.png :align: center :width: 80% diff --git a/docs/source/component/environment.rst b/docs/source/component/environment.rst index 3fd00c30..688d9f77 100644 --- a/docs/source/component/environment.rst +++ b/docs/source/component/environment.rst @@ -285,7 +285,7 @@ Client-Server Architecture The :guilabel:`Core/AsyncSocket` module defines a **Client-Server architecture** where a *TcpIpServer* communicates with several *TcpIpClients* using a **TcpIp protocol**. -.. figure:: ../_static/image/environment_tcpip.png +.. figure:: ../_static/image/environment_tcp_ip.png :alt: environment_tcpip.png :width: 80% :align: center diff --git a/docs/source/conf.py b/docs/source/conf.py index 1de1c421..feee0ceb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,7 +15,7 @@ # DeepPhysX root root = abspath(join(abspath(__file__), pardir, pardir, pardir, 'src')) -all_modules = ['AsyncSocket', 'Dataset', 'Environment', 'Manager', 'Network', 'Pipelines', 'Visualizer'] +all_modules = ['AsyncSocket', 'Database', 'Environment', 'Manager', 'Network', 'Pipelines', 'Visualization'] # Import all modules sys.path.append(root) @@ -73,14 +73,15 @@ html_static_path = ['_static'] html_css_files = ['theme.css'] -extlinks = {'Caribou': ('https://caribou.readthedocs.io/', None), - 'CaribouI': ('https://caribou.readthedocs.io/en/latest/Building.html#', None), - 'Numpy': ('https://numpy.org/', None), - 'PyTorch': ('https://pytorch.org/', None), - 'SOFA': ('https://www.sofa-framework.org/%s', None), - 'SOFAI': ('https://www.sofa-framework.org/community/doc/getting-started/build/linux/', None), - 'SP3': ('https://sofapython3.readthedocs.io/en/latest/', None), - 'SP3I': ('https://sofapython3.readthedocs.io/en/latest/menu/Compilation.html', None), - 'Tensorboard': ('https://www.tensorflow.org/tensorboard/', None), - 'Vedo': ('https://vedo.embl.es/', None), - 'VedoObject': ('https://vedo.embl.es/autodocs/content/vedo/%s', '%s')} +extlinks = {'Caribou': ('https://caribou.readthedocs.io/%s', '%s'), + 'CaribouI': ('https://caribou.readthedocs.io/en/latest/Building.html#/%s', '%s'), + 'Numpy': ('https://numpy.org/%s', '%s'), + 'PyTorch': ('https://pytorch.org/%s', '%s'), + 'SOFA': ('https://www.sofa-framework.org/%s', '%s'), + 'SOFAI': ('https://www.sofa-framework.org/community/doc/getting-started/build/linux/%s', '%s'), + 'SP3': ('https://sofapython3.readthedocs.io/en/latest/%s', '%s'), + 'SP3I': ('https://sofapython3.readthedocs.io/en/latest/menu/Compilation.html/%s', '%s'), + 'Tensorboard': ('https://www.tensorflow.org/tensorboard/%s', '%s'), + 'Vedo': ('https://vedo.embl.es/%s', '%s'), + 'VedoObject': ('https://vedo.embl.es/autodocs/content/vedo/%s', '%s'), + 'SSD': ('https://github.com/RobinEnjalbert/SimulationSimpleDatabase/%s', '%s')} diff --git a/docs/source/index.rst b/docs/source/index.rst index 2329b202..8429c2b8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -10,6 +10,8 @@ with **learning algorithms**. **DeepPhysX** is mainly designed for :SOFA:`SOFA <>` and :PyTorch:`PyTorch <>` frameworks, but other simulation and AI frameworks can also be used. +The project is closely linked to the :SSD:`SSD <>` external Python library. + Let's get started ----------------- diff --git a/docs/source/presentation/about.rst b/docs/source/presentation/about.rst index 1aca8eb1..105d1bb4 100644 --- a/docs/source/presentation/about.rst +++ b/docs/source/presentation/about.rst @@ -70,5 +70,5 @@ typically contains the following **tree structure**: :width: 80% :align: center - * - .. image:: ../_static/image/about_working_session.png + * - .. image:: ../_static/image/about_tree.png :alt: about_working_session.png diff --git a/docs/source/presentation/install.rst b/docs/source/presentation/install.rst index 6f9a757a..58083f50 100644 --- a/docs/source/presentation/install.rst +++ b/docs/source/presentation/install.rst @@ -20,23 +20,25 @@ Prerequisites .. table:: :widths: 20 20 10 30 - +-----------------------------+-------------------------------+--------------+-------------------------------------+ - | **Package** | **Dependency** | **Type** | **Install** | - +=============================+===============================+==============+=====================================+ - | :guilabel:`DeepPhysX_Core` | :Numpy:`Numpy <>` | **Required** | ``pip install numpy`` | - | +-------------------------------+--------------+-------------------------------------+ - | | :Vedo:`Vedo <>` | **Required** | ``pip install vedo`` | - | +-------------------------------+--------------+-------------------------------------+ - | | :Tensorboard:`Tensorboard <>` | **Required** | ``pip install tensorboard`` | - +-----------------------------+-------------------------------+--------------+-------------------------------------+ - | :guilabel:`DeepPhysX_Sofa` | :SOFA:`SOFA Framework <>` | **Required** | :SOFAI:`Follow instructions <>` | - | +-------------------------------+--------------+-------------------------------------+ - | | :SP3:`SofaPython3 <>` | **Required** | :SP3I:`Follow instructions <>` | - | +-------------------------------+--------------+-------------------------------------+ - | | :Caribou:`Caribou <>` | Optional | :CaribouI:`Follow instructions <>` | - +-----------------------------+-------------------------------+--------------+-------------------------------------+ - | :guilabel:`DeepPhysX_Torch` | :PyTorch:`PyTorch <>` | **Required** | ``pip install torch`` | - +-----------------------------+-------------------------------+--------------+-------------------------------------+ + +-----------------------------+-------------------------------+--------------+------------------------------------------+ + | **Package** | **Dependency** | **Type** | **Install** | + +=============================+===============================+==============+==========================================+ + | :guilabel:`DeepPhysX_Core` | :Numpy:`Numpy <>` | **Required** | ``pip install numpy`` | + | +-------------------------------+--------------+------------------------------------------+ + | | :Vedo:`Vedo <>` | **Required** | ``pip install vedo`` | + | +-------------------------------+--------------+------------------------------------------+ + | | :Tensorboard:`Tensorboard <>` | **Required** | ``pip install tensorboard`` | + | +-------------------------------+--------------+------------------------------------------+ + | | :SSD:`SSD <>` | **Required** | ``pip install SimulationSimpleDatabase`` | + +-----------------------------+-------------------------------+--------------+------------------------------------------+ + | :guilabel:`DeepPhysX_Sofa` | :SOFA:`SOFA Framework <>` | **Required** | :SOFAI:`Follow instructions <>` | + | +-------------------------------+--------------+------------------------------------------+ + | | :SP3:`SofaPython3 <>` | **Required** | :SP3I:`Follow instructions <>` | + | +-------------------------------+--------------+------------------------------------------+ + | | :Caribou:`Caribou <>` | Optional | :CaribouI:`Follow instructions <>` | + +-----------------------------+-------------------------------+--------------+------------------------------------------+ + | :guilabel:`DeepPhysX_Torch` | :PyTorch:`PyTorch <>` | **Required** | ``pip install torch`` | + +-----------------------------+-------------------------------+--------------+------------------------------------------+ .. note:: :guilabel:`DeepPhysX.Sofa` has a dependency to :Caribou:`Caribou <>` to run the demo scripts from diff --git a/docs/source/presentation/overview.rst b/docs/source/presentation/overview.rst index 87a920fe..8d1ac8de 100644 --- a/docs/source/presentation/overview.rst +++ b/docs/source/presentation/overview.rst @@ -31,7 +31,7 @@ This package is named :guilabel:`DeepPhysX.Core`. .. admonition:: Dependencies - NumPy, Tensorboard, Vedo + NumPy, Tensorboard, Vedo, SimulationSimpleDatabase Simulation """""""""" @@ -70,7 +70,7 @@ Users might use one of the provided *Pipelines* for their **data generation**, t These *Pipelines* trigger a **loop** which defines the number of samples to produce, the number of epochs to perform during a training session or the number of steps of prediction. -.. figure:: ../_static/image/overview_architecture.png +.. figure:: ../_static/image/overview_components.png :alt: overview_architecture.png :width: 80% :align: center @@ -81,12 +81,12 @@ The *Pipeline* will involve several components (data producers and data consumer communicate with their *Manager* first. A main *Manager* will provide the *Pipeline* an intermediary with all the existing *Managers*: -:``DatasetManager``: It will manage the *Dataset* component to create **storage** partitions, to fill these partitions - with the synthetic training data produced by the *Environment* and to **reload** an existing *Dataset* for training or +:``DatabaseManager``: It will manage the *Database* component to create **storage** partitions, to fill these partitions + with the synthetic training data produced by the *Environment* and to **reload** an existing *Database* for training or prediction sessions. .. note:: - If training and data generation are done simultaneously (by default for the training *Pipeline*), the *Dataset* + If training and data generation are done simultaneously (by default for the training *Pipeline*), the *Database* can be built only during the first epoch and then reloaded for the remaining epochs. :``EnvironmentManager``: It will manage the *Environment* (the numerical simulation) component to **create** it, to @@ -99,10 +99,10 @@ A main *Manager* will provide the *Pipeline* an intermediary with all the existi :ref:`dedicated section `). .. note:: - The two above *Managers* are managed by the ``DataManager`` since both the *Environment* and the *Dataset* + The two above *Managers* are managed by the ``DataManager`` since both the *Environment* and the *Database* components provide training data to the *Network*. This ``DataManager`` is the one who decides if data should be requested from the *Environment* or from the - *Dataset* depending on the current state of the *Pipeline* and on the components configurations. + *Database* depending on the current state of the *Pipeline* and on the components configurations. :``NetworkManager``: It will manage several objects to **train** or **exploit** your *Network*: @@ -126,16 +126,6 @@ A main *Manager* will provide the *Pipeline* an intermediary with all the existi mean and the variance of this loss value per batch and per epoch), but other custom fields can be added and filled as well. -:``VisualizerManager``: It will manage the *Visualizer* which **initialize** and **update** visualization data. - Then, it updates the **render** of the simulated objects defined in the visualization data. - *Factories* are provided to easily **template** visualization data for a wide variety of objects (meshes, point clouds, - etc.). - - .. note:: - It must be specified in an *Environment* which objects to add in the *Visualizer* and when they must be updated. - In the case where several *Environments* are running in parallel, the rendering windows will be split in several - sub-windows to gather all the renderings. - .. warning:: It is not possible to use the default *Network* and *Environment* provided in the :core:`CORE` package, since they are not implemented at all. From 66fecd43e1abbc6b157e0a4afcc74536478fa975 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 24 Nov 2022 15:03:31 +0100 Subject: [PATCH 47/61] Include examples in installation. --- examples/demos/Armadillo/__init__.py | 0 examples/demos/Beam/__init__.py | 0 examples/demos/Liver/__init__.py | 0 examples/demos/__init__.py | 0 setup.py | 81 +++++++++------------------- 5 files changed, 26 insertions(+), 55 deletions(-) create mode 100644 examples/demos/Armadillo/__init__.py create mode 100644 examples/demos/Beam/__init__.py create mode 100644 examples/demos/Liver/__init__.py create mode 100644 examples/demos/__init__.py diff --git a/examples/demos/Armadillo/__init__.py b/examples/demos/Armadillo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/demos/Beam/__init__.py b/examples/demos/Beam/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/demos/Liver/__init__.py b/examples/demos/Liver/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/demos/__init__.py b/examples/demos/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/setup.py b/setup.py index 7bc7443a..48d3d403 100644 --- a/setup.py +++ b/setup.py @@ -1,69 +1,40 @@ -# SHARED: packages are installed within the shared namespace 'DeepPhysX'. Local install with pip. -# SINGLE: only Core package is installed with name 'DeepPhysX'. Distant install with pip. - from setuptools import setup, find_packages -from os.path import join, pardir, exists -from json import load, dump +from os.path import join PROJECT = 'DeepPhysX' PACKAGE = 'Core' -PACKAGES = {'Torch': False, - 'Sofa': False} -DEPENDENCIES = {'Core': ['numpy', 'SimulationSimpleDatabase', 'tensorboard', 'tensorboardX', 'pyDataverse'], - 'Sofa': [], - 'Torch': ['torch']} - -# (SHARED) Loading existing configuration file -if exists('config.json'): - with open('config.json') as file: - PACKAGES = load(file) - # Check config validity - correct_config = True - for package_name, do_install in PACKAGES.items(): - if do_install and not exists(join(pardir, package_name)): - PACKAGES[package_name] = False - correct_config = False - # Write correction - if not correct_config: - with open('config.json', 'w') as file: - dump(PACKAGES, file) -# (SINGLE / SHARED) Getting the packages to be installed -roots = [PACKAGE] -for package_name, do_install in PACKAGES.items(): - if do_install: - roots.append(package_name) -packages = [] -packages_dir = {} -requires = [] +packages = [f'{PROJECT}.{PACKAGE}'] +packages_dir = {f'{PROJECT}.{PACKAGE}': 'src'} -# (SINGLE) Specifying package list and corresponding directories -if len(roots) == 1: - packages.append(f'{PROJECT}.{PACKAGE}') - packages_dir[f'{PROJECT}.{PACKAGE}'] = 'src' - requires += DEPENDENCIES[PACKAGE] - for subpackage in find_packages(where='src'): - packages.append(f'{PROJECT}.{PACKAGE}.{subpackage}') - packages_dir[f'{PROJECT}.{PACKAGE}.{subpackage}'] = join('src', *subpackage.split('.')) +# Configure packages list and directories +for subpackage in find_packages(where='src'): + packages.append(f'{PROJECT}.{PACKAGE}.{subpackage}') + packages_dir[f'{PROJECT}.{PACKAGE}.{subpackage}'] = join('src', *subpackage.split('.')) -# (SHARED) Specifying package list and corresponding directories -else: - for package_name in roots: - packages.append(f'{PROJECT}.{package_name}') - packages_dir[f'{PROJECT}.{package_name}'] = join(pardir, package_name, 'src') - requires += DEPENDENCIES[package_name] - for sub_package in find_packages(where=join(pardir, package_name, 'src')): - packages.append(f'{PROJECT}.{package_name}.{sub_package}') - packages_dir[f'{PROJECT}.{package_name}.{sub_package}'] = join(pardir, package_name, 'src', - *sub_package.split('.')) +# Add examples as subpackages +packages.append(f'{PROJECT}.examples.{PACKAGE}') +packages_dir[f'{PROJECT}.examples.{PACKAGE}'] = 'examples' +for example_dir in find_packages(where='examples'): + packages.append(f'{PROJECT}.examples.{PACKAGE}.{example_dir}') -# (SINGLE / SHARED) Extract README.md content +# Extract README.md content with open('README.md') as f: long_description = f.read() -# (SINGLE / SHARED) Installation + +def get_SSD(): + # If SSD was installed in dev mode, pip will re-install it + try: + import SSD + except ModuleNotFoundError: + return ['SimulationSimpleDatabase'] + return [] + + +# Installation setup(name='DeepPhysX', - version='22.06', + version='22.12', description='A Python framework interfacing AI with numerical simulation.', long_description=long_description, long_description_content_type='text/markdown', @@ -72,4 +43,4 @@ url='https://github.com/mimesis-inria/DeepPhysX', packages=packages, package_dir=packages_dir, - install_requires=requires) + install_requires=get_SSD() + ['numpy', 'tensorboard', 'tensorboardX', 'pyDataverse']) From 54085be9a1f3e41a6dc469013fb1869c85b499b7 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 24 Nov 2022 15:35:07 +0100 Subject: [PATCH 48/61] Installation under namespace DeepPhysX. --- src/Database/__init__.py => __init__.py | 0 setup.py | 11 ++++++----- src/{ => Core}/AsyncSocket/AbstractEnvironment.py | 0 src/{ => Core}/AsyncSocket/BytesConverter.py | 0 src/{ => Core}/AsyncSocket/TcpIpClient.py | 0 src/{ => Core}/AsyncSocket/TcpIpObject.py | 0 src/{ => Core}/AsyncSocket/TcpIpServer.py | 0 src/{ => Core}/AsyncSocket/__init__.py | 0 src/{ => Core}/Database/BaseDatabaseConfig.py | 0 src/{ => Core}/Database/DatabaseHandler.py | 0 src/{Utils/Visualizer => Core/Database}/__init__.py | 0 src/{ => Core}/Environment/BaseEnvironment.py | 0 src/{ => Core}/Environment/BaseEnvironmentConfig.py | 0 src/{ => Core}/Environment/__init__.py | 0 src/{ => Core}/Environment/launcherBaseEnvironment.py | 0 src/{ => Core}/Manager/DataManager.py | 0 src/{ => Core}/Manager/DatabaseManager.py | 0 src/{ => Core}/Manager/EnvironmentManager.py | 0 src/{ => Core}/Manager/NetworkManager.py | 0 src/{ => Core}/Manager/StatsManager.py | 0 src/{ => Core}/Manager/__init__.py | 0 src/{ => Core}/Network/BaseNetwork.py | 0 src/{ => Core}/Network/BaseNetworkConfig.py | 0 src/{ => Core}/Network/BaseOptimization.py | 0 src/{ => Core}/Network/BaseTransformation.py | 0 src/{ => Core}/Network/__init__.py | 0 src/{ => Core}/Pipelines/BaseDataGeneration.py | 0 src/{ => Core}/Pipelines/BasePipeline.py | 0 src/{ => Core}/Pipelines/BasePrediction.py | 0 src/{ => Core}/Pipelines/BaseTraining.py | 0 src/{ => Core}/Pipelines/__init__.py | 0 src/{ => Core}/Utils/Visualizer/GridMapping.py | 0 src/{ => Core}/Utils/Visualizer/SampleVisualizer.py | 0 .../Utils/Visualizer}/__init__.py | 0 .../Utils/Visualizer/barycentric_mapping.py | 0 src/{ => Core}/Utils/__init__.py | 0 src/{ => Core}/Utils/configs.py | 0 src/{ => Core}/Utils/converter.py | 0 src/{ => Core}/Utils/data_downloader.py | 0 src/{ => Core}/Utils/jsonUtils.py | 0 src/{ => Core}/Utils/mathUtils.py | 0 src/{ => Core}/Utils/path.py | 0 src/{ => Core}/Utils/tensor_transform_utils.py | 0 src/{ => Core}/Visualization/VedoFactory.py | 0 src/{ => Core}/Visualization/VedoVisualizer.py | 0 src/Core/Visualization/__init__.py | 0 src/Core/__init__.py | 9 +++++++++ src/__init__.py | 10 +--------- 48 files changed, 16 insertions(+), 14 deletions(-) rename src/Database/__init__.py => __init__.py (100%) rename src/{ => Core}/AsyncSocket/AbstractEnvironment.py (100%) rename src/{ => Core}/AsyncSocket/BytesConverter.py (100%) rename src/{ => Core}/AsyncSocket/TcpIpClient.py (100%) rename src/{ => Core}/AsyncSocket/TcpIpObject.py (100%) rename src/{ => Core}/AsyncSocket/TcpIpServer.py (100%) rename src/{ => Core}/AsyncSocket/__init__.py (100%) rename src/{ => Core}/Database/BaseDatabaseConfig.py (100%) rename src/{ => Core}/Database/DatabaseHandler.py (100%) rename src/{Utils/Visualizer => Core/Database}/__init__.py (100%) rename src/{ => Core}/Environment/BaseEnvironment.py (100%) rename src/{ => Core}/Environment/BaseEnvironmentConfig.py (100%) rename src/{ => Core}/Environment/__init__.py (100%) rename src/{ => Core}/Environment/launcherBaseEnvironment.py (100%) rename src/{ => Core}/Manager/DataManager.py (100%) rename src/{ => Core}/Manager/DatabaseManager.py (100%) rename src/{ => Core}/Manager/EnvironmentManager.py (100%) rename src/{ => Core}/Manager/NetworkManager.py (100%) rename src/{ => Core}/Manager/StatsManager.py (100%) rename src/{ => Core}/Manager/__init__.py (100%) rename src/{ => Core}/Network/BaseNetwork.py (100%) rename src/{ => Core}/Network/BaseNetworkConfig.py (100%) rename src/{ => Core}/Network/BaseOptimization.py (100%) rename src/{ => Core}/Network/BaseTransformation.py (100%) rename src/{ => Core}/Network/__init__.py (100%) rename src/{ => Core}/Pipelines/BaseDataGeneration.py (100%) rename src/{ => Core}/Pipelines/BasePipeline.py (100%) rename src/{ => Core}/Pipelines/BasePrediction.py (100%) rename src/{ => Core}/Pipelines/BaseTraining.py (100%) rename src/{ => Core}/Pipelines/__init__.py (100%) rename src/{ => Core}/Utils/Visualizer/GridMapping.py (100%) rename src/{ => Core}/Utils/Visualizer/SampleVisualizer.py (100%) rename src/{Visualization => Core/Utils/Visualizer}/__init__.py (100%) rename src/{ => Core}/Utils/Visualizer/barycentric_mapping.py (100%) rename src/{ => Core}/Utils/__init__.py (100%) rename src/{ => Core}/Utils/configs.py (100%) rename src/{ => Core}/Utils/converter.py (100%) rename src/{ => Core}/Utils/data_downloader.py (100%) rename src/{ => Core}/Utils/jsonUtils.py (100%) rename src/{ => Core}/Utils/mathUtils.py (100%) rename src/{ => Core}/Utils/path.py (100%) rename src/{ => Core}/Utils/tensor_transform_utils.py (100%) rename src/{ => Core}/Visualization/VedoFactory.py (100%) rename src/{ => Core}/Visualization/VedoVisualizer.py (100%) create mode 100644 src/Core/Visualization/__init__.py create mode 100644 src/Core/__init__.py diff --git a/src/Database/__init__.py b/__init__.py similarity index 100% rename from src/Database/__init__.py rename to __init__.py diff --git a/setup.py b/setup.py index 48d3d403..1cc1b7e7 100644 --- a/setup.py +++ b/setup.py @@ -4,13 +4,13 @@ PROJECT = 'DeepPhysX' PACKAGE = 'Core' -packages = [f'{PROJECT}.{PACKAGE}'] -packages_dir = {f'{PROJECT}.{PACKAGE}': 'src'} +packages = [] +packages_dir = {} # Configure packages list and directories for subpackage in find_packages(where='src'): - packages.append(f'{PROJECT}.{PACKAGE}.{subpackage}') - packages_dir[f'{PROJECT}.{PACKAGE}.{subpackage}'] = join('src', *subpackage.split('.')) + packages.append(f'{PROJECT}.{subpackage}') + packages_dir[f'{PROJECT}.{subpackage}'] = join('src', *subpackage.split('.')) # Add examples as subpackages packages.append(f'{PROJECT}.examples.{PACKAGE}') @@ -33,7 +33,7 @@ def get_SSD(): # Installation -setup(name='DeepPhysX', +setup(name=f'{PROJECT}', version='22.12', description='A Python framework interfacing AI with numerical simulation.', long_description=long_description, @@ -43,4 +43,5 @@ def get_SSD(): url='https://github.com/mimesis-inria/DeepPhysX', packages=packages, package_dir=packages_dir, + namespace_packages=[PROJECT], install_requires=get_SSD() + ['numpy', 'tensorboard', 'tensorboardX', 'pyDataverse']) diff --git a/src/AsyncSocket/AbstractEnvironment.py b/src/Core/AsyncSocket/AbstractEnvironment.py similarity index 100% rename from src/AsyncSocket/AbstractEnvironment.py rename to src/Core/AsyncSocket/AbstractEnvironment.py diff --git a/src/AsyncSocket/BytesConverter.py b/src/Core/AsyncSocket/BytesConverter.py similarity index 100% rename from src/AsyncSocket/BytesConverter.py rename to src/Core/AsyncSocket/BytesConverter.py diff --git a/src/AsyncSocket/TcpIpClient.py b/src/Core/AsyncSocket/TcpIpClient.py similarity index 100% rename from src/AsyncSocket/TcpIpClient.py rename to src/Core/AsyncSocket/TcpIpClient.py diff --git a/src/AsyncSocket/TcpIpObject.py b/src/Core/AsyncSocket/TcpIpObject.py similarity index 100% rename from src/AsyncSocket/TcpIpObject.py rename to src/Core/AsyncSocket/TcpIpObject.py diff --git a/src/AsyncSocket/TcpIpServer.py b/src/Core/AsyncSocket/TcpIpServer.py similarity index 100% rename from src/AsyncSocket/TcpIpServer.py rename to src/Core/AsyncSocket/TcpIpServer.py diff --git a/src/AsyncSocket/__init__.py b/src/Core/AsyncSocket/__init__.py similarity index 100% rename from src/AsyncSocket/__init__.py rename to src/Core/AsyncSocket/__init__.py diff --git a/src/Database/BaseDatabaseConfig.py b/src/Core/Database/BaseDatabaseConfig.py similarity index 100% rename from src/Database/BaseDatabaseConfig.py rename to src/Core/Database/BaseDatabaseConfig.py diff --git a/src/Database/DatabaseHandler.py b/src/Core/Database/DatabaseHandler.py similarity index 100% rename from src/Database/DatabaseHandler.py rename to src/Core/Database/DatabaseHandler.py diff --git a/src/Utils/Visualizer/__init__.py b/src/Core/Database/__init__.py similarity index 100% rename from src/Utils/Visualizer/__init__.py rename to src/Core/Database/__init__.py diff --git a/src/Environment/BaseEnvironment.py b/src/Core/Environment/BaseEnvironment.py similarity index 100% rename from src/Environment/BaseEnvironment.py rename to src/Core/Environment/BaseEnvironment.py diff --git a/src/Environment/BaseEnvironmentConfig.py b/src/Core/Environment/BaseEnvironmentConfig.py similarity index 100% rename from src/Environment/BaseEnvironmentConfig.py rename to src/Core/Environment/BaseEnvironmentConfig.py diff --git a/src/Environment/__init__.py b/src/Core/Environment/__init__.py similarity index 100% rename from src/Environment/__init__.py rename to src/Core/Environment/__init__.py diff --git a/src/Environment/launcherBaseEnvironment.py b/src/Core/Environment/launcherBaseEnvironment.py similarity index 100% rename from src/Environment/launcherBaseEnvironment.py rename to src/Core/Environment/launcherBaseEnvironment.py diff --git a/src/Manager/DataManager.py b/src/Core/Manager/DataManager.py similarity index 100% rename from src/Manager/DataManager.py rename to src/Core/Manager/DataManager.py diff --git a/src/Manager/DatabaseManager.py b/src/Core/Manager/DatabaseManager.py similarity index 100% rename from src/Manager/DatabaseManager.py rename to src/Core/Manager/DatabaseManager.py diff --git a/src/Manager/EnvironmentManager.py b/src/Core/Manager/EnvironmentManager.py similarity index 100% rename from src/Manager/EnvironmentManager.py rename to src/Core/Manager/EnvironmentManager.py diff --git a/src/Manager/NetworkManager.py b/src/Core/Manager/NetworkManager.py similarity index 100% rename from src/Manager/NetworkManager.py rename to src/Core/Manager/NetworkManager.py diff --git a/src/Manager/StatsManager.py b/src/Core/Manager/StatsManager.py similarity index 100% rename from src/Manager/StatsManager.py rename to src/Core/Manager/StatsManager.py diff --git a/src/Manager/__init__.py b/src/Core/Manager/__init__.py similarity index 100% rename from src/Manager/__init__.py rename to src/Core/Manager/__init__.py diff --git a/src/Network/BaseNetwork.py b/src/Core/Network/BaseNetwork.py similarity index 100% rename from src/Network/BaseNetwork.py rename to src/Core/Network/BaseNetwork.py diff --git a/src/Network/BaseNetworkConfig.py b/src/Core/Network/BaseNetworkConfig.py similarity index 100% rename from src/Network/BaseNetworkConfig.py rename to src/Core/Network/BaseNetworkConfig.py diff --git a/src/Network/BaseOptimization.py b/src/Core/Network/BaseOptimization.py similarity index 100% rename from src/Network/BaseOptimization.py rename to src/Core/Network/BaseOptimization.py diff --git a/src/Network/BaseTransformation.py b/src/Core/Network/BaseTransformation.py similarity index 100% rename from src/Network/BaseTransformation.py rename to src/Core/Network/BaseTransformation.py diff --git a/src/Network/__init__.py b/src/Core/Network/__init__.py similarity index 100% rename from src/Network/__init__.py rename to src/Core/Network/__init__.py diff --git a/src/Pipelines/BaseDataGeneration.py b/src/Core/Pipelines/BaseDataGeneration.py similarity index 100% rename from src/Pipelines/BaseDataGeneration.py rename to src/Core/Pipelines/BaseDataGeneration.py diff --git a/src/Pipelines/BasePipeline.py b/src/Core/Pipelines/BasePipeline.py similarity index 100% rename from src/Pipelines/BasePipeline.py rename to src/Core/Pipelines/BasePipeline.py diff --git a/src/Pipelines/BasePrediction.py b/src/Core/Pipelines/BasePrediction.py similarity index 100% rename from src/Pipelines/BasePrediction.py rename to src/Core/Pipelines/BasePrediction.py diff --git a/src/Pipelines/BaseTraining.py b/src/Core/Pipelines/BaseTraining.py similarity index 100% rename from src/Pipelines/BaseTraining.py rename to src/Core/Pipelines/BaseTraining.py diff --git a/src/Pipelines/__init__.py b/src/Core/Pipelines/__init__.py similarity index 100% rename from src/Pipelines/__init__.py rename to src/Core/Pipelines/__init__.py diff --git a/src/Utils/Visualizer/GridMapping.py b/src/Core/Utils/Visualizer/GridMapping.py similarity index 100% rename from src/Utils/Visualizer/GridMapping.py rename to src/Core/Utils/Visualizer/GridMapping.py diff --git a/src/Utils/Visualizer/SampleVisualizer.py b/src/Core/Utils/Visualizer/SampleVisualizer.py similarity index 100% rename from src/Utils/Visualizer/SampleVisualizer.py rename to src/Core/Utils/Visualizer/SampleVisualizer.py diff --git a/src/Visualization/__init__.py b/src/Core/Utils/Visualizer/__init__.py similarity index 100% rename from src/Visualization/__init__.py rename to src/Core/Utils/Visualizer/__init__.py diff --git a/src/Utils/Visualizer/barycentric_mapping.py b/src/Core/Utils/Visualizer/barycentric_mapping.py similarity index 100% rename from src/Utils/Visualizer/barycentric_mapping.py rename to src/Core/Utils/Visualizer/barycentric_mapping.py diff --git a/src/Utils/__init__.py b/src/Core/Utils/__init__.py similarity index 100% rename from src/Utils/__init__.py rename to src/Core/Utils/__init__.py diff --git a/src/Utils/configs.py b/src/Core/Utils/configs.py similarity index 100% rename from src/Utils/configs.py rename to src/Core/Utils/configs.py diff --git a/src/Utils/converter.py b/src/Core/Utils/converter.py similarity index 100% rename from src/Utils/converter.py rename to src/Core/Utils/converter.py diff --git a/src/Utils/data_downloader.py b/src/Core/Utils/data_downloader.py similarity index 100% rename from src/Utils/data_downloader.py rename to src/Core/Utils/data_downloader.py diff --git a/src/Utils/jsonUtils.py b/src/Core/Utils/jsonUtils.py similarity index 100% rename from src/Utils/jsonUtils.py rename to src/Core/Utils/jsonUtils.py diff --git a/src/Utils/mathUtils.py b/src/Core/Utils/mathUtils.py similarity index 100% rename from src/Utils/mathUtils.py rename to src/Core/Utils/mathUtils.py diff --git a/src/Utils/path.py b/src/Core/Utils/path.py similarity index 100% rename from src/Utils/path.py rename to src/Core/Utils/path.py diff --git a/src/Utils/tensor_transform_utils.py b/src/Core/Utils/tensor_transform_utils.py similarity index 100% rename from src/Utils/tensor_transform_utils.py rename to src/Core/Utils/tensor_transform_utils.py diff --git a/src/Visualization/VedoFactory.py b/src/Core/Visualization/VedoFactory.py similarity index 100% rename from src/Visualization/VedoFactory.py rename to src/Core/Visualization/VedoFactory.py diff --git a/src/Visualization/VedoVisualizer.py b/src/Core/Visualization/VedoVisualizer.py similarity index 100% rename from src/Visualization/VedoVisualizer.py rename to src/Core/Visualization/VedoVisualizer.py diff --git a/src/Core/Visualization/__init__.py b/src/Core/Visualization/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/Core/__init__.py b/src/Core/__init__.py new file mode 100644 index 00000000..d431a0bb --- /dev/null +++ b/src/Core/__init__.py @@ -0,0 +1,9 @@ +from os.path import dirname +from os import listdir + +package = dirname(__file__) +exceptions = ['__init__.py', '__pycache__'] +modules = [module for module in listdir(package) if module not in exceptions] +__all__ = [] +for module in sorted(modules): + __all__.append(module) diff --git a/src/__init__.py b/src/__init__.py index d431a0bb..6745185f 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,9 +1 @@ -from os.path import dirname -from os import listdir - -package = dirname(__file__) -exceptions = ['__init__.py', '__pycache__'] -modules = [module for module in listdir(package) if module not in exceptions] -__all__ = [] -for module in sorted(modules): - __all__.append(module) +__import__('pkg_resources').declare_namespace('DeepPhysX') From 3d0c097095714660343f054a091a1b399c7844e0 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 25 Nov 2022 15:25:13 +0100 Subject: [PATCH 49/61] Update user and dev installation processes. --- config.py | 86 ------------------------------- dev.py | 52 ------------------- setup_dev.py | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ setup_user.py | 25 +++++++++ 4 files changed, 162 insertions(+), 138 deletions(-) delete mode 100644 config.py delete mode 100644 dev.py create mode 100644 setup_dev.py create mode 100644 setup_user.py diff --git a/config.py b/config.py deleted file mode 100644 index 294405f7..00000000 --- a/config.py +++ /dev/null @@ -1,86 +0,0 @@ -from os import chdir, pardir, system, sep, rename, listdir, getcwd, mkdir -from os.path import exists, abspath, join -from json import dump -from shutil import move - -PROJECT = 'DeepPhysX' -PACKAGES = {'Torch': False, - 'Sofa': False} -AVAILABLE = {'AI': ['Torch'], - 'Simulation': ['Sofa']} -GIT = {'Torch': 'https://github.com/mimesis-inria/DeepPhysX.Torch.git', - 'Sofa': 'https://github.com/mimesis-inria/DeepPhysX.Sofa.git'} -ANSWERS = ['y', 'yes', 'n', 'no'] - - -def check_repositories(): - - # Check current repository - path = abspath(join(__file__, pardir, pardir)) - repository = abspath(join(__file__, pardir)).split(sep)[-1] - chdir(join(path, repository)) - size = 2 - if repository != 'Core': - print(f"WARNING: Wrong repository, moving '{repository}' --> '{join(PROJECT, 'Core')}'") - chdir(pardir) - rename(repository, 'Core') - mkdir(PROJECT) - move(src=join(path, f'Core{sep}'), - dst=join(path, PROJECT)) - size += 1 - path = join(path, PROJECT) - chdir(join(path, 'Core')) - - # Check other repositories - for i in range(size): - for repository in listdir(getcwd()): - if 'DeepPhysX.' in repository: - if repository[10:] in PACKAGES.keys(): - print(f"WARNING: Wrong repository, moving '{repository}' --> '{join(PROJECT, repository[10:])}'") - rename(repository, repository[10:]) - if not exists(join(path, f'{repository[10:]}{sep}')): - move(src=join(getcwd(), f'{repository[10:]}{sep}'), - dst=path) - chdir(pardir) - chdir(join(path, 'Core')) - - -if __name__ == '__main__': - - # Check repositories names - check_repositories() - - # Get user entry for each package - for package_type, package_names in AVAILABLE.items(): - print(f"\nAvailable {package_type} packages : {package_names}") - for package_name in package_names: - while (do_install := input(f" >> Installing package {package_name} (y/n): ").lower()) not in ANSWERS: - pass - PACKAGES[package_name] = do_install in ANSWERS[:2] - - # Ask user confirmation - print("\nApplying following configuration: \n * DeepPhysX.Core: True (default)") - for package_name, do_install in PACKAGES.items(): - print(f" * DeepPhysX.{package_name}: {do_install}") - while (do_validate := input("Confirm (y/n): ")) not in ANSWERS: - pass - if do_validate in ANSWERS[2:]: - print("Aborting") - quit() - - # Save config - with open('config.json', 'w') as file: - dump(PACKAGES, file) - print("Configuration saved in 'config.json'") - - # Clone missing packages - chdir(pardir) - for package_name, do_install in PACKAGES.items(): - if do_install and not exists(package_name): - print(f"\nPackage {package_name} not found, cloning from {GIT[package_name]}") - system(f'git clone {GIT[package_name]} {package_name}') - - # End config - print("\nConfiguration done, install DeepPhysX with the following commands:" - "\n - 'pip install .' for user mode" - "\n - 'python3 dev.py set' for developer mode") diff --git a/dev.py b/dev.py deleted file mode 100644 index 4b59b5f3..00000000 --- a/dev.py +++ /dev/null @@ -1,52 +0,0 @@ -from os import listdir, symlink, unlink, mkdir -from os.path import join, islink, abspath, pardir, isdir -from pathlib import Path -from sys import argv -from site import USER_SITE -from shutil import rmtree - -from config import check_repositories - - -# Check user entry -if len(argv) != 2 or argv[1] not in ['set', 'del']: - print("\nInvalid script option." - "\nRun 'python3 dev.py set' to link DPX to your site package." - "\nRun 'python3 dev.py del' to remove DPX links in your site package.") - quit() - -# Check repositories names -check_repositories() - -# Init DeepPhysX packages and dependencies to install -PROJECT = 'DeepPhysX' -packages = ['Core'] -available = ['Torch', 'Sofa'] -root = abspath(join(Path(__file__).parent.absolute(), pardir)) - -# Option 1: create the symbolic links -if argv[1] == 'set': - - # Create main repository in site-packages - if not isdir(join(USER_SITE, PROJECT)): - mkdir(join(USER_SITE, PROJECT)) - - # Link to every existing packages - for package_name in listdir(root): - if package_name in available: - packages.append(package_name) - - # Create symbolic links in site-packages - for package_name in packages: - if not islink(join(USER_SITE, PROJECT, package_name)): - symlink(src=join(root, package_name, 'src'), dst=join(USER_SITE, PROJECT, package_name)) - print(f"Linked {join(USER_SITE, PROJECT, package_name)} -> {join(root, package_name, 'src')}") - -# Option 2: remove the symbolic links -else: - - if isdir(join(USER_SITE, PROJECT)): - for package_name in listdir(join(USER_SITE, PROJECT)): - unlink(join(USER_SITE, PROJECT, package_name)) - print(f"Unlinked {join(USER_SITE, PROJECT, package_name)} -> {join(root, package_name, 'src')}") - rmtree(join(USER_SITE, PROJECT)) diff --git a/setup_dev.py b/setup_dev.py new file mode 100644 index 00000000..21996c1e --- /dev/null +++ b/setup_dev.py @@ -0,0 +1,137 @@ +from os import sep, chdir, mkdir, listdir, getcwd, rename, symlink, unlink, remove +from os.path import dirname, join, isfile, exists, isdir, islink +from shutil import move, rmtree, which +from site import USER_SITE +from subprocess import run +from sys import argv + +PROJECT = 'DeepPhysX' +AI_PACKAGES = ['Torch'] +SIMU_PACKAGES = ['Sofa'] +PACKAGES = ['Core'] + AI_PACKAGES + SIMU_PACKAGES +GIT = {'Torch': 'https://github.com/mimesis-inria/DeepPhysX.Torch.git', + 'Sofa': 'https://github.com/mimesis-inria/DeepPhysX.Sofa.git'} + + +def check_repositories(): + + dpx_path = dirname(dirname(__file__)) + + # Check the DeepPhysX root + if dpx_path.split(sep)[-1] != PROJECT: + # Create the right root + chdir(dpx_path) + if dirname(__file__).split(sep)[-1] == PROJECT: + rename(PROJECT, f'{PROJECT}.Core') + mkdir(PROJECT) + # Move DeepPhysX packages in the root + for repository in listdir(getcwd()): + if is_dpx_package(repository): + move(src=join(dpx_path, repository), + dst=join(dpx_path, PROJECT)) + dpx_path = join(dpx_path, PROJECT) + + # Check the packages repositories + for repository in listdir(dpx_path): + # Core package + if repository == PROJECT: + rename(repository, 'Core') + # Layers + if 'DeepPhysX.' in repository and repository[10:] in PACKAGES: + rename(src=join(dpx_path, repository), + dst=join(dpx_path, repository[10:])) + + return dpx_path + + +def is_dpx_package(repository): + + for key in ['DeepPhysX', 'DeepPhysX.'] + PACKAGES: + if key in repository and isfile(join(repository, 'README.md')): + with open(join(repository, 'README.md')) as f: + if PROJECT in f.readline(): + return True + return False + + +def define_config(root_dir): + + # Get the user configuration + config = ['Core'] + answers = ['y', 'yes', 'n', 'no'] + for package_list, package_type in zip([SIMU_PACKAGES, AI_PACKAGES], ['SIMULATION', 'AI']): + print(f"\nAvailable {package_type} packages: {[f'{PROJECT}.{pkg}' for pkg in package_list]}") + for pkg in package_list: + while (user := input(f" >> Install package {f'{PROJECT}.{pkg}'} (y/n): ").lower()) not in answers: + pass + if user in answers[:2]: + config.append(pkg) + print(f"\nThe following packages will be installed : {[f'{PROJECT}.{package}' for package in config]}") + while (user := input("Confirm (y/n): ").lower()) not in answers: + pass + if user in answers[2:]: + quit(print("Aborting.")) + + # Clone the missing packages + chdir(root_dir) + if len(config) > 1: + for pkg in config[1:]: + if not exists(pkg): + print(f"\nPackage {f'{PROJECT}.{pkg}'} not found, cloning from {GIT[pkg]}...") + run(['git', 'clone', f'{GIT[pkg]}', f'{pkg}'], cwd=root_dir) + + return config + + +if __name__ == '__main__': + + # Check the project tree + root = check_repositories() + + # Check user entry + if len(argv) == 2 and argv[1] not in ['set', 'del']: + quit(print(f"\nInvalid script option." + f"\nRun 'python3 setup_dev.py set to link {PROJECT} to your site-packages folder." + f"\nRun 'python3 setup_dev.py del to remove {PROJECT} link from your site-packages folder.")) + + # Option 1: create the symbolic links + if len(argv) == 1 or argv[1] == 'set': + + # Get the user configuration + packages = define_config(root) + + # Create the main repository in site-packages + if not isdir(join(USER_SITE, PROJECT)): + mkdir(join(USER_SITE, PROJECT)) + + # Create symbolic links in site-packages + for package in packages: + if not islink(join(USER_SITE, PROJECT, package)): + symlink(src=join(root, package, 'src', package), + dst=join(USER_SITE, PROJECT, package)) + print(f"\nLinked {join(USER_SITE, PROJECT, package)} -> {join(root, package)}") + + # Add examples and the CLI script + # TODO + + # Create the CLI + # TODO + + # Option 2: remove the symbolic links + else: + + # Remove everything from site-packages + if isdir(join(USER_SITE, PROJECT)): + for package in listdir(join(USER_SITE, PROJECT)): + if islink(join(USER_SITE, PROJECT, package)): + unlink(join(USER_SITE, PROJECT, package)) + print(f"Unlinked {join(USER_SITE, PROJECT, package)} -> {join(root, package)}") + elif isdir(join(USER_SITE, PROJECT, package)): + rmtree(join(USER_SITE, PROJECT, package)) + elif isfile(join(USER_SITE, PROJECT, package)): + remove(join(USER_SITE, PROJECT, package)) + rmtree(join(USER_SITE, PROJECT)) + + # Remove the CLI + if isfile(which('DPX')): + remove(which('DPX')) diff --git a/setup_user.py b/setup_user.py new file mode 100644 index 00000000..bc7cd348 --- /dev/null +++ b/setup_user.py @@ -0,0 +1,25 @@ +from os.path import join +from subprocess import run + +from setup_dev import check_repositories, define_config + +PROJECT = 'DeepPhysX' +AI_PACKAGES = ['Torch'] +SIMU_PACKAGES = ['Sofa'] +PACKAGES = ['Core'] + AI_PACKAGES + SIMU_PACKAGES +GIT = {'Torch': 'https://github.com/mimesis-inria/DeepPhysX.Torch.git', + 'Sofa': 'https://github.com/mimesis-inria/DeepPhysX.Sofa.git'} + + +if __name__ == '__main__': + + # Check the project tree + root = check_repositories() + + # Get the user configuration + config = define_config(root) + + # Pip install packages + for package in config: + print(f"\nInstalling {f'{PROJECT}.{package}'} package...") + run(['pip', 'install', '.'], cwd=join(root, package)) From 7c41e7d00c606324d315cd2e0c7f8f99dfa18840 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 25 Nov 2022 16:12:46 +0100 Subject: [PATCH 50/61] Add a CLI to run the demos. --- setup.py | 7 ++- setup_dev.py | 19 +++++- src/cli.py | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 src/cli.py diff --git a/setup.py b/setup.py index 1cc1b7e7..7a4c1bc5 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ PROJECT = 'DeepPhysX' PACKAGE = 'Core' -packages = [] -packages_dir = {} +packages = [f'{PROJECT}'] +packages_dir = {f'{PROJECT}': 'src'} # Configure packages list and directories for subpackage in find_packages(where='src'): @@ -44,4 +44,5 @@ def get_SSD(): packages=packages, package_dir=packages_dir, namespace_packages=[PROJECT], - install_requires=get_SSD() + ['numpy', 'tensorboard', 'tensorboardX', 'pyDataverse']) + install_requires=get_SSD() + ['numpy', 'tensorboard', 'tensorboardX', 'pyDataverse'], + entry_points={'console_scripts': ['DPX=DeepPhysX.cli:execute_cli']}) diff --git a/setup_dev.py b/setup_dev.py index 21996c1e..948f487c 100644 --- a/setup_dev.py +++ b/setup_dev.py @@ -1,10 +1,13 @@ from os import sep, chdir, mkdir, listdir, getcwd, rename, symlink, unlink, remove from os.path import dirname, join, isfile, exists, isdir, islink +from pathlib import Path from shutil import move, rmtree, which from site import USER_SITE from subprocess import run from sys import argv +from pip._internal.operations.install.wheel import PipScriptMaker + PROJECT = 'DeepPhysX' AI_PACKAGES = ['Torch'] SIMU_PACKAGES = ['Sofa'] @@ -112,10 +115,22 @@ def define_config(root_dir): print(f"\nLinked {join(USER_SITE, PROJECT, package)} -> {join(root, package)}") # Add examples and the CLI script - # TODO + if not isdir(join(USER_SITE, PROJECT, 'examples')): + symlink(src=join(Path(__file__).parent.absolute(), 'examples'), + dst=join(USER_SITE, PROJECT, 'examples')) + print(f"\nLinked {join(USER_SITE, PROJECT, 'examples')} -> {join(Path(__file__).parent.absolute(), 'examples')}") + if not isfile(join(USER_SITE, PROJECT, 'cli.py')): + symlink(src=join(Path(__file__).parent.absolute(), 'src', 'cli.py'), + dst=join(USER_SITE, PROJECT, 'cli.py')) # Create the CLI - # TODO + if which('DPX') is None: + # Generate the scripts + maker = PipScriptMaker(None, dirname(which('vedo'))) + generated_scripts = maker.make_multiple(['DPX = DeepPhysX.cli:execute_cli']) + for script in generated_scripts: + if script.split(sep)[-1].split('.')[0] != 'DPX': + remove(script) # Option 2: remove the symbolic links else: diff --git a/src/cli.py b/src/cli.py new file mode 100644 index 00000000..6ef4f533 --- /dev/null +++ b/src/cli.py @@ -0,0 +1,161 @@ +from argparse import ArgumentParser +from json import load +from os import listdir, getcwd, readlink, chdir +from os.path import islink, dirname, join, abspath, isdir +from platform import system +from shutil import copytree, rmtree +from subprocess import run +from sys import executable + + +def is_pip_installed(): + + import DeepPhysX.Core + return not islink(DeepPhysX.Core.__path__[0]) + + +def get_sources(): + + import DeepPhysX + site_packages = dirname(DeepPhysX.__path__[0]) + metadata_repo = [f for f in listdir(site_packages) if 'DeepPhysX' in f and + ('.dist-info' in f or '.egg-info' in f)] + if len(metadata_repo) == 0: + quit(print("The project does not seem to be properly installed. Try to re-install using 'pip'.")) + elif len(metadata_repo) > 1: + quit(print("There might be several version of the project, try to clean your site-packages.")) + metadata_repo = metadata_repo.pop(0) + if 'direct_url.json' not in listdir(join(site_packages, metadata_repo)): + return None + with open(join(site_packages, metadata_repo, 'direct_url.json'), 'r') as file: + direct_url = load(file) + if system() == 'Linux': + return abspath(direct_url['url'].split('//')[1]) + elif system() == 'Windows': + return abspath(direct_url['url'].split('///')[1]) + else: + return abspath(direct_url['url'].split('///')[1]) + + +def copy_examples_dir(): + + user = input(f"WARNING: The project was installed with pip, examples must be run in a new repository to avoid " + f"writing data in your installation of SSD. Allow the creation of this new repository " + f"'{join(getcwd(), 'DPX_examples')}' to run examples (use 'DPX --clean' to cleanly" + f"remove it afterward) (y/n):") + if user.lower() not in ['y', 'yes']: + quit(print("Aborting.")) + + import DeepPhysX.examples + copytree(src=DeepPhysX.examples.__path__[0], + dst=join(getcwd(), 'DPX_examples')) + + +def clean_examples_dir(): + + if not isdir(examples_dir := join(getcwd(), 'DPX_examples')): + quit(print(f"The directory '{examples_dir}' does not exists.")) + user = input(f"Do you want to remove the repository '{examples_dir}' (y/n):") + if user.lower() not in ['y', 'yes']: + quit(print("Aborting.")) + rmtree(examples_dir) + + +def print_available_examples(examples): + + example_names = sorted(list(examples.keys())) + example_per_repo = {} + for example_name in example_names: + if type(examples[example_name]) == str: + root, repo = examples[example_name].split('.')[0], examples[example_name].split('.')[1] + else: + root, repo = examples[example_name][0].split('.')[0], examples[example_name][0].split('.')[1] + repo = 'rendering' if repo == 'rendering-offscreen' else repo + if root not in example_per_repo: + example_per_repo[root] = {} + if repo not in example_per_repo[root]: + example_per_repo[root][repo] = [] + example_per_repo[root][repo].append(example_name) + + description = '\navailable examples:' + for repo, sub_repos in example_per_repo.items(): + for sub_repo, names in sub_repos.items(): + description += f'\n {repo}.{sub_repo}: {names}' + print(description) + + +def execute_cli(): + + description = "Command Line Interface dedicated to DPX examples." + parser = ArgumentParser(prog='SSD', description=description) + parser.add_argument('-c', '--clean', help='clean the example repository.', action='store_true') + parser.add_argument('-g', '--get', help='get the full example repository locally.', action='store_true') + parser.add_argument('-r', '--run', type=str, help='run one of the demo sessions.', metavar='') + args = parser.parse_args() + + # Get a copy of the example repository if pip installed from PyPi.org + if args.get: + # Installed with setup_dev.py + if not is_pip_installed(): + quit(print("The project was installed from sources in dev mode, examples will then be run in " + "'DeepPhysX..examples'.")) + # Installed with pip from sources + if (source_dir := get_sources()) is not None: + quit(print(f"The project was installed with pip from sources, examples will then be run in " + f"'{join(source_dir, 'examples')}'.")) + # Installed with pip from PyPi + copy_examples_dir() + return + + # Clean the examples repository if pip installed from PyPi.org + elif args.clean: + # Installed with setup_dev.py + if not is_pip_installed(): + quit(print("The project was installed from sources in dev mode, you cannot clean " + "'DPX..examples'.")) + # Installed with pip from sources + if (source_dir := get_sources()) is not None: + quit(print(f"The project was installed with pip from sources, you cannot clean " + f"'{join(source_dir, 'examples')}'.")) + # Installed with pip from PyPi + clean_examples_dir() + return + + examples = {'armadillo': 'Core/demos/Armadillo/FC/interactive.py', + 'beam': 'Core/demos/Beam/FC/interactive.py', + 'liver': 'Core/demos/Liver/FC/interactive.py'} + + # Run a demo script + if (example := args.run) is not None: + # Check the example name + if example.lower() not in examples.keys(): + print(f"Unknown demo '{example}'.") + quit(print_available_examples(examples)) + # Get the example directory + if not is_pip_installed(): + import DeepPhysX.Core + source_dir = readlink(DeepPhysX.Core.__path__[0]) + examples_dir = join(dirname(dirname(source_dir)), 'examples') + elif (source_dir := get_sources()) is not None: + examples_dir = join(source_dir, 'examples') + else: + if not isdir(join(getcwd(), 'DPX_examples')): + print(f"The directory '{join(getcwd(), 'DPX_examples')}' does not exists.") + copy_examples_dir() + examples_dir = join(getcwd(), 'DPX_examples') + # Run the example + repo = join(*examples[example].split('/')[:-1]) if is_pip_installed() else join(*examples[example].split('/')[1:-1]) + script = examples[example].split('/')[-1] + chdir(join(examples_dir, repo)) + run([f'{executable}', f'{script}'], cwd=join(examples_dir, repo)) + + return + + # No command + else: + parser.print_help() + print_available_examples(examples) + + +if __name__ == '__main__': + execute_cli() From 8a2ea7008813172dc7d0d8b1b465d377b5d1e256 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 25 Nov 2022 16:42:41 +0100 Subject: [PATCH 51/61] Update installation instructions. --- README.md | 26 +++--------- demo.py | 18 -------- docs/source/conf.py | 2 +- docs/source/presentation/install.rst | 63 +++++++++++++--------------- 4 files changed, 36 insertions(+), 73 deletions(-) delete mode 100644 demo.py diff --git a/README.md b/README.md index 3af98e74..4d564c2e 100644 --- a/README.md +++ b/README.md @@ -40,34 +40,18 @@ $ pip install DeepPhysX.Sofa # Install simulation package $ pip install DeepPhysX.Torch # Install AI package ``` -If cloning sources, create a `DeepPhysX` repository to store every package. -Packages must be cloned in a directory with the corresponding name as shown below: - -``` bash -$ mkdir DeepPhysX -$ cd DeepPhysX -$ git clone https://github.com/mimesis-inria/DeepPhysX.git Core # Clone default package -$ git clone https://github.com/mimesis-inria/DeepPhysX.Sofa.git Sofa # Clone simulation package -$ git clone https://github.com/mimesis-inria/DeepPhysX.Torch.git Torch # Clone AI package -$ ls -Core Sofa Torch -``` - ### Demos **DeepPhysX** includes a set of detailed tutorials, examples and demos. -Following this installation process to directly try the **interactive demos**: +As these scripts are producing data, they cannot be run in the python site-packages, thus they should be run locally. +Use the *command line interface* to get the examples or to run **interactive demos**: ``` bash -$ mkdir DeepPhysX -$ cd DeepPhysX -$ git clone https://github.com/mimesis/deepphysx.git Core # Make shure to clone this repository in 'DeepPhysX/Core' -$ cd Core -$ python3 config.py # Answer 'yes' to install Torch package to launch examples -$ pip install . +$ DPX --get # Get the full example repository locally +$ DPX --run # Run one of the demo scripts ``` -| **Armadillo**
    `python3 demo.py armadillo` | **Beam**
    `python3 demo.py beam` | **Liver**
    `python3 demo.py liver` | +| **Armadillo**
    `DPX -r armadillo` | **Beam**
    `DPX -r beam` | **Liver**
    `DPX -r liver` | |:-----------------------------------------------------:|:-------------------------------------------:|:---------------------------------------------:| | ![armadillo](docs/source/_static/image/armadillo.png) | ![beam](docs/source/_static/image/beam.png) | ![liver](docs/source/_static/image/liver.png) | diff --git a/demo.py b/demo.py deleted file mode 100644 index aec206f2..00000000 --- a/demo.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import sys -import argparse - - -parser = argparse.ArgumentParser(prog='demo', description='Specify the name of the demo.') -parser.add_argument('Demo', metavar='demo', type=str, help='Name of the demo to run') -args = parser.parse_args() - -demo = args.Demo -demos = ['armadillo', 'beam', 'liver'] - -if demo not in demos: - raise ValueError(f"Unknown demo '{demo}', available are: {demos}") - -repo = os.path.join(os.path.dirname(__file__), 'examples', 'demos', demo[0].upper() + demo[1:].lower(), 'FC') -os.chdir(repo) -os.system(f'{sys.executable} interactive.py') diff --git a/docs/source/conf.py b/docs/source/conf.py index feee0ceb..7f35763d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,7 +14,7 @@ import sys # DeepPhysX root -root = abspath(join(abspath(__file__), pardir, pardir, pardir, 'src')) +root = abspath(join(abspath(__file__), pardir, pardir, pardir, 'src', 'Core')) all_modules = ['AsyncSocket', 'Database', 'Environment', 'Manager', 'Network', 'Pipelines', 'Visualization'] # Import all modules diff --git a/docs/source/presentation/install.rst b/docs/source/presentation/install.rst index 58083f50..516f7183 100644 --- a/docs/source/presentation/install.rst +++ b/docs/source/presentation/install.rst @@ -55,17 +55,17 @@ They can easily be installed with ``pip``: .. code-block:: bash - pip3 install DeepPhysX - pip3 install DeepPhysX.Sofa - pip3 install DeepPhysX.Torch + $ pip3 install DeepPhysX + $ pip3 install DeepPhysX.Sofa + $ pip3 install DeepPhysX.Torch Then, you should be able to run: .. code-block:: bash - pip3 show DeepPhysX - pip3 show DeepPhysX.Sofa - pip3 show DeepPhysX.Torch + $ pip3 show DeepPhysX + $ pip3 show DeepPhysX.Sofa + $ pip3 show DeepPhysX.Torch .. code-block:: python @@ -94,51 +94,48 @@ Start by cloning the **DeepPhysX** source code from its Github repository in a d $ mkdir DeepPhysX $ cd DeepPhysX $ git clone https://github.com/mimesis-inria/DeepPhysX.git Core - $ cd Core -Specify which packages to install by running the configuration script. -This way, all the packages are gathered in a single installation. +Then, you can add compatibility layers to your **DeepPhysX** environment and install packages: -.. code-block:: bash +* **Option 1 (recommended):** run one of the ``setup_.py`` scripts that handle the installation of all packages - $ python3 config.py - > Available AI packages : ['Torch'] - > >> Installing package Torch (y/n): yes - > - > Available Simulation packages : ['Sofa'] - > >> Installing package Sofa (y/n): yes - > - > Applying following configuration: - > * DeepPhysX.Core: True (default) - > * DeepPhysX.Torch: True - > * DeepPhysX.Sofa: True - > Confirm (y/n): yes - > Configuration saved in 'config.json' + * Use ``setup_user.py`` to install and manage packages with ``pip`` as non-editable. + .. code-block:: bash -.. note:: - Configuration script will **automatically clone** missing packages. + $ python3 setup_user.py -Finally, install the defined packages: + * Use ``setup_dev.py`` to link packages in the site-packages. -* by using ``pip`` to install and manage them as non-editable + .. code-block:: bash - .. code-block:: bash + $ python3 setup_dev.py set - $ pip3 install . + .. note:: + Both scripts will asks the packages to install and will **automatically clone** missing packages. -* by running ``dev.py`` to link them as editable in the site-packages +* **Option 2:** clone the corresponding Github repositories in the created ``DeepPhysX`` directory, then install + packages manually. .. code-block:: bash - $ python3 dev.py set + # Clone compatibility layers + $ git clone https://github.com/mimesis-inria/DeepPhysX.Sofa.git Sofa + $ git clone https://github.com/mimesis-inria/DeepPhysX.Torch.git Torch -Then, you should be able to run: + # Install packages manually + $ cd Core ; pip3 install . + $ cd ../Sofa ; pip3 install . + $ cd ../Torch ; pip3 install . + +Finally, you should be able to run: .. code-block:: bash # If installed with pip - $ pip show DeepPhysX + $ pip3 show DeepPhysX + $ pip3 show DeepPhysX.Sofa + $ pip3 show DeepPhysX.Torch .. code-block:: python From 419d93e6ffe45535b3aa784c4186206560a0ed3f Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 25 Nov 2022 17:11:19 +0100 Subject: [PATCH 52/61] Specify dependencies versions. --- setup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 7a4c1bc5..c481050e 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def get_SSD(): try: import SSD except ModuleNotFoundError: - return ['SimulationSimpleDatabase'] + return ['SimulationSimpleDatabase >= 22.12'] return [] @@ -44,5 +44,8 @@ def get_SSD(): packages=packages, package_dir=packages_dir, namespace_packages=[PROJECT], - install_requires=get_SSD() + ['numpy', 'tensorboard', 'tensorboardX', 'pyDataverse'], + install_requires=get_SSD() + ['numpy >1.23.5', + 'tensorboard >= 2.10.0', + 'tensorboardX >= 2.5.1', + 'pyDataverse >= 0.3.1'], entry_points={'console_scripts': ['DPX=DeepPhysX.cli:execute_cli']}) From fd1869c81588085be56c861f11007b336d12c499 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 25 Nov 2022 17:44:50 +0100 Subject: [PATCH 53/61] Remove tensorboardX dependency and update torch version. --- requirements.txt | 1 - setup.py | 3 ++- src/Core/Manager/StatsManager.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 859b901f..8b68c135 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ numpy SimulationSimpleDatabase tensorboard -tensorboardX pyDataverse torch \ No newline at end of file diff --git a/setup.py b/setup.py index c481050e..41dba749 100644 --- a/setup.py +++ b/setup.py @@ -47,5 +47,6 @@ def get_SSD(): install_requires=get_SSD() + ['numpy >1.23.5', 'tensorboard >= 2.10.0', 'tensorboardX >= 2.5.1', - 'pyDataverse >= 0.3.1'], + 'pyDataverse >= 0.3.1' + 'torch >= 1.13.0'], entry_points={'console_scripts': ['DPX=DeepPhysX.cli:execute_cli']}) diff --git a/src/Core/Manager/StatsManager.py b/src/Core/Manager/StatsManager.py index 4ee80d3b..060c986f 100644 --- a/src/Core/Manager/StatsManager.py +++ b/src/Core/Manager/StatsManager.py @@ -1,5 +1,5 @@ from typing import Dict, Any, Iterable, Optional -from tensorboardX import SummaryWriter +from torch.utils.tensorboard import SummaryWriter from tensorboard import program from webbrowser import open as w_open from numpy import full, inf, array, ndarray, append, concatenate From 4173c018ecd89f2159bda1d3e35e98a41e06ea0d Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 28 Nov 2022 14:32:48 +0100 Subject: [PATCH 54/61] Center plane in beam demo. --- examples/demos/Beam/FC/Environment/BeamInteractive.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/examples/demos/Beam/FC/Environment/BeamInteractive.py b/examples/demos/Beam/FC/Environment/BeamInteractive.py index 3c98d7d9..14f9a069 100644 --- a/examples/demos/Beam/FC/Environment/BeamInteractive.py +++ b/examples/demos/Beam/FC/Environment/BeamInteractive.py @@ -65,6 +65,7 @@ def create(self): # Load the meshes and the sparse grid self.mesh = Mesh(p_model.grid, c='o').lineWidth(0.1).lighting('ambient') + self.mesh.shift(0, -7.5, -7.5) self.mesh_init = self.mesh.clone().points() # Get the surface points @@ -109,19 +110,12 @@ def create(self): (pts[:, other[1]] >= o1_min) & (pts[:, other[1]] <= o1_max)) self.areas.append(np.array(list(set(zone[0].tolist()).intersection(set(list(self.surface)))))) - # Define fixed plane - mesh_x = self.mesh.points()[:, 0] - fixed = np.where(mesh_x <= np.min(mesh_x) + 0.05 * (np.max(mesh_x) - np.min(mesh_x))) - plane_origin = [np.min(mesh_x), - np.mean(self.mesh.points()[:, 1][fixed]), - np.mean(self.mesh.points()[:, 2][fixed])+10] - # Create plotter self.plotter = Plotter(title='Interactive Beam', N=1, interactive=True, offscreen=False, bg2='lightgray') self.plotter.render() self.plotter.add(*self.spheres) self.plotter.add(self.mesh) - self.plotter.add(Plane(pos=plane_origin, normal=[1, 0, 0], s=(20, 20), c='darkred', alpha=0.2)) + self.plotter.add(Plane(pos=[0., 0., 0.], normal=[1, 0, 0], s=(20, 20), c='darkred', alpha=0.2)) self.plotter.add(Text2D("Press 'Alt' to interact with the object.\n" "Left click to select a sphere.\n" "Right click to unselect a sphere.", s=0.75)) From e314d2b7669b92b478a0f7ba5c72185d4e6e5850 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 28 Nov 2022 16:00:07 +0100 Subject: [PATCH 55/61] Smal fix with DataTransormation types. --- src/Core/Network/BaseTransformation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Network/BaseTransformation.py b/src/Core/Network/BaseTransformation.py index 522bfc58..f2956770 100644 --- a/src/Core/Network/BaseTransformation.py +++ b/src/Core/Network/BaseTransformation.py @@ -20,7 +20,7 @@ def __init__(self, config: namedtuple): def check_type(func: Callable[[Any, Any], Any]): def inner(self, *args): - for data in args: + for data in [a for a in args if a is not None]: for value in data.values(): if value is not None and type(value) != self.data_type: raise TypeError(f"[{self.name}] Wrong data type: {self.data_type} required, get {type(value)}") From 2f489bdaf7f660ad38d31ccd250b87a817c950b9 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 28 Nov 2022 16:48:32 +0100 Subject: [PATCH 56/61] Fix install dependencies error. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 41dba749..72acda40 100644 --- a/setup.py +++ b/setup.py @@ -44,9 +44,9 @@ def get_SSD(): packages=packages, package_dir=packages_dir, namespace_packages=[PROJECT], - install_requires=get_SSD() + ['numpy >1.23.5', + install_requires=get_SSD() + ['numpy >= 1.23.5', 'tensorboard >= 2.10.0', 'tensorboardX >= 2.5.1', - 'pyDataverse >= 0.3.1' + 'pyDataverse >= 0.3.1', 'torch >= 1.13.0'], entry_points={'console_scripts': ['DPX=DeepPhysX.cli:execute_cli']}) From 37c3816cc0dad52c4aca91c959a165ce34a3f79a Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 28 Nov 2022 17:04:02 +0100 Subject: [PATCH 57/61] Fix cli running path in local pip installation mode. --- src/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cli.py b/src/cli.py index 6ef4f533..44c21431 100644 --- a/src/cli.py +++ b/src/cli.py @@ -18,7 +18,7 @@ def get_sources(): import DeepPhysX site_packages = dirname(DeepPhysX.__path__[0]) - metadata_repo = [f for f in listdir(site_packages) if 'DeepPhysX' in f and + metadata_repo = [f for f in listdir(site_packages) if f.split('-')[0] == 'DeepPhysX' and ('.dist-info' in f or '.egg-info' in f)] if len(metadata_repo) == 0: quit(print("The project does not seem to be properly installed. Try to re-install using 'pip'.")) @@ -136,15 +136,17 @@ def execute_cli(): import DeepPhysX.Core source_dir = readlink(DeepPhysX.Core.__path__[0]) examples_dir = join(dirname(dirname(source_dir)), 'examples') + repo = join(*examples[example].split('/')[1:-1]) elif (source_dir := get_sources()) is not None: examples_dir = join(source_dir, 'examples') + repo = join(*examples[example].split('/')[1:-1]) else: if not isdir(join(getcwd(), 'DPX_examples')): print(f"The directory '{join(getcwd(), 'DPX_examples')}' does not exists.") copy_examples_dir() examples_dir = join(getcwd(), 'DPX_examples') + repo = join(*examples[example].split('/')[:-1]) # Run the example - repo = join(*examples[example].split('/')[:-1]) if is_pip_installed() else join(*examples[example].split('/')[1:-1]) script = examples[example].split('/')[-1] chdir(join(examples_dir, repo)) run([f'{executable}', f'{script}'], cwd=join(examples_dir, repo)) From 3800e1c93e09198c89e8c8d081bcab80f7867ec6 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 29 Nov 2022 17:44:31 +0100 Subject: [PATCH 58/61] Update the documentation. --- docs/source/api/core.rst | 21 +- docs/source/api/core/asyncsocket.rst | 9 - docs/source/api/core/dataset.rst | 16 +- docs/source/api/core/environment.rst | 3 - docs/source/api/core/factories.rst | 59 ------ docs/source/api/core/manager.rst | 24 +-- docs/source/api/core/network.rst | 2 +- docs/source/api/core/pipeline.rst | 20 +- docs/source/api/core/visualizer.rst | 11 +- docs/source/component/dataset.rst | 47 ++--- docs/source/component/environment.rst | 161 ++++----------- docs/source/component/network.rst | 22 ++- docs/source/component/pipelines.rst | 77 ++++---- docs/source/component/visualizer.rst | 272 +------------------------- docs/source/conf.py | 3 +- 15 files changed, 163 insertions(+), 584 deletions(-) delete mode 100644 docs/source/api/core/factories.rst diff --git a/docs/source/api/core.rst b/docs/source/api/core.rst index 51945bd6..c7dec455 100644 --- a/docs/source/api/core.rst +++ b/docs/source/api/core.rst @@ -11,25 +11,22 @@ CORE - :doc:`core/environment` - :doc:`core/manager` - * - | :ref:`asyncsocket.abstractenvironment` - | :ref:`asyncsocket.bytesconverter` + * - | :ref:`asyncsocket.bytesconverter` | :ref:`asyncsocket.tcpipclient` | :ref:`asyncsocket.tcpipobject` | :ref:`asyncsocket.tcpipserver` - - | :ref:`dataset.basedataset` - | :ref:`dataset.basedatasetconfig` + - | :ref:`database.databasehandler` + | :ref:`database.basedatabaseconfig` - | :ref:`environment.baseenvironment` | :ref:`environment.baseenvironmentconfig` - | :ref:`manager.datamanager` - | :ref:`manager.datasetmanager` + | :ref:`manager.databasemanager` | :ref:`manager.environmentmanager` - | :ref:`manager.manager` | :ref:`manager.networkmanager` | :ref:`manager.statsmanager` - | :ref:`manager.visualizermanager` .. list-table:: @@ -46,15 +43,13 @@ CORE | :ref:`network.baseoptimization` | :ref:`network.datatransformation` - - | :ref:`pipelines.basedatagenerator` + - | :ref:`pipelines.basedatageneration` | :ref:`pipelines.basepipeline` - | :ref:`pipelines.baserunner` - | :ref:`pipelines.basetrainer` + | :ref:`pipelines.baseprediction` + | :ref:`pipelines.basetraining` - - | :ref:`visualizer.vedoobjects` + - | :ref:`visualizer.vedofactory` | :ref:`visualizer.vedovisualizer` - | - | :ref:`Visualizer.Factories ` .. toctree:: diff --git a/docs/source/api/core/asyncsocket.rst b/docs/source/api/core/asyncsocket.rst index bf4d54e8..b097d540 100644 --- a/docs/source/api/core/asyncsocket.rst +++ b/docs/source/api/core/asyncsocket.rst @@ -1,14 +1,6 @@ AsyncSocket =========== -.. _asyncsocket.abstractenvironment: - -AbstractEnvironment -___________________ - -.. autoclass:: AbstractEnvironment.AbstractEnvironment - :members: - .. _asyncsocket.bytesconverter: BytesConverter @@ -23,7 +15,6 @@ TcpIpClient ___________ Bases: -:py:class:`AbstractEnvironment.AbstractEnvironment` :py:class:`TcpIpObject.TcpIpObject` .. autoclass:: TcpIpClient.TcpIpClient diff --git a/docs/source/api/core/dataset.rst b/docs/source/api/core/dataset.rst index 69d9eb87..469d2f7e 100644 --- a/docs/source/api/core/dataset.rst +++ b/docs/source/api/core/dataset.rst @@ -1,18 +1,18 @@ Dataset ======= -.. _dataset.basedataset: +.. _database.databasehandler: -BaseDataset ------------ +DatabaseHandler +--------------- -.. autoclass:: BaseDataset.BaseDataset +.. autoclass:: DatabaseHandler.DatabaseHandler :members: -.. _dataset.basedatasetconfig: +.. _database.basedatabaseconfig: -BaseDatasetConfig ------------------ +BaseDatabaseConfig +------------------ -.. autoclass:: BaseDatasetConfig.BaseDatasetConfig +.. autoclass:: BaseDatabaseConfig.BaseDatabaseConfig :members: diff --git a/docs/source/api/core/environment.rst b/docs/source/api/core/environment.rst index f14829bc..53d1fdc4 100644 --- a/docs/source/api/core/environment.rst +++ b/docs/source/api/core/environment.rst @@ -6,9 +6,6 @@ Environment BaseEnvironment --------------- -Bases: -:py:class:`TcpIpClient.TcpIpClient` - .. autoclass:: BaseEnvironment.BaseEnvironment :members: diff --git a/docs/source/api/core/factories.rst b/docs/source/api/core/factories.rst deleted file mode 100644 index 9c7c8681..00000000 --- a/docs/source/api/core/factories.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. _factories: - -Factories -========= - -ArrowsFactory -------------- - -.. autoclass:: VedoObjectFactories.ArrowsFactory.ArrowsFactory - :members: - - -BaseObjectFactory ------------------ - -.. autoclass:: VedoObjectFactories.BaseObjectFactory.BaseObjectFactory - :members: - - -GlyphFactory ------------- - -.. autoclass:: VedoObjectFactories.GlyphFactory.GlyphFactory - :members: - - -MarkerFactory -------------- - -.. autoclass:: VedoObjectFactories.MarkerFactory.MarkerFactory - :members: - - -MeshFactory ------------ - -.. autoclass:: VedoObjectFactories.MeshFactory.MeshFactory - :members: - - -PointsFactory -------------- - -.. autoclass:: VedoObjectFactories.PointsFactory.PointsFactory - :members: - - -VedoObjectFactory ------------------ - -.. autoclass:: VedoObjectFactories.VedoObjectFactory.VedoObjectFactory - :members: - - -WindowFactory -------------- - -.. autoclass:: VedoObjectFactories.WindowFactory.WindowFactory - :members: diff --git a/docs/source/api/core/manager.rst b/docs/source/api/core/manager.rst index 379febbc..ce177ab3 100644 --- a/docs/source/api/core/manager.rst +++ b/docs/source/api/core/manager.rst @@ -9,12 +9,12 @@ DataManager .. autoclass:: DataManager.DataManager :members: -.. _manager.datasetmanager: +.. _manager.databasemanager: -DatasetManager --------------- +DatabaseManager +--------------- -.. autoclass:: DatasetManager.DatasetManager +.. autoclass:: DatabaseManager.DatabaseManager :members: .. _manager.environmentmanager: @@ -25,14 +25,6 @@ EnvironmentManager .. autoclass:: EnvironmentManager.EnvironmentManager :members: -.. _manager.manager: - -Manager -------- - -.. autoclass:: Manager.Manager - :members: - .. _manager.networkmanager: NetworkManager @@ -48,11 +40,3 @@ StatsManager .. autoclass:: StatsManager.StatsManager :members: - -.. _manager.visualizermanager: - -VisualizerManager ------------------ - -.. autoclass:: VisualizerManager.VisualizerManager - :members: diff --git a/docs/source/api/core/network.rst b/docs/source/api/core/network.rst index a25b0b0f..7fb22845 100644 --- a/docs/source/api/core/network.rst +++ b/docs/source/api/core/network.rst @@ -30,5 +30,5 @@ BaseOptimization DataTransformation ------------------ -.. autoclass:: DataTransformation.DataTransformation +.. autoclass:: BaseTransformation.BaseTransformation :members: diff --git a/docs/source/api/core/pipeline.rst b/docs/source/api/core/pipeline.rst index 39f44301..05acebc3 100644 --- a/docs/source/api/core/pipeline.rst +++ b/docs/source/api/core/pipeline.rst @@ -1,15 +1,15 @@ Pipelines ========= -.. _pipelines.basedatagenerator: +.. _pipelines.basedatageneration: -BaseDataGenerator ------------------ +BaseDataGeneration +------------------ Bases: :py:class:`BasePipeline.BasePipeline` -.. autoclass:: BaseDataGenerator.BaseDataGenerator +.. autoclass:: BaseDataGeneration.BaseDataGeneration :members: .. _pipelines.basepipeline: @@ -20,18 +20,18 @@ BasePipeline .. autoclass:: BasePipeline.BasePipeline :members: -.. _pipelines.baserunner: +.. _pipelines.baseprediction: -BaseRunner ----------- +BasePrediction +-------------- Bases: :py:class:`BasePipeline.BasePipeline` -.. autoclass:: BaseRunner.BaseRunner +.. autoclass:: BasePrediction.BasePrediction :members: -.. _pipelines.basetrainer: +.. _pipelines.basetraining: BaseTrainer ----------- @@ -39,5 +39,5 @@ BaseTrainer Bases: :py:class:`BasePipeline.BasePipeline` -.. autoclass:: BaseTrainer.BaseTrainer +.. autoclass:: BaseTraining.BaseTraining :members: diff --git a/docs/source/api/core/visualizer.rst b/docs/source/api/core/visualizer.rst index 14e87172..df95549a 100644 --- a/docs/source/api/core/visualizer.rst +++ b/docs/source/api/core/visualizer.rst @@ -1,17 +1,12 @@ Visualizer ========== -.. toctree:: - :hidden: +.. _visualizer.vedofactory: - factories.rst - -.. _visualizer.vedoobjects: - -VedoObjects +VedoFactory ----------- -.. autoclass:: VedoObjects.VedoObjects +.. autoclass:: VedoFactory.VedoFactory :members: .. _visualizer.vedovisualizer: diff --git a/docs/source/component/dataset.rst b/docs/source/component/dataset.rst index ff5f6e9c..a751ef41 100644 --- a/docs/source/component/dataset.rst +++ b/docs/source/component/dataset.rst @@ -6,9 +6,9 @@ Dedicated code in :guilabel:`Core/Dataset` module. Behavior -------- -**DeepPhysX** comes with its own *Dataset* management system. -The synthetic data produced in *Environments* is stored as Numpy arrays in a dedicated repository named ``dataset`` in -the training session. +**DeepPhysX** comes with its own *Dataset* management system, using the features from the :SSD:`SSD <>` library. +The synthetic data produced in *Environments* is stored on a *Database* that is in a dedicated repository named +``dataset`` in the training session. Data is stored as partitions: these partitions correspond to the different *Dataset* modes (training data, test data, prediction data) and can be multiple for each data field in order not to exceed the maximum size of the current amount of loaded data. @@ -24,16 +24,14 @@ Each partition will have a unique name: ``___ When adding a batch to the *Dataset*, a new partition is created for each data field if the current *Dataset* size exceeds the threshold. The batch is then appended to the *Dataset* for each data field. -Default *Dataset* fields are inputs and outputs, but users can add any data to the *Dataset* from *Environment* using -``additional_in_dataset`` or ``additional_out_dataset`` (see :ref:`dedicated section `). +Default *Dataset* fields are inputs and outputs, but users can define any data field from *Environment* +(see :ref:`dedicated section `). Each field must always be filled at each batch. -A ``dataset.json`` file gathers information about the produced dataset. +A ``dataset.json`` file gathers information about the produced dataset and normalization coefficients if the +normalization is applied. -When loading data from an existing *Dataset*, the repository is loaded first. -If there is a single partition for each field, only those partitions are loaded into the *Dataset*. -Otherwise, a proportional part of each partition will be loaded each time. -Batches of data are accessed in read order (random or not) until the read cursor reaches the end, triggering either the -reloading of a single partition or the loading of the subsequent slices of partitions. +When loading data from an existing *Dataset*, the partitions are loaded and can be accessed randomly or not among the +whole set of partitions. Configuration @@ -49,33 +47,30 @@ Here is a description of attributes related to *Dataset* configuration. :width: 100% :widths: 15 85 - * - ``dataset_class`` - - *Dataset* class from which an instance will be created (*BaseDataset* by default). - - * - ``dataset_dir`` + * - ``existing_dir`` - Path to an existing *Dataset* repository if this repository needs to be loaded or completed. - * - ``partition_size`` + * - ``max_file_size`` - Maximum size (in Gb) of the total *Dataset* object. - * - ``shuffle_dataset`` - - Specify if the loading order is random or not (True by default). - - * - ``use_mode`` + * - ``mode`` - Specify the *Dataset* mode between "Training", "Validation" and "Running". - * - ``normalize_data`` + * - ``normalize`` - If True, normalization parameters are computed from training data and applied to any loaded data. + * - ``shuffle`` + - Specify if the loading order is random or not (True by default). + .. highlight:: python See following example:: # Import DatasetConfig - from DeepPhysX_Core.Dataset.BaseDatasetConfig import BaseDatasetConfig + from DeepPhysX_Core.Database.BaseDatabaseConfig import BaseDatabaseConfig # Create the config - dataset_config = BaseDatasetConfig(partition_size=1, - shuffle_dataset=True, - use_mode='Training', - normalize_data=True) + database_config = BaseDatabaseConfig(max_file_size=1, + shuffle=True, + mode='Training', + normalize=True) diff --git a/docs/source/component/environment.rst b/docs/source/component/environment.rst index 688d9f77..e7912fb1 100644 --- a/docs/source/component/environment.rst +++ b/docs/source/component/environment.rst @@ -26,6 +26,11 @@ implement its *Environment* regardless of this dependence. This method is automatically called when the *Environment* component is created. + * - ``init_database`` + - Define the fields of the training dataset. + + This method is automatically called when the *Environment* component is created. + * - ``step`` - Describe the transitions between simulation states. @@ -47,17 +52,12 @@ implement its *Environment* regardless of this dependence. This method is automatically called when the *Environment* component is created, right after the create method. - * - ``recv_parameters`` - - In the case of using multiple *Environments* connected to one *TcpIpServer*, each of them can be parameterize - differently. - - These parameters can be set in the *EnvironmentConfig* and are then automatically sent to *Environments* right - after their initialization. + * - ``init_visualization`` + - Define the visualization objects to send to the *Visualizer*. - * - ``send_parameters`` - - On the other side, one might want to send back data from *Environments* to the *EnvironmentConfig*. + A *Factory* is available in the *Environment* to easily design visualization objects. - If the method is implemented, parameters will be sent right after the previous receipt. + This method is automatically called when the *Environment* component is created, right after the init. * - ``check_sample`` - Each step will be followed by a sample checking. @@ -83,32 +83,22 @@ implement its *Environment* regardless of this dependence. :width: 100% :widths: 15 85 + * - ``define_training_data`` + - This method must be used in ``init_database`` to specify the training data fields names and types. + + * - ``define_additional_data`` + - This method must be used in ``init_database`` to specify the additional data fields names and types. + * - ``set_training_data`` - This method defines which data will be considered as training data (data sent to the *NetworkManager* to feed the *Network*). - * - ``set_loss_data`` - - This method allows adding specific data to compute the loss function with numeric values from the simulation. - * - ``set_additional_dataset`` - In addition to the training data, some additional data can be sent directly to the dataset to replay some simulation states. This method adds a new field to the *Dataset* and must be then called at each step. - * - ``set_dataset_sample`` - - This method is already implemented and must not be overwritten by the user. - - When loading existing data, the *DataManager* might need to send *Dataset* samples to the *Environment* to - produce training data. - - These samples are set in the variables ``sample_in`` & ``sample_out``. - - If *Dataset* has additional data fields, these additional fields are set in ``additional_inputs`` & - ``additional_outputs``. - - Each sample can then be processed in the step function. - | **Requests** | The *Environment* is also able to perform some requests. These requests are sent either directly to the *EnvironmentManager* or through a *TcpIpServer*. @@ -117,6 +107,12 @@ implement its *Environment* regardless of this dependence. :width: 100% :widths: 15 85 + * - ``save_parameters`` + - Save a set of parameters in the Database. + + * - ``load_parameters`` + - Load a set of parameters from the Database. + * - ``get_prediction`` - Depending on the application, a prediction of the *Network* can be useful before the end of a step. @@ -125,23 +121,11 @@ implement its *Environment* regardless of this dependence. The training data must obviously be set before triggering this request. - * - ``send_visualization`` - - The framework comes with an integrated *Visualizer* tool to render some components of the simulation. - - Parts of the simulation to render must be defined when creating or when initializing the *Environment*. - - An *Environment* has a visualization factory to easily create visualization data from templates with - ``addObject`` method. - - User only has to set the object type and to fill the required fields to init this type of object in the - *Visualizer* (see :ref:`dedicated section `). - * - ``update_visualization`` - If a *Visualizer* was created, it must be manually updated at each step by sending the updated state of the simulation. - The factory allows to easily create updated visualization data from objects ids with ``updateObject_dict`` - method. + The factory allows to easily create updated visualization data from objects ids with ``update_obj`` methods. User only has to fill the object id (same as initialization order) and the required fields (detailed for each object in :ref:`dedicated section `). @@ -151,8 +135,8 @@ Configuration ------------- Using an *Environment* in one of the **DeepPhysX** *Pipeline* always requires an *EnvironmentConfig*. -This component’s role is both to bring together all the options for configuring an *Environment* and to either create -an instance of a single *Environment* or launch a *TcpIpServer* with several *TcpIpClients*. +The role of this component is both to bring together all the options for configuring an *Environment* and to create an +instance of a single *Environment* or launch a *TcpIpServer* with several *TcpIpClients*. In the first case, the single *Environment* will simply be created within the ``create_environment`` method, while in the other case, the ``create_server`` method will simply create and launch a *TcpIpServer* and then start several subprocesses, each using the ``launcherBaseEnvironment.py`` script to create and launch an *Environment* as a *Client*. @@ -169,19 +153,19 @@ subprocesses, each using the ``launcherBaseEnvironment.py`` script to create and The attribute requires the class and not an instance, as it will be automatically created as explained above. - * - ``visualizer`` - - A visualization tool is provided, which renders the specified parts of each *Environment*. - - If no *Visualizer* is provided, the pipeline will run without any render window. - * - ``simulations_per_step`` - The number of iterations to compute in the *Environment* at each time step. An *Environment* will compute one iteration by default. - * - ``use_prediction_in_environment`` - - Each *Network* prediction will be automatically applied in the *Environment* if this flag is set to True - (set to False by default). + * - ``visualizer`` + - A visualization tool is provided, which renders the specified parts of each *Environment*. + + If no *Visualizer* is provided, the pipeline will run without any render window. + + * - ``env_kwargs`` + - *Environments* can receive additional parameters within this dictionary if they need to be parameterized + differently. | **Data parameters** | Here is a description of attributes related to sample generation. @@ -190,7 +174,7 @@ subprocesses, each using the ``launcherBaseEnvironment.py`` script to create and :width: 100% :widths: 15 85 - * - ``always_create_data`` + * - ``always_produce`` - This flag is useful for the training *Pipeline*. If False (by default), the *DataManager* requests batches to the *Environment* during the first epoch and @@ -198,26 +182,13 @@ subprocesses, each using the ``launcherBaseEnvironment.py`` script to create and If set to True, the *DataManager* requests new batches to the *Environment* during the whole training session. - * - ``screenshot_sample_rate`` - - This option is only available if a *Visualizer* is defined. - - In addition to *Dataset* partitions, samples can also be saved as screenshots so representative ones can be - easily found. - - A screenshot of the viewer will be taken every x samples (set to 0 by default). + * - ``load_samples`` + - If True, the dataset will always be used in the environment. - * - ``record_wrong_samples`` - - By default, only the good samples are stored in the *Dataset* (sorted by check_sample, see the section above). + * - ``only_first_epoch`` + - If True, data will always be created from *Environment*. - If this flag is set to True, the wrong samples will also be saved to dedicated partitions. - - * - ``max_wrong_samples_per_step`` - - If an *Environment* produces too many wrong samples, it may be configured incorrectly. - - To avoid an unnecessary extended data generation, a threshold can be set so that the session can be stopped - early. - - This happens when too many wrong samples are produced to fill a single batch. + If False, data will be created from the *Environment* during the first epoch and then re-used from the *Dataset*. | **TcpIP parameters** | Here is a description of attributes related to the *Client* configuration. @@ -238,27 +209,11 @@ subprocesses, each using the ``launcherBaseEnvironment.py`` script to create and The default value is “localhost” to host the *Server* and *Clients* locally. - * - ``port`` - - TCP port’s number through which *TcpIpObjects* will communicate (10000 by default). - - * - ``environment_file`` - - When launching an *Environment* as a *Client*, the *EnvironmentConfig* starts a subprocess involving that - *Environment*. - - To do this, the launcher will need the script path in which the *Environment* is defined. - - This script is in most cases automatically detected, but users may need to enter the path to their python file. - * - ``number_of_thread`` - The number of *Environments* to launch simultaneously if the flag ``as_tcp_ip_client`` is True. - * - ``max_client_connection`` - - The maximum number of *Client* connections allowed by a *Server*. - - * - ``param_dict`` - - *Environments* can receive additional parameters if they need to be parameterized differently. - - These parameters are sent in the form of dictionaries by the *Server* when creating the *Environment*. + * - ``port`` + - TCP port’s number through which *TcpIpObjects* will communicate (10000 by default). .. highlight:: python @@ -309,39 +264,3 @@ The data is sent as a custom bytes message converted with a *BytesConverter*, wh NumPy arrays. On top of these low level data exchange methods are built higher level protocols to send labeled data, labeled dictionaries and commands. - -A list of available commands is defined. *TcpIpServer* and *TcpIpClients* have then their own action implementations -to perform when receiving a command: - -* A *TcpIpClient* defines the following actions to perform on commands: - - .. list-table:: - :width: 90% - :widths: 15 85 - - * - ``exit`` - - Set the closing flag to True to terminate the communication loop. - - * - ``prediction`` - - Receive the prediction sent by the *TcpIpServer* and apply it in the *Environment*. - - * - ``sample`` - - When using data from *Dataset*, the sample is received and defined in the *Environment* on this command - - * - ``step`` - - Trigger a simulation step to produce data. - - Data should be sent to the *TcpIpServer* when the produced sample is identified as usable by sample - checking. - -* A *TcpIpServer* defines the following actions to perform on commands: - - .. list-table:: - :width: 90% - :widths: 15 85 - - * - ``prediction`` - - Receive data to feed the *Network*, then send back the prediction to the same *TcpIpClient*. - - * - ``visualization`` - - Receive initial or updated visualization data, then call the *Visualizer* update. diff --git a/docs/source/component/network.rst b/docs/source/component/network.rst index 114e32f5..11ef7c95 100644 --- a/docs/source/component/network.rst +++ b/docs/source/component/network.rst @@ -23,6 +23,9 @@ Even if a *BaseNetwork* is not usable, any **DeepPhysX** AI package provides the * - ``__init__`` - Define the *Network* architecture. + * - ``predict`` + - Define the data fields used to compute a prediction. + * - ``forward`` - Define the forward pass of the *Network*. @@ -57,16 +60,23 @@ Even if a *BaseNetwork* is not usable, any **DeepPhysX** AI package provides the * - ``nb_parameters`` - Return the number of parameters in the architecture. - * - ``transform_from_numpy`` + * - ``numpy_to_tensor`` - Convert a Numpy array to a tensor with the compatible type. Received data from Core will always be Numpy arrays. - * - ``transform_to_numpy`` + * - ``tensor_to_numpy`` - Convert a tensor to a Numpy array. Data provided to Core must be converted to Numpy arrays. +| **Data fields** +| The training data is given to the *Network* as a dictionary of tensors. + By default, the data used for inference has a single "input" field, the data produced by the *Network* has a single + "prediction" field, the data used for optimization has a single "ground_truth" field. + If another field should be defined, the *Network* class should specify them in the corresponding ``self.net_fields``, + ``self.pred_fields`` & ``self.opt_fields`` variables. + .. _network-optimization: Optimization Implementation @@ -126,14 +136,14 @@ Users are then free to define their own tensor transformations with the followin :widths: 15 85 * - ``transform_before_prediction`` - - Apply a tensor transformation to the input data before *Network* forward pass. + - Apply a tensor transformation to the *Network* data (before a prediction). * - ``transform_before_loss`` - - Apply a tensor transformation to the ground truth data and / or the *Network* output before the loss - computation. + - Apply a tensor transformation to the ground truth data and / or the prediction data (after the prediction, + before the loss computation). * - ``transform_before_apply`` - - Apply a tensor transformation to the *Network* prediction before sending it to the *Environment*. + - Apply a tensor transformation to the prediction data (before sending it to the *Environment*). Configurations diff --git a/docs/source/component/pipelines.rst b/docs/source/component/pipelines.rst index f61e3099..c84f34ea 100644 --- a/docs/source/component/pipelines.rst +++ b/docs/source/component/pipelines.rst @@ -8,9 +8,9 @@ General Policy Several *Pipelines* are available with **DeepPhysX**, allowing the user to: - * **Generate** synthetic data from simulations → ``DataGenerator`` - * **Train** artificial neural networks with synthetic data → ``Trainer`` - * Use the **predictions** of trained networks inside a simulation → ``Runner`` + * **Generate** synthetic data from simulations → ``DataGeneration`` + * **Train** artificial neural networks with synthetic data → ``Training`` + * Use the **predictions** of trained networks inside a simulation → ``Prediction`` A *Pipeline* is always associated with a :ref:`working session `, whether it already exists or whether it is automatically created when the *Pipeline* is launched. @@ -27,8 +27,8 @@ Once these *Configurations* are defined, the *Pipeline* can be created and launc Pipeline - Data generation -------------------------- -The *DataGenerator* will only involve an *Environment* and a *Dataset*, so this *Pipeline* requires the corresponding -*Configurations* +The *DataGeneration* will only involve an *Environment* and a *Dataset*, so this *Pipeline* requires the corresponding +*Configurations*. As the purpose of this *Pipeline* is only to create synthetic data, the working session will always be created at the same time. @@ -48,8 +48,8 @@ Furthermore, users have to define which data to save and how much : See following example:: - # Import BaseDataGenerator and Config objects - from DeepPhysX_Core.Pipelines.BaseDataGenerator import BaseDataGenerator + # Import BaseDataGeneration and Config objects + from DeepPhysX.Core.Pipelines.BaseDataGeneration import BaseDataGeneration ... # Define configs @@ -57,11 +57,12 @@ See following example:: environment_config = ... # Create the pipeline - data_generator = BaseDataGenerator(session_name='sessions/my_data_generation', - dataset_config=dataset_config, - environment_config=environment_config, - nb_batches=500, - batch_size=16) + data_generator = BaseDataGeneration(session_dir='sessions', + session_name='my_data_generation', + dataset_config=dataset_config, + environment_config=environment_config, + batch_nb=500, + batch_size=16) # Launch the pipeline data_generator.execute() @@ -70,12 +71,12 @@ See following example:: Pipeline - Training ------------------- -The *Trainer* can involve an *Environment*, a *Dataset* and a *Network*, so this *Pipeline* might require the +The *Training* can involve an *Environment*, a *Dataset* and a *Network*, so this *Pipeline* might require the corresponding *Configurations*. There are several ways to use this pipeline: **Training a Network from scratch** - To train a *Network* from scratch, the *Trainer* requires the whole set of *Configurations*. + To train a *Network* from scratch, the *Training* requires the whole set of *Configurations*. A new working session will be created, whose name can be set as a parameter. **Training a Network with an existing Dataset** @@ -88,7 +89,7 @@ There are several ways to use this pipeline: **Training a Network from an existing Network state** Training from an existing *Network* state can be done both in an existing session or in a new session. - If you want to work in the same session, you have to configure the *Trainer* to do so, otherwise a new working + If you want to work in the same session, you have to configure the *Training* to do so, otherwise a new working session will be automatically created. In the same session, a new set of trained parameters will be added in the ``network`` repository, either trained with data from an external *Dataset* (whose path must be provided) or with data from the *Environment* (whose @@ -109,8 +110,8 @@ The last parameters to set in the *Trainer* are: See following example:: - # Import BaseTrainer and Config objects - from DeepPhysX_Core.Pipelines.BaseTrainer import BaseTrainer + # Import BaseTraining and Config objects + from DeepPhysX.Core.Pipelines.BaseTraining import BaseTraining ... # Define configs @@ -119,13 +120,14 @@ See following example:: network_config = ... # Create the pipeline - trainer = BaseTrainer(session_name='sessions/my_training', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_epochs=100, - nb_batches=500, - batch_size=16) + trainer = BaseTraining(session_dir='sessions', + session_name='my_training', + dataset_config=dataset_config, + environment_config=env_config, + network_config=net_config, + epoch_nb=100, + batch_nb=500, + batch_size=16) # Launch the pipeline trainer.execute() @@ -134,25 +136,25 @@ See following example:: Pipeline - Prediction --------------------- -The *Runner* always requires a *Network* to compute predictions and an *Environment* to apply them, so this *Pipeline* -will always require the corresponding *Configurations*. +The *Prediction* always requires a *Network* to compute predictions and an *Environment* to apply them, so this +*Pipeline* will always require the corresponding *Configurations*. -This *Pipeline* always works with an existing working session, no new sessions can be created within a *Runner*. +This *Pipeline* always works with an existing working session, no new sessions can be created within a *Prediction*. The path to the session is therefore required, assuming that it contains a trained *Network*. -The *Runner* can either run a specified **number of steps** or run an **infinite loop**. +The *Prediction* can either run a specified **number of steps** or run an **infinite loop**. A *Dataset* configuration can be provided. -In this case, the *Runner* can record input or / and output data. +In this case, the *Prediction* can record prediction data. Each sample computed during the prediction phase will then be added to the *Dataset* in dedicated partitions. -With a *Dataset*, the *Runner* can also load its data to **replay** stored samples. +With a *Dataset*, the *Prediction* can also load its data to **replay** stored samples. .. highlight:: python See following example:: - # Import BaseRunner and Config objects - from DeepPhysX_Core.Pipelines.BaseRunner import BaseTrainer + # Import BasePrediction and Config objects + from DeepPhysX.Core.Pipelines.BasePrediction import BasePrediction ... # Define configs @@ -161,11 +163,12 @@ See following example:: network_config = ... # Create the pipeline - runner = BaseRunner(session_dir='sessions/my_training', - dataset_config=dataset_config, - environment_config=env_config, - network_config=net_config, - nb_steps=-1) + runner = BasePrediction(session_dir='sessions', + session_name='my_training', + dataset_config=dataset_config, + environment_config=env_config, + network_config=net_config, + step_nb=-1) # Launch the pipeline runner.execute() diff --git a/docs/source/component/visualizer.rst b/docs/source/component/visualizer.rst index b30d7234..577ac3c9 100644 --- a/docs/source/component/visualizer.rst +++ b/docs/source/component/visualizer.rst @@ -9,7 +9,7 @@ How to use ---------- **DeepPhysX** provides a visualization tool written with :Vedo:`Vedo <>` (a Python library based on Numpy and VTK) -called *VedoVisualizer*. +called *VedoVisualizer*, using the implementation form the :SSD:`SSD <>` library. This *Visualizer* brings several advantages: * Users can add any component of the simulation in the *Visualizer*; @@ -17,272 +17,20 @@ This *Visualizer* brings several advantages: * Parallel running *Environments* are rendered in the same window with sub-windows; * A *Factory* is created with each *Environment* so that users can access templates to define visualization data. -Objects are created using the ``add_object`` method of the *Factory* in the *Environment*. -This method requires the object type name and a dictionary containing the required fields detailed above (common fields -and object-specific fields). -These objects must be defined in the ``send_visualization`` method of the *Environment*, which must return the objects -dictionary of the *Factory*. +Objects are created using the ``add_`` methods of the *Factory* in the *Environment*. +These methods require the fields detailed above (common fields and object-specific fields). +These objects must be added in the ``init_visualization`` method of the *Environment*, which is automatically called to +create the objects within the *Factory*. -Objects are updated using the ``update_object_dict`` method of the *Factory* in the *Environment*. -This method requires the object index (indices follow the order of creation) and a dictionary containing the updated -data fields. +Objects are updated using the ``update_`` methods of the *Factory* in the *Environment*. +These methods require the object index (indices follow the order of creation) and a the updated data fields. The *Factory* will only use templates to create an updated objects dictionary which must be sent with the request ``update_visualization`` to update the view. -| **General parameters** -| Visual objects share default data fields that could also be filled at init and are all optional: - -.. list-table:: - :width: 95% - :widths: 21 11 11 57 - :header-rows: 1 - - * - Field - - Init - - Update - - Description - - * - ``c`` - - Optional - - Unnecessary - - Opacity of the object between 0 and 1. - - * - ``alpha`` - - Optional - - Unnecessary - - Marker object. - - * - ``at`` - - Optional - - Unnecessary - - Sub-window in which the object will be rendered. - - Set to -1 by default, meaning a new window is created for the object. - - Advise: set to ``self.instance_id`` to gather objects from an *Environment* in the same sub-window. - - * - ``colormap`` - - Optional - - Unnecessary - - Name of color palette that samples a continuous function between two end colors. - - * - ``scalar_field`` - - Optional - - Unnecessary - - List of scalar values to set individual points or cell color. - - * - ``scalar_field_name`` - - Optional - - Unnecessary - - Name of the scalar field. | **Visual Objects Parameters** -| A list of templates are available in the *Factory* to initialize and update a list of objects. - Here is a description of available objects and the required data fields: - -* Create a :VedoObject:`Mesh `: - - .. list-table:: - :width: 95% - :widths: 21 11 11 57 - :header-rows: 1 - - * - Field - - Init - - Update - - Description - - * - ``positions`` - - **Required** - - **Required** - - List of vertices. - Updated position vector must always have the same size. - - * - ``cells`` - - **Required** - - Unnecessary - - List of connections between vertices. - - * - ``computeNormals`` - - Optional - - Unnecessary - - Compute cells and points normals at creation. - -* Create a :VedoObject:`Point Cloud `: - - .. list-table:: - :width: 95% - :widths: 21 11 11 57 - :header-rows: 1 - - * - Field - - Init - - Update - - Description - - * - ``positions`` - - **Required** - - **Required** - - List of vertices. - Updated position vector must always have the same size. - - * - ``r`` - - Optional - - Optional - - Radius of points. - -* Create a :VedoObject:`Marker ` (single point with associated symbol): - - .. list-table:: - :width: 95% - :widths: 21 11 11 57 - :header-rows: 1 - - * - Field - - Init - - Update - - Description - - * - ``positions`` - - **Required** - - **Required** - - Position of the Marker. - - * - ``symbol`` - - **Required** - - Unnecessary - - Associated symbol. - - * - ``s`` - - Optional - - Unnecessary - - Radius of symbol. - - * - ``filled`` - - Optional - - Unnecessary - - Fill the shape or only draw outline. - -* Create a :VedoObject:`Glyph ` (point cloud with oriented markers): - - .. list-table:: - :width: 95% - :widths: 21 11 11 57 - :header-rows: 1 - - * - Field - - Init - - Update - - Description - - * - ``positions`` - - **Required** - - **Required** - - Position of the Markers. - - * - ``glyphObj`` - - **Required** - - Unnecessary - - Marker object. - - * - ``orientationArray`` - - **Required** - - Unnecessary - - List of orientation vectors. - - * - ``scaleByScalar`` - - Optional - - Unnecessary - - Glyph is scaled by the scalar field. - - * - ``scaleByVectorSize`` - - Optional - - Unnecessary - - Glyph is scaled by the size of the orientation vectors. - - * - ``scaleByVectorComponents`` - - Optional - - Unnecessary - - Glyph is scaled by the components of the orientation vectors. - - * - ``colorByScalar`` - - Optional - - Unnecessary - - Glyph is colored based on the colormap and the scalar field. - - * - ``colorByVectorSize`` - - Optional - - Unnecessary - - Glyph is colored based on the size of the orientation vectors. - - * - ``tol`` - - Optional - - Unnecessary - - Minimum distance between two Glyphs. - -* Create :VedoObject:`3D Arrows `: - - .. list-table:: - :width: 95% - :widths: 21 11 11 57 - :header-rows: 1 - - * - Field - - Init - - Update - - Description - - * - ``positions`` - - **Required** - - **Required** - - Start points of the arrows. - - * - ``vectors`` - - **Required** - - **Required** - - Vector that must represent the arrows. - - * - ``res`` - - Optional - - Unnecessary - - Arrows visual resolution. - -* Change window parameters - - .. list-table:: - :width: 95% - :widths: 21 11 11 57 - :header-rows: 1 - - * - Field - - Init - - Update - - Description - - * - ``objects_id`` - - **Required** - - Unnecessary - - Indices of objects to set in this particular window. - - * - ``title`` - - Optional - - Unnecessary - - Title of the window. - - * - ``axes`` - - Optional - - Unnecessary - - Type of axes to show. - - * - ``sharecam`` - - Optional - - Unnecessary - - If True (default), all subwindows will share the same camera parameters. - - * - ``interactive`` - - Optional - - Unnecessary - - If True (default), the window will be interactive. +| All the available objects and their parameters are listed on the + :SSDd:`SSD documentation `. Configuration @@ -300,7 +48,7 @@ See following example:: # Import EnvironmentConfig and Visualizer from DeepPhysX_Core.Environment.BaseEnvironmentConfig import BaseEnvironmentConfig - from DeepPhysX_Core.Visualizer.VedoVisualizer import VedoVisualizer + from DeepPhysX_Core.Visualization.VedoVisualizer import VedoVisualizer # Create the config env_config = BaseEnvironmentConfig(environment_class=MyEnvironment, diff --git a/docs/source/conf.py b/docs/source/conf.py index 7f35763d..ae492141 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -84,4 +84,5 @@ 'Tensorboard': ('https://www.tensorflow.org/tensorboard/%s', '%s'), 'Vedo': ('https://vedo.embl.es/%s', '%s'), 'VedoObject': ('https://vedo.embl.es/autodocs/content/vedo/%s', '%s'), - 'SSD': ('https://github.com/RobinEnjalbert/SimulationSimpleDatabase/%s', '%s')} + 'SSD': ('https://github.com/RobinEnjalbert/SimulationSimpleDatabase/%s', '%s'), + 'SSDd': ('https://simulationsimpledatabase.readthedocs.io/en/latest/%s', '%s')} From 60ae650d4a8e25dc7e97cc1fcde7c1cab8717222 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 29 Nov 2022 17:48:05 +0100 Subject: [PATCH 59/61] Update the documentation. --- docs/source/api/sofa.rst | 2 +- docs/source/api/torch.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/api/sofa.rst b/docs/source/api/sofa.rst index 71a4752d..3d2c866c 100644 --- a/docs/source/api/sofa.rst +++ b/docs/source/api/sofa.rst @@ -1,4 +1,4 @@ SOFA ==== -Find **DeepPhysX_Sofa** API `here `_ +Find **DeepPhysX.Sofa** API `here `_ diff --git a/docs/source/api/torch.rst b/docs/source/api/torch.rst index 62141b27..ba810e24 100644 --- a/docs/source/api/torch.rst +++ b/docs/source/api/torch.rst @@ -1,4 +1,4 @@ TORCH ===== -Find **DeepPhysX_Torch** API `here `_. +Find **DeepPhysX.Torch** API `here `_. From e971785d32648e98d450a4d955d771deb9ceb88f Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 14 Dec 2022 10:29:09 +0100 Subject: [PATCH 60/61] Fix Dataset converter. --- LICENSE | 0 src/Core/Utils/converter.py | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..e69de29b diff --git a/src/Core/Utils/converter.py b/src/Core/Utils/converter.py index bec3c387..4302df12 100644 --- a/src/Core/Utils/converter.py +++ b/src/Core/Utils/converter.py @@ -58,6 +58,7 @@ def numpy_to_database(self, new_session=False) database_handler = DatabaseHandler() database_manager.connect_handler(database_handler) + database_manager.first_add = True # 3.2. Create Fields in Tables training_fields = [] @@ -79,6 +80,7 @@ def numpy_to_database(self, # 3.3. Add each partition to the Database if nb_partition == 0: print(" No partition.") + database_manager.normalize = False for i in range(nb_partition): data_training = {field: load(join(self.dataset_dir, f'{partitions[mode][field][i]}.npy')) for field, _ in training_fields} @@ -143,4 +145,3 @@ def load_numpy_partitions(self) -> Dict[str, Dict[str, List[str]]]: all_partitions[mode][name].append(partition) return all_partitions - From acfeda4851fcc229afead13f9437dfa29436aca4 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 14 Dec 2022 10:29:39 +0100 Subject: [PATCH 61/61] Add license. --- LICENSE | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 8 + 2 files changed, 669 insertions(+) diff --git a/LICENSE b/LICENSE index e69de29b..be3f7b28 100644 --- a/LICENSE +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md index 4d564c2e..2452a651 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,11 @@ $ DPX --run # Run one of the demo scripts | **Armadillo**
    `DPX -r armadillo` | **Beam**
    `DPX -r beam` | **Liver**
    `DPX -r liver` | |:-----------------------------------------------------:|:-------------------------------------------:|:---------------------------------------------:| | ![armadillo](docs/source/_static/image/armadillo.png) | ![beam](docs/source/_static/image/beam.png) | ![liver](docs/source/_static/image/liver.png) | + + +### References + +Did this project help you for your research ? Please cite us as: + +R. Enjalbert, A. Odot and S. Cotin, *DeepPhysX, a python framework to interface AI with numerical simulation*, +Zenodo, 2022, [**DOI**](https://doi.org/10.5281/zenodo.7389505)

    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

GQ>?LcMg2)>6 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 KfC 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