Skip to content

Commit

Permalink
Repair and add tests to filter
Browse files Browse the repository at this point in the history
  • Loading branch information
nyoungbq committed Jun 14, 2024
1 parent 0e2cc2a commit fb625fc
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 72 deletions.
20 changes: 8 additions & 12 deletions src/Plugins/SimplnxCore/src/SimplnxCore/Filters/DBSCANFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<ChoicesParameter>(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<NumberParameter<uint64>>(k_SeedValue_Key, "Seed Value", "The seed fed into the random generator", std::mt19937::default_seed));
params.insert(std::make_unique<DataObjectNameParameter>(k_SeedArrayName_Key, "Stored Seed Value Array Name", "Name of array holding the seed value", "DBSCAN SeedValue"));
Expand Down Expand Up @@ -104,6 +97,9 @@ Parameters DBSCANFilter::parameters() const
params.insert(std::make_unique<DataGroupCreationParameter>(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<ChoicesParameter::ValueType>(to_underlying(AlgType::Random)));
params.linkParameters(k_SeedChoice_Key, k_SeedValue_Key, static_cast<ChoicesParameter::ValueType>(to_underlying(AlgType::SeededRandom)));
params.linkParameters(k_SeedChoice_Key, k_SeedArrayName_Key, static_cast<ChoicesParameter::ValueType>(to_underlying(AlgType::SeededRandom)));
params.linkParameters(k_UseMask_Key, k_MaskArrayPath_Key, true);

return params;
Expand Down Expand Up @@ -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<AlgType>(filterArgs.value<ChoicesParameter::ValueType>(k_SeedChoice_Key)) != AlgType::Iterative)
{
auto createAction = std::make_unique<CreateArrayAction>(DataType::uint64, std::vector<usize>{1}, std::vector<usize>{1}, DataPath({filterArgs.value<std::string>(k_SeedArrayName_Key)}));
resultOutputActions.value().appendAction(std::move(createAction));
Expand All @@ -181,12 +177,12 @@ Result<> DBSCANFilter::executeImpl(DataStructure& dataStructure, const Arguments
}

auto seed = filterArgs.value<std::mt19937_64::result_type>(k_SeedValue_Key);
if(filterArgs.value<::AlgType>(k_SeedChoice_Key) != AlgType::SeededRandom)
if(static_cast<AlgType>(filterArgs.value<ChoicesParameter::ValueType>(k_SeedChoice_Key)) != AlgType::SeededRandom)
{
seed = static_cast<std::mt19937_64::result_type>(std::chrono::steady_clock::now().time_since_epoch().count());
}

if(filterArgs.value<::AlgType>(k_SeedChoice_Key) != AlgType::Iterative)
if(static_cast<AlgType>(filterArgs.value<ChoicesParameter::ValueType>(k_SeedChoice_Key)) != AlgType::Iterative)
{
// Store Seed Value in Top Level Array
dataStructure.getDataRefAs<UInt64Array>(DataPath({filterArgs.value<std::string>(k_SeedArrayName_Key)}))[0] = seed;
Expand All @@ -204,7 +200,7 @@ Result<> DBSCANFilter::executeImpl(DataStructure& dataStructure, const Arguments
inputValues.FeatureIdsArrayPath = fIdsPath;
inputValues.FeatureAM = filterArgs.value<DataPath>(k_FeatureAMPath_Key);
inputValues.AllowCaching = filterArgs.value<bool>(k_UsePrecaching_Key);
inputValues.UseRandom = filterArgs.value<::AlgType>(k_SeedChoice_Key) != AlgType::Iterative;
inputValues.UseRandom = static_cast<AlgType>(filterArgs.value<ChoicesParameter::ValueType>(k_SeedChoice_Key)) != AlgType::Iterative;
inputValues.Seed = filterArgs.value<std::mt19937_64::result_type>(k_SeedValue_Key);

return DBSCAN(dataStructure, messageHandler, shouldCancel, &inputValues)();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@

namespace nx::core
{
enum AlgType
{
Iterative,
Random,
SeededRandom
};

/**
* @class DBSCANFilter
* @brief This filter will ....
Expand Down
184 changes: 124 additions & 60 deletions src/Plugins/SimplnxCore/test/DBSCANTest.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <catch2/catch.hpp>

#include "simplnx/DataStructure/DataArray.hpp"
#include "simplnx/Parameters/ChoicesParameter.hpp"
#include "simplnx/UnitTest/UnitTestCommon.hpp"

#include "SimplnxCore/Filters/DBSCANFilter.hpp"
Expand All @@ -17,37 +18,38 @@ constexpr std::array<uint32, 12> k_CircleIndexes = {553, 554, 555, 557, 601, 602
constexpr std::array<uint32, 7> k_TriangleIndexes = {556, 600, 603, 647, 694, 743, 745};
constexpr std::array<uint32, 6> 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
DBSCANFilter filter;
Arguments args;

// Create default Parameters for the filter.
args.insertOrAssign(DBSCANFilter::k_SeedChoice_Key, std::make_any<ChoicesParameter::ValueType>(to_underlying(AlgType::Iterative)));
args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any<bool>(true));
args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any<bool>(0.1));
args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any<int32>(2));
args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any<float32>(0.01));
args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any<int32>(50));
args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any<bool>(false));
args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any<DataPath>(k_CellPath.createChildPath("DAMAGE")));
args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any<DataPath>(k_TargetArrayPath));
args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any<std::string>(k_ClusterIdsNameNX));
args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any<DataPath>(k_ClusterDataPathNX));

Expand All @@ -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<Int32Array>(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<int32>(dataStructure.getDataRefAs<Int32Array>(k_ClusterIdsPath), dataStructure.getDataRefAs<Int32Array>(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<ChoicesParameter::ValueType>(to_underlying(AlgType::Iterative)));
args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any<bool>(false));
args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any<float32>(0.01));
args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any<int32>(50));
args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any<bool>(false));
args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any<DataPath>(k_TargetArrayPath));
args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any<std::string>(k_ClusterIdsNameNX));
args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any<DataPath>(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<int32>(dataStructure.getDataRefAs<Int32Array>(k_ClusterIdsPath), dataStructure.getDataRefAs<Int32Array>(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<ChoicesParameter::ValueType>(to_underlying(AlgType::SeededRandom)));
args.insertOrAssign(DBSCANFilter::k_SeedValue_Key, std::make_any<uint64>(5489));
args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any<bool>(true));
args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any<float32>(0.01));
args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any<int32>(50));
args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any<bool>(false));
args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any<DataPath>(k_TargetArrayPath));
args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any<std::string>(k_ClusterIdsNameNX));
args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any<DataPath>(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<Int32Array>(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<ChoicesParameter::ValueType>(to_underlying(AlgType::SeededRandom)));
args.insertOrAssign(DBSCANFilter::k_SeedValue_Key, std::make_any<uint64>(5489));
args.insertOrAssign(DBSCANFilter::k_UsePrecaching_Key, std::make_any<bool>(false));
args.insertOrAssign(DBSCANFilter::k_Epsilon_Key, std::make_any<float32>(0.01));
args.insertOrAssign(DBSCANFilter::k_MinPoints_Key, std::make_any<int32>(50));
args.insertOrAssign(DBSCANFilter::k_UseMask_Key, std::make_any<bool>(false));
args.insertOrAssign(DBSCANFilter::k_SelectedArrayPath_Key, std::make_any<DataPath>(k_TargetArrayPath));
args.insertOrAssign(DBSCANFilter::k_FeatureIdsArrayName_Key, std::make_any<std::string>(k_ClusterIdsNameNX));
args.insertOrAssign(DBSCANFilter::k_FeatureAMPath_Key, std::make_any<DataPath>(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<Int32Array>(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
}

0 comments on commit fb625fc

Please sign in to comment.