From 338be29a531bb61e03310dc5d0d8daf9fd22f094 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Wed, 21 Jan 2026 12:29:40 -0500 Subject: [PATCH] FILT: Port ComputeArrayNorm from simpl to simplnx. --- CMakeLists.txt | 2 + docs/ComputeArrayNormFilter.md | 32 ++++ .../Filters/Algorithms/ComputeArrayNorm.cpp | 87 ++++++++++ .../Filters/Algorithms/ComputeArrayNorm.hpp | 45 ++++++ .../Filters/ComputeArrayNormFilter.cpp | 149 ++++++++++++++++++ .../Filters/ComputeArrayNormFilter.hpp | 122 ++++++++++++++ .../SimplnxReviewLegacyUUIDMapping.hpp | 2 + 7 files changed, 439 insertions(+) create mode 100644 docs/ComputeArrayNormFilter.md create mode 100644 src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.cpp create mode 100644 src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp create mode 100644 src/SimplnxReview/Filters/ComputeArrayNormFilter.cpp create mode 100644 src/SimplnxReview/Filters/ComputeArrayNormFilter.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d887159..2c65f55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ set(${PLUGIN_NAME}_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}) # These are all the filters in the plugin. All filters should be kept in the # SimplnxReview/src/SimplnxReview/Filters/ directory. set(FilterList + ComputeArrayNormFilter ComputeGroupingDensityFilter ComputeLocalAverageCAxisMisalignmentsFilter ComputeMicroTextureRegionsFilter @@ -44,6 +45,7 @@ set(ActionList # This should be integrated with the `create_simplnx_plugin` function call # ------------------------------------------------------------------------------ set(AlgorithmList + ComputeArrayNorm ComputeGroupingDensity ComputeLocalAverageCAxisMisalignments ComputeMicroTextureRegions diff --git a/docs/ComputeArrayNormFilter.md b/docs/ComputeArrayNormFilter.md new file mode 100644 index 0000000..86d5d13 --- /dev/null +++ b/docs/ComputeArrayNormFilter.md @@ -0,0 +1,32 @@ +# Compute Array Norm + +## Group (Subgroup) + +SimplnxReview (Statistics) + +## Description + +This **Filter** computes the pth norm of an **Attribute Array**. Specifically, for each tuple of the array, the following is computed: + +$$\left\| \mathbf{x} \right\| _p := \bigg( \sum_{i=1}^n \left| x_i \right| ^p \bigg) ^{1/p}$$ + +where $n$ is the number of components for the **Attribute Array**. + +- When $p = 2$, this results in the *Euclidean norm* +- When $p = 1$, this results in the *Manhattan norm* (also called the *taxicab norm*) + +The p-space value may be any real number greater than or equal to zero. When $0 \leq p < 1$, the result may not strictly be a *norm* in the exact mathematical sense. Additionally, when $p = 0$, the result is simply the number of components for the **Attribute Array**. + +**Note:** If the input array is a scalar array (1 component), the output array will contain the same values as the input array, but in 32-bit floating point precision. + +% Auto generated parameter table will be inserted here + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this plugin. + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues/discussions) GitHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.cpp b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.cpp new file mode 100644 index 0000000..ffc4a20 --- /dev/null +++ b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.cpp @@ -0,0 +1,87 @@ +#include "ComputeArrayNorm.hpp" + +#include "simplnx/DataStructure/DataArray.hpp" +#include "simplnx/Utilities/DataArrayUtilities.hpp" +#include "simplnx/Utilities/FilterUtilities.hpp" + +#include + +using namespace nx::core; + +namespace +{ +template +class ComputeNormImpl +{ +public: + ComputeNormImpl(const IDataArray& inputArray, Float32Array& normArray, float32 pSpace) + : m_InputArray(dynamic_cast&>(inputArray)) + , m_NormArray(normArray) + , m_PSpace(pSpace) + { + } + + void operator()() const + { + const auto& inputStore = m_InputArray.getDataStoreRef(); + auto& normStore = m_NormArray.getDataStoreRef(); + + usize numTuples = m_InputArray.getNumberOfTuples(); + usize numComponents = m_InputArray.getNumberOfComponents(); + + for(usize i = 0; i < numTuples; i++) + { + float32 normTmp = 0.0f; + for(usize j = 0; j < numComponents; j++) + { + float32 value = static_cast(inputStore[numComponents * i + j]); + normTmp += std::pow(std::abs(value), m_PSpace); + } + normStore[i] = std::pow(normTmp, 1.0f / m_PSpace); + } + } + +private: + const DataArray& m_InputArray; + Float32Array& m_NormArray; + float32 m_PSpace; +}; + +struct ComputeNormFunctor +{ + template + void operator()(const IDataArray& inputArray, Float32Array& normArray, float32 pSpace) + { + ComputeNormImpl(inputArray, normArray, pSpace)(); + } +}; +} // namespace + +// ----------------------------------------------------------------------------- +ComputeArrayNorm::ComputeArrayNorm(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, ComputeArrayNormInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +ComputeArrayNorm::~ComputeArrayNorm() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& ComputeArrayNorm::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> ComputeArrayNorm::operator()() +{ + const auto& inputArray = m_DataStructure.getDataRefAs(m_InputValues->SelectedArrayPath); + auto& normArray = m_DataStructure.getDataRefAs(m_InputValues->NormArrayPath); + + ExecuteDataFunction(ComputeNormFunctor{}, inputArray.getDataType(), inputArray, normArray, m_InputValues->PSpace); + + return {}; +} diff --git a/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp new file mode 100644 index 0000000..44af109 --- /dev/null +++ b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "SimplnxReview/SimplnxReview_export.hpp" + +#include "simplnx/DataStructure/DataPath.hpp" +#include "simplnx/DataStructure/DataStructure.hpp" +#include "simplnx/Filter/IFilter.hpp" + +namespace nx::core +{ + +struct SIMPLNXREVIEW_EXPORT ComputeArrayNormInputValues +{ + float32 PSpace; + DataPath SelectedArrayPath; + DataPath NormArrayPath; +}; + +/** + * @class ComputeArrayNorm + * @brief This algorithm computes the p-th norm of an Attribute Array. + */ +class SIMPLNXREVIEW_EXPORT ComputeArrayNorm +{ +public: + ComputeArrayNorm(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, ComputeArrayNormInputValues* inputValues); + ~ComputeArrayNorm() noexcept; + + ComputeArrayNorm(const ComputeArrayNorm&) = delete; + ComputeArrayNorm(ComputeArrayNorm&&) noexcept = delete; + ComputeArrayNorm& operator=(const ComputeArrayNorm&) = delete; + ComputeArrayNorm& operator=(ComputeArrayNorm&&) noexcept = delete; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const ComputeArrayNormInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; +}; + +} // namespace nx::core diff --git a/src/SimplnxReview/Filters/ComputeArrayNormFilter.cpp b/src/SimplnxReview/Filters/ComputeArrayNormFilter.cpp new file mode 100644 index 0000000..79b5157 --- /dev/null +++ b/src/SimplnxReview/Filters/ComputeArrayNormFilter.cpp @@ -0,0 +1,149 @@ +#include "ComputeArrayNormFilter.hpp" + +#include "SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp" + +#include "simplnx/DataStructure/DataArray.hpp" +#include "simplnx/DataStructure/DataPath.hpp" +#include "simplnx/Filter/Actions/CreateArrayAction.hpp" +#include "simplnx/Parameters/ArraySelectionParameter.hpp" +#include "simplnx/Parameters/DataObjectNameParameter.hpp" +#include "simplnx/Parameters/NumberParameter.hpp" +#include "simplnx/Utilities/SIMPLConversion.hpp" + +using namespace nx::core; + +namespace nx::core +{ +//------------------------------------------------------------------------------ +std::string ComputeArrayNormFilter::name() const +{ + return FilterTraits::name.str(); +} + +//------------------------------------------------------------------------------ +std::string ComputeArrayNormFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid ComputeArrayNormFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string ComputeArrayNormFilter::humanName() const +{ + return "Compute Array Norm"; +} + +//------------------------------------------------------------------------------ +std::vector ComputeArrayNormFilter::defaultTags() const +{ + return {className(), "Statistics", "DREAM3DReview"}; +} + +//------------------------------------------------------------------------------ +Parameters ComputeArrayNormFilter::parameters() const +{ + Parameters params; + + params.insertSeparator(Parameters::Separator{"Input Parameter"}); + params.insert(std::make_unique(k_PSpace_Key, "p-Space Value", "p-Value used for computing the norm (2 = Euclidean, 1 = Manhattan)", 2.0f)); + + params.insertSeparator(Parameters::Separator{"Input Data"}); + params.insert(std::make_unique(k_SelectedArrayPath_Key, "Input Attribute Array", "The input array for computing the norm", DataPath{}, + ArraySelectionParameter::AllowedTypes{DataType::int8, DataType::uint8, DataType::int16, DataType::uint16, DataType::int32, DataType::uint32, + DataType::int64, DataType::uint64, DataType::float32, DataType::float64}, + ArraySelectionParameter::AllowedComponentShapes{})); + + params.insertSeparator(Parameters::Separator{"Output Data"}); + params.insert(std::make_unique(k_NormArrayName_Key, "Norm Array Name", "The name of the output norm array", "Norm")); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer ComputeArrayNormFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::VersionType ComputeArrayNormFilter::parametersVersion() const +{ + return 1; +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult ComputeArrayNormFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const +{ + auto pSpaceValue = filterArgs.value(k_PSpace_Key); + auto pSelectedArrayPathValue = filterArgs.value(k_SelectedArrayPath_Key); + auto pNormArrayNameValue = filterArgs.value(k_NormArrayName_Key); + + PreflightResult preflightResult; + nx::core::Result resultOutputActions; + std::vector preflightUpdatedValues; + + if(pSpaceValue < 0.0f) + { + return {MakeErrorResult(-11002, "p-space value must be greater than or equal to 0")}; + } + + const auto* inputArray = dataStructure.getDataAs(pSelectedArrayPathValue); + if(inputArray == nullptr) + { + return {MakeErrorResult(-11003, fmt::format("Cannot find the selected input array at path '{}'", pSelectedArrayPathValue.toString()))}; + } + + DataPath normArrayPath = pSelectedArrayPathValue.replaceName(pNormArrayNameValue); + + { + auto createArrayAction = std::make_unique(DataType::float32, inputArray->getTupleShape(), std::vector{1}, normArrayPath); + resultOutputActions.value().appendAction(std::move(createArrayAction)); + } + + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> ComputeArrayNormFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const +{ + ComputeArrayNormInputValues inputValues; + + inputValues.PSpace = filterArgs.value(k_PSpace_Key); + inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key); + inputValues.NormArrayPath = inputValues.SelectedArrayPath.replaceName(filterArgs.value(k_NormArrayName_Key)); + + return ComputeArrayNorm(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} + +namespace +{ +namespace SIMPL +{ +constexpr StringLiteral k_SelectedArrayPathKey = "SelectedArrayPath"; +constexpr StringLiteral k_NormArrayPathKey = "NormArrayPath"; +constexpr StringLiteral k_PSpaceKey = "PSpace"; +} // namespace SIMPL +} // namespace + +Result ComputeArrayNormFilter::FromSIMPLJson(const nlohmann::json& json) +{ + Arguments args = ComputeArrayNormFilter().getDefaultArguments(); + + std::vector> results; + + results.push_back(SIMPLConversion::ConvertParameter>(args, json, SIMPL::k_PSpaceKey, k_PSpace_Key)); + results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_SelectedArrayPathKey, k_SelectedArrayPath_Key)); + results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_NormArrayPathKey, k_NormArrayName_Key)); + + Result<> conversionResult = MergeResults(std::move(results)); + + return ConvertResultTo(std::move(conversionResult), std::move(args)); +} +} // namespace nx::core diff --git a/src/SimplnxReview/Filters/ComputeArrayNormFilter.hpp b/src/SimplnxReview/Filters/ComputeArrayNormFilter.hpp new file mode 100644 index 0000000..757fd66 --- /dev/null +++ b/src/SimplnxReview/Filters/ComputeArrayNormFilter.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include "SimplnxReview/SimplnxReview_export.hpp" + +#include "simplnx/Filter/FilterTraits.hpp" +#include "simplnx/Filter/IFilter.hpp" + +namespace nx::core +{ +/** + * @class ComputeArrayNormFilter + * @brief This filter computes the p-th norm of an Attribute Array. For each tuple of the array, + * the norm is calculated as: ||x||_p = (sum(|x_i|^p))^(1/p) where n is the number of components. + * When p=2, this results in the Euclidean norm; when p=1, this results in the Manhattan norm. + */ +class SIMPLNXREVIEW_EXPORT ComputeArrayNormFilter : public IFilter +{ +public: + ComputeArrayNormFilter() = default; + ~ComputeArrayNormFilter() noexcept override = default; + + ComputeArrayNormFilter(const ComputeArrayNormFilter&) = delete; + ComputeArrayNormFilter(ComputeArrayNormFilter&&) noexcept = delete; + + ComputeArrayNormFilter& operator=(const ComputeArrayNormFilter&) = delete; + ComputeArrayNormFilter& operator=(ComputeArrayNormFilter&&) noexcept = delete; + + // Parameter Keys + static constexpr StringLiteral k_PSpace_Key = "p_space_value"; + static constexpr StringLiteral k_SelectedArrayPath_Key = "selected_array_path"; + static constexpr StringLiteral k_NormArrayName_Key = "norm_array_name"; + + /** + * @brief Reads SIMPL json and converts it simplnx Arguments. + * @param json + * @return Result + */ + static Result FromSIMPLJson(const nlohmann::json& json); + + /** + * @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 parameters version integer. + * Initial version should always be 1. + * Should be incremented everytime the parameters change. + * @return VersionType + */ + VersionType parametersVersion() 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 dataStructure 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 + * @param shouldCancel Atomic boolean value that can be checked to cancel the filter + * @param executionContext The ExecutionContext that can be used to determine the correct absolute path from a relative path + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + PreflightResult preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel, + const ExecutionContext& executionContext) 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 dataStructure The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param pipelineNode The node in the pipeline that is being executed + * @param messageHandler The MessageHandler object + * @param shouldCancel Atomic boolean value that can be checked to cancel the filter + * @param executionContext The ExecutionContext that can be used to determine the correct absolute path from a relative path + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + Result<> executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel, + const ExecutionContext& executionContext) const override; +}; +} // namespace nx::core + +SIMPLNX_DEF_FILTER_TRAITS(nx::core, ComputeArrayNormFilter, "a9943ec6-1276-46d1-b9a4-c1537e8e5d5f"); +/* LEGACY UUID FOR THIS FILTER 5d0cd577-3e3e-57b8-a36d-b215b834251f */ diff --git a/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp b/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp index d56f487..b3fdbba 100644 --- a/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp +++ b/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp @@ -5,6 +5,7 @@ #include /* clang-format off */ +#include "SimplnxReview/Filters/ComputeArrayNormFilter.hpp" #include "SimplnxReview/Filters/GroupMicroTextureRegionsFilter.hpp" #include "SimplnxReview/Filters/MergeColoniesFilter.hpp" #include "SimplnxReview/Filters/ComputeSaltykovSizesFilter.hpp" @@ -22,6 +23,7 @@ namespace nx::core static const AbstractPlugin::SIMPLMapType k_SIMPL_to_SimplnxReview { // syntax std::make_pair {Dream3d UUID , Dream3dnx UUID, {}}}, // dream3d-class-name + {nx::core::Uuid::FromString("5d0cd577-3e3e-57b8-a36d-b215b834251f").value(), {nx::core::FilterTraits::uuid, &ComputeArrayNormFilter::FromSIMPLJson}}, // FindNorm {nx::core::Uuid::FromString("5e18a9e2-e342-56ac-a54e-3bd0ca8b9c53").value(), {nx::core::FilterTraits::uuid, &GroupMicroTextureRegionsFilter::FromSIMPLJson}}, // GroupMicroTextureRegions {nx::core::Uuid::FromString("2c4a6d83-6a1b-56d8-9f65-9453b28845b9").value(), {nx::core::FilterTraits::uuid, &MergeColoniesFilter::FromSIMPLJson}}, // MergeColonies {nx::core::Uuid::FromString("cc76cffe-81ad-5ece-be2a-ce127c5fa6d7").value(), {nx::core::FilterTraits::uuid, &ComputeSaltykovSizesFilter::FromSIMPLJson}}, // FindSaltykovSizes