From 5669d362505d078365561a52b1a2b1930dc6b9d4 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Thu, 17 Nov 2022 08:59:20 -0500 Subject: [PATCH 1/3] CI: Add GitHub CI for Clang-Format Signed-off-by: Michael Jackson --- {.github-disabled/workflows => .github}/format_pr.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {.github-disabled/workflows => .github}/format_pr.yml (100%) diff --git a/.github-disabled/workflows/format_pr.yml b/.github/format_pr.yml similarity index 100% rename from .github-disabled/workflows/format_pr.yml rename to .github/format_pr.yml From d5f4598e2a060ccc0290b0c95a05cdf391d724c4 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Thu, 3 Nov 2022 09:56:24 -0400 Subject: [PATCH 2/3] Clustering Filters for PW-Alarm project Signed-off-by: Michael Jackson --- Core/CMakeLists.txt | 10 + Core/docs/ImportDeformKeyFilev12.md | 42 ++++ Core/docs/KMeans.md | 84 ++++++++ Core/docs/KMedoids.md | 84 ++++++++ Core/docs/Silhouette.md | 54 ++++++ .../Algorithms/ImportDeformKeyFilev12.cpp | 48 +++++ .../Algorithms/ImportDeformKeyFilev12.hpp | 69 +++++++ Core/src/Core/Filters/Algorithms/KMeans.cpp | 48 +++++ Core/src/Core/Filters/Algorithms/KMeans.hpp | 77 ++++++++ Core/src/Core/Filters/Algorithms/KMedoids.cpp | 48 +++++ Core/src/Core/Filters/Algorithms/KMedoids.hpp | 77 ++++++++ .../Core/Filters/Algorithms/Silhouette.cpp | 48 +++++ .../Core/Filters/Algorithms/Silhouette.hpp | 71 +++++++ .../Filters/ImportDeformKeyFilev12Filter.cpp | 151 +++++++++++++++ .../Filters/ImportDeformKeyFilev12Filter.hpp | 101 ++++++++++ Core/src/Core/Filters/KMeansFilter.cpp | 169 ++++++++++++++++ Core/src/Core/Filters/KMeansFilter.hpp | 104 ++++++++++ Core/src/Core/Filters/KMedoidsFilter.cpp | 171 ++++++++++++++++ Core/src/Core/Filters/KMedoidsFilter.hpp | 104 ++++++++++ Core/src/Core/Filters/SilhouetteFilter.cpp | 182 ++++++++++++++++++ Core/src/Core/Filters/SilhouetteFilter.hpp | 102 ++++++++++ Core/test/CMakeLists.txt | 11 ++ .../test/ImportDeformKeyFilev12FilterTest.cpp | 70 +++++++ Core/test/ImportDeformKeyFilev12Test.cpp | 70 +++++++ Core/test/KMeansTest.cpp | 72 +++++++ Core/test/KMedoidsTest.cpp | 72 +++++++ Core/test/SilhouetteTest.cpp | 69 +++++++ 27 files changed, 2208 insertions(+) create mode 100644 Core/docs/ImportDeformKeyFilev12.md create mode 100644 Core/docs/KMeans.md create mode 100644 Core/docs/KMedoids.md create mode 100644 Core/docs/Silhouette.md create mode 100644 Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.cpp create mode 100644 Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.hpp create mode 100644 Core/src/Core/Filters/Algorithms/KMeans.cpp create mode 100644 Core/src/Core/Filters/Algorithms/KMeans.hpp create mode 100644 Core/src/Core/Filters/Algorithms/KMedoids.cpp create mode 100644 Core/src/Core/Filters/Algorithms/KMedoids.hpp create mode 100644 Core/src/Core/Filters/Algorithms/Silhouette.cpp create mode 100644 Core/src/Core/Filters/Algorithms/Silhouette.hpp create mode 100644 Core/src/Core/Filters/ImportDeformKeyFilev12Filter.cpp create mode 100644 Core/src/Core/Filters/ImportDeformKeyFilev12Filter.hpp create mode 100644 Core/src/Core/Filters/KMeansFilter.cpp create mode 100644 Core/src/Core/Filters/KMeansFilter.hpp create mode 100644 Core/src/Core/Filters/KMedoidsFilter.cpp create mode 100644 Core/src/Core/Filters/KMedoidsFilter.hpp create mode 100644 Core/src/Core/Filters/SilhouetteFilter.cpp create mode 100644 Core/src/Core/Filters/SilhouetteFilter.hpp create mode 100644 Core/test/ImportDeformKeyFilev12FilterTest.cpp create mode 100644 Core/test/ImportDeformKeyFilev12Test.cpp create mode 100644 Core/test/KMeansTest.cpp create mode 100644 Core/test/KMedoidsTest.cpp create mode 100644 Core/test/SilhouetteTest.cpp diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 8322425f1..7349c7a92 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -32,6 +32,11 @@ set(FilterList RemoveFlaggedFeaturesFilter SplitAttributeArrayFilter FeatureDataCSVWriterFilter + + KMedoidsFilter + KMeansFilter + SilhouetteFilter + ImportDeformKeyFilev12Filter ) set(STUB_FILTERS @@ -105,6 +110,11 @@ set(filter_algorithms CalculateArrayHistogram RemoveFlaggedFeatures SplitAttributeArray + + KMedoids + KMeans + Silhouette + ImportDeformKeyFilev12 ) diff --git a/Core/docs/ImportDeformKeyFilev12.md b/Core/docs/ImportDeformKeyFilev12.md new file mode 100644 index 000000000..acf6ee06f --- /dev/null +++ b/Core/docs/ImportDeformKeyFilev12.md @@ -0,0 +1,42 @@ +# ImportDeformKeyFilev12 # + + +## Group (Subgroup) ## + +SimulationIO (SimulationIO) + +## Description ## + +This **Filter** reads DEFORM v12 key files and saves the data in a newly created **Data Container**. + +It reads the quadrilateral mesh data (nodal coordinates and connectivity), and the value of variables such as stress, strain, ndtmp, etc at cells and nodes. + +## Parameters ## + +| Name | Type | Description | +|------|------|------| +| Input File | Path | Name and address of the input DEFORM v12 key file | + +## Required Geometry ## + +Not Applicable + +## Required Objects ## + +## Created Objects ## +| Kind | Default Name | Type | Component Dimensions | Description | +|------|--------------|-------------|---------|-----| +| **Data Container** | DataContainer | N/A | N/A | Created **Data Container** | +| **Attribute Matrix** | VertexData | Vertex | N/A | Created **Vertex Attribute Matrix** name | +| **Attribute Matrix** | CellData | Cell | N/A | Created **Cell Attribute Matrix** name | + +## Example Pipelines ## + +## License & Copyright ## + +Please see the description file distributed with this plugin. + +## DREAM3D Mailing Lists ## + +If you need more help with a filter, please consider asking your question on the DREAM3D Users mailing list: +https://groups.google.com/forum/?hl=en#!forum/dream3d-users \ No newline at end of file diff --git a/Core/docs/KMeans.md b/Core/docs/KMeans.md new file mode 100644 index 000000000..25fdb6ffd --- /dev/null +++ b/Core/docs/KMeans.md @@ -0,0 +1,84 @@ +# K Means # + +## Group (Subgroup) ## + +DREAM3D Review (Clustering) + +## Description ## + +This **Filter** applies the k means algorithm to an **Attribute Array**. K means is a _clustering algorithm_ that assigns to each point of the **Attribute Array** a _cluster Id_. The user must specify the number of clusters in which to partition the array. Specifically, a k means partitioning is a _Voronoi tesselation_; an optimal solution to the k means problem is such that each point in the data set is associated with the cluster that has the closest mean. This partitioning is the one that minimizes the within cluster variance (i.e., minimizes the within cluster sum of squares differences). Thus, the "metric" used for k means is the 2-norm (the _Euclidean norm_; the squared Euclidean norm may also be used since this maintains the triangle inequality). + +Optimal solutions to the k means partitioning problem are computationally difficult; this **Filter** used _Lloyd's algorithm_ to approximate the solution. Lloyd's algorithm is an iterative algorithm that proceeds as follows: + +1. Choose k points at random to serve as the initial cluster "means" +2. Until convergence, repeat the following steps: + * Associate each point with the closest mean, where "closest" is the smallest 2-norm distance + * Recompute the means based on the new tesselation + +Convergence is defined as when the computed means change very little (precisely, when the differences are within machine epsilon). Since Lloyd's algorithm is iterative, it only serves as an approximation, and may result in different classifications on each execution with the same input data. The user may opt to use a mask to ignore certain points; where the mask is _false_, the points will be placed in cluster 0. + +A clustering algorithm can be considered a kind of segmentation; this implementation of k means does not rely on the **Geometry** on which the data lie, only the _topology_ of the space that the array itself forms. Therefore, this **Filter** has the effect of creating either **Features** or **Ensembles** depending on the kind of array passed to it for clustering. If an **Element** array (e.g., voxel-level **Cell** data) is passed to the **Filter**, then **Features** are created (in the previous example, a **Cell Feature Attribute Matrix** will be created). If a **Feature** array is passed to the **Filter**, then an **Ensemble Attribute Matrix** is created. The following table shows what type of **Attribute Matrix** is created based on what sort of array is used for clustering: + +| Attribute Matrix Source | Attribute Matrix Created | +|------------------|--------------------| +| Generic | Generic | +| Vertex | Vertex Feature | +| Edge | Edge Feature | +| Face | Face Feature | +| Cell | Cell Feature| +| Vertex Feature | Vertex Ensemble | +| Edge Feature | Edge Ensemble | +| Face Feature | Face Ensemble | +| Cell Feature | Cell Ensemble| +| Vertex Ensemble | Vertex Ensemble | +| Edge Ensemble | Edge Ensemble | +| Face Ensemble | Face Ensemble | +| Cell Ensemble | Cell Ensemble| + +This **Filter** will store the means for the final clusters within the created **Attribute Matrix**. + +## Parameters ## + +| Name | Type | Description | +|------|------|-------------| +| Number of Clusters | int32_t | The number of clusters in which to partition the array | +| Distance Metric | Enumeration | The metric used to determine the distances between points; only 2-norm metrics (i.e., Euclidean or squared Euclidean) may be chosen | +| Use Mask | bool | Whether to use a boolean mask array to ignore certain points flagged as _false_ from the algorithm | + +## Required Geometry ### + +None + +## Required Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|--------------|------|----------------------|-------------| +| Any **Attribute Array** | None | Any| Any | The **Attribute Array** to cluster | +| **Attrubute Array** | Mask | bool | (1) | Specifies if the point is to be counted in the algorithm, if _Use Mask_ is checked | + +## Created Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|--------------|------|----------------------|-------------| +| **Attribute Matrix** | ClusterData | Feature/Ensemble | N/A | The **Attribute Matrix** in which to store information associated with the created clusters | +| **Attribute Array** | ClusterIds | int32_t | (1) | Specifies to which cluster each point belongs | +| **Attribute Array** | ClusterMeans | double | (1) | The means of the final clusters | + + +## References ## + +[1] Least squares quantization in PCM, S.P. Lloyd, IEEE Transactions on Information Theory, vol. 28 (2), pp. 129-137, 1982. + +## Example Pipelines ## + + + +## License & Copyright ## + +Please see the description file distributed with this plugin. + +## DREAM3D Mailing Lists ## + +If you need more help with a filter, please consider asking your question on the DREAM3D Users mailing list: +https://groups.google.com/forum/?hl=en#!forum/dream3d-users + diff --git a/Core/docs/KMedoids.md b/Core/docs/KMedoids.md new file mode 100644 index 000000000..fafddc82b --- /dev/null +++ b/Core/docs/KMedoids.md @@ -0,0 +1,84 @@ +# K Medoids # + +## Group (Subgroup) ## + +DREAM3D Review (Clustering) + +## Description ## + +This **Filter** applies the k medoids algorithm to an **Attribute Array**. K medoids is a _clustering algorithm_ that assigns to each point of the **Attribute Array** a _cluster Id_. The user must specify the number of clusters in which to partition the array. Specifically, a k medoids partitioning is such that each point in the data set is associated with the cluster that minimizes the sum of the pair-wise distances between the data points and their associated cluster centers (medoids). This approach is analogous to [k means](@ref kmeans), but uses actual data points (the medoids) as the cluster exemplars instead of the means. Medoids in this context refer to the data point in each cluster that is most like all other data points, i.e., that data point whose average distance to all other data points in the cluster is smallest. Unlike [k means](@ref kmeans), since pair-wise distances are minimized instead of variance, any arbirtary concept of "distance" may be used; this **Filter** allows for the selection of a variety of distance metrics. + +This **Filter** uses the _Voronoi iteration_ algorithm to produce the clustering. The algorithm is iterative and proceeds as follows: + +1. Choose k points at random to serve as the initial cluster medoids +2. Associate each point to the closest medoid +3. Until convergence, repeat the following steps: + * For each cluster, change the medoid to the point in that cluster that minimizes the sum of distances between that point and all other points in the cluster + * Reassign each point to the closest medoid + +Convergence is defined as when the medoids no longer change position. Since the algorithm is iterative, it only serves as an approximation, and may result in different classifications on each execution with the same input data. The user may opt to use a mask to ignore certain points; where the mask is _false_, the points will be placed in cluster 0. + +A clustering algorithm can be considered a kind of segmentation; this implementation of k medoids does not rely on the **Geometry** on which the data lie, only the _topology_ of the space that the array itself forms. Therefore, this **Filter** has the effect of creating either **Features** or **Ensembles** depending on the kind of array passed to it for clustering. If an **Element** array (e.g., voxel-level **Cell** data) is passed to the **Filter**, then **Features** are created (in the previous example, a **Cell Feature Attribute Matrix** will be created). If a **Feature** array is passed to the **Filter**, then an **Ensemble Attribute Matrix** is created. The following table shows what type of **Attribute Matrix** is created based on what sort of array is used for clustering: + +| Attribute Matrix Source | Attribute Matrix Created | +|------------------|--------------------| +| Generic | Generic | +| Vertex | Vertex Feature | +| Edge | Edge Feature | +| Face | Face Feature | +| Cell | Cell Feature| +| Vertex Feature | Vertex Ensemble | +| Edge Feature | Edge Ensemble | +| Face Feature | Face Ensemble | +| Cell Feature | Cell Ensemble| +| Vertex Ensemble | Vertex Ensemble | +| Edge Ensemble | Edge Ensemble | +| Face Ensemble | Face Ensemble | +| Cell Ensemble | Cell Ensemble| + +This **Filter** will store the medoids for the final clusters within the created **Attribute Matrix**. + +## Parameters ## + +| Name | Type | Description | +|------|------|-------------| +| Number of Clusters | int32_t | The number of clusters in which to partition the array | +| Distance Metric | Enumeration | The metric used to determine the distances between points | +| Use Mask | bool | Whether to use a boolean mask array to ignore certain points flagged as _false_ from the algorithm | + +## Required Geometry ### + +None + +## Required Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|--------------|------|----------------------|-------------| +| Any **Attribute Array** | None | Any| Any | The **Attribute Array** to cluster | +| **Attrubute Array** | Mask | bool | (1) | Specifies if the point is to be counted in the algorithm, if _Use Mask_ is checked | + +## Created Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|--------------|------|----------------------|-------------| +| **Attribute Matrix** | ClusterData | Feature/Ensemble | N/A | The **Attribute Matrix** in which to store information associated with the created clusters | +| **Attribute Array** | ClusterIds | int32_t | (1) | Specifies to which cluster each point belongs | +| **Attribute Array** | ClusterMeans | double | (1) | The means of the final clusters | + + +## References ## + +[1] A simple and fast algorithm for K-medoids clustering, H.S. Park and C.H. Jun, Expert Systems with Applications, vol. 28 (2), pp. 3336-3341, 2009. + +## Example Pipelines ## + + + +## License & Copyright ## + +Please see the description file distributed with this plugin. + +## DREAM3D Mailing Lists ## + +If you need more help with a filter, please consider asking your question on the DREAM3D Users mailing list: +https://groups.google.com/forum/?hl=en#!forum/dream3d-users diff --git a/Core/docs/Silhouette.md b/Core/docs/Silhouette.md new file mode 100644 index 000000000..881b02c45 --- /dev/null +++ b/Core/docs/Silhouette.md @@ -0,0 +1,54 @@ +# Silhouette # + +## Group (Subgroup) ## + +DREAM3D Review (Clustering) + +## Description ## + +This **Filter** computes the silhouette for a clustered **Attribute Array**. The user must select both the original array that has been clustered and the array of cluster Ids. The silhouette represents a measure for the quality of a clustering. Specifically, the silhouette provides a measure for how strongly a given point belongs to its own cluster compared to all other clusters. The silhouette is computed as follows: + +\f[ s_{i} = \frac{b_{i} - a_{i}}{\max\{a_{i},b_{i}\}} \f] + +where \f$ a \f$ is the average distance between point \f$ i \f$ and all other points in the cluster point \f$ i \f$ belongs to, \f$ b \f$ is the _next closest_ average distance among all other clusters, and \f$ s \f$ is the silhouette value. Using this definition, \f$ s \f$ exists on the interval \f$ [-1, 1] \f$, where 1 indicates that the point strongly belongs to its current cluster and -1 indicates that the point does not belong well to its current cluster. The user may select from a variety of options to use as the distance metric. Additionally, the user may opt to use a mask array to ignore points in the silhouette; these points will contain a silhouette value of 0. + +The silhouette can be used to determine how well a particular clustering has performed, such as [k means](@ref kmeans) or [k medoids](@ref kmedoids). + +## Parameters ## + +| Name | Type | Description | +|------|------|-------------| +| Distance Metric | Enumeration | The metric used to determine the distances between points | +| Use Mask | bool | Whether to use a boolean mask array to ignore certain points flagged as _false_ from the algorithm | + +## Required Geometry ### + +None + +## Required Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|--------------|------|----------------------|-------------| +| Any **Attribute Array** | None | Any| Any | The **Attribute Array** to silhouette | +| **Attribute Array** | ClusterIds | int32_t | (1) | Specifies to which cluster each point belongs | +| **Attribute Array** | Mask | bool | (1) | Specifies if the point is to be counted in the algorithm, if _Use Mask_ is checked | + +## Created Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|--------------|------|----------------------|-------------| +| **Attribute Array** | Silhouette | double | (1) | Silhouette value for each point | + +## Example Pipelines ## + + + +## License & Copyright ## + +Please see the description file distributed with this plugin. + +## DREAM3D Mailing Lists ## + +If you need more help with a filter, please consider asking your question on the DREAM3D Users mailing list: +https://groups.google.com/forum/?hl=en#!forum/dream3d-users + diff --git a/Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.cpp b/Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.cpp new file mode 100644 index 000000000..f66ab10ca --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.cpp @@ -0,0 +1,48 @@ +#include "ImportDeformKeyFilev12.hpp" + +#include "complex/DataStructure/DataArray.hpp" +#include "complex/DataStructure/DataGroup.hpp" + +using namespace complex; + +// ----------------------------------------------------------------------------- +ImportDeformKeyFilev12::ImportDeformKeyFilev12(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, ImportDeformKeyFilev12InputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +ImportDeformKeyFilev12::~ImportDeformKeyFilev12() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& ImportDeformKeyFilev12::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> ImportDeformKeyFilev12::operator()() +{ + /** + * This section of the code should contain the actual algorithmic codes that + * will accomplish the goal of the file. + * + * If you can parallelize the code there are a number of examples on how to do that. + * GenerateIPFColors is one example + * + * If you need to determine what kind of array you have (Int32Array, Float32Array, etc) + * look to the ExecuteDataFunction() in complex/Utilities/FilterUtilities.hpp template + * function to help with that code. + * An Example algorithm class is `CombineAttributeArrays` and `RemoveFlaggedVertices` + * + * There are other utility classes that can help alleviate the amount of code that needs + * to be written. + * + * REMOVE THIS COMMENT BLOCK WHEN YOU ARE FINISHED WITH THE FILTER_HUMAN_NAME + */ + + return {}; +} diff --git a/Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.hpp b/Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.hpp new file mode 100644 index 000000000..485c7f33e --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/ImportDeformKeyFilev12.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/DataStructure/DataStructure.hpp" +#include "complex/Filter/IFilter.hpp" +#include "complex/Parameters/FileSystemPathParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/StringParameter.hpp" +#include "complex/Parameters/StringParameter.hpp" +#include "complex/Parameters/StringParameter.hpp" + + +/** +* This is example code to put in the Execute Method of the filter. + ImportDeformKeyFilev12InputValues inputValues; + + inputValues.DEFORMInputFile = filterArgs.value(k_DEFORMInputFile_Key); + inputValues.VerboseOutput = filterArgs.value(k_VerboseOutput_Key); + inputValues.DataContainerName = filterArgs.value(k_DataContainerName_Key); + inputValues.VertexAttributeMatrixName = filterArgs.value(k_VertexAttributeMatrixName_Key); + inputValues.CellAttributeMatrixName = filterArgs.value(k_CellAttributeMatrixName_Key); + + return ImportDeformKeyFilev12(dataStructure, messageHandler, shouldCancel, &inputValues)(); +*/ + +namespace complex +{ + +struct CORE_EXPORT ImportDeformKeyFilev12InputValues +{ + FileSystemPathParameter::ValueType DEFORMInputFile; + bool VerboseOutput; + StringParameter::ValueType DataContainerName; + StringParameter::ValueType VertexAttributeMatrixName; + StringParameter::ValueType CellAttributeMatrixName; + +}; + +/** + * @class ConditionalSetValue + * @brief This filter replaces values in the target array with a user specified value + * where a bool mask array specifies. + */ + +class CORE_EXPORT ImportDeformKeyFilev12 +{ +public: + ImportDeformKeyFilev12(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, ImportDeformKeyFilev12InputValues* inputValues); + ~ImportDeformKeyFilev12() noexcept; + + ImportDeformKeyFilev12(const ImportDeformKeyFilev12&) = delete; + ImportDeformKeyFilev12(ImportDeformKeyFilev12&&) noexcept = delete; + ImportDeformKeyFilev12& operator=(const ImportDeformKeyFilev12&) = delete; + ImportDeformKeyFilev12& operator=(ImportDeformKeyFilev12&&) noexcept = delete; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const ImportDeformKeyFilev12InputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; +}; + +} // namespace complex diff --git a/Core/src/Core/Filters/Algorithms/KMeans.cpp b/Core/src/Core/Filters/Algorithms/KMeans.cpp new file mode 100644 index 000000000..dec3fd9e2 --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/KMeans.cpp @@ -0,0 +1,48 @@ +#include "KMeans.hpp" + +#include "complex/DataStructure/DataArray.hpp" +#include "complex/DataStructure/DataGroup.hpp" + +using namespace complex; + +// ----------------------------------------------------------------------------- +KMeans::KMeans(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, KMeansInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +KMeans::~KMeans() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& KMeans::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> KMeans::operator()() +{ + /** + * This section of the code should contain the actual algorithmic codes that + * will accomplish the goal of the file. + * + * If you can parallelize the code there are a number of examples on how to do that. + * GenerateIPFColors is one example + * + * If you need to determine what kind of array you have (Int32Array, Float32Array, etc) + * look to the ExecuteDataFunction() in complex/Utilities/FilterUtilities.hpp template + * function to help with that code. + * An Example algorithm class is `CombineAttributeArrays` and `RemoveFlaggedVertices` + * + * There are other utility classes that can help alleviate the amount of code that needs + * to be written. + * + * REMOVE THIS COMMENT BLOCK WHEN YOU ARE FINISHED WITH THE FILTER_HUMAN_NAME + */ + + return {}; +} diff --git a/Core/src/Core/Filters/Algorithms/KMeans.hpp b/Core/src/Core/Filters/Algorithms/KMeans.hpp new file mode 100644 index 000000000..7bf682756 --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/KMeans.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/DataStructure/DataStructure.hpp" +#include "complex/Filter/IFilter.hpp" +#include "complex/Parameters/NumberParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" + + +/** +* This is example code to put in the Execute Method of the filter. + KMeansInputValues inputValues; + + inputValues.InitClusters = filterArgs.value(k_InitClusters_Key); + inputValues.DistanceMetric = filterArgs.value(k_DistanceMetric_Key); + inputValues.UseMask = filterArgs.value(k_UseMask_Key); + inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key); + inputValues.MaskArrayPath = filterArgs.value(k_MaskArrayPath_Key); + inputValues.FeatureIdsArrayName = filterArgs.value(k_FeatureIdsArrayName_Key); + inputValues.FeatureAttributeMatrixName = filterArgs.value(k_FeatureAttributeMatrixName_Key); + inputValues.MeansArrayName = filterArgs.value(k_MeansArrayName_Key); + + return KMeans(dataStructure, messageHandler, shouldCancel, &inputValues)(); +*/ + +namespace complex +{ + +struct CORE_EXPORT KMeansInputValues +{ + int32 InitClusters; + ChoicesParameter::ValueType DistanceMetric; + bool UseMask; + DataPath SelectedArrayPath; + DataPath MaskArrayPath; + DataPath FeatureIdsArrayName; + DataPath FeatureAttributeMatrixName; + DataPath MeansArrayName; + +}; + +/** + * @class ConditionalSetValue + * @brief This filter replaces values in the target array with a user specified value + * where a bool mask array specifies. + */ + +class CORE_EXPORT KMeans +{ +public: + KMeans(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, KMeansInputValues* inputValues); + ~KMeans() noexcept; + + KMeans(const KMeans&) = delete; + KMeans(KMeans&&) noexcept = delete; + KMeans& operator=(const KMeans&) = delete; + KMeans& operator=(KMeans&&) noexcept = delete; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const KMeansInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; +}; + +} // namespace complex diff --git a/Core/src/Core/Filters/Algorithms/KMedoids.cpp b/Core/src/Core/Filters/Algorithms/KMedoids.cpp new file mode 100644 index 000000000..d4c001931 --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/KMedoids.cpp @@ -0,0 +1,48 @@ +#include "KMedoids.hpp" + +#include "complex/DataStructure/DataArray.hpp" +#include "complex/DataStructure/DataGroup.hpp" + +using namespace complex; + +// ----------------------------------------------------------------------------- +KMedoids::KMedoids(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, KMedoidsInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +KMedoids::~KMedoids() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& KMedoids::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> KMedoids::operator()() +{ + /** + * This section of the code should contain the actual algorithmic codes that + * will accomplish the goal of the file. + * + * If you can parallelize the code there are a number of examples on how to do that. + * GenerateIPFColors is one example + * + * If you need to determine what kind of array you have (Int32Array, Float32Array, etc) + * look to the ExecuteDataFunction() in complex/Utilities/FilterUtilities.hpp template + * function to help with that code. + * An Example algorithm class is `CombineAttributeArrays` and `RemoveFlaggedVertices` + * + * There are other utility classes that can help alleviate the amount of code that needs + * to be written. + * + * REMOVE THIS COMMENT BLOCK WHEN YOU ARE FINISHED WITH THE FILTER_HUMAN_NAME + */ + + return {}; +} diff --git a/Core/src/Core/Filters/Algorithms/KMedoids.hpp b/Core/src/Core/Filters/Algorithms/KMedoids.hpp new file mode 100644 index 000000000..088c3be14 --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/KMedoids.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/DataStructure/DataStructure.hpp" +#include "complex/Filter/IFilter.hpp" +#include "complex/Parameters/NumberParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" + + +/** +* This is example code to put in the Execute Method of the filter. + KMedoidsInputValues inputValues; + + inputValues.InitClusters = filterArgs.value(k_InitClusters_Key); + inputValues.DistanceMetric = filterArgs.value(k_DistanceMetric_Key); + inputValues.UseMask = filterArgs.value(k_UseMask_Key); + inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key); + inputValues.MaskArrayPath = filterArgs.value(k_MaskArrayPath_Key); + inputValues.FeatureIdsArrayName = filterArgs.value(k_FeatureIdsArrayName_Key); + inputValues.FeatureAttributeMatrixName = filterArgs.value(k_FeatureAttributeMatrixName_Key); + inputValues.MedoidsArrayName = filterArgs.value(k_MedoidsArrayName_Key); + + return KMedoids(dataStructure, messageHandler, shouldCancel, &inputValues)(); +*/ + +namespace complex +{ + +struct CORE_EXPORT KMedoidsInputValues +{ + int32 InitClusters; + ChoicesParameter::ValueType DistanceMetric; + bool UseMask; + DataPath SelectedArrayPath; + DataPath MaskArrayPath; + DataPath FeatureIdsArrayName; + DataPath FeatureAttributeMatrixName; + DataPath MedoidsArrayName; + +}; + +/** + * @class ConditionalSetValue + * @brief This filter replaces values in the target array with a user specified value + * where a bool mask array specifies. + */ + +class CORE_EXPORT KMedoids +{ +public: + KMedoids(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, KMedoidsInputValues* inputValues); + ~KMedoids() noexcept; + + KMedoids(const KMedoids&) = delete; + KMedoids(KMedoids&&) noexcept = delete; + KMedoids& operator=(const KMedoids&) = delete; + KMedoids& operator=(KMedoids&&) noexcept = delete; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const KMedoidsInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; +}; + +} // namespace complex diff --git a/Core/src/Core/Filters/Algorithms/Silhouette.cpp b/Core/src/Core/Filters/Algorithms/Silhouette.cpp new file mode 100644 index 000000000..1f208246d --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/Silhouette.cpp @@ -0,0 +1,48 @@ +#include "Silhouette.hpp" + +#include "complex/DataStructure/DataArray.hpp" +#include "complex/DataStructure/DataGroup.hpp" + +using namespace complex; + +// ----------------------------------------------------------------------------- +Silhouette::Silhouette(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, SilhouetteInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +Silhouette::~Silhouette() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& Silhouette::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> Silhouette::operator()() +{ + /** + * This section of the code should contain the actual algorithmic codes that + * will accomplish the goal of the file. + * + * If you can parallelize the code there are a number of examples on how to do that. + * GenerateIPFColors is one example + * + * If you need to determine what kind of array you have (Int32Array, Float32Array, etc) + * look to the ExecuteDataFunction() in complex/Utilities/FilterUtilities.hpp template + * function to help with that code. + * An Example algorithm class is `CombineAttributeArrays` and `RemoveFlaggedVertices` + * + * There are other utility classes that can help alleviate the amount of code that needs + * to be written. + * + * REMOVE THIS COMMENT BLOCK WHEN YOU ARE FINISHED WITH THE FILTER_HUMAN_NAME + */ + + return {}; +} diff --git a/Core/src/Core/Filters/Algorithms/Silhouette.hpp b/Core/src/Core/Filters/Algorithms/Silhouette.hpp new file mode 100644 index 000000000..e18494604 --- /dev/null +++ b/Core/src/Core/Filters/Algorithms/Silhouette.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/DataStructure/DataStructure.hpp" +#include "complex/Filter/IFilter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" + + +/** +* This is example code to put in the Execute Method of the filter. + SilhouetteInputValues inputValues; + + inputValues.DistanceMetric = filterArgs.value(k_DistanceMetric_Key); + inputValues.UseMask = filterArgs.value(k_UseMask_Key); + inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key); + inputValues.MaskArrayPath = filterArgs.value(k_MaskArrayPath_Key); + inputValues.FeatureIdsArrayPath = filterArgs.value(k_FeatureIdsArrayPath_Key); + inputValues.SilhouetteArrayPath = filterArgs.value(k_SilhouetteArrayPath_Key); + + return Silhouette(dataStructure, messageHandler, shouldCancel, &inputValues)(); +*/ + +namespace complex +{ + +struct CORE_EXPORT SilhouetteInputValues +{ + ChoicesParameter::ValueType DistanceMetric; + bool UseMask; + DataPath SelectedArrayPath; + DataPath MaskArrayPath; + DataPath FeatureIdsArrayPath; + DataPath SilhouetteArrayPath; + +}; + +/** + * @class ConditionalSetValue + * @brief This filter replaces values in the target array with a user specified value + * where a bool mask array specifies. + */ + +class CORE_EXPORT Silhouette +{ +public: + Silhouette(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, SilhouetteInputValues* inputValues); + ~Silhouette() noexcept; + + Silhouette(const Silhouette&) = delete; + Silhouette(Silhouette&&) noexcept = delete; + Silhouette& operator=(const Silhouette&) = delete; + Silhouette& operator=(Silhouette&&) noexcept = delete; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const SilhouetteInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; +}; + +} // namespace complex diff --git a/Core/src/Core/Filters/ImportDeformKeyFilev12Filter.cpp b/Core/src/Core/Filters/ImportDeformKeyFilev12Filter.cpp new file mode 100644 index 000000000..9fa12a862 --- /dev/null +++ b/Core/src/Core/Filters/ImportDeformKeyFilev12Filter.cpp @@ -0,0 +1,151 @@ +#include "ImportDeformKeyFilev12Filter.hpp" + +#include "Core/Filters/Algorithms/ImportDeformKeyFilev12.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/Filter/Actions/EmptyAction.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/DataGroupCreationParameter.hpp" +#include "complex/Parameters/DataObjectNameParameter.hpp" +#include "complex/Parameters/FileSystemPathParameter.hpp" +#include "complex/Parameters/NumberParameter.hpp" + +#include +namespace fs = std::filesystem; + +using namespace complex; + +namespace complex +{ +//------------------------------------------------------------------------------ +std::string ImportDeformKeyFilev12Filter::name() const +{ + return FilterTraits::name.str(); +} + +//------------------------------------------------------------------------------ +std::string ImportDeformKeyFilev12Filter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid ImportDeformKeyFilev12Filter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string ImportDeformKeyFilev12Filter::humanName() const +{ + return "Import Deform Key File (v12)"; +} + +//------------------------------------------------------------------------------ +std::vector ImportDeformKeyFilev12Filter::defaultTags() const +{ + return {"#Unsupported", "#SimulationIO"}; +} + +//------------------------------------------------------------------------------ +Parameters ImportDeformKeyFilev12Filter::parameters() const +{ + Parameters params; + + // Create the parameter descriptors that are needed for this filter + params.insertSeparator(Parameters::Separator{"Input Parameters"}); + params.insert(std::make_unique(k_DEFORMInputFile_Key, "Input File", "The input DEFORM V12 file path", fs::path(), FileSystemPathParameter::ExtensionsType{"*.key"}, + FileSystemPathParameter::PathType::InputFile)); + params.insert(std::make_unique(k_VerboseOutput_Key, "Verbose Output", "", false)); + params.insertSeparator(Parameters::Separator{"Created Data Objects"}); + params.insert(std::make_unique(k_DataContainerName_Key, "Created Quad Geometry", "", DataPath({"DEFORM Geometry"}))); + params.insert(std::make_unique(k_VertexAttributeMatrixName_Key, "Created Vertex Attribute Matrix Name", "", "VertexData")); + params.insert(std::make_unique(k_CellAttributeMatrixName_Key, "Created Face Attribute Matrix Name", "", "FaceData")); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer ImportDeformKeyFilev12Filter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult ImportDeformKeyFilev12Filter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + /**************************************************************************** + * Write any preflight sanity checking codes in this function + ***************************************************************************/ + + /** + * These are the values that were gathered from the UI or the pipeline file or + * otherwise passed into the filter. These are here for your convenience. If you + * do not need some of them remove them. + */ + auto pDEFORMInputFileValue = filterArgs.value(k_DEFORMInputFile_Key); + auto pVerboseOutputValue = filterArgs.value(k_VerboseOutput_Key); + auto pDataContainerNameValue = filterArgs.value(k_DataContainerName_Key); + auto pVertexAttributeMatrixNameValue = filterArgs.value(k_VertexAttributeMatrixName_Key); + auto pCellAttributeMatrixNameValue = filterArgs.value(k_CellAttributeMatrixName_Key); + + // Declare the preflightResult variable that will be populated with the results + // of the preflight. The PreflightResult type contains the output Actions and + // any preflight updated values that you want to be displayed to the user, typically + // through a user interface (UI). + PreflightResult preflightResult; + + // If your filter is making structural changes to the DataStructure then the filter + // is going to create OutputActions subclasses that need to be returned. This will + // store those actions. + complex::Result resultOutputActions; + + // If your filter is going to pass back some `preflight updated values` then this is where you + // would create the code to store those values in the appropriate object. Note that we + // in line creating the pair (NOT a std::pair<>) of Key:Value that will get stored in + // the std::vector object. + std::vector preflightUpdatedValues; + + // If the filter needs to pass back some updated values via a key:value string:string set of values + // you can declare and update that string here. + // None found in this filter based on the filter parameters + + // If this filter makes changes to the DataStructure in the form of + // creating/deleting/moving/renaming DataGroups, Geometries, DataArrays then you + // will need to use one of the `*Actions` classes located in complex/Filter/Actions + // to relay that information to the preflight and execute methods. This is done by + // creating an instance of the Action class and then storing it in the resultOutputActions variable. + // This is done through a `push_back()` method combined with a `std::move()`. For the + // newly initiated to `std::move` once that code is executed what was once inside the Action class + // instance variable is *no longer there*. The memory has been moved. If you try to access that + // variable after this line you will probably get a crash or have subtle bugs. To ensure that this + // does not happen we suggest using braces `{}` to scope each of the action's declaration and store + // so that the programmer is not tempted to use the action instance past where it should be used. + // You have to create your own Actions class if there isn't something specific for your filter's needs + + // Store the preflight updated value(s) into the preflightUpdatedValues vector using + // the appropriate methods. + // None found based on the filter parameters + + // Return both the resultOutputActions and the preflightUpdatedValues via std::move() + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> ImportDeformKeyFilev12Filter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + + ImportDeformKeyFilev12InputValues inputValues; + + inputValues.DEFORMInputFile = filterArgs.value(k_DEFORMInputFile_Key); + inputValues.VerboseOutput = filterArgs.value(k_VerboseOutput_Key); + inputValues.DataContainerName = filterArgs.value(k_DataContainerName_Key); + inputValues.VertexAttributeMatrixName = filterArgs.value(k_VertexAttributeMatrixName_Key); + inputValues.CellAttributeMatrixName = filterArgs.value(k_CellAttributeMatrixName_Key); + + return ImportDeformKeyFilev12(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} +} // namespace complex diff --git a/Core/src/Core/Filters/ImportDeformKeyFilev12Filter.hpp b/Core/src/Core/Filters/ImportDeformKeyFilev12Filter.hpp new file mode 100644 index 000000000..868bfd2d6 --- /dev/null +++ b/Core/src/Core/Filters/ImportDeformKeyFilev12Filter.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/Filter/FilterTraits.hpp" +#include "complex/Filter/IFilter.hpp" + +namespace complex +{ +/** + * @class ImportDeformKeyFilev12Filter + * @brief This filter will .... + */ +class CORE_EXPORT ImportDeformKeyFilev12Filter : public IFilter +{ +public: + ImportDeformKeyFilev12Filter() = default; + ~ImportDeformKeyFilev12Filter() noexcept override = default; + + ImportDeformKeyFilev12Filter(const ImportDeformKeyFilev12Filter&) = delete; + ImportDeformKeyFilev12Filter(ImportDeformKeyFilev12Filter&&) noexcept = delete; + + ImportDeformKeyFilev12Filter& operator=(const ImportDeformKeyFilev12Filter&) = delete; + ImportDeformKeyFilev12Filter& operator=(ImportDeformKeyFilev12Filter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_DEFORMInputFile_Key = "deform_input_file"; + static inline constexpr StringLiteral k_VerboseOutput_Key = "verbose_output"; + static inline constexpr StringLiteral k_DataContainerName_Key = "data_container_name"; + static inline constexpr StringLiteral k_VertexAttributeMatrixName_Key = "vertex_attribute_matrix_name"; + static inline constexpr StringLiteral k_CellAttributeMatrixName_Key = "cell_attribute_matrix_name"; + + /** + * @brief Returns the name of the filter. + * @return + */ + std::string name() const override; + + /** + * @brief Returns the C++ classname of this filter. + * @return + */ + std::string className() const override; + + /** + * @brief Returns the uuid of the filter. + * @return + */ + Uuid uuid() const override; + + /** + * @brief Returns the human readable name of the filter. + * @return + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief Returns the parameters of the filter (i.e. its inputs) + * @return + */ + Parameters parameters() const override; + + /** + * @brief Returns a copy of the filter. + * @return + */ + UniquePointer clone() const override; + +protected: + /** + * @brief Takes in a DataStructure and checks that the filter can be run on it with the given arguments. + * Returns any warnings/errors. Also returns the changes that would be applied to the DataStructure. + * Some parts of the actions may not be completely filled out if all the required information is not available at preflight time. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + PreflightResult preflightImpl(const DataStructure& ds, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; + + /** + * @brief Applies the filter's algorithm to the DataStructure with the given arguments. Returns any warnings/errors. + * On failure, there is no guarantee that the DataStructure is in a correct state. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + Result<> executeImpl(DataStructure & data, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) + const override; +}; +} // namespace complex + +COMPLEX_DEF_FILTER_TRAITS(complex, ImportDeformKeyFilev12Filter, "2eab4aeb-202f-59bc-834e-ee20b8e543bb"); +/* LEGACY UUID FOR THIS FILTER {3c6337da-e232-4420-a5ca-451496748d88} */ diff --git a/Core/src/Core/Filters/KMeansFilter.cpp b/Core/src/Core/Filters/KMeansFilter.cpp new file mode 100644 index 000000000..59a20da87 --- /dev/null +++ b/Core/src/Core/Filters/KMeansFilter.cpp @@ -0,0 +1,169 @@ +#include "KMeansFilter.hpp" + +#include "Core/Filters/Algorithms/KMeans.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/Filter/Actions/EmptyAction.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/NumberParameter.hpp" + +using namespace complex; + +namespace +{ +ChoicesParameter::Choices k_ClusterChoices = {"Euclidean", "Squared Euclidean"}; +} + +namespace complex +{ +//------------------------------------------------------------------------------ +std::string KMeansFilter::name() const +{ + return FilterTraits::name.str(); +} + +//------------------------------------------------------------------------------ +std::string KMeansFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid KMeansFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string KMeansFilter::humanName() const +{ + return "K Means"; +} + +//------------------------------------------------------------------------------ +std::vector KMeansFilter::defaultTags() const +{ + return {"#DREAM3D Review", "#Clustering"}; +} + +//------------------------------------------------------------------------------ +Parameters KMeansFilter::parameters() const +{ + Parameters params; + + // Create the parameter descriptors that are needed for this filter + params.insertSeparator(Parameters::Separator{"Input Parameters"}); + params.insert(std::make_unique(k_InitClusters_Key, "Number of Clusters", "", 2)); + params.insert(std::make_unique(k_DistanceMetric_Key, "Distance Metric", "", 0, k_ClusterChoices)); + + params.insertSeparator(Parameters::Separator{"Optional Mask Array"}); + params.insertLinkableParameter(std::make_unique(k_UseMask_Key, "Use Mask", "", false)); + std::make_unique(k_MaskArrayPath_Key, "Mask", "", DataPath{}, complex::GetAllDataTypes()); + + params.insertSeparator(Parameters::Separator{"Required Input Data"}); + params.insert(std::make_unique(k_SelectedArrayPath_Key, "Attribute Array to Cluster", "", DataPath{}, + complex::GetAllDataTypes() /* This will allow ANY data type. Adjust as necessary for your filter*/)); + + params.insertSeparator(Parameters::Separator{"Created Cell Data Objects"}); + params.insert(std::make_unique(k_FeatureIdsArrayName_Key, "Cluster Ids", "", DataPath{})); + + params.insertSeparator(Parameters::Separator{"Created Attribute Matrix Data Objects"}); + params.insert(std::make_unique(k_FeatureAttributeMatrixName_Key, "Cluster Attribute Matrix", "", DataPath{})); + params.insert(std::make_unique(k_MeansArrayName_Key, "Cluster Means", "", DataPath{})); + // Associate the Linkable Parameter(s) to the children parameters that they control + params.linkParameters(k_UseMask_Key, k_MaskArrayPath_Key, true); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer KMeansFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult KMeansFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const +{ + /**************************************************************************** + * Write any preflight sanity checking codes in this function + ***************************************************************************/ + + /** + * These are the values that were gathered from the UI or the pipeline file or + * otherwise passed into the filter. These are here for your convenience. If you + * do not need some of them remove them. + */ + auto pInitClustersValue = filterArgs.value(k_InitClusters_Key); + auto pDistanceMetricValue = filterArgs.value(k_DistanceMetric_Key); + auto pUseMaskValue = filterArgs.value(k_UseMask_Key); + auto pSelectedArrayPathValue = filterArgs.value(k_SelectedArrayPath_Key); + auto pMaskArrayPathValue = filterArgs.value(k_MaskArrayPath_Key); + auto pFeatureIdsArrayNameValue = filterArgs.value(k_FeatureIdsArrayName_Key); + auto pFeatureAttributeMatrixNameValue = filterArgs.value(k_FeatureAttributeMatrixName_Key); + auto pMeansArrayNameValue = filterArgs.value(k_MeansArrayName_Key); + + // Declare the preflightResult variable that will be populated with the results + // of the preflight. The PreflightResult type contains the output Actions and + // any preflight updated values that you want to be displayed to the user, typically + // through a user interface (UI). + PreflightResult preflightResult; + + // If your filter is making structural changes to the DataStructure then the filter + // is going to create OutputActions subclasses that need to be returned. This will + // store those actions. + complex::Result resultOutputActions; + + // If your filter is going to pass back some `preflight updated values` then this is where you + // would create the code to store those values in the appropriate object. Note that we + // in line creating the pair (NOT a std::pair<>) of Key:Value that will get stored in + // the std::vector object. + std::vector preflightUpdatedValues; + + // If the filter needs to pass back some updated values via a key:value string:string set of values + // you can declare and update that string here. + // None found in this filter based on the filter parameters + + // If this filter makes changes to the DataStructure in the form of + // creating/deleting/moving/renaming DataGroups, Geometries, DataArrays then you + // will need to use one of the `*Actions` classes located in complex/Filter/Actions + // to relay that information to the preflight and execute methods. This is done by + // creating an instance of the Action class and then storing it in the resultOutputActions variable. + // This is done through a `push_back()` method combined with a `std::move()`. For the + // newly initiated to `std::move` once that code is executed what was once inside the Action class + // instance variable is *no longer there*. The memory has been moved. If you try to access that + // variable after this line you will probably get a crash or have subtle bugs. To ensure that this + // does not happen we suggest using braces `{}` to scope each of the action's declaration and store + // so that the programmer is not tempted to use the action instance past where it should be used. + // You have to create your own Actions class if there isn't something specific for your filter's needs + + // Store the preflight updated value(s) into the preflightUpdatedValues vector using + // the appropriate methods. + // None found based on the filter parameters + + // Return both the resultOutputActions and the preflightUpdatedValues via std::move() + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> KMeansFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + + KMeansInputValues inputValues; + + inputValues.InitClusters = filterArgs.value(k_InitClusters_Key); + inputValues.DistanceMetric = filterArgs.value(k_DistanceMetric_Key); + inputValues.UseMask = filterArgs.value(k_UseMask_Key); + inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key); + inputValues.MaskArrayPath = filterArgs.value(k_MaskArrayPath_Key); + inputValues.FeatureIdsArrayName = filterArgs.value(k_FeatureIdsArrayName_Key); + inputValues.FeatureAttributeMatrixName = filterArgs.value(k_FeatureAttributeMatrixName_Key); + inputValues.MeansArrayName = filterArgs.value(k_MeansArrayName_Key); + + return KMeans(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} +} // namespace complex diff --git a/Core/src/Core/Filters/KMeansFilter.hpp b/Core/src/Core/Filters/KMeansFilter.hpp new file mode 100644 index 000000000..5d6f8f47f --- /dev/null +++ b/Core/src/Core/Filters/KMeansFilter.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/Filter/FilterTraits.hpp" +#include "complex/Filter/IFilter.hpp" + +namespace complex +{ +/** + * @class KMeansFilter + * @brief This filter will .... + */ +class CORE_EXPORT KMeansFilter : public IFilter +{ +public: + KMeansFilter() = default; + ~KMeansFilter() noexcept override = default; + + KMeansFilter(const KMeansFilter&) = delete; + KMeansFilter(KMeansFilter&&) noexcept = delete; + + KMeansFilter& operator=(const KMeansFilter&) = delete; + KMeansFilter& operator=(KMeansFilter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_InitClusters_Key = "init_clusters"; + static inline constexpr StringLiteral k_DistanceMetric_Key = "distance_metric"; + static inline constexpr StringLiteral k_UseMask_Key = "use_mask"; + static inline constexpr StringLiteral k_SelectedArrayPath_Key = "selected_array_path"; + static inline constexpr StringLiteral k_MaskArrayPath_Key = "mask_array_path"; + static inline constexpr StringLiteral k_FeatureIdsArrayName_Key = "feature_ids_array_name"; + static inline constexpr StringLiteral k_FeatureAttributeMatrixName_Key = "feature_attribute_matrix_name"; + static inline constexpr StringLiteral k_MeansArrayName_Key = "means_array_name"; + + /** + * @brief Returns the name of the filter. + * @return + */ + std::string name() const override; + + /** + * @brief Returns the C++ classname of this filter. + * @return + */ + std::string className() const override; + + /** + * @brief Returns the uuid of the filter. + * @return + */ + Uuid uuid() const override; + + /** + * @brief Returns the human readable name of the filter. + * @return + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief Returns the parameters of the filter (i.e. its inputs) + * @return + */ + Parameters parameters() const override; + + /** + * @brief Returns a copy of the filter. + * @return + */ + UniquePointer clone() const override; + +protected: + /** + * @brief Takes in a DataStructure and checks that the filter can be run on it with the given arguments. + * Returns any warnings/errors. Also returns the changes that would be applied to the DataStructure. + * Some parts of the actions may not be completely filled out if all the required information is not available at preflight time. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + PreflightResult preflightImpl(const DataStructure& ds, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; + + /** + * @brief Applies the filter's algorithm to the DataStructure with the given arguments. Returns any warnings/errors. + * On failure, there is no guarantee that the DataStructure is in a correct state. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + Result<> executeImpl(DataStructure & data, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) + const override; +}; +} // namespace complex + +COMPLEX_DEF_FILTER_TRAITS(complex, KMeansFilter, "061f74ad-9173-523a-a774-c84e07d380a0"); +/* LEGACY UUID FOR THIS FILTER {b56a04de-0ca0-509d-809f-52219fca9c98} */ diff --git a/Core/src/Core/Filters/KMedoidsFilter.cpp b/Core/src/Core/Filters/KMedoidsFilter.cpp new file mode 100644 index 000000000..f19023566 --- /dev/null +++ b/Core/src/Core/Filters/KMedoidsFilter.cpp @@ -0,0 +1,171 @@ +#include "KMedoidsFilter.hpp" + +#include "Core/Filters/Algorithms/KMedoids.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/Filter/Actions/EmptyAction.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/NumberParameter.hpp" + +using namespace complex; + +namespace +{ +ChoicesParameter::Choices k_ClusterChoices = {"Euclidean", "Squared Euclidean", "Manhattan", "Cosine", "Pearson", "Squared Pearson"}; +} + +namespace complex +{ +//------------------------------------------------------------------------------ +std::string KMedoidsFilter::name() const +{ + return FilterTraits::name.str(); +} + +//------------------------------------------------------------------------------ +std::string KMedoidsFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid KMedoidsFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string KMedoidsFilter::humanName() const +{ + return "K Medoids"; +} + +//------------------------------------------------------------------------------ +std::vector KMedoidsFilter::defaultTags() const +{ + return {"#DREAM3D Review", "#Clustering"}; +} + +//------------------------------------------------------------------------------ +Parameters KMedoidsFilter::parameters() const +{ + Parameters params; + + // Create the parameter descriptors that are needed for this filter + params.insertSeparator(Parameters::Separator{"Input Parameters"}); + + params.insert(std::make_unique(k_InitClusters_Key, "Number of Clusters", "", 2)); + params.insert(std::make_unique(k_DistanceMetric_Key, "Distance Metric", "", 0, k_ClusterChoices)); + + params.insertSeparator(Parameters::Separator{"Optional Mask Array"}); + params.insertLinkableParameter(std::make_unique(k_UseMask_Key, "Use Mask", "", false)); + params.insert(std::make_unique(k_MaskArrayPath_Key, "Mask", "", DataPath{}, complex::GetAllDataTypes())); + + params.insertSeparator(Parameters::Separator{"Required Input Data"}); + params.insert(std::make_unique(k_SelectedArrayPath_Key, "Attribute Array to Cluster", "", DataPath{}, complex::GetAllDataTypes())); + params.insertSeparator(Parameters::Separator{"Created Cell Data Objects"}); + + params.insert(std::make_unique(k_FeatureIdsArrayName_Key, "Cluster Ids", "", DataPath{})); + + params.insertSeparator(Parameters::Separator{"Created Attribute Matrix Data Objects"}); + params.insert(std::make_unique(k_FeatureAttributeMatrixName_Key, "Cluster Attribute Matrix", "", DataPath{})); + params.insert(std::make_unique(k_MedoidsArrayName_Key, "Cluster Medoids", "", DataPath{})); + + // Associate the Linkable Parameter(s) to the children parameters that they control + params.linkParameters(k_UseMask_Key, k_MaskArrayPath_Key, true); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer KMedoidsFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult KMedoidsFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + /**************************************************************************** + * Write any preflight sanity checking codes in this function + ***************************************************************************/ + + /** + * These are the values that were gathered from the UI or the pipeline file or + * otherwise passed into the filter. These are here for your convenience. If you + * do not need some of them remove them. + */ + auto pInitClustersValue = filterArgs.value(k_InitClusters_Key); + auto pDistanceMetricValue = filterArgs.value(k_DistanceMetric_Key); + auto pUseMaskValue = filterArgs.value(k_UseMask_Key); + auto pSelectedArrayPathValue = filterArgs.value(k_SelectedArrayPath_Key); + auto pMaskArrayPathValue = filterArgs.value(k_MaskArrayPath_Key); + auto pFeatureIdsArrayNameValue = filterArgs.value(k_FeatureIdsArrayName_Key); + auto pFeatureAttributeMatrixNameValue = filterArgs.value(k_FeatureAttributeMatrixName_Key); + auto pMedoidsArrayNameValue = filterArgs.value(k_MedoidsArrayName_Key); + + // Declare the preflightResult variable that will be populated with the results + // of the preflight. The PreflightResult type contains the output Actions and + // any preflight updated values that you want to be displayed to the user, typically + // through a user interface (UI). + PreflightResult preflightResult; + + // If your filter is making structural changes to the DataStructure then the filter + // is going to create OutputActions subclasses that need to be returned. This will + // store those actions. + complex::Result resultOutputActions; + + // If your filter is going to pass back some `preflight updated values` then this is where you + // would create the code to store those values in the appropriate object. Note that we + // in line creating the pair (NOT a std::pair<>) of Key:Value that will get stored in + // the std::vector object. + std::vector preflightUpdatedValues; + + // If the filter needs to pass back some updated values via a key:value string:string set of values + // you can declare and update that string here. + // None found in this filter based on the filter parameters + + // If this filter makes changes to the DataStructure in the form of + // creating/deleting/moving/renaming DataGroups, Geometries, DataArrays then you + // will need to use one of the `*Actions` classes located in complex/Filter/Actions + // to relay that information to the preflight and execute methods. This is done by + // creating an instance of the Action class and then storing it in the resultOutputActions variable. + // This is done through a `push_back()` method combined with a `std::move()`. For the + // newly initiated to `std::move` once that code is executed what was once inside the Action class + // instance variable is *no longer there*. The memory has been moved. If you try to access that + // variable after this line you will probably get a crash or have subtle bugs. To ensure that this + // does not happen we suggest using braces `{}` to scope each of the action's declaration and store + // so that the programmer is not tempted to use the action instance past where it should be used. + // You have to create your own Actions class if there isn't something specific for your filter's needs + + // Store the preflight updated value(s) into the preflightUpdatedValues vector using + // the appropriate methods. + // None found based on the filter parameters + + // Return both the resultOutputActions and the preflightUpdatedValues via std::move() + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> KMedoidsFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + + KMedoidsInputValues inputValues; + + inputValues.InitClusters = filterArgs.value(k_InitClusters_Key); + inputValues.DistanceMetric = filterArgs.value(k_DistanceMetric_Key); + inputValues.UseMask = filterArgs.value(k_UseMask_Key); + inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key); + inputValues.MaskArrayPath = filterArgs.value(k_MaskArrayPath_Key); + inputValues.FeatureIdsArrayName = filterArgs.value(k_FeatureIdsArrayName_Key); + inputValues.FeatureAttributeMatrixName = filterArgs.value(k_FeatureAttributeMatrixName_Key); + inputValues.MedoidsArrayName = filterArgs.value(k_MedoidsArrayName_Key); + + return KMedoids(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} +} // namespace complex diff --git a/Core/src/Core/Filters/KMedoidsFilter.hpp b/Core/src/Core/Filters/KMedoidsFilter.hpp new file mode 100644 index 000000000..5d57f2135 --- /dev/null +++ b/Core/src/Core/Filters/KMedoidsFilter.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/Filter/FilterTraits.hpp" +#include "complex/Filter/IFilter.hpp" + +namespace complex +{ +/** + * @class KMedoidsFilter + * @brief This filter will .... + */ +class CORE_EXPORT KMedoidsFilter : public IFilter +{ +public: + KMedoidsFilter() = default; + ~KMedoidsFilter() noexcept override = default; + + KMedoidsFilter(const KMedoidsFilter&) = delete; + KMedoidsFilter(KMedoidsFilter&&) noexcept = delete; + + KMedoidsFilter& operator=(const KMedoidsFilter&) = delete; + KMedoidsFilter& operator=(KMedoidsFilter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_InitClusters_Key = "init_clusters"; + static inline constexpr StringLiteral k_DistanceMetric_Key = "distance_metric"; + static inline constexpr StringLiteral k_UseMask_Key = "use_mask"; + static inline constexpr StringLiteral k_SelectedArrayPath_Key = "selected_array_path"; + static inline constexpr StringLiteral k_MaskArrayPath_Key = "mask_array_path"; + static inline constexpr StringLiteral k_FeatureIdsArrayName_Key = "feature_ids_array_name"; + static inline constexpr StringLiteral k_FeatureAttributeMatrixName_Key = "feature_attribute_matrix_name"; + static inline constexpr StringLiteral k_MedoidsArrayName_Key = "medoids_array_name"; + + /** + * @brief Returns the name of the filter. + * @return + */ + std::string name() const override; + + /** + * @brief Returns the C++ classname of this filter. + * @return + */ + std::string className() const override; + + /** + * @brief Returns the uuid of the filter. + * @return + */ + Uuid uuid() const override; + + /** + * @brief Returns the human readable name of the filter. + * @return + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief Returns the parameters of the filter (i.e. its inputs) + * @return + */ + Parameters parameters() const override; + + /** + * @brief Returns a copy of the filter. + * @return + */ + UniquePointer clone() const override; + +protected: + /** + * @brief Takes in a DataStructure and checks that the filter can be run on it with the given arguments. + * Returns any warnings/errors. Also returns the changes that would be applied to the DataStructure. + * Some parts of the actions may not be completely filled out if all the required information is not available at preflight time. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + PreflightResult preflightImpl(const DataStructure& ds, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; + + /** + * @brief Applies the filter's algorithm to the DataStructure with the given arguments. Returns any warnings/errors. + * On failure, there is no guarantee that the DataStructure is in a correct state. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + Result<> executeImpl(DataStructure & data, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) + const override; +}; +} // namespace complex + +COMPLEX_DEF_FILTER_TRAITS(complex, KMedoidsFilter, "1205ab95-ceef-536a-aa38-a40d7654e5a6"); +/* LEGACY UUID FOR THIS FILTER {f7486aa6-3049-5be7-8511-ae772b70c90b} */ diff --git a/Core/src/Core/Filters/SilhouetteFilter.cpp b/Core/src/Core/Filters/SilhouetteFilter.cpp new file mode 100644 index 000000000..5e97704ea --- /dev/null +++ b/Core/src/Core/Filters/SilhouetteFilter.cpp @@ -0,0 +1,182 @@ +#include "SilhouetteFilter.hpp" + +#include "Core/Filters/Algorithms/Silhouette.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/Filter/Actions/CreateArrayAction.hpp" +#include "complex/Filter/Actions/EmptyAction.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" + +using namespace complex; + +namespace +{ +ChoicesParameter::Choices k_ClusterChoices = {"Euclidean", "Squared Euclidean", "Manhattan", "Cosine", "Pearson", "Squared Pearson"}; +} + +namespace complex +{ +//------------------------------------------------------------------------------ +std::string SilhouetteFilter::name() const +{ + return FilterTraits::name.str(); +} + +//------------------------------------------------------------------------------ +std::string SilhouetteFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid SilhouetteFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string SilhouetteFilter::humanName() const +{ + return "Silhouette"; +} + +//------------------------------------------------------------------------------ +std::vector SilhouetteFilter::defaultTags() const +{ + return {"#DREAM3D Review", "#Clustering"}; +} + +//------------------------------------------------------------------------------ +Parameters SilhouetteFilter::parameters() const +{ + Parameters params; + + /** + * Please separate the parameters into groups generally of the following: + * + * params.insertSeparator(Parameters::Separator{"Input Parameters"}); + * params.insertSeparator(Parameters::Separator{"Required Input Cell Data"}); + * params.insertSeparator(Parameters::Separator{"Required Input Feature Data"}); + * params.insertSeparator(Parameters::Separator{"Created Cell Data"}); + * params.insertSeparator(Parameters::Separator{"Created Cell Feature Data"}); + * + * .. or create appropriate separators as needed. The UI in COMPLEX no longer + * does this for the developer by using catgories as in SIMPL + */ + + // Create the parameter descriptors that are needed for this filter + params.insertSeparator(Parameters::Separator{"Input Parameters"}); + params.insert(std::make_unique(k_DistanceMetric_Key, "Distance Metric", "", 0, k_ClusterChoices)); + + params.insertSeparator(Parameters::Separator{"Optional Mask Array"}); + params.insertLinkableParameter(std::make_unique(k_UseMask_Key, "Use Mask", "", false)); + params.insert(std::make_unique(k_MaskArrayPath_Key, "Mask", "", DataPath{}, complex::GetAllDataTypes())); + + params.insertSeparator(Parameters::Separator{"Required Input Data Arrays"}); + params.insert(std::make_unique(k_SelectedArrayPath_Key, "Attribute Array to Silhouette", "", DataPath{}, + complex::GetAllDataTypes() /* This will allow ANY data type. Adjust as necessary for your filter*/)); + params.insert(std::make_unique(k_FeatureIdsArrayPath_Key, "Cluster Ids", "", DataPath{}, + complex::GetAllDataTypes() /* This will allow ANY data type. Adjust as necessary for your filter*/)); + + params.insertSeparator(Parameters::Separator{"Created Data Arrays"}); + params.insert(std::make_unique(k_SilhouetteArrayPath_Key, "Silhouette", "", DataPath{})); + // Associate the Linkable Parameter(s) to the children parameters that they control + params.linkParameters(k_UseMask_Key, k_MaskArrayPath_Key, true); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer SilhouetteFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult SilhouetteFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + /**************************************************************************** + * Write any preflight sanity checking codes in this function + ***************************************************************************/ + + /** + * These are the values that were gathered from the UI or the pipeline file or + * otherwise passed into the filter. These are here for your convenience. If you + * do not need some of them remove them. + */ + auto pDistanceMetricValue = filterArgs.value(k_DistanceMetric_Key); + auto pUseMaskValue = filterArgs.value(k_UseMask_Key); + auto pSelectedArrayPathValue = filterArgs.value(k_SelectedArrayPath_Key); + auto pMaskArrayPathValue = filterArgs.value(k_MaskArrayPath_Key); + auto pFeatureIdsArrayPathValue = filterArgs.value(k_FeatureIdsArrayPath_Key); + auto pSilhouetteArrayPathValue = filterArgs.value(k_SilhouetteArrayPath_Key); + + // Declare the preflightResult variable that will be populated with the results + // of the preflight. The PreflightResult type contains the output Actions and + // any preflight updated values that you want to be displayed to the user, typically + // through a user interface (UI). + PreflightResult preflightResult; + + // If your filter is making structural changes to the DataStructure then the filter + // is going to create OutputActions subclasses that need to be returned. This will + // store those actions. + complex::Result resultOutputActions; + + // If your filter is going to pass back some `preflight updated values` then this is where you + // would create the code to store those values in the appropriate object. Note that we + // in line creating the pair (NOT a std::pair<>) of Key:Value that will get stored in + // the std::vector object. + std::vector preflightUpdatedValues; + + // If the filter needs to pass back some updated values via a key:value string:string set of values + // you can declare and update that string here. + // None found in this filter based on the filter parameters + + // If this filter makes changes to the DataStructure in the form of + // creating/deleting/moving/renaming DataGroups, Geometries, DataArrays then you + // will need to use one of the `*Actions` classes located in complex/Filter/Actions + // to relay that information to the preflight and execute methods. This is done by + // creating an instance of the Action class and then storing it in the resultOutputActions variable. + // This is done through a `push_back()` method combined with a `std::move()`. For the + // newly initiated to `std::move` once that code is executed what was once inside the Action class + // instance variable is *no longer there*. The memory has been moved. If you try to access that + // variable after this line you will probably get a crash or have subtle bugs. To ensure that this + // does not happen we suggest using braces `{}` to scope each of the action's declaration and store + // so that the programmer is not tempted to use the action instance past where it should be used. + // You have to create your own Actions class if there isn't something specific for your filter's needs + // These are some proposed Actions based on the FilterParameters used. Please check them for correctness. + // This block is commented out because it needs some variables to be filled in. + { + // auto createArrayAction = std::make_unique(complex::NumericType::FILL_ME_IN, std::vector{NUM_TUPLES_VALUE}, NUM_COMPONENTS, pSilhouetteArrayPathValue); + // resultOutputActions.value().actions.push_back(std::move(createArrayAction)); + } + + // Store the preflight updated value(s) into the preflightUpdatedValues vector using + // the appropriate methods. + // None found based on the filter parameters + + // Return both the resultOutputActions and the preflightUpdatedValues via std::move() + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> SilhouetteFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + + SilhouetteInputValues inputValues; + + inputValues.DistanceMetric = filterArgs.value(k_DistanceMetric_Key); + inputValues.UseMask = filterArgs.value(k_UseMask_Key); + inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key); + inputValues.MaskArrayPath = filterArgs.value(k_MaskArrayPath_Key); + inputValues.FeatureIdsArrayPath = filterArgs.value(k_FeatureIdsArrayPath_Key); + inputValues.SilhouetteArrayPath = filterArgs.value(k_SilhouetteArrayPath_Key); + + return Silhouette(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} +} // namespace complex diff --git a/Core/src/Core/Filters/SilhouetteFilter.hpp b/Core/src/Core/Filters/SilhouetteFilter.hpp new file mode 100644 index 000000000..f23c6797a --- /dev/null +++ b/Core/src/Core/Filters/SilhouetteFilter.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include "Core/Core_export.hpp" + +#include "complex/Filter/FilterTraits.hpp" +#include "complex/Filter/IFilter.hpp" + +namespace complex +{ +/** + * @class SilhouetteFilter + * @brief This filter will .... + */ +class CORE_EXPORT SilhouetteFilter : public IFilter +{ +public: + SilhouetteFilter() = default; + ~SilhouetteFilter() noexcept override = default; + + SilhouetteFilter(const SilhouetteFilter&) = delete; + SilhouetteFilter(SilhouetteFilter&&) noexcept = delete; + + SilhouetteFilter& operator=(const SilhouetteFilter&) = delete; + SilhouetteFilter& operator=(SilhouetteFilter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_DistanceMetric_Key = "distance_metric"; + static inline constexpr StringLiteral k_UseMask_Key = "use_mask"; + static inline constexpr StringLiteral k_SelectedArrayPath_Key = "selected_array_path"; + static inline constexpr StringLiteral k_MaskArrayPath_Key = "mask_array_path"; + static inline constexpr StringLiteral k_FeatureIdsArrayPath_Key = "feature_ids_array_path"; + static inline constexpr StringLiteral k_SilhouetteArrayPath_Key = "silhouette_array_path"; + + /** + * @brief Returns the name of the filter. + * @return + */ + std::string name() const override; + + /** + * @brief Returns the C++ classname of this filter. + * @return + */ + std::string className() const override; + + /** + * @brief Returns the uuid of the filter. + * @return + */ + Uuid uuid() const override; + + /** + * @brief Returns the human readable name of the filter. + * @return + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief Returns the parameters of the filter (i.e. its inputs) + * @return + */ + Parameters parameters() const override; + + /** + * @brief Returns a copy of the filter. + * @return + */ + UniquePointer clone() const override; + +protected: + /** + * @brief Takes in a DataStructure and checks that the filter can be run on it with the given arguments. + * Returns any warnings/errors. Also returns the changes that would be applied to the DataStructure. + * Some parts of the actions may not be completely filled out if all the required information is not available at preflight time. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + PreflightResult preflightImpl(const DataStructure& ds, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; + + /** + * @brief Applies the filter's algorithm to the DataStructure with the given arguments. Returns any warnings/errors. + * On failure, there is no guarantee that the DataStructure is in a correct state. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + Result<> executeImpl(DataStructure & data, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) + const override; +}; +} // namespace complex + +COMPLEX_DEF_FILTER_TRAITS(complex, SilhouetteFilter, "746d10be-6b34-57af-9596-d75b27e1dc85"); +/* LEGACY UUID FOR THIS FILTER {f84d4d69-9ea5-54b6-a71c-df76d76d50cf} */ diff --git a/Core/test/CMakeLists.txt b/Core/test/CMakeLists.txt index 5b5b325ed..72d8a564f 100644 --- a/Core/test/CMakeLists.txt +++ b/Core/test/CMakeLists.txt @@ -19,7 +19,18 @@ set(${PLUGIN_NAME}UnitTest_SRCS FindEuclideanDistMapTest.cpp FindNeighborhoodsTest.cpp FindSurfaceAreaToVolumeTest.cpp +<<<<<<< HEAD FeatureDataCSVWriterTest.cpp +======= + CalculateArrayHistogramTest.cpp + RemoveFlaggedFeaturesTest.cpp + SplitAttributeArrayTest.cpp + + KMedoidsTest.cpp + KMeansTest.cpp + SilhouetteTest.cpp + ImportDeformKeyFilev12Test.cpp +>>>>>>> 88155031 (Clustering Filters for PW-Alarm project) ) create_complex_plugin_unit_test(PLUGIN_NAME ${PLUGIN_NAME} diff --git a/Core/test/ImportDeformKeyFilev12FilterTest.cpp b/Core/test/ImportDeformKeyFilev12FilterTest.cpp new file mode 100644 index 000000000..e97743087 --- /dev/null +++ b/Core/test/ImportDeformKeyFilev12FilterTest.cpp @@ -0,0 +1,70 @@ +/** + * This file is auto generated from the original Core/ImportDeformKeyFilev12FilterFilter + * runtime information. These are the steps that need to be taken to utilize this + * unit test in the proper way. + * + * 1: Validate each of the default parameters that gets created. + * 2: Inspect the actual filter to determine if the filter in its default state + * would pass or fail BOTH the preflight() and execute() methods + * 3: UPDATE the ```REQUIRE(result.result.valid());``` code to have the proper + * + * 4: Add additional unit tests to actually test each code path within the filter + * + * There are some example Catch2 ```TEST_CASE``` sections for your inspiration. + * + * NOTE the format of the ```TEST_CASE``` macro. Please stick to this format to + * allow easier parsing of the unit tests. + * + * When you start working on this unit test remove "[ImportDeformKeyFilev12FilterFilter][.][UNIMPLEMENTED]" + * from the TEST_CASE macro. This will enable this unit test to be run by default + * and report errors. + */ + + +#include + +#include "complex/Parameters/FileSystemPathParameter.hpp" +#include "complex/Parameters/StringParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" + +#include +namespace fs = std::filesystem; + +#include "Core/Filters/ImportDeformKeyFilev12FilterFilter.hpp" +#include "Core/Core_test_dirs.hpp" + +using namespace complex; + +TEST_CASE("Core::ImportDeformKeyFilev12FilterFilter: Instantiation and Parameter Check","[Core][ImportDeformKeyFilev12FilterFilter][.][UNIMPLEMENTED][!mayfail]") +{ + // Instantiate the filter, a DataStructure object and an Arguments Object + ImportDeformKeyFilev12FilterFilter filter; + DataStructure ds; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(ImportDeformKeyFilev12FilterFilter::k_DEFORMInputFile_Key, std::make_any(fs::path("/Path/To/Input/File/To/Read.data"))); + args.insertOrAssign(ImportDeformKeyFilev12FilterFilter::k_VerboseOutput_Key, std::make_any(false)); + args.insertOrAssign(ImportDeformKeyFilev12FilterFilter::k_DataContainerName_Key, std::make_any("SomeString")); + args.insertOrAssign(ImportDeformKeyFilev12FilterFilter::k_VertexAttributeMatrixName_Key, std::make_any("SomeString")); + args.insertOrAssign(ImportDeformKeyFilev12FilterFilter::k_CellAttributeMatrixName_Key, std::make_any("SomeString")); + + + // Preflight the filter and check result + auto preflightResult = filter.preflight(ds, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(ds, args); + REQUIRE(executeResult.result.valid()); +} + +//TEST_CASE("Core::ImportDeformKeyFilev12FilterFilter: Valid filter execution") +//{ +// +//} + +//TEST_CASE("Core::ImportDeformKeyFilev12FilterFilter: InValid filter execution") +//{ +// +//} diff --git a/Core/test/ImportDeformKeyFilev12Test.cpp b/Core/test/ImportDeformKeyFilev12Test.cpp new file mode 100644 index 000000000..101c02129 --- /dev/null +++ b/Core/test/ImportDeformKeyFilev12Test.cpp @@ -0,0 +1,70 @@ +/** + * This file is auto generated from the original Core/ImportDeformKeyFilev12Filter + * runtime information. These are the steps that need to be taken to utilize this + * unit test in the proper way. + * + * 1: Validate each of the default parameters that gets created. + * 2: Inspect the actual filter to determine if the filter in its default state + * would pass or fail BOTH the preflight() and execute() methods + * 3: UPDATE the ```REQUIRE(result.result.valid());``` code to have the proper + * + * 4: Add additional unit tests to actually test each code path within the filter + * + * There are some example Catch2 ```TEST_CASE``` sections for your inspiration. + * + * NOTE the format of the ```TEST_CASE``` macro. Please stick to this format to + * allow easier parsing of the unit tests. + * + * When you start working on this unit test remove "[ImportDeformKeyFilev12Filter][.][UNIMPLEMENTED]" + * from the TEST_CASE macro. This will enable this unit test to be run by default + * and report errors. + */ + + +#include + +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/StringParameter.hpp" +#include "complex/Parameters/FileSystemPathParameter.hpp" + +#include +namespace fs = std::filesystem; + +#include "Core/Filters/ImportDeformKeyFilev12Filter.hpp" +#include "Core/Core_test_dirs.hpp" + +using namespace complex; + +TEST_CASE("Core::ImportDeformKeyFilev12Filter: Instantiation and Parameter Check","[Core][ImportDeformKeyFilev12Filter][.][UNIMPLEMENTED][!mayfail]") +{ + // Instantiate the filter, a DataStructure object and an Arguments Object + ImportDeformKeyFilev12Filter filter; + DataStructure ds; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(ImportDeformKeyFilev12Filter::k_DEFORMInputFile_Key, std::make_any(fs::path("/Path/To/Input/File/To/Read.data"))); + args.insertOrAssign(ImportDeformKeyFilev12Filter::k_VerboseOutput_Key, std::make_any(false)); + args.insertOrAssign(ImportDeformKeyFilev12Filter::k_DataContainerName_Key, std::make_any("SomeString")); + args.insertOrAssign(ImportDeformKeyFilev12Filter::k_VertexAttributeMatrixName_Key, std::make_any("SomeString")); + args.insertOrAssign(ImportDeformKeyFilev12Filter::k_CellAttributeMatrixName_Key, std::make_any("SomeString")); + + + // Preflight the filter and check result + auto preflightResult = filter.preflight(ds, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(ds, args); + REQUIRE(executeResult.result.valid()); +} + +//TEST_CASE("Core::ImportDeformKeyFilev12Filter: Valid filter execution") +//{ +// +//} + +//TEST_CASE("Core::ImportDeformKeyFilev12Filter: InValid filter execution") +//{ +// +//} diff --git a/Core/test/KMeansTest.cpp b/Core/test/KMeansTest.cpp new file mode 100644 index 000000000..a4e3b1217 --- /dev/null +++ b/Core/test/KMeansTest.cpp @@ -0,0 +1,72 @@ +/** + * This file is auto generated from the original Core/KMeansFilter + * runtime information. These are the steps that need to be taken to utilize this + * unit test in the proper way. + * + * 1: Validate each of the default parameters that gets created. + * 2: Inspect the actual filter to determine if the filter in its default state + * would pass or fail BOTH the preflight() and execute() methods + * 3: UPDATE the ```REQUIRE(result.result.valid());``` code to have the proper + * + * 4: Add additional unit tests to actually test each code path within the filter + * + * There are some example Catch2 ```TEST_CASE``` sections for your inspiration. + * + * NOTE the format of the ```TEST_CASE``` macro. Please stick to this format to + * allow easier parsing of the unit tests. + * + * When you start working on this unit test remove "[KMeansFilter][.][UNIMPLEMENTED]" + * from the TEST_CASE macro. This will enable this unit test to be run by default + * and report errors. + */ + + +#include + +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/NumberParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" + +#include "Core/Filters/KMeansFilter.hpp" +#include "Core/Core_test_dirs.hpp" + +using namespace complex; + +TEST_CASE("Core::KMeansFilter: Instantiation and Parameter Check","[Core][KMeansFilter][.][UNIMPLEMENTED][!mayfail]") +{ + // Instantiate the filter, a DataStructure object and an Arguments Object + KMeansFilter filter; + DataStructure ds; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(KMeansFilter::k_InitClusters_Key, std::make_any(1234356)); + args.insertOrAssign(KMeansFilter::k_DistanceMetric_Key, std::make_any(0)); + args.insertOrAssign(KMeansFilter::k_UseMask_Key, std::make_any(false)); + args.insertOrAssign(KMeansFilter::k_SelectedArrayPath_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMeansFilter::k_MaskArrayPath_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMeansFilter::k_FeatureIdsArrayName_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMeansFilter::k_FeatureAttributeMatrixName_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMeansFilter::k_MeansArrayName_Key, std::make_any(DataPath{})); + + + // Preflight the filter and check result + auto preflightResult = filter.preflight(ds, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(ds, args); + REQUIRE(executeResult.result.valid()); +} + +//TEST_CASE("Core::KMeansFilter: Valid filter execution") +//{ +// +//} + +//TEST_CASE("Core::KMeansFilter: InValid filter execution") +//{ +// +//} diff --git a/Core/test/KMedoidsTest.cpp b/Core/test/KMedoidsTest.cpp new file mode 100644 index 000000000..4f811ca84 --- /dev/null +++ b/Core/test/KMedoidsTest.cpp @@ -0,0 +1,72 @@ +/** + * This file is auto generated from the original Core/KMedoidsFilter + * runtime information. These are the steps that need to be taken to utilize this + * unit test in the proper way. + * + * 1: Validate each of the default parameters that gets created. + * 2: Inspect the actual filter to determine if the filter in its default state + * would pass or fail BOTH the preflight() and execute() methods + * 3: UPDATE the ```REQUIRE(result.result.valid());``` code to have the proper + * + * 4: Add additional unit tests to actually test each code path within the filter + * + * There are some example Catch2 ```TEST_CASE``` sections for your inspiration. + * + * NOTE the format of the ```TEST_CASE``` macro. Please stick to this format to + * allow easier parsing of the unit tests. + * + * When you start working on this unit test remove "[KMedoidsFilter][.][UNIMPLEMENTED]" + * from the TEST_CASE macro. This will enable this unit test to be run by default + * and report errors. + */ + + +#include + +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/NumberParameter.hpp" + +#include "Core/Filters/KMedoidsFilter.hpp" +#include "Core/Core_test_dirs.hpp" + +using namespace complex; + +TEST_CASE("Core::KMedoidsFilter: Instantiation and Parameter Check","[Core][KMedoidsFilter][.][UNIMPLEMENTED][!mayfail]") +{ + // Instantiate the filter, a DataStructure object and an Arguments Object + KMedoidsFilter filter; + DataStructure ds; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(KMedoidsFilter::k_InitClusters_Key, std::make_any(1234356)); + args.insertOrAssign(KMedoidsFilter::k_DistanceMetric_Key, std::make_any(0)); + args.insertOrAssign(KMedoidsFilter::k_UseMask_Key, std::make_any(false)); + args.insertOrAssign(KMedoidsFilter::k_SelectedArrayPath_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMedoidsFilter::k_MaskArrayPath_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMedoidsFilter::k_FeatureIdsArrayName_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMedoidsFilter::k_FeatureAttributeMatrixName_Key, std::make_any(DataPath{})); + args.insertOrAssign(KMedoidsFilter::k_MedoidsArrayName_Key, std::make_any(DataPath{})); + + + // Preflight the filter and check result + auto preflightResult = filter.preflight(ds, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(ds, args); + REQUIRE(executeResult.result.valid()); +} + +//TEST_CASE("Core::KMedoidsFilter: Valid filter execution") +//{ +// +//} + +//TEST_CASE("Core::KMedoidsFilter: InValid filter execution") +//{ +// +//} diff --git a/Core/test/SilhouetteTest.cpp b/Core/test/SilhouetteTest.cpp new file mode 100644 index 000000000..4774377a3 --- /dev/null +++ b/Core/test/SilhouetteTest.cpp @@ -0,0 +1,69 @@ +/** + * This file is auto generated from the original Core/SilhouetteFilter + * runtime information. These are the steps that need to be taken to utilize this + * unit test in the proper way. + * + * 1: Validate each of the default parameters that gets created. + * 2: Inspect the actual filter to determine if the filter in its default state + * would pass or fail BOTH the preflight() and execute() methods + * 3: UPDATE the ```REQUIRE(result.result.valid());``` code to have the proper + * + * 4: Add additional unit tests to actually test each code path within the filter + * + * There are some example Catch2 ```TEST_CASE``` sections for your inspiration. + * + * NOTE the format of the ```TEST_CASE``` macro. Please stick to this format to + * allow easier parsing of the unit tests. + * + * When you start working on this unit test remove "[SilhouetteFilter][.][UNIMPLEMENTED]" + * from the TEST_CASE macro. This will enable this unit test to be run by default + * and report errors. + */ + + +#include + +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ArrayCreationParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" + +#include "Core/Filters/SilhouetteFilter.hpp" +#include "Core/Core_test_dirs.hpp" + +using namespace complex; + +TEST_CASE("Core::SilhouetteFilter: Instantiation and Parameter Check","[Core][SilhouetteFilter][.][UNIMPLEMENTED][!mayfail]") +{ + // Instantiate the filter, a DataStructure object and an Arguments Object + SilhouetteFilter filter; + DataStructure ds; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(SilhouetteFilter::k_DistanceMetric_Key, std::make_any(0)); + args.insertOrAssign(SilhouetteFilter::k_UseMask_Key, std::make_any(false)); + args.insertOrAssign(SilhouetteFilter::k_SelectedArrayPath_Key, std::make_any(DataPath{})); + args.insertOrAssign(SilhouetteFilter::k_MaskArrayPath_Key, std::make_any(DataPath{})); + args.insertOrAssign(SilhouetteFilter::k_FeatureIdsArrayPath_Key, std::make_any(DataPath{})); + args.insertOrAssign(SilhouetteFilter::k_SilhouetteArrayPath_Key, std::make_any(DataPath{})); + + + // Preflight the filter and check result + auto preflightResult = filter.preflight(ds, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(ds, args); + REQUIRE(executeResult.result.valid()); +} + +//TEST_CASE("Core::SilhouetteFilter: Valid filter execution") +//{ +// +//} + +//TEST_CASE("Core::SilhouetteFilter: InValid filter execution") +//{ +// +//} From c4865edaa7e1d2c151de069b5795bc26b3bacccb Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Fri, 4 Nov 2022 12:02:26 -0400 Subject: [PATCH 3/3] Intermediate commit. NOT compiling Signed-off-by: Michael Jackson --- Core/CMakeLists.txt | 6 +- Core/src/Core/Filters/Algorithms/KMedoids.cpp | 31 +- Core/src/Core/Filters/KMedoidsFilter.cpp | 9 +- .../ClusteringAlgorithms/DBSCANTemplate.hpp | 327 ++++++++++++++++++ .../ClusteringAlgorithms/KMeansTemplate.hpp | 252 ++++++++++++++ .../ClusteringAlgorithms/KMedoidsTemplate.hpp | 236 +++++++++++++ Core/src/Core/Utilities/DistanceTemplate.hpp | 161 +++++++++ 7 files changed, 1000 insertions(+), 22 deletions(-) create mode 100644 Core/src/Core/Utilities/ClusteringAlgorithms/DBSCANTemplate.hpp create mode 100644 Core/src/Core/Utilities/ClusteringAlgorithms/KMeansTemplate.hpp create mode 100644 Core/src/Core/Utilities/ClusteringAlgorithms/KMedoidsTemplate.hpp create mode 100644 Core/src/Core/Utilities/DistanceTemplate.hpp diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 7349c7a92..52f4460fc 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -65,7 +65,11 @@ create_complex_plugin(NAME ${PLUGIN_NAME} # can use the target_sources(...) cmake call target_sources(${PLUGIN_NAME} PUBLIC - "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Utilities/CoreUtilities.hpp" + "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Utilities/CoreUtilities.hpp" + "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Utilities/DistanceTemplate.hpp" + "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Utilities/ClusteringAlgorithms/DBSCANTemplate.hpp" + "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Utilities/ClusteringAlgorithms/KMedoidsTemplate.hpp" + "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Utilities/ClusteringAlgorithms/KMeansTemplate.hpp" ) source_group(TREE "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}" PREFIX ${PLUGIN_NAME} diff --git a/Core/src/Core/Filters/Algorithms/KMedoids.cpp b/Core/src/Core/Filters/Algorithms/KMedoids.cpp index d4c001931..7867596cf 100644 --- a/Core/src/Core/Filters/Algorithms/KMedoids.cpp +++ b/Core/src/Core/Filters/Algorithms/KMedoids.cpp @@ -2,6 +2,9 @@ #include "complex/DataStructure/DataArray.hpp" #include "complex/DataStructure/DataGroup.hpp" +#include "complex/Utilities/FilterUtilities.hpp" + +#include "Core/Utilities/ClusteringAlgorithms/KMedoidsTemplate.hpp" using namespace complex; @@ -26,23 +29,17 @@ const std::atomic_bool& KMedoids::getCancel() // ----------------------------------------------------------------------------- Result<> KMedoids::operator()() { - /** - * This section of the code should contain the actual algorithmic codes that - * will accomplish the goal of the file. - * - * If you can parallelize the code there are a number of examples on how to do that. - * GenerateIPFColors is one example - * - * If you need to determine what kind of array you have (Int32Array, Float32Array, etc) - * look to the ExecuteDataFunction() in complex/Utilities/FilterUtilities.hpp template - * function to help with that code. - * An Example algorithm class is `CombineAttributeArrays` and `RemoveFlaggedVertices` - * - * There are other utility classes that can help alleviate the amount of code that needs - * to be written. - * - * REMOVE THIS COMMENT BLOCK WHEN YOU ARE FINISHED WITH THE FILTER_HUMAN_NAME - */ + if(m_InputValues->UseMask) + { + ExecuteDataFunction(KMedoidsTemplate{}, m_InDataPtr.getDataType(), m_InDataPtr, m_MedoidsArrayPtr, m_MaskPtr, m_InitClusters, m_FeatureIdsPtr, m_DistanceMetric) + } + else + { + size_t numTuples = m_InDataPtr->getNumberOfTuples(); + std::vector tmpMask(numTuples, true); + ExecuteDataFunction(KMedoidsTemplate{}, m_InDataPtr.getDataType(), m_InDataPtr, this, m_InDataPtr, m_MedoidsArrayPtr, tmpMask, m_InitClusters, m_FeatureIdsPtr, m_DistanceMetric) + } + return {}; } diff --git a/Core/src/Core/Filters/KMedoidsFilter.cpp b/Core/src/Core/Filters/KMedoidsFilter.cpp index f19023566..a4e5bcad2 100644 --- a/Core/src/Core/Filters/KMedoidsFilter.cpp +++ b/Core/src/Core/Filters/KMedoidsFilter.cpp @@ -10,11 +10,12 @@ #include "complex/Parameters/ChoicesParameter.hpp" #include "complex/Parameters/NumberParameter.hpp" +#include "Core/Utilities/DistanceTemplate.hpp" + using namespace complex; namespace { -ChoicesParameter::Choices k_ClusterChoices = {"Euclidean", "Squared Euclidean", "Manhattan", "Cosine", "Pearson", "Squared Pearson"}; } namespace complex @@ -58,16 +59,16 @@ Parameters KMedoidsFilter::parameters() const params.insertSeparator(Parameters::Separator{"Input Parameters"}); params.insert(std::make_unique(k_InitClusters_Key, "Number of Clusters", "", 2)); - params.insert(std::make_unique(k_DistanceMetric_Key, "Distance Metric", "", 0, k_ClusterChoices)); + params.insert(std::make_unique(k_DistanceMetric_Key, "Distance Metric", "", 0, DistanceTemplate::GetDistanceMetricsOptions())); params.insertSeparator(Parameters::Separator{"Optional Mask Array"}); params.insertLinkableParameter(std::make_unique(k_UseMask_Key, "Use Mask", "", false)); - params.insert(std::make_unique(k_MaskArrayPath_Key, "Mask", "", DataPath{}, complex::GetAllDataTypes())); + params.insert(std::make_unique(k_MaskArrayPath_Key, "Mask", "", DataPath{}, ArraySelectionParameter::AllowedTypes{DataType::boolean, DataType::uint8})); params.insertSeparator(Parameters::Separator{"Required Input Data"}); params.insert(std::make_unique(k_SelectedArrayPath_Key, "Attribute Array to Cluster", "", DataPath{}, complex::GetAllDataTypes())); - params.insertSeparator(Parameters::Separator{"Created Cell Data Objects"}); + params.insertSeparator(Parameters::Separator{"Created Cell Data Objects"}); params.insert(std::make_unique(k_FeatureIdsArrayName_Key, "Cluster Ids", "", DataPath{})); params.insertSeparator(Parameters::Separator{"Created Attribute Matrix Data Objects"}); diff --git a/Core/src/Core/Utilities/ClusteringAlgorithms/DBSCANTemplate.hpp b/Core/src/Core/Utilities/ClusteringAlgorithms/DBSCANTemplate.hpp new file mode 100644 index 000000000..a5c59c60f --- /dev/null +++ b/Core/src/Core/Utilities/ClusteringAlgorithms/DBSCANTemplate.hpp @@ -0,0 +1,327 @@ +/* ============================================================================ + * Copyright (c) 2009-2016 BlueQuartz Software, LLC + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of BlueQuartz Software, the US Air Force, nor the names of its + * contributors may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The code contained herein was partially funded by the following contracts: + * United States Air Force Prime Contract FA8650-07-D-5800 + * United States Air Force Prime Contract FA8650-10-D-5210 + * United States Prime Contract Navy N00173-07-C-2068 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +#pragma once + +#ifdef SIMPL_USE_PARALLEL_ALGORITHMS +#include +#include +#include +#endif + +#include "SIMPLib/SIMPLib.h" +#include "SIMPLib/Filtering/AbstractFilter.h" + +#include "DREAM3DReview/DREAM3DReviewFilters/util/DistanceTemplate.hpp" + +template +class FindEpsilonNeighborhoodsImpl +{ +public: + FindEpsilonNeighborhoodsImpl(AbstractFilter* filter, double epsilon, T* inputData, bool* mask, size_t numCompDims, size_t numTuples, int32_t distMetric, + std::vector>& neighborhoods) + : m_Filter(filter) + , m_Epsilon(epsilon) + , m_InputData(inputData) + , m_Mask(mask) + , m_NumCompDims(numCompDims) + , m_NumTuples(numTuples) + , m_DistMetric(distMetric) + , m_Neighborhoods(neighborhoods) + { + } + + void compute(size_t start, size_t end) const + { + // int64_t counter = 0; + // int64_t totalElements = end - start; + // int64_t progIncrement = static_cast(totalElements / 100); + + for(size_t i = start; i < end; i++) + { + if(m_Mask[i]) + { + std::list neighbors = epsilon_neighbors(i); + m_Neighborhoods[i] = neighbors; + } + } + } + + std::list epsilon_neighbors(size_t index) const + { + std::list neighbors; + + for(size_t i = 0; i < m_NumTuples; i++) + { + if(m_Filter->getCancel()) + { + return std::list(); + } + if(m_Mask[i]) + { + double dist = DistanceTemplate::GetDistance(m_InputData + (m_NumCompDims * index), m_InputData + (m_NumCompDims * i), m_NumCompDims, m_DistMetric); + if(dist < m_Epsilon) + { + neighbors.push_back(i); + } + } + } + + return neighbors; + } + +#ifdef SIMPL_USE_PARALLEL_ALGORITHMS + void operator()(const tbb::blocked_range& r) const + { + compute(r.begin(), r.end()); + } +#endif + +private: + AbstractFilter* m_Filter; + double m_Epsilon; + T* m_InputData; + bool* m_Mask; + size_t m_NumCompDims; + size_t m_NumTuples; + int32_t m_DistMetric; + std::vector>& m_Neighborhoods; +}; + +template +class DBSCANTemplate +{ +public: + using Self = DBSCANTemplate; + using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; + using WeakPointer = std::weak_ptr; + using ConstWeakPointer = std::weak_ptr; + static Pointer NullPointer() + { + return Pointer(static_cast(nullptr)); + } + + /** + * @brief Returns the name of the class for DBSCANTemplate + */ + /** + * @brief Returns the name of the class for DBSCANTemplate + */ + QString getNameOfClass() const + { + return QString("DBSCANTemplate"); + } + + /** + * @brief Returns the name of the class for DBSCANTemplate + */ + QString ClassName() + { + return QString("DBSCANTemplate"); + } + + DBSCANTemplate() + { + } + virtual ~DBSCANTemplate() + { + } + + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + bool operator()(IDataArray::Pointer p) + { + return (std::dynamic_pointer_cast>(p) != nullptr); + } + + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + void Execute(AbstractFilter* filter, IDataArray::Pointer inputIDataArray, BoolArrayType::Pointer maskDataArray, Int32ArrayType::Pointer fIds, float epsilon, int32_t minPnts, int32_t distMetric) + { + typename DataArray::Pointer inputDataPtr = std::dynamic_pointer_cast>(inputIDataArray); + T* inputData = inputDataPtr->getPointer(0); + int32_t* fPtr = fIds->getPointer(0); + + size_t numTuples = inputDataPtr->getNumberOfTuples(); + size_t numCompDims = inputDataPtr->getNumberOfComponents(); + std::vector visited(numTuples, false); + std::vector clustered(numTuples, false); + + double minDist = static_cast(epsilon); + int32_t cluster = 0; + bool* mask = maskDataArray->getPointer(0); + + int64_t progIncrement = static_cast(numTuples / 100); + int64_t prog = 1; + int64_t progressInt = 0; + int64_t counter = 0; + + std::vector> epsilonNeighborhoods(numTuples); +#ifdef SIMPL_USE_PARALLEL_ALGORITHMS + bool doParallel = true; +#endif + +#ifdef SIMPL_USE_PARALLEL_ALGORITHMS + if(doParallel == true) + { + tbb::parallel_for(tbb::blocked_range(0, numTuples), FindEpsilonNeighborhoodsImpl(filter, minDist, inputData, mask, numCompDims, numTuples, distMetric, epsilonNeighborhoods), + tbb::auto_partitioner()); + } + else +#endif + { + FindEpsilonNeighborhoodsImpl serial(filter, minDist, inputData, mask, numCompDims, numTuples, distMetric, epsilonNeighborhoods); + serial.compute(0, numTuples); + } + + prog = 1; + progressInt = 0; + counter = 0; + + for(size_t i = 0; i < numTuples; i++) + { + if(filter->getCancel()) + { + return; + } + if(mask[i] && !visited[i]) + { + visited[i] = true; + + if(counter > prog) + { + progressInt = static_cast((static_cast(counter) / numTuples) * 100.0f); + QString ss = QObject::tr("Scanning Data || Visited Point %1 of %2 || %3% Completed").arg(counter).arg(numTuples).arg(progressInt); + filter->notifyStatusMessage(ss); + prog = prog + progIncrement; + } + counter++; + + std::list neighbors = epsilonNeighborhoods[i]; + + if(static_cast(neighbors.size()) < minPnts) + { + fPtr[i] = 0; + clustered[i] = true; + } + else + { + cluster++; + expand_cluster(filter, neighbors, fPtr, cluster, minPnts, visited, clustered, minDist, i, inputData, mask, numCompDims, numTuples, distMetric, progIncrement, prog, progressInt, counter, + epsilonNeighborhoods); + } + } + } + } + +private: + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + std::list epsilon_neighbors(AbstractFilter* filter, double eps, size_t index, T* data, bool* mask, size_t dims, size_t numTuples, int32_t metric) + { + std::list neighbors; + + for(size_t i = 0; i < numTuples; i++) + { + if(filter->getCancel()) + { + return std::list(); + } + if(mask[i]) + { + double dist = DistanceTemplate::GetDistance(data + (dims * index), data + (dims * i), dims, metric); + if(dist < eps) + { + neighbors.push_back(i); + } + } + } + + return neighbors; + } + + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + void expand_cluster(AbstractFilter* filter, std::list& neighbors, int32_t* features, int32_t cluster, int32_t minPnts, std::vector& visited, std::vector& clustered, double eps, + size_t index, T* data, bool* mask, size_t dims, size_t numTuples, int32_t metric, int64_t& progIncrement, int64_t& prog, int64_t& progressInt, int64_t& counter, + std::vector>& epsNeighbors) + { + features[index] = cluster; + clustered[index] = true; + + for(auto&& idx : neighbors) + { + if(filter->getCancel()) + { + return; + } + if(mask[idx]) + { + if(!visited[idx]) + { + visited[idx] = true; + + if(counter > prog) + { + progressInt = static_cast((static_cast(counter) / numTuples) * 100.0f); + QString ss = QObject::tr("Scanning Data || Visited Point %1 of %2 || %3% Completed").arg(counter).arg(numTuples).arg(progressInt); + filter->notifyStatusMessage(ss); + prog = prog + progIncrement; + } + counter++; + + std::list neighbors_prime = epsNeighbors[idx]; + if(static_cast(neighbors_prime.size()) >= minPnts) + { + neighbors.splice(std::end(neighbors), neighbors_prime); + } + } + if(!clustered[idx]) + { + features[idx] = cluster; + clustered[idx] = true; + } + } + } + } + + DBSCANTemplate(const DBSCANTemplate&); // Copy Constructor Not Implemented + void operator=(const DBSCANTemplate&); // Move assignment Not Implemented +}; diff --git a/Core/src/Core/Utilities/ClusteringAlgorithms/KMeansTemplate.hpp b/Core/src/Core/Utilities/ClusteringAlgorithms/KMeansTemplate.hpp new file mode 100644 index 000000000..6f6a29665 --- /dev/null +++ b/Core/src/Core/Utilities/ClusteringAlgorithms/KMeansTemplate.hpp @@ -0,0 +1,252 @@ +/* ============================================================================ + * Copyright (c) 2009-2016 BlueQuartz Software, LLC + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of BlueQuartz Software, the US Air Force, nor the names of its + * contributors may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The code contained herein was partially funded by the following contracts: + * United States Air Force Prime Contract FA8650-07-D-5800 + * United States Air Force Prime Contract FA8650-10-D-5210 + * United States Prime Contract Navy N00173-07-C-2068 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +#pragma once + +#include +#include + +#include "SIMPLib/SIMPLib.h" +#include "SIMPLib/Filtering/AbstractFilter.h" +#include "SIMPLib/Math/SIMPLibMath.h" + +#include "DREAM3DReview/DREAM3DReviewFilters/util/DistanceTemplate.hpp" + +template +class KMeansTemplate +{ +public: + using Self = KMeansTemplate; + using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; + using WeakPointer = std::weak_ptr; + using ConstWeakPointer = std::weak_ptr; + static Pointer NullPointer() + { + return Pointer(static_cast(nullptr)); + } + + /** + * @brief Returns the name of the class for KMeansTemplate + */ + /** + * @brief Returns the name of the class for KMeansTemplate + */ + QString getNameOfClass() const + { + return QString("KMeansTemplate"); + } + + /** + * @brief Returns the name of the class for KMeansTemplate + */ + QString ClassName() + { + return QString("KMeansTemplate"); + } + + KMeansTemplate() + { + } + virtual ~KMeansTemplate() + { + } + + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + bool operator()(IDataArray::Pointer p) + { + return (std::dynamic_pointer_cast>(p).get() != nullptr); + } + + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + void Execute(AbstractFilter* filter, IDataArray::Pointer inputIDataArray, DoubleArrayType::Pointer outputDataArray, BoolArrayType::Pointer maskDataArray, size_t numClusters, + Int32ArrayType::Pointer fIds, int distMetric) + { + typename DataArray::Pointer inputDataPtr = std::dynamic_pointer_cast>(inputIDataArray); + T* inputData = inputDataPtr->getPointer(0); + double* outputData = outputDataArray->getPointer(0); + + size_t numTuples = inputDataPtr->getNumberOfTuples(); + int32_t numCompDims = inputDataPtr->getNumberOfComponents(); + + size_t rangeMin = 0; + size_t rangeMax = numTuples - 1; + std::mt19937_64::result_type seed = static_cast(std::chrono::steady_clock::now().time_since_epoch().count()); + std::mt19937_64 gen(seed); + std::uniform_int_distribution dist(rangeMin, rangeMax); + + std::vector initClusterIdxs(numClusters); + bool* mask = maskDataArray->getPointer(0); + size_t clusterChoices = 0; + + while(clusterChoices < numClusters) + { + size_t index = dist(gen); + if(mask[index]) + { + initClusterIdxs[clusterChoices] = index; + clusterChoices++; + } + } + + for(size_t i = 0; i < numClusters; i++) + { + for(int32_t j = 0; j < numCompDims; j++) + { + outputData[numCompDims * (i + 1) + j] = inputData[numCompDims * initClusterIdxs[i] + j]; + } + } + + size_t updateCheck = 0; + std::vector oldMeans(numClusters); + std::vector differences(numClusters); + int32_t* fPtr = fIds->getPointer(0); + size_t iteration = 1; + + while(true) + { + if(filter->getCancel()) + { + return; + } + findClusters(filter, mask, inputData, outputData, fPtr, numTuples, numClusters, numCompDims, distMetric); + + for(size_t i = 0; i < numClusters; i++) + { + oldMeans[i] = outputData[i + 1]; + } + + findMeans(mask, inputData, outputData, fPtr, numTuples, numClusters, numCompDims); + + updateCheck = 0; + for(size_t i = 0; i < numClusters; i++) + { + differences[i] = oldMeans[i] - outputData[i + 1]; + if(SIMPLibMath::closeEnough(differences[i], 0.0)) + { + updateCheck++; + } + } + + double sum = std::accumulate(std::begin(differences), std::end(differences), 0.0); + QString ss = QObject::tr("Clustering Data || Iteration %1 || Total Mean Shift: %2").arg(iteration).arg(sum); + filter->notifyStatusMessage(ss); + iteration++; + + if(updateCheck == numClusters) + { + break; + } + } + } + +private: + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + void findClusters(AbstractFilter* filter, bool* mask, T* input, double* averages, int32_t* fIds, size_t tuples, int32_t clusters, int32_t dims, int32_t distMetric) + { + double dist = 0.0; + + for(size_t i = 0; i < tuples; i++) + { + if(filter->getCancel()) + { + return; + } + if(mask[i]) + { + double minDist = std::numeric_limits::max(); + for(size_t j = 0; j < clusters; j++) + { + dist = DistanceTemplate::GetDistance(input + (dims * i), averages + (dims * (j + 1)), dims, distMetric); + if(dist < minDist) + { + minDist = dist; + fIds[i] = j + 1; + } + } + } + } + } + + // ----------------------------------------------------------------------------- + // + // ----------------------------------------------------------------------------- + void findMeans(bool* mask, T* input, double* averages, int32_t* fIds, size_t tuples, int32_t clusters, int32_t dims) + { + double value = 0.0; + int32_t feature = 0; + std::vector counts(clusters + 1, 0); + + for(size_t i = 0; i <= clusters; i++) + { + for(size_t j = 0; j < dims; j++) + { + averages[dims * i + j] = 0.0; + } + } + + for(size_t i = 0; i < dims; i++) + { + for(size_t j = 0; j < tuples; j++) + { + value = static_cast(input[dims * j + i]); + feature = fIds[j]; + averages[dims * feature + i] += value; + counts[feature] += 1; + } + for(size_t j = 0; j <= clusters; j++) + { + if(counts[j] == 0) + { + averages[dims * j + i] = 0.0; + } + else + { + averages[dims * j + i] /= static_cast(counts[j]); + } + } + std::fill(std::begin(counts), std::end(counts), 0); + } + } + + KMeansTemplate(const KMeansTemplate&); // Copy Constructor Not Implemented + void operator=(const KMeansTemplate&); // Move assignment Not Implemented +}; diff --git a/Core/src/Core/Utilities/ClusteringAlgorithms/KMedoidsTemplate.hpp b/Core/src/Core/Utilities/ClusteringAlgorithms/KMedoidsTemplate.hpp new file mode 100644 index 000000000..952fb4a17 --- /dev/null +++ b/Core/src/Core/Utilities/ClusteringAlgorithms/KMedoidsTemplate.hpp @@ -0,0 +1,236 @@ +/* ============================================================================ + * Copyright (c) 2009-2016 BlueQuartz Software, LLC + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of BlueQuartz Software, the US Air Force, nor the names of its + * contributors may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The code contained herein was partially funded by the following contracts: + * United States Air Force Prime Contract FA8650-07-D-5800 + * United States Air Force Prime Contract FA8650-10-D-5210 + * United States Prime Contract Navy N00173-07-C-2068 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +#pragma once + +#include "Core/Utilities/DistanceTemplate.hpp" + +#include "complex/Filter/IFilter.hpp" + +#include +#include + +using namespace complex; + +template +struct KMedoidsTemplate +{ + + /** + * @brief + * @param filter + * @param inputIDataArray + * @param outputIDataArray + * @param maskDataArray + * @param numClusters + * @param featureIDs + * @param distMetric + */ + + Result<> operator()(const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, const IDataArray& inputIDataArray, IDataArray& outputIDataArray, const BoolArray& maskDataArray, + size_t numClusters, const Int32Array& featureIds, int32_t distMetric) + { + typename DataArray::Pointer inputDataPtr = std::dynamic_pointer_cast>(inputIDataArray); + typename DataArray::Pointer outputDataPtr = std::dynamic_pointer_cast>(outputIDataArray); + T* inputData = inputDataPtr->getPointer(0); + T* outputData = outputDataPtr->getPointer(0); + + size_t numTuples = inputDataPtr->getNumberOfTuples(); + int32_t numCompDims = inputDataPtr->getNumberOfComponents(); + + size_t rangeMin = 0; + size_t rangeMax = numTuples - 1; + std::mt19937_64::result_type seed = static_cast(std::chrono::steady_clock::now().time_since_epoch().count()); + std::mt19937_64 gen(seed); + std::uniform_int_distribution dist(rangeMin, rangeMax); + + std::vector clusterIdxs(numClusters); + size_t clusterChoices = 0; + + while(clusterChoices < numClusters) + { + size_t index = dist(gen); + if(maskDataArray[index]) + { + clusterIdxs[clusterChoices] = index; + clusterChoices++; + } + } + + for(size_t i = 0; i < numClusters; i++) + { + for(int32_t j = 0; j < numCompDims; j++) + { + outputData[numCompDims * (i + 1) + j] = inputData[numCompDims * clusterIdxs[i] + j]; + } + } + + // size_t updateCheck = 0; + + findClusters(mesgHandler, shouldCancel, maskDataArray, inputData, outputData, featureIds, numTuples, numClusters, numCompDims, distMetric); + + std::vector optClusterIdxs(clusterIdxs); + std::vector costs; + + costs = optimizeClusters(mesgHandler, shouldCancel, maskDataArray, inputData, outputData, featureIds, numTuples, numClusters, numCompDims, clusterIdxs, distMetric); + + bool update = optClusterIdxs == clusterIdxs ? false : true; + size_t iteration = 1; + + while(update) + { + findClusters(mesgHandler, shouldCancel, maskDataArray, inputData, outputData, featureIds, numTuples, numClusters, numCompDims, distMetric); + + optClusterIdxs = clusterIdxs; + + costs = optimizeClusters(mesgHandler, shouldCancel, maskDataArray, inputData, outputData, featureIds, numTuples, numClusters, numCompDims, clusterIdxs, distMetric); + + update = optClusterIdxs == clusterIdxs ? false : true; + + double sum = std::accumulate(std::begin(costs), std::end(costs), 0.0); + mesgHandler.operator()(IFilter::Message::Type::Info, fmt::format("Clustering Data || Iteration %1 || Total Cost: %2", iteration, sum)); + iteration++; + } + } + + /** + * @brief + * @param filter + * @param mask + * @param input + * @param medoids + * @param fIds + * @param tuples + * @param clusters + * @param dims + * @param distMetric + */ + void findClusters(const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, bool* mask, T* input, T* medoids, int32_t* fIds, size_t tuples, int32_t clusters, int32_t dims, + int32_t distMetric) + { + double dist = 0.0; + for(size_t i = 0; i < tuples; i++) + { + if(shouldCancel) + { + return; + } + if(mask[i]) + { + double minDist = std::numeric_limits::max(); + for(size_t j = 0; j < clusters; j++) + { + dist = DistanceTemplate::GetDistance(input + (dims * i), medoids + (dims * (j + 1)), dims, distMetric); + if(dist < minDist) + { + minDist = dist; + fIds[i] = j + 1; + } + } + } + } + } + /** + * @brief + * @param filter + * @param mask + * @param input + * @param medoids + * @param fIds + * @param tuples + * @param clusters + * @param dims + * @param clusterIdxs + * @param distMetric + * @return + */ + std::vector optimizeClusters(const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, bool* mask, T* input, T* medoids, int32_t* fIds, size_t tuples, + int32_t clusters, int32_t dims, std::vector& clusterIdxs, int32_t distMetric) + { + double dist = 0.0; + std::vector minCosts(clusters, std::numeric_limits::max()); + + for(size_t i = 0; i < clusters; i++) + { + if(shouldCancel) + { + return {}; + } + for(size_t j = 0; j < tuples; j++) + { + if(shouldCancel) + { + return {}; + } + if(mask[j]) + { + double cost = 0.0; + if(fIds[j] == i + 1) + { + for(size_t k = 0; k < tuples; k++) + { + if(shouldCancel) + { + return {}; + } + if(fIds[k] == i + 1 && mask[k]) + { + dist = DistanceTemplate::GetDistance(input + (dims * k), input + (dims * j), dims, distMetric); + cost += dist; + } + } + + if(cost < minCosts[i]) + { + minCosts[i] = cost; + clusterIdxs[i] = j; + } + } + } + } + } + + for(size_t i = 0; i < clusters; i++) + { + for(int32_t j = 0; j < dims; j++) + { + medoids[dims * (i + 1) + j] = input[dims * clusterIdxs[i] + j]; + } + } + + return minCosts; + } + +}; // namespace KMedoidsTemplate diff --git a/Core/src/Core/Utilities/DistanceTemplate.hpp b/Core/src/Core/Utilities/DistanceTemplate.hpp new file mode 100644 index 000000000..315edf0cd --- /dev/null +++ b/Core/src/Core/Utilities/DistanceTemplate.hpp @@ -0,0 +1,161 @@ +/* + * Your License or Copyright Information can go here + */ + +#pragma once + +#include +#include +#include + +/** + * @brief The DistanceTemplate class contains a templated function getDistance to find the distance, via a variety of + * metrics, between two vectors of arbitrary dimensions. The developer should ensure that the pointers passed to + * getDistance do indeed contain vectors of the same component dimensions and start at the desired tuples. + */ +namespace DistanceTemplate +{ +const int32_t k_EuclideanIndex = 0; +const int32_t k_SquaredEuclideanIndex = 1; +const int32_t k_ManhattanIndex = 2; +const int32_t k_CosineIndex = 3; +const int32_t k_PearsonIndex = 4; +const int32_t k_SquaredPearsonIndex = 5; + +/** + * @brief + * @return + */ +std::vector GetDistanceMetricsOptions() +{ + return {"Euclidean", "Squared Euclidean", "Manhattan", "Cosine", "Pearson", "Squared Pearson"}; +} + +/** + * @brief + * @tparam leftDataType + * @tparam rightDataType + * @tparam outDataType + * @param leftVector + * @param rightVector + * @param compDims + * @param distMetric + * @return + */ +template +outDataType GetDistance(leftDataType* leftVector, rightDataType* rightVector, size_t compDims, int distMetric) +{ + double dist = 0.0; + double lVal = 0.0; + double rVal = 0.0; + + double epsilon = std::numeric_limits::min(); + + // Euclidean + if(distMetric == k_EuclideanIndex) + { + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + dist += (lVal - rVal) * (lVal - rVal); + } + + dist = sqrt(dist); + } + // Squared Euclidean + else if(distMetric == k_SquaredEuclideanIndex) + { + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + dist += (lVal - rVal) * (lVal - rVal); + } + } + // Manhattan + else if(distMetric == k_ManhattanIndex) + { + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + dist += fabs(lVal - rVal); + } + } + // Cosine + else if(distMetric == k_CosineIndex) + { + double r = 0; + double x = 0; + double y = 0; + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + r += lVal * rVal; + x += lVal * lVal; + y += rVal * rVal; + } + dist = 1 - (r / (sqrt(x * y) + epsilon)); + } + // Pearson + else if(distMetric == k_PearsonIndex) + { + double r = 0; + double x = 0; + double y = 0; + double xAvg = 0; + double yAvg = 0; + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + xAvg += lVal; + yAvg += rVal; + } + xAvg /= static_cast(compDims); + yAvg /= static_cast(compDims); + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + r += (lVal - xAvg) * (rVal - yAvg); + x += (lVal - xAvg) * (lVal - xAvg); + y += (rVal - yAvg) * (rVal - yAvg); + } + dist = 1 - (r / (sqrt(x * y) + epsilon)); + } + // Squared Pearson + else if(distMetric == k_SquaredPearsonIndex) + { + double r = 0; + double x = 0; + double y = 0; + double xAvg = 0; + double yAvg = 0; + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + xAvg += lVal; + yAvg += rVal; + } + xAvg /= static_cast(compDims); + yAvg /= static_cast(compDims); + for(size_t i = 0; i < compDims; i++) + { + lVal = static_cast(leftVector[i]); + rVal = static_cast(rightVector[i]); + r += (lVal - xAvg) * (rVal - yAvg); + x += (lVal - xAvg) * (lVal - xAvg); + y += (rVal - yAvg) * (rVal - yAvg); + } + dist = 1 - ((r * r) / ((x * y) + epsilon)); + } + + // Return the correct primitive type for distance + return static_cast(dist); +} + +}; // namespace DistanceTemplate