diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.cpp index 4575220642..5d263f8b6c 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.cpp @@ -26,13 +26,6 @@ using namespace nx::core; namespace { const std::string k_MaskName = "temp_mask"; - -enum AlgType -{ - Iterative, - Random, - SeededRandom -}; } // namespace namespace nx::core @@ -75,7 +68,7 @@ Parameters DBSCANFilter::parameters() const // Create the parameter descriptors that are needed for this filter params.insertSeparator(Parameters::Separator{"Random Number Seed Parameters"}); params.insertLinkableParameter(std::make_unique(k_SeedChoice_Key, "Initialization Type", "Whether to use random or iterative for start state. See Documentation for further detail", - to_underlying(::AlgType::SeededRandom), + to_underlying(AlgType::SeededRandom), ChoicesParameter::Choices{"Iterative", "Random", "Seeded Random"})); // sequence dependent DO NOT REORDER params.insert(std::make_unique>(k_SeedValue_Key, "Seed Value", "The seed fed into the random generator", std::mt19937::default_seed)); params.insert(std::make_unique(k_SeedArrayName_Key, "Stored Seed Value Array Name", "Name of array holding the seed value", "DBSCAN SeedValue")); @@ -104,6 +97,9 @@ Parameters DBSCANFilter::parameters() const params.insert(std::make_unique(k_FeatureAMPath_Key, "Cluster Attribute Matrix", "name and path of Attribute Matrix to hold Cluster Data", DataPath{})); // Associate the Linkable Parameter(s) to the children parameters that they control + params.linkParameters(k_SeedChoice_Key, k_SeedArrayName_Key, static_cast(to_underlying(AlgType::Random))); + params.linkParameters(k_SeedChoice_Key, k_SeedValue_Key, static_cast(to_underlying(AlgType::SeededRandom))); + params.linkParameters(k_SeedChoice_Key, k_SeedArrayName_Key, static_cast(to_underlying(AlgType::SeededRandom))); params.linkParameters(k_UseMask_Key, k_MaskArrayPath_Key, true); return params; @@ -159,7 +155,7 @@ IFilter::PreflightResult DBSCANFilter::preflightImpl(const DataStructure& dataSt } // For caching seed run to run - if(filterArgs.value<::AlgType>(k_SeedChoice_Key) != AlgType::Iterative) + if(static_cast(filterArgs.value(k_SeedChoice_Key)) != AlgType::Iterative) { auto createAction = std::make_unique(DataType::uint64, std::vector{1}, std::vector{1}, DataPath({filterArgs.value(k_SeedArrayName_Key)})); resultOutputActions.value().appendAction(std::move(createAction)); @@ -181,12 +177,12 @@ Result<> DBSCANFilter::executeImpl(DataStructure& dataStructure, const Arguments } auto seed = filterArgs.value(k_SeedValue_Key); - if(filterArgs.value<::AlgType>(k_SeedChoice_Key) != AlgType::SeededRandom) + if(static_cast(filterArgs.value(k_SeedChoice_Key)) != AlgType::SeededRandom) { seed = static_cast(std::chrono::steady_clock::now().time_since_epoch().count()); } - if(filterArgs.value<::AlgType>(k_SeedChoice_Key) != AlgType::Iterative) + if(static_cast(filterArgs.value(k_SeedChoice_Key)) != AlgType::Iterative) { // Store Seed Value in Top Level Array dataStructure.getDataRefAs(DataPath({filterArgs.value(k_SeedArrayName_Key)}))[0] = seed; @@ -204,7 +200,7 @@ Result<> DBSCANFilter::executeImpl(DataStructure& dataStructure, const Arguments inputValues.FeatureIdsArrayPath = fIdsPath; inputValues.FeatureAM = filterArgs.value(k_FeatureAMPath_Key); inputValues.AllowCaching = filterArgs.value(k_UsePrecaching_Key); - inputValues.UseRandom = filterArgs.value<::AlgType>(k_SeedChoice_Key) != AlgType::Iterative; + inputValues.UseRandom = static_cast(filterArgs.value(k_SeedChoice_Key)) != AlgType::Iterative; inputValues.Seed = filterArgs.value(k_SeedValue_Key); return DBSCAN(dataStructure, messageHandler, shouldCancel, &inputValues)(); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.hpp index 54b3bfba67..ef7e88bad2 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.hpp @@ -7,6 +7,13 @@ namespace nx::core { +enum AlgType +{ + Iterative, + Random, + SeededRandom +}; + /** * @class DBSCANFilter * @brief This filter will .... diff --git a/src/Plugins/SimplnxCore/test/DBSCANTest.cpp b/src/Plugins/SimplnxCore/test/DBSCANTest.cpp index 839054adc1..ac564430e2 100644 --- a/src/Plugins/SimplnxCore/test/DBSCANTest.cpp +++ b/src/Plugins/SimplnxCore/test/DBSCANTest.cpp @@ -1,6 +1,7 @@ #include #include "simplnx/DataStructure/DataArray.hpp" +#include "simplnx/Parameters/ChoicesParameter.hpp" #include "simplnx/UnitTest/UnitTestCommon.hpp" #include "SimplnxCore/Filters/DBSCANFilter.hpp" @@ -17,25 +18,25 @@ constexpr std::array k_CircleIndexes = {553, 554, 555, 557, 601, 602 constexpr std::array k_TriangleIndexes = {556, 600, 603, 647, 694, 743, 745}; constexpr std::array k_XIndexes = {604, 648, 650, 695, 698, 741}; -const std::string k_ClusterData = "ClusterData"; -const std::string k_ClusterDataNX = k_ClusterData + "NX"; +const std::string k_TargetArrayName = "STRAIN"; +const std::string k_ClusterDataNX = "ClusterAM"; -const DataPath k_QuadGeomPath = DataPath({Constants::k_DataContainer}); -const DataPath k_CellPath = k_QuadGeomPath.createChildPath(Constants::k_CellData); -const DataPath k_ClusterDataPathNX = k_QuadGeomPath.createChildPath(k_ClusterDataNX); +const DataPath k_GeomPath = DataPath({"6_6_DBSCAN"}); +const DataPath k_CellPath = k_GeomPath.createChildPath(Constants::k_CellData); +const DataPath k_TargetArrayPath = k_CellPath.createChildPath(k_TargetArrayName); +const DataPath k_ClusterDataPathNX = k_GeomPath.createChildPath(k_ClusterDataNX); const std::string k_ClusterIdsName = "ClusterIds"; -const std::string k_MeansName = "ClusterMeans"; const std::string k_ClusterIdsNameNX = k_ClusterIdsName + "NX"; -const std::string k_MeansNameNX = k_MeansName + "NX"; +const DataPath k_ClusterIdsPath = k_CellPath.createChildPath(k_ClusterIdsName); const DataPath k_ClusterIdsPathNX = k_CellPath.createChildPath(k_ClusterIdsNameNX); } // namespace -TEST_CASE("SimplnxCore::DBSCAN: Valid Filter Execution (Precached)", "[SimplnxCore][DBSCAN]") +TEST_CASE("SimplnxCore::DBSCAN: Valid Filter Execution (Precached, Iterative)", "[SimplnxCore][DBSCAN]") { - const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "k_files.tar.gz", "k_files"); - DataStructure dataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/k_files/7_0_means_exemplar.dream3d", unit_test::k_TestFilesDir))); + const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "DBSCAN_tests.tar.gz", "DBSCAN_tests"); + DataStructure dataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/DBSCAN_tests/default/6_5_DBSCAN_Data.dream3d", unit_test::k_TestFilesDir))); { // Instantiate the filter and an Arguments Object @@ -43,11 +44,12 @@ TEST_CASE("SimplnxCore::DBSCAN: Valid Filter Execution (Precached)", "[SimplnxCo Arguments args; // Create default Parameters for the filter. + args.insertOrAssign(DBSCANFilter::k_SeedChoice_Key, std::make_any(to_underlying(AlgType::Iterative))); args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any(true)); - args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any(0.1)); - args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any(2)); + args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any(0.01)); + args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any(50)); args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any(false)); - args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any(k_CellPath.createChildPath("DAMAGE"))); + args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any(k_TargetArrayPath)); args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any(k_ClusterIdsNameNX)); args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any(k_ClusterDataPathNX)); @@ -60,63 +62,125 @@ TEST_CASE("SimplnxCore::DBSCAN: Valid Filter Execution (Precached)", "[SimplnxCo REQUIRE(executeResult.result.valid()); } - /** - * To check the validity of the filter we will be testing for a 5x5 square cut out as a pattern - * rather then specific data constants. This is due to the disparity between cross platform random distribution. - * - * Here's how it should look: - * T = triangle - * C = Circle - * X = X - * - * X C T C T - * T X C C X - * T X C X C - * T C C T X - * C C C T C - * - * The identifiers for the types is most easily defined by checking the following: - * |--------------| - * | Type | Index | - * |--------------| - * | X | 741 | - * |--------------| - * | C | 742 | - * |--------------| - * | T | 743 | - * |--------------| - * - * Be sure to check that oll of those values are unique before validating the rest of the indexes, - * i.e. index 741 and 742 should not be the same - */ - - auto& clusterIds = dataStructure.getDataRefAs(k_ClusterIdsPathNX); - - int32 xVal = clusterIds[741]; - int32 cVal = clusterIds[742]; - int32 tVal = clusterIds[743]; - - REQUIRE(xVal != cVal); - REQUIRE(cVal != tVal); - REQUIRE(tVal != xVal); - - for(auto index : k_XIndexes) + UnitTest::CompareDataArrays(dataStructure.getDataRefAs(k_ClusterIdsPath), dataStructure.getDataRefAs(k_ClusterIdsPathNX)); + + // Write the DataStructure out to the file system +#ifdef SIMPLNX_WRITE_TEST_OUTPUT + WriteTestDataStructure(dataStructure, fs::path(fmt::format("{}/7_0_DBSCAN_precached_iterative_test.dream3d", unit_test::k_BinaryTestOutputDir))); +#endif +} + +TEST_CASE("SimplnxCore::DBSCAN: Valid Filter Execution (uncached, Iterative)", "[SimplnxCore][DBSCAN]") +{ + const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "DBSCAN_tests.tar.gz", "DBSCAN_tests"); + DataStructure dataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/DBSCAN_tests/default/6_5_DBSCAN_Data.dream3d", unit_test::k_TestFilesDir))); + { - REQUIRE(xVal == clusterIds[index]); + // Instantiate the filter and an Arguments Object + DBSCANFilter filter; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(DBSCANFilter::k_SeedChoice_Key, std::make_any(to_underlying(AlgType::Iterative))); + args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any(false)); + args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any(0.01)); + args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any(50)); + args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any(false)); + args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any(k_TargetArrayPath)); + args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any(k_ClusterIdsNameNX)); + args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any(k_ClusterDataPathNX)); + + // Preflight the filter and check result + auto preflightResult = filter.preflight(dataStructure, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(dataStructure, args); + REQUIRE(executeResult.result.valid()); } - for(auto index : k_CircleIndexes) + UnitTest::CompareDataArrays(dataStructure.getDataRefAs(k_ClusterIdsPath), dataStructure.getDataRefAs(k_ClusterIdsPathNX)); + + // Write the DataStructure out to the file system +#ifdef SIMPLNX_WRITE_TEST_OUTPUT + WriteTestDataStructure(dataStructure, fs::path(fmt::format("{}/7_0_DBSCAN_uncached_iterative_test.dream3d", unit_test::k_BinaryTestOutputDir))); +#endif +} + +TEST_CASE("SimplnxCore::DBSCAN: Valid Filter Execution (precached, Random)", "[SimplnxCore][DBSCAN]") +{ + const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "DBSCAN_tests.tar.gz", "DBSCAN_tests"); + DataStructure dataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/DBSCAN_tests/default/6_5_DBSCAN_Data.dream3d", unit_test::k_TestFilesDir))); + { - REQUIRE(cVal == clusterIds[index]); + // Instantiate the filter and an Arguments Object + DBSCANFilter filter; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(DBSCANFilter::k_SeedChoice_Key, std::make_any(to_underlying(AlgType::SeededRandom))); + args.insertOrAssign(DBSCANFilter::k_SeedValue_Key, std::make_any(5489)); + args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any(true)); + args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any(0.01)); + args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any(50)); + args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any(false)); + args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any(k_TargetArrayPath)); + args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any(k_ClusterIdsNameNX)); + args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any(k_ClusterDataPathNX)); + + // Preflight the filter and check result + auto preflightResult = filter.preflight(dataStructure, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(dataStructure, args); + REQUIRE(executeResult.result.valid()); } - for(auto index : k_TriangleIndexes) + const auto& clusterIdsDataStore = dataStructure.getDataRefAs(k_ClusterIdsPathNX).getDataStoreRef(); + REQUIRE(*std::max_element(clusterIdsDataStore.cbegin(), clusterIdsDataStore.cend()) == 1); + + // Write the DataStructure out to the file system +#ifdef SIMPLNX_WRITE_TEST_OUTPUT + WriteTestDataStructure(dataStructure, fs::path(fmt::format("{}/7_0_DBSCAN_precached_random_test.dream3d", unit_test::k_BinaryTestOutputDir))); +#endif +} + +TEST_CASE("SimplnxCore::DBSCAN: Valid Filter Execution (uncached, Random)", "[SimplnxCore][DBSCAN]") +{ + const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "DBSCAN_tests.tar.gz", "DBSCAN_tests"); + DataStructure dataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/DBSCAN_tests/default/6_5_DBSCAN_Data.dream3d", unit_test::k_TestFilesDir))); + { - REQUIRE(tVal == clusterIds[index]); + // Instantiate the filter and an Arguments Object + DBSCANFilter filter; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(DBSCANFilter::k_SeedChoice_Key, std::make_any(to_underlying(AlgType::SeededRandom))); + args.insertOrAssign(DBSCANFilter::k_SeedValue_Key, std::make_any(5489)); + args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any(false)); + args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any(0.01)); + args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any(50)); + args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any(false)); + args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any(k_TargetArrayPath)); + args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any(k_ClusterIdsNameNX)); + args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any(k_ClusterDataPathNX)); + + // Preflight the filter and check result + auto preflightResult = filter.preflight(dataStructure, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(dataStructure, args); + REQUIRE(executeResult.result.valid()); } + const auto& clusterIdsDataStore = dataStructure.getDataRefAs(k_ClusterIdsPathNX).getDataStoreRef(); + REQUIRE(*std::max_element(clusterIdsDataStore.cbegin(), clusterIdsDataStore.cend()) == 1); + // Write the DataStructure out to the file system #ifdef SIMPLNX_WRITE_TEST_OUTPUT - WriteTestDataStructure(dataStructure, fs::path(fmt::format("{}/7_0_k_means_0_test.dream3d", unit_test::k_BinaryTestOutputDir))); + WriteTestDataStructure(dataStructure, fs::path(fmt::format("{}/7_0_DBSCAN_uncached_random_test.dream3d", unit_test::k_BinaryTestOutputDir))); #endif }