piel
====

.. py:module:: piel

.. autoapi-nested-parse::

   Top-level package for piel.



Submodules
----------

.. toctree::
   :maxdepth: 1

   /autoapi/piel/analysis/index
   /autoapi/piel/base/index
   /autoapi/piel/cli/index
   /autoapi/piel/connectivity/index
   /autoapi/piel/conversion/index
   /autoapi/piel/develop/index
   /autoapi/piel/experimental/index
   /autoapi/piel/file_system/index
   /autoapi/piel/flows/index
   /autoapi/piel/integration/index
   /autoapi/piel/materials/index
   /autoapi/piel/models/index
   /autoapi/piel/project_structure/index
   /autoapi/piel/tools/index
   /autoapi/piel/types/index
   /autoapi/piel/units/index
   /autoapi/piel/utils/index
   /autoapi/piel/visual/index


Attributes
----------

.. autoapisummary::

   piel.rp
   piel.PathTypes
   piel.ModuleType
   piel.__author__
   piel.__email__
   piel.__version__


Functions
---------

.. autoapisummary::

   piel.check_path_exists
   piel.check_example_design
   piel.copy_source_folder
   piel.copy_example_design
   piel.create_new_directory
   piel.create_piel_home_directory
   piel.delete_path
   piel.delete_path_list_in_directory
   piel.get_files_recursively_in_directory
   piel.get_top_level_script_directory
   piel.get_id_map_directory_dictionary
   piel.list_prefix_match_directories
   piel.permit_directory_all
   piel.permit_script_execution
   piel.read_json
   piel.rename_file
   piel.rename_files_in_directory
   piel.replace_string_in_file
   piel.replace_string_in_directory_files
   piel.return_path
   piel.run_script
   piel.write_file
   piel.write_model_to_json
   piel.return_path
   piel.read_json
   piel.check_path_exists
   piel.create_new_directory
   piel.write_file
   piel.create_setup_py
   piel.create_empty_piel_project
   piel.get_module_folder_type_location
   piel.pip_install_local_module
   piel.read_configuration
   piel.round_complex_array
   piel.single_parameter_sweep
   piel.multi_parameter_sweep
   piel.get_unique_dataframe_subsets
   piel.create_all_connections
   piel.create_component_connections
   piel.create_sequential_component_path
   piel.create_connection_list_from_ports_lists
   piel.get_port_index_from_name


Package Contents
----------------

.. py:function:: check_path_exists(path: piel.types.PathTypes, raise_errors: bool = False) -> bool

   Checks if a directory exists.

   :param path: Input path.
   :type path: PathTypes

   :returns: True if directory exists.
   :rtype: directory_exists(bool)


.. py:function:: check_example_design(design_name: str = 'simple_design', designs_directory: piel.types.PathTypes | None = None) -> bool

   We copy the example simple_design from docs to the `/foss/designs` in the `iic-osic-tools` environment.

   :param design_name: Name of the design to check.
   :type design_name: str
   :param designs_directory: Directory that contains the DESIGNS environment flag.
   :type designs_directory: PathTypes
   :param # TODO:

   :returns: None


.. py:function:: copy_source_folder(source_directory: piel.types.PathTypes, target_directory: piel.types.PathTypes, delete: bool = None) -> None

   Copies the files from a source_directory to a target_directory

   :param source_directory: Source directory.
   :type source_directory: PathTypes
   :param target_directory: Target directory.
   :type target_directory: PathTypes
   :param delete: Delete target directory. Default: False.
   :type delete: bool

   :returns: None


.. py:function:: copy_example_design(project_source: piel.types.ProjectType = 'piel', example_name: str = 'simple_design', target_directory: piel.types.PathTypes = None, target_project_name: Optional[str] = None, **kwargs) -> None

   We copy the example simple_design from docs to the `/foss/designs` in the `iic-osic-tools` environment.

   :param project_source: Source of the project.
   :type project_source: str
   :param example_name: Name of the example design.
   :type example_name: str
   :param target_directory: Target directory.
   :type target_directory: PathTypes
   :param target_project_name: Name of the target project.
   :type target_project_name: str

   :returns: None


.. py:function:: create_new_directory(directory_path: str | pathlib.Path, overwrite: bool = False) -> bool

   Creates a new directory.

   If the parents of the target_directory do not exist, they will be created too.

   :param overwrite: Overwrite directory if it already exists.
   :param directory_path: Input path.
   :type directory_path: str | pathlib.Path

   :returns: None


.. py:function:: create_piel_home_directory() -> None

   Creates the piel home directory.

   :returns: None


.. py:function:: delete_path(path: str | pathlib.Path) -> None

   Deletes a path.

   :param path: Input path.
   :type path: str | pathlib.Path

   :returns: None


.. py:function:: delete_path_list_in_directory(directory_path: piel.types.PathTypes, path_list: list, ignore_confirmation: bool = False, validate_individual: bool = False) -> None

   Deletes a list of files in a directory.

   Usage:

   ```python
   delete_path_list_in_directory(
       directory_path=directory_path, path_list=path_list, ignore_confirmation=True
   )
   ```

   :param directory_path: Input path.
   :type directory_path: PathTypes
   :param path_list: List of files.
   :type path_list: list
   :param ignore_confirmation: Ignore confirmation. Default: False.
   :type ignore_confirmation: bool
   :param validate_individual: Validate individual files. Default: False.
   :type validate_individual: bool

   :returns: None


.. py:function:: get_files_recursively_in_directory(path: piel.types.PathTypes, extension: str = '*')

   Returns a list of files in a directory.

   Usage:

       get_files_recursively_in_directory('path/to/directory', 'extension')

   :param path: Input path.
   :type path: PathTypes
   :param extension: File extension.
   :type extension: str

   :returns: List of files.
   :rtype: file_list(list)


.. py:function:: get_top_level_script_directory() -> pathlib.Path

   Attempts to return the top-level script directory when this file is run,
   compatible with various execution environments like Jupyter Lab, pytest, PDM, etc.
   TODO run full verification.

   :returns: Top level script directory.
   :rtype: top_level_script_directory(pathlib.Path)


.. py:function:: get_id_map_directory_dictionary(path_list: list[piel.types.PathTypes], target_prefix: str)

   Returns a dictionary of ids to directories.

   Usage:

       get_id_to_directory_dictionary(path_list, target_prefix)

   :param path_list: List of paths.
   :type path_list: list[PathTypes]
   :param target_prefix: Target prefix.
   :type target_prefix: str

   :returns: Dictionary of ids to directories.
   :rtype: id_dict(dict)


.. py:function:: list_prefix_match_directories(output_directory: piel.types.PathTypes, target_prefix: str)

   Returns a list of directories that match a prefix.

   Usage:

       list_prefix_match_directories('path/to/directory', 'prefix')

   :param output_directory: Output directory.
   :type output_directory: PathTypes
   :param target_prefix: Target prefix.
   :type target_prefix: str

   :returns: List of directories.
   :rtype: matching_dirs(list)


.. py:function:: permit_directory_all(directory_path: piel.types.PathTypes) -> None

   Permits a directory to be read, written and executed. Use with care as it can be a source for security issues.

   Usage:

       permit_directory_all('path/to/directory')

   :param directory_path: Input path.
   :type directory_path: PathTypes

   :returns: None


.. py:function:: permit_script_execution(script_path: piel.types.PathTypes) -> None

   Permits the execution of a script.

   Usage:

       permit_script_execution('path/to/script')

   :param script_path: Script path.
   :type script_path: PathTypes

   :returns: None


.. py:function:: read_json(path: piel.types.PathTypes) -> dict

   Reads a JSON file.

   Usage:

       read_json('path/to/file.json')

   :param path: Input path.
   :type path: PathTypes

   :returns: JSON files.
   :rtype: json_data(dict)


.. py:function:: rename_file(match_file_path: piel.types.PathTypes, renamed_file_path: piel.types.PathTypes) -> None

   Renames a file.

   Usage:

       rename_file('path/to/match_file', 'path/to/renamed_file')

   :param match_file_path: Input path.
   :type match_file_path: PathTypes
   :param renamed_file_path: Input path.
   :type renamed_file_path: PathTypes

   :returns: None


.. py:function:: rename_files_in_directory(target_directory: piel.types.PathTypes, match_string: str, renamed_string: str) -> None

   Renames all files in a directory.

   Usage:

       rename_files_in_directory('path/to/directory', 'match_string', 'renamed_string')

   :param target_directory: Input path.
   :type target_directory: PathTypes
   :param match_string: String to match.
   :type match_string: str
   :param renamed_string: String to replace.
   :type renamed_string: str

   :returns: None


.. py:function:: replace_string_in_file(file_path: piel.types.PathTypes, match_string: str, replace_string: str)

   Replaces a string in a file.

   Usage:

       replace_string_in_file('path/to/file', 'match_string', 'replace_string')

   :param file_path: Input path.
   :type file_path: PathTypes
   :param match_string: String to match.
   :type match_string: str
   :param replace_string: String to replace.
   :type replace_string: str

   :returns: None


.. py:function:: replace_string_in_directory_files(target_directory: piel.types.PathTypes, match_string: str, replace_string: str)

   Replaces a string in all files in a directory.

   Usage:

       replace_string_in_directory_files('path/to/directory', 'match_string', 'replace_string')

   :param target_directory: Input path.
   :type target_directory: PathTypes
   :param match_string: String to match.
   :type match_string: str
   :param replace_string: String to replace.
   :type replace_string: str

   :returns: None


.. py:function:: return_path(input_path: piel.types.PathTypes, as_piel_module: bool = False) -> pathlib.Path

   Returns a pathlib.Path to be able to perform operations accordingly internally.

   This allows us to maintain compatibility between POSIX and Windows systems. When the `as_piel_module` flag is
   enabled, it will analyse whether the input path can be treated as a piel module, and treat the returned path as a
   module would be treated. This comes useful when analysing files generated in this particular structure accordingly.

   Usage:

       return_path('path/to/file')

   :param input_path: Input path.
   :type input_path: str

   :returns: Pathlib path.
   :rtype: pathlib.Path


.. py:data:: rp

.. py:function:: run_script(script_path: piel.types.PathTypes, program: str = None) -> None

   Runs a script on the filesystem `script_path`. By default this is a bash script.

   :param script_path: Script path.
   :type script_path: PathTypes

   :returns: None


.. py:function:: write_file(directory_path: piel.types.PathTypes, file_text: str, file_name: str, append: bool = False) -> bool

   Writes a file to a directory. Appends to the file if it exists and append is True.

   :param directory_path: Directory path.
   :type directory_path: PathTypes
   :param file_text: File text.
   :type file_text: str
   :param file_name: File name.
   :type file_name: str
   :param append: If True, appends to the file if it exists. Defaults to False.
   :type append: bool

   :returns: True if successful.
   :rtype: bool


.. py:function:: write_model_to_json(model: piel.types.PielBaseModel, file_path: piel.types.PathTypes)

   Writes a pydantic model to a JSON file.


.. py:data:: PathTypes

.. py:data:: ModuleType

.. py:function:: return_path(input_path: piel.types.PathTypes, as_piel_module: bool = False) -> pathlib.Path

   Returns a pathlib.Path to be able to perform operations accordingly internally.

   This allows us to maintain compatibility between POSIX and Windows systems. When the `as_piel_module` flag is
   enabled, it will analyse whether the input path can be treated as a piel module, and treat the returned path as a
   module would be treated. This comes useful when analysing files generated in this particular structure accordingly.

   Usage:

       return_path('path/to/file')

   :param input_path: Input path.
   :type input_path: str

   :returns: Pathlib path.
   :rtype: pathlib.Path


.. py:function:: read_json(path: piel.types.PathTypes) -> dict

   Reads a JSON file.

   Usage:

       read_json('path/to/file.json')

   :param path: Input path.
   :type path: PathTypes

   :returns: JSON files.
   :rtype: json_data(dict)


.. py:function:: check_path_exists(path: piel.types.PathTypes, raise_errors: bool = False) -> bool

   Checks if a directory exists.

   :param path: Input path.
   :type path: PathTypes

   :returns: True if directory exists.
   :rtype: directory_exists(bool)


.. py:function:: create_new_directory(directory_path: str | pathlib.Path, overwrite: bool = False) -> bool

   Creates a new directory.

   If the parents of the target_directory do not exist, they will be created too.

   :param overwrite: Overwrite directory if it already exists.
   :param directory_path: Input path.
   :type directory_path: str | pathlib.Path

   :returns: None


.. py:function:: write_file(directory_path: piel.types.PathTypes, file_text: str, file_name: str, append: bool = False) -> bool

   Writes a file to a directory. Appends to the file if it exists and append is True.

   :param directory_path: Directory path.
   :type directory_path: PathTypes
   :param file_text: File text.
   :type file_text: str
   :param file_name: File name.
   :type file_name: str
   :param append: If True, appends to the file if it exists. Defaults to False.
   :type append: bool

   :returns: True if successful.
   :rtype: bool


.. py:function:: create_setup_py(design_directory: piel.types.PathTypes, project_name: Optional[str] = None, from_config_json: bool = True) -> None

   This function creates a setup.py file from the config.json file found in the design directory.

   :param design_directory: Design directory PATH or module name.
   :type design_directory: PathTypes

   :returns: None


.. py:function:: create_empty_piel_project(project_name: str, parent_directory: piel.types.PathTypes) -> None

   This function creates an empty piel-structure project in the target directory. Structuring your files in this way
   enables the co-design and use of the tools supported by piel whilst maintaining the design flow ordered,
   clean and extensible. You can read more about it in the documentation TODO add link.

   TODO just make this a cookiecutter. TO BE DEPRECATED whenever I get round to that.

   :param project_name: Name of the project.
   :type project_name: str
   :param parent_directory: Parent directory of the project.
   :type parent_directory: PathTypes

   :returns: None


.. py:function:: get_module_folder_type_location(module: piel.types.ModuleType, folder_type: Literal['digital_source', 'digital_testbench'])

   This is an easy helper function that saves a particular file in the corresponding location of a `piel` project structure.

   TODO DOCS


.. py:function:: pip_install_local_module(module_path: piel.types.PathTypes)

   This function installs a local module in editable mode.

   :param module_path: Path to the module to be installed.
   :type module_path: PathTypes

   :returns: None


.. py:function:: read_configuration(design_directory: piel.types.PathTypes) -> dict

   This function reads the configuration file found in the design directory.

   :param design_directory: Design directory PATH.
   :type design_directory: PathTypes

   :returns: Configuration dictionary.
   :rtype: config_dictionary(dict)


.. py:function:: round_complex_array(array: piel.types.ArrayTypes, to_absolute: bool = False)

   Rounds the elements of a complex JAX numpy array to the nearest integer.

   Parameters:
   - array: A complex JAX numpy array.
   - absolute: A boolean that determines whether the complex numbers are rounded to the nearest integers in their absolute value.

   Returns:
   - A JAX numpy array with the complex elements rounded to the nearest integers.


.. py:function:: single_parameter_sweep(base_design_configuration: dict, parameter_name: str, parameter_sweep_values: list)

   This function takes a base_design_configuration dictionary and sweeps a single parameter over a list of values. It returns a list of dictionaries that correspond to the parameter sweep.

   :param base_design_configuration: Base design configuration dictionary.
   :type base_design_configuration: dict
   :param parameter_name: Name of parameter to sweep.
   :type parameter_name: str
   :param parameter_sweep_values: List of values to sweep.
   :type parameter_sweep_values: list

   :returns: List of dictionaries that correspond to the parameter sweep.
   :rtype: parameter_sweep_design_dictionary_array(list)


.. py:function:: multi_parameter_sweep(base_design_configuration: dict, parameter_sweep_dictionary: dict) -> list

   This multiparameter sweep is pretty cool, as it will generate designer list of dictionaries that comprise of all the possible combinations of your parameter sweeps. For example, if you are sweeping `parameter_1 = np.arange(0, 2) = array([0, 1])`, and `parameter_2 = np.arange(2, 4) = array([2, 3])`, then this function will generate list of dictionaries based on the default_design dictionary, but that will comprise of all the potential parameter combinations within this list.

   For the example above, there arould be 4 combinations [(0, 2), (0, 3), (1, 2), (1, 3)].

   If you were instead sweeping for `parameter_1 = np.arange(0, 5)` and `parameter_2 = np.arange(0, 5)`, the dictionary generated would correspond to these parameter combinations of::
       [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)].

   Make sure to use the parameter_names from default_design when writing up the parameter_sweep dictionary key name.

   Example project_structure formats::

       example_parameter_sweep_dictionary = {
           "parameter_1": np.arange(1, -40, 1),
           "parameter_2": np.arange(1, -40, 1),
       }

       example_base_design_configuration = {
           "parameter_1": 10.0,
           "parameter_2": 40.0,
           "parameter_3": 0,
       }

   :param base_design_configuration: Dictionary of the default design configuration.
   :type base_design_configuration: dict
   :param parameter_sweep_dictionary: Dictionary of the parameter sweep. The keys should be the same as the keys in the base_design_configuration dictionary.
   :type parameter_sweep_dictionary: dict

   :returns: List of dictionaries that comprise of all the possible combinations of your parameter sweeps.
   :rtype: parameter_sweep_design_dictionary_array(list)


.. py:function:: get_unique_dataframe_subsets(dataframe: pandas.DataFrame, max_depth: int = 2) -> dict[str, pandas.DataFrame]

   This function takes a pandas DataFrame and returns a dictionary of unique subsets of the DataFrame.
   It is useful for identifying unique operating points in a dataset.
   The function returns a dictionary where the keys are string identifiers for the subsets, and the values are DataFrames containing
   the unique combinations of values for each column in the input DataFrame.

   :param dataframe:
   :type dataframe: pd.DataFrame
   :param max_depth: The maximum depth of combinations to consider. For example, if max_depth is 2, only combinations of up to 2 columns will be generated.
   :type max_depth: int, optional

   :returns: A dictionary where the keys are string identifiers for the subsets and the values are DataFrames containing the unique combinations of values.
   :rtype: dict[str, pd.DataFrame]


.. py:function:: create_all_connections(ports: list[piel.types.Port], connection_factory: Optional[Callable[[list[piel.types.Port]], piel.types.Connection]] = None, connection_type_output: Optional[piel.types.ConnectionTypes] = Connection) -> list[piel.types.ConnectionTypes]

   This function receives a list of connection and creates the connections between them as two-port relationships.
   It returns a list of connections. More than two connection can be provided, and it will create all the possible connections.

   :param ports: The connection list to create connections.
   :type ports: list[Port]
   :param connection_factory: A function that creates a connection object from a list of connection.
                              The function should receive a list of connection and return a connection object.
                              If not provided, a default connection factory will be used.
                              The default connection factory creates a tuple of connection as a connection object.
                              The default is None.
   :type connection_factory: Optional[Callable[[list[Port]], Connection]], optional
   :param connection_type_output: The type of connection object to return.

                                  If not provided, the default connection factory will be used.

                                  The default is None
   :type connection_type_output: Optional[type[Connection]], optional

   :returns: A list of connections that were created.
   :rtype: list[Connection]


.. py:function:: create_component_connections(components: list[piel.types.ComponentTypes], connection_reference_str_list: list[str] | list[list[str]]) -> list[piel.types.ConnectionTypes]

   The way this function works is by composing the connection namespaces from the names of the components,
   and a given connection dot notation which corresponds to that component.

   .. rubric:: Notes

   The dot notation would be in the format ``"component_1.port1"``. Hence, the input to a connection would be
   ``["component1.port1", "component2.port1"]`` and this function would compile into generating the corresponding
   connection. This is by splitting the component name and port name accordingly and then programmatically acquiring
   the corresponding `Port` reference and creating the `Connection` from this.

   :param components: The components to create connections from.
   :type components: list[ComponentTypes]
   :param connection_reference_str_list: The list of strings that represent the connections to create.
   :type connection_reference_str_list: list[str] | list[list[str]]

   :returns: The list of connections created from the components.
   :rtype: list[ConnectionTypes]


.. py:function:: create_sequential_component_path(components: list[piel.types.ComponentTypes], name: str = '', **kwargs) -> piel.types.ComponentTypes

   This function takes in a list of components and creates a sequential path connectivity of components with all the connection defined in each component.
   By default, the connectivity will be implemented with the first two connection of the components. There is a clear input and output on each component.
   The timing metric calculations is provided by the timing model of each connection of the component, if there is none defined it will assume a default zero
   time connectivity between the relevant connection. For the output component collection, it will output the timing of the network as a whole based on the
   defined subcomponents.
   This will create an output component with all the subcomponents, TODO more than two connection, and the list of connection

   Creates a sequential path connectivity of components with all the connection defined in each component.

   Parameters:
   -----------
   components : List[ComponentTypes]
         A list of components to be connected sequentially.

   Returns:
   --------
   :
   ComponentTypes
       A new component that encapsulates the sequential path of input components.


.. py:function:: create_connection_list_from_ports_lists(port_connection_list: list[list[piel.types.Port]]) -> list[piel.types.ConnectionTypes]

   When a list of a list of connection is provided, we construct all the required connections accordingly. TODO more docs.


.. py:function:: get_port_index_from_name(port: piel.types.Port, starting_index: int | None = None) -> int

   Extracts the numerical index from a port identifier and adjusts based on starting index.
   If port numbering starts at 0, adds 1. If starts at 1 or is None, leaves as is.

   Parameters:
   - port (int or str): The port identifier.
   - starting_index (int, optional): The starting index (0 or 1). Defaults to None.

   Returns:
   - int: The adjusted numerical index of the port.

   Raises:
   - ValueError: If starting_index is not 0, 1, or None.
   - ValueError: If the port string does not contain a numerical index.
   - TypeError: If the port is neither int nor str.


.. py:data:: __author__
   :value: 'Dario Quintero'


.. py:data:: __email__
   :value: 'darioaquintero@gmail.com'


.. py:data:: __version__
   :value: '0.1.0'


