def run(self, x, y, data_summary: str='', real_cast_modes: Optional[Union[(str, List[Optional[str]], Tuple[Optional[str]])]]=None, validation_split: float=0.2, validation_data: Optional[Union[(Tuple[(np.ndarray, np.ndarray)], data.Dataset)]]=None, test_data: Optional[Union[(Tuple[(np.ndarray, np.ndarray)], data.Dataset)]]=None, iterations: int=100, epochs: int=10, batch_size: int=100, early_stop: bool=False, shuffle: bool=True, verbose: Optional[Union[(bool, int, str)]]=1, display_freq: int=1, same_weights: bool=False, process_dataset: bool=True):
    "\n        This function is used to compare all models added with `self.add_model` method.\n        Runs the iteration dataset (x, y).\n        1. It then runs a monte carlo simulation of several iterations of both CVNN and an equivalent RVNN model.\n        2. Saves several files into ./log/montecarlo/date/of/run/\n            2.1. run_summary.txt: Summary of the run models and data\n            2.2. run_data.csv: Full information of performance of iteration of each model at each epoch\n            2.3. <model.name>_network_statistical_result.csv: Statistical results of all iterations of CVNN per epoch\n            2.4. (Optional with parameter plot_all)\n                `plot/` folder with the corresponding plots generated by MonteCarloAnalyzer.do_all()\n        :param x: Input data. It could be:\n            - A Numpy array (or array-like), or a list of arrays (in case the model has multiple inputs).\n            - A TensorFlow tensor, or a list of tensors (in case the model has multiple inputs).\n            - A tf.data dataset. Should return a tuple (inputs, targets). Preferred data type (less overhead).\n        :param y: Labels/Target data. Like the input data x, it could be either Numpy array(s) or TensorFlow tensor(s).\n            If f x is a dataset then y will be ignored (default None)\n        :param data_summary:  (String) Dataset name to keep track of it\n        :param real_cast_modes: mode parameter used by cvnn.utils.transform_to_real to be used when the model to\n            train is real-valued. One of the following:\n            - String with the mode listed in cvnn.utils.transform_to_real to be used by all the real-valued models to\n                cast complex data to real.\n            - List or Tuple of strings: Same size of self.models. mode on how to cast complex data to real for each\n                model in self.model.\n                real_cast_modes[i] will indicate how to cast data for self.models[i] (ignored when model is complex).\n        :param validation_split: Float between 0 and 1.\n            Percentage of the input data to be used as test set (the rest will be use as train set)\n            Default: 0.0 (No validation set).\n            This input is ignored if validation_data is given.\n        :param validation_data: Data on which to evaluate the loss and any model metrics at the end of each epoch.\n            The model will not be trained on this data. This parameter takes precedence over validation_split.\n            It can be:\n                - tuple (x_val, y_val) of Numpy arrays or tensors. Preferred data type (less overhead).\n                - A tf.data dataset.\n        :param test_data: Data on which to evaluate the loss and any model metrics at the end of a model training.\n            The model will not be trained on this data.\n            If test data is not None (default) it will generate a file called `test_results.csv` with the\n                statistical results from the test data.\n            It can be:\n                - tuple (x_val, y_val) of Numpy arrays or tensors. Preferred data type (less overhead).\n                - A tf.data dataset.\n        :param iterations: Number of iterations to be done for each model\n        :param epochs: Number of epochs for each iteration\n        :param batch_size: Batch size at each iteration\n        :param display_freq: Integer (Default 1). Only relevant if validation data is provided.\n            Frequency on terms of epochs before running the validation.\n        :param shuffle: (Boolean) Whether to shuffle the training data before each epoch.\n        :param verbose: Different modes according to number:\n            - 0 or 'silent': No output at all\n            - 1 or False: Progress bar per iteration\n            - 2 or True or 'debug': Progress bar per epoch\n        :param early_stop: (Default: False) Wheather to implement early stop on training.\n        :param same_weights: (Default False) If True it will use the same weights at each iteration.\n        :return: (string) Full path to the run_data.csv generated file.\n            It can be used by cvnn.data_analysis.SeveralMonteCarloComparison to compare several runs.\n        "
    if verbose:
        self.verbose = self._parse_verbose(verbose)
    test_data_cols = None
    if (test_data is not None):
        test_data_cols = (['network'] + [n.get_config()['name'] for n in self.models[0].metrics])
    real_cast_modes = self._check_real_cast_modes(real_cast_modes)
    (confusion_matrix, pbar, test_results) = self._beginning_callback(iterations, epochs, batch_size, shuffle, data_summary, test_data_cols)
    w_save = []
    for model in self.models:
        w_save.append(model.get_weights())
    for it in range(iterations):
        if (self.verbose == 2):
            logger.info('Iteration {}/{}'.format((it + 1), iterations))
        for (i, model) in enumerate(self.models):
            (x_fit, val_data_fit, test_data_fit) = self._get_fit_dataset(model.inputs[0].dtype.is_complex, x, validation_data, test_data, real_cast_modes[i], process_dataset=process_dataset)
            clone_model = tf.keras.models.clone_model(model)
            if isinstance(model.loss, tf.keras.losses.Loss):
                loss = model.loss.__class__.from_config(config=model.loss.get_config())
            else:
                loss = model.loss
            clone_model.compile(optimizer=model.optimizer.__class__.from_config(model.optimizer.get_config()), loss=loss, metrics=['accuracy'])
            if same_weights:
                clone_model.set_weights(w_save[i])
            temp_path = (self.monte_carlo_analyzer.path / f'run/iteration{it}_model{i}_{model.name}')
            os.makedirs(temp_path, exist_ok=True)
            callbacks = []
            if self.output_config['tensorboard']:
                tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=(temp_path / 'tensorboard'), histogram_freq=1)
                callbacks.append(tensorboard_callback)
            if early_stop:
                eas = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)
                callbacks.append(eas)
            run_result = clone_model.fit(x_fit, y, validation_split=validation_split, validation_data=val_data_fit, epochs=epochs, batch_size=batch_size, verbose=(self.verbose == 2), validation_freq=display_freq, callbacks=callbacks, shuffle=shuffle)
            test_results = self._inner_callback(clone_model, validation_data, confusion_matrix, real_cast_modes[i], i, run_result, test_results, test_data_fit, temp_path)
        self._outer_callback(pbar)
    return self._end_callback(x, y, iterations, data_summary, real_cast_modes, epochs, batch_size, confusion_matrix, test_results, pbar, w_save)
This function is used to compare all models added with `self.add_model` method.
Runs the iteration dataset (x, y).
1. It then runs a monte carlo simulation of several iterations of both CVNN and an equivalent RVNN model.
2. Saves several files into ./log/montecarlo/date/of/run/
    2.1. run_summary.txt: Summary of the run models and data
    2.2. run_data.csv: Full information of performance of iteration of each model at each epoch
    2.3. <model.name>_network_statistical_result.csv: Statistical results of all iterations of CVNN per epoch
    2.4. (Optional with parameter plot_all)
        `plot/` folder with the corresponding plots generated by MonteCarloAnalyzer.do_all()
:param x: Input data. It could be:
    - A Numpy array (or array-like), or a list of arrays (in case the model has multiple inputs).
    - A TensorFlow tensor, or a list of tensors (in case the model has multiple inputs).
    - A tf.data dataset. Should return a tuple (inputs, targets). Preferred data type (less overhead).
:param y: Labels/Target data. Like the input data x, it could be either Numpy array(s) or TensorFlow tensor(s).
    If f x is a dataset then y will be ignored (default None)
:param data_summary:  (String) Dataset name to keep track of it
:param real_cast_modes: mode parameter used by cvnn.utils.transform_to_real to be used when the model to
    train is real-valued. One of the following:
    - String with the mode listed in cvnn.utils.transform_to_real to be used by all the real-valued models to
        cast complex data to real.
    - List or Tuple of strings: Same size of self.models. mode on how to cast complex data to real for each
        model in self.model.
        real_cast_modes[i] will indicate how to cast data for self.models[i] (ignored when model is complex).
:param validation_split: Float between 0 and 1.
    Percentage of the input data to be used as test set (the rest will be use as train set)
    Default: 0.0 (No validation set).
    This input is ignored if validation_data is given.
:param validation_data: Data on which to evaluate the loss and any model metrics at the end of each epoch.
    The model will not be trained on this data. This parameter takes precedence over validation_split.
    It can be:
        - tuple (x_val, y_val) of Numpy arrays or tensors. Preferred data type (less overhead).
        - A tf.data dataset.
:param test_data: Data on which to evaluate the loss and any model metrics at the end of a model training.
    The model will not be trained on this data.
    If test data is not None (default) it will generate a file called `test_results.csv` with the
        statistical results from the test data.
    It can be:
        - tuple (x_val, y_val) of Numpy arrays or tensors. Preferred data type (less overhead).
        - A tf.data dataset.
:param iterations: Number of iterations to be done for each model
:param epochs: Number of epochs for each iteration
:param batch_size: Batch size at each iteration
:param display_freq: Integer (Default 1). Only relevant if validation data is provided.
    Frequency on terms of epochs before running the validation.
:param shuffle: (Boolean) Whether to shuffle the training data before each epoch.
:param verbose: Different modes according to number:
    - 0 or 'silent': No output at all
    - 1 or False: Progress bar per iteration
    - 2 or True or 'debug': Progress bar per epoch
:param early_stop: (Default: False) Wheather to implement early stop on training.
:param same_weights: (Default False) If True it will use the same weights at each iteration.
:return: (string) Full path to the run_data.csv generated file.
    It can be used by cvnn.data_analysis.SeveralMonteCarloComparison to compare several runs.
def run(self, x, y, data_summary: str=, real_cast_modes: Optional[Union[(str, List[Optional[str]], Tuple[Optional[str]])]]=None, validation_split: float=0.2, validation_data: Optional[Union[(Tuple[(np.ndarray, np.ndarray)], data.Dataset)]]=None, test_data: Optional[Union[(Tuple[(np.ndarray, np.ndarray)], data.Dataset)]]=None, iterations: int=100, epochs: int=10, batch_size: int=100, early_stop: bool=False, shuffle: bool=True, verbose: Optional[Union[(bool, int, str)]]=1, display_freq: int=1, same_weights: bool=False, process_dataset: bool=True):
    "\n        This function is used to compare all models added with `self.add_model` method.\n        Runs the iteration dataset (x, y).\n        1. It then runs a monte carlo simulation of several iterations of both CVNN and an equivalent RVNN model.\n        2. Saves several files into ./log/montecarlo/date/of/run/\n            2.1. run_summary.txt: Summary of the run models and data\n            2.2. run_data.csv: Full information of performance of iteration of each model at each epoch\n            2.3. <model.name>_network_statistical_result.csv: Statistical results of all iterations of CVNN per epoch\n            2.4. (Optional with parameter plot_all)\n                `plot/` folder with the corresponding plots generated by MonteCarloAnalyzer.do_all()\n        :param x: Input data. It could be:\n            - A Numpy array (or array-like), or a list of arrays (in case the model has multiple inputs).\n            - A TensorFlow tensor, or a list of tensors (in case the model has multiple inputs).\n            - A tf.data dataset. Should return a tuple (inputs, targets). Preferred data type (less overhead).\n        :param y: Labels/Target data. Like the input data x, it could be either Numpy array(s) or TensorFlow tensor(s).\n            If f x is a dataset then y will be ignored (default None)\n        :param data_summary:  (String) Dataset name to keep track of it\n        :param real_cast_modes: mode parameter used by cvnn.utils.transform_to_real to be used when the model to\n            train is real-valued. One of the following:\n            - String with the mode listed in cvnn.utils.transform_to_real to be used by all the real-valued models to\n                cast complex data to real.\n            - List or Tuple of strings: Same size of self.models. mode on how to cast complex data to real for each\n                model in self.model.\n                real_cast_modes[i] will indicate how to cast data for self.models[i] (ignored when model is complex).\n        :param validation_split: Float between 0 and 1.\n            Percentage of the input data to be used as test set (the rest will be use as train set)\n            Default: 0.0 (No validation set).\n            This input is ignored if validation_data is given.\n        :param validation_data: Data on which to evaluate the loss and any model metrics at the end of each epoch.\n            The model will not be trained on this data. This parameter takes precedence over validation_split.\n            It can be:\n                - tuple (x_val, y_val) of Numpy arrays or tensors. Preferred data type (less overhead).\n                - A tf.data dataset.\n        :param test_data: Data on which to evaluate the loss and any model metrics at the end of a model training.\n            The model will not be trained on this data.\n            If test data is not None (default) it will generate a file called `test_results.csv` with the\n                statistical results from the test data.\n            It can be:\n                - tuple (x_val, y_val) of Numpy arrays or tensors. Preferred data type (less overhead).\n                - A tf.data dataset.\n        :param iterations: Number of iterations to be done for each model\n        :param epochs: Number of epochs for each iteration\n        :param batch_size: Batch size at each iteration\n        :param display_freq: Integer (Default 1). Only relevant if validation data is provided.\n            Frequency on terms of epochs before running the validation.\n        :param shuffle: (Boolean) Whether to shuffle the training data before each epoch.\n        :param verbose: Different modes according to number:\n            - 0 or 'silent': No output at all\n            - 1 or False: Progress bar per iteration\n            - 2 or True or 'debug': Progress bar per epoch\n        :param early_stop: (Default: False) Wheather to implement early stop on training.\n        :param same_weights: (Default False) If True it will use the same weights at each iteration.\n        :return: (string) Full path to the run_data.csv generated file.\n            It can be used by cvnn.data_analysis.SeveralMonteCarloComparison to compare several runs.\n        "
    if verbose:
        self.verbose = self._parse_verbose(verbose)
    test_data_cols = None
    if (test_data is not None):
        test_data_cols = (['network'] + [n.get_config()['name'] for n in self.models[0].metrics])
    real_cast_modes = self._check_real_cast_modes(real_cast_modes)
    (confusion_matrix, pbar, test_results) = self._beginning_callback(iterations, epochs, batch_size, shuffle, data_summary, test_data_cols)
    w_save = []
    for model in self.models:
        w_save.append(model.get_weights())
    for it in range(iterations):
        if (self.verbose == 2):
            logger.info('Iteration {}/{}'.format((it + 1), iterations))
        for (i, model) in enumerate(self.models):
            (x_fit, val_data_fit, test_data_fit) = self._get_fit_dataset(model.inputs[0].dtype.is_complex, x, validation_data, test_data, real_cast_modes[i], process_dataset=process_dataset)
            clone_model = tf.keras.models.clone_model(model)
            if isinstance(model.loss, tf.keras.losses.Loss):
                loss = model.loss.__class__.from_config(config=model.loss.get_config())
            else:
                loss = model.loss
            clone_model.compile(optimizer=model.optimizer.__class__.from_config(model.optimizer.get_config()), loss=loss, metrics=['accuracy'])
            if same_weights:
                clone_model.set_weights(w_save[i])
            temp_path = (self.monte_carlo_analyzer.path / f'run/iteration{it}_model{i}_{model.name}')
            os.makedirs(temp_path, exist_ok=True)
            callbacks = []
            if self.output_config['tensorboard']:
                tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=(temp_path / 'tensorboard'), histogram_freq=1)
                callbacks.append(tensorboard_callback)
            if early_stop:
                eas = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)
                callbacks.append(eas)
            run_result = clone_model.fit(x_fit, y, validation_split=validation_split, validation_data=val_data_fit, epochs=epochs, batch_size=batch_size, verbose=(self.verbose == 2), validation_freq=display_freq, callbacks=callbacks, shuffle=shuffle)
            test_results = self._inner_callback(clone_model, validation_data, confusion_matrix, real_cast_modes[i], i, run_result, test_results, test_data_fit, temp_path)
        self._outer_callback(pbar)
    return self._end_callback(x, y, iterations, data_summary, real_cast_modes, epochs, batch_size, confusion_matrix, test_results, pbar, w_save)