From bd6a6db1d57ae42196926d03d6cee40b97043508 Mon Sep 17 00:00:00 2001 From: Michael McKinsey Date: Tue, 25 Jun 2024 16:04:37 -0500 Subject: [PATCH 1/5] Add to docs --- docs/index.rst | 2 ++ docs/thicket_properties.rst | 50 +++++++++++++++++++++++++++++++++++++ docs/unification.rst | 11 ++++++++ 3 files changed, 63 insertions(+) create mode 100644 docs/thicket_properties.rst create mode 100644 docs/unification.rst diff --git a/docs/index.rst b/docs/index.rst index cd1ad1ea..a0c3ff3c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -64,6 +64,8 @@ If you encounter bugs while using thicket, you can report them by opening an iss :caption: Developer Docs developer_guide + thicket_properties + unification.rst .. toctree:: :maxdepth: 2 diff --git a/docs/thicket_properties.rst b/docs/thicket_properties.rst new file mode 100644 index 00000000..133063b6 --- /dev/null +++ b/docs/thicket_properties.rst @@ -0,0 +1,50 @@ +.. + Copyright 2022 Lawrence Livermore National Security, LLC and other + Thicket Project Developers. See the top-level LICENSE file for details. + + SPDX-License-Identifier: MIT + +################# + Thicket Properties +################# + +Thicket compositional operations assume certain properties about the state of the Thicket and its components. We check properties about the Thicket and its components after operations to ensure the Thicket is in a valid state using utility functions `thicket/utils.py`. + +Nodes +===== + +:code:`hatchet.Node` objects represent regions from the executed program. The Thicket components that contain *Nodes* are: + + - :code:`Thicket.graph` + - :code:`Thicket.dataframe` + - :code:`Thicket.statsframe.graph` + - :code:`Thicket.statsframe.dataframe` + +1. :code:`utils.validate_nodes` - *Node* objects are identical between components. :code:`id(node1) == id(node2)`. + + - The :code:`Thicket.statsframe.graph` is the :code:`Thicket.graph`, so this is implicit. + +Profiles +========= + +A *profile* in Thicket is a unique identifier, which is directly mapped to the performance "profile" it represents (:code:`Thicket.profile_mapping`). The *profile* may either be an integer or a tuple. The Thicket components that contain profiles are: + + - :code:`Thicket.profile` + - :code:`Thicket.profile_mapping` + - :code:`Thicket.dataframe` + - :code:`Thicket.metadata` + +1. :code:`utils.validate_profile._validate_all_same` - *profiles* are **equal**. :code:`profile1 == profile2`. +2. :code:`utils.validate_profile._validate_no_duplicates` - There are no duplicate *profiles* in any component. +3. :code:`utils.validate_profile._validate_multiindex_column` - :code:`Thicket.dataframe` and :code:`Thicket.metadata` must both contain :code:`pd.MultiIndex` columns, if either one does. + + - If the columns are *MultiIndex*, the *profiles* are tuples, otherwise the *profiles* are integers. + +Performance Data +================== + +The :code:`Thicket.dataframe` contains the performance data and is checked for the following properties: + +1. :code:`utils.validate_dataframe._check_duplicate_inner_idx` - There are no duplicate indices. +2. :code:`utils.validate_dataframe._check_missing_hnid` - *Node* objects, identified by their :code:`_hatchet_nid` are in ascending order without gaps. +3. :code:`utils.validate_dataframe._validate_name_column` - The values in the "name" column match the :code:`Node.frame["name"]` attribute for that row or are :code:`None`. \ No newline at end of file diff --git a/docs/unification.rst b/docs/unification.rst new file mode 100644 index 00000000..c3b34389 --- /dev/null +++ b/docs/unification.rst @@ -0,0 +1,11 @@ +.. + Copyright 2022 Lawrence Livermore National Security, LLC and other + Thicket Project Developers. See the top-level LICENSE file for details. + + SPDX-License-Identifier: MIT + +################# + Unifying Thickets +################# + +Unifying multiple Thickets is crucial in the process of concatenating multiple Thickets into one Thicket object. From f3f419c353e62cd651565e8500bfebe6c90bff3c Mon Sep 17 00:00:00 2001 From: Michael McKinsey Date: Tue, 2 Jul 2024 15:48:29 -0500 Subject: [PATCH 2/5] add documentation --- docs/concatenating_thickets.rst | 62 +++++++++++++++++++++++++++++++ docs/images/reading_thickets.png | Bin 0 -> 54426 bytes docs/index.rst | 2 +- docs/unification.rst | 11 ------ 4 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 docs/concatenating_thickets.rst create mode 100644 docs/images/reading_thickets.png delete mode 100644 docs/unification.rst diff --git a/docs/concatenating_thickets.rst b/docs/concatenating_thickets.rst new file mode 100644 index 00000000..e9186d26 --- /dev/null +++ b/docs/concatenating_thickets.rst @@ -0,0 +1,62 @@ +.. + Copyright 2022 Lawrence Livermore National Security, LLC and other + Thicket Project Developers. See the top-level LICENSE file for details. + + SPDX-License-Identifier: MIT + +*************************** + Concatenating Thickets +*************************** + +Thicket does **not** implement any native profile readers (meaning readers that read performance profiles directly into Thicket objects). All readers used in Thicket use Hatchet readers to read each performance profile into a :code:`Hatchet.GraphFrame` (Fig 1B), which is then converted to a :code:`Thicket.Thicket` object containing one profile using the :code:`Thicket.thicketize_graphframe()` function (Fig 1C). The one profile Thickets are then concatenated into a single Thicket object using the :code:`Thicket.concat_thickets()` function (Fig 1D), which is capable of concatenating a set of n Thickets, T: + +.. math:: + T = \{t_1, t_2, ..., t_n\} + +where each :math:`t_i` (:math:`i = 1, 2, ..., n`) has number of profiles :math:`p_i >= 1`. + +.. figure:: images/reading_thickets.png + :align: center + + Figure 1: Example of the Thicket reader process for 4 Caliper profiles. + +| + +The process of concatenating Thickets requires the unification of some structures like the :code:`Thicket.graph` (calltree), which is explained below. + +################## +Unifying Thickets +################## + +This section mainly refers to the :code:`Thicket.Ensemble._unify()` function. + +=================== +Unifying Calltrees +=================== + +*Unifying Calltrees* is the process of performing a **set operation** (e.g. :code:`Hatchet.graph.union()`) on multiple :code:`Thicket.graph`'s. Comparing two graphs involves comparing :code:`Hatchet.Node` objects between the graphs. The :code:`Hatchet.graph.union()` function computes the union graph between two Hatchet graphs. Nodes are compared by: + +1. `Their depth in the tree `_ - :code:`Node._depth`. +2. `Their frame `_ ("name" and "type") - :code:`Node.frame._tuple_repr` + +Nodes that match in #1 and #2 are merged in the resulting union graph as a new :code:`Hatchet.Node` object (`deep copy of the first node `_). Deep copies of nodes that do **not** match are inserted into the union graph at the appropriate depth. + +*Note:* The :code:`Thicket.intersection()` function first applies the :code:`Hatchet.graph.union()` before computing the intersection of the graphs, since their does not exist a :code:`Hatchet.graph.intersection` function. + +====================== +Updating Node Objects +====================== + +Because Node objects must be identical between Thicket components (see :ref:`/thicket_properties.rst`), The resulting new nodes in the union graph must replace the old node objects in components like the :code:`Thicket.dataframe.index` (see `code `_). The :code:`Hatchet.graph.union()` function provides a dictionary mapping old nodes to new nodes, however to avoid applying these updates after every union between two graphs, we `update a dictionary of all the node mappings `_ and apply the updates after all of the unions have been computed. This is **only** necessary when concatenating more than **two** Thickets, as only one union will be performed when concatenating two Thickets. We `apply this idea when reading files `_ to avoid this cost. + +#################### +Index Concatenation +#################### + +todo + +##################### +Column Concatenation +##################### + +todo \ No newline at end of file diff --git a/docs/images/reading_thickets.png b/docs/images/reading_thickets.png new file mode 100644 index 0000000000000000000000000000000000000000..8bab652eac9ef59d75d978aeb1a8833d8a0342eb GIT binary patch literal 54426 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 Date: Tue, 2 Jul 2024 16:04:01 -0500 Subject: [PATCH 3/5] Add short descriptions for index and column --- docs/concatenating_thickets.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/concatenating_thickets.rst b/docs/concatenating_thickets.rst index e9186d26..22baa05b 100644 --- a/docs/concatenating_thickets.rst +++ b/docs/concatenating_thickets.rst @@ -47,16 +47,16 @@ Nodes that match in #1 and #2 are merged in the resulting union graph as a new : Updating Node Objects ====================== -Because Node objects must be identical between Thicket components (see :ref:`/thicket_properties.rst`), The resulting new nodes in the union graph must replace the old node objects in components like the :code:`Thicket.dataframe.index` (see `code `_). The :code:`Hatchet.graph.union()` function provides a dictionary mapping old nodes to new nodes, however to avoid applying these updates after every union between two graphs, we `update a dictionary of all the node mappings `_ and apply the updates after all of the unions have been computed. This is **only** necessary when concatenating more than **two** Thickets, as only one union will be performed when concatenating two Thickets. We `apply this idea when reading files `_ to avoid this cost. +Because Node objects must be identical between Thicket components (see :ref:`/thicket_properties.rst#nodes`), The resulting new nodes in the union graph must replace the old node objects in components like the :code:`Thicket.dataframe.index` (see `code `_). The :code:`Hatchet.graph.union()` function provides a dictionary mapping old nodes to new nodes, however to avoid applying these updates after every union between two graphs, we `update a dictionary of all the node mappings `_ and apply the updates after all of the unions have been computed. This is **only** necessary when concatenating more than **two** Thickets, as only one union will be performed when concatenating two Thickets. We `apply this idea when reading files `_ to avoid this cost. #################### Index Concatenation #################### -todo +*Index Concatenation* refers to the process that happens for the performance and metadata tables. We concatenate the tables, which is essentially "stacking the rows on top of each other". Because we check that the performance profiles we concatenate are unique (:ref:`/thicket_properties.rst#profiles`), we do not need to worry about duplicate indices in either table. We sort the index of both tables, which interleaves the profiles in the MultiIndex of the performance table to visually group all of the profiles in the table for each node. An example of this operation can be seen in the :ref:`/thicket_tutorial.ipynb`, when :code:`axis="columns"`. ##################### Column Concatenation ##################### -todo \ No newline at end of file +*Column Concatenation* refers to the process that happens in the performance, metadata, and statistics tables. We create a MultiIndex out of the columns, such that for each metric, there is a higher level index label. An example of this operation can be seen in the :ref:`/thicket_tutorial.ipynb`, when :code:`axis="columns"`. \ No newline at end of file From b66baf350b325dc14a1b5afa73128f217370aad2 Mon Sep 17 00:00:00 2001 From: Michael McKinsey Date: Tue, 2 Jul 2024 17:27:06 -0500 Subject: [PATCH 4/5] update --- docs/concatenating_thickets.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/concatenating_thickets.rst b/docs/concatenating_thickets.rst index 22baa05b..c2dc75f1 100644 --- a/docs/concatenating_thickets.rst +++ b/docs/concatenating_thickets.rst @@ -34,14 +34,16 @@ This section mainly refers to the :code:`Thicket.Ensemble._unify()` function. Unifying Calltrees =================== -*Unifying Calltrees* is the process of performing a **set operation** (e.g. :code:`Hatchet.graph.union()`) on multiple :code:`Thicket.graph`'s. Comparing two graphs involves comparing :code:`Hatchet.Node` objects between the graphs. The :code:`Hatchet.graph.union()` function computes the union graph between two Hatchet graphs. Nodes are compared by: +*Unifying Calltrees* is the process of performing a **set operation** (e.g. :code:`Hatchet.graph.union()`) on multiple :code:`Thicket.graph`'s. Comparing two graphs involves comparing :code:`Hatchet.Node` objects between the graphs. The :code:`Hatchet.graph.union()` function computes the union graph between two Hatchet graphs. For the union, nodes are compared by: 1. `Their depth in the tree `_ - :code:`Node._depth`. 2. `Their frame `_ ("name" and "type") - :code:`Node.frame._tuple_repr` Nodes that match in #1 and #2 are merged in the resulting union graph as a new :code:`Hatchet.Node` object (`deep copy of the first node `_). Deep copies of nodes that do **not** match are inserted into the union graph at the appropriate depth. -*Note:* The :code:`Thicket.intersection()` function first applies the :code:`Hatchet.graph.union()` before computing the intersection of the graphs, since their does not exist a :code:`Hatchet.graph.intersection` function. +*Note:* Comparing nodes with the equality operator (:code:`==`) is not sufficient, as the equality operator only compares the :code:`Node._hatchet_nid`, which is not the same as the above comparison. + +*Note:* The :code:`Thicket.intersection()` function first applies the :code:`Hatchet.graph.union()` before computing the intersection of the graphs, since their does not exist a :code:`Hatchet.graph.intersection()` function. ====================== Updating Node Objects From 3324682c388b0dd007abf6ce131a9f14358a2ca7 Mon Sep 17 00:00:00 2001 From: Michael McKinsey Date: Tue, 2 Jul 2024 19:17:50 -0500 Subject: [PATCH 5/5] Fix wording --- docs/concatenating_thickets.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concatenating_thickets.rst b/docs/concatenating_thickets.rst index c2dc75f1..d7a294c1 100644 --- a/docs/concatenating_thickets.rst +++ b/docs/concatenating_thickets.rst @@ -34,7 +34,7 @@ This section mainly refers to the :code:`Thicket.Ensemble._unify()` function. Unifying Calltrees =================== -*Unifying Calltrees* is the process of performing a **set operation** (e.g. :code:`Hatchet.graph.union()`) on multiple :code:`Thicket.graph`'s. Comparing two graphs involves comparing :code:`Hatchet.Node` objects between the graphs. The :code:`Hatchet.graph.union()` function computes the union graph between two Hatchet graphs. For the union, nodes are compared by: +*Unifying Calltrees* is the process of performing a **graph operation** (e.g. :code:`Hatchet.graph.union()`) on multiple :code:`Thicket.graph`'s. Comparing two graphs involves comparing :code:`Hatchet.Node` objects between the graphs. The :code:`Hatchet.graph.union()` function computes the union graph between two Hatchet graphs. For the union, nodes are compared by: 1. `Their depth in the tree `_ - :code:`Node._depth`. 2. `Their frame `_ ("name" and "type") - :code:`Node.frame._tuple_repr`