diff --git a/cpp/oneapi/dal/algo/correlation_distance/backend/cpu/compute_kernel_dense.cpp b/cpp/oneapi/dal/algo/correlation_distance/backend/cpu/compute_kernel_dense.cpp index c5b5781ca0a..4630b1a8112 100644 --- a/cpp/oneapi/dal/algo/correlation_distance/backend/cpu/compute_kernel_dense.cpp +++ b/cpp/oneapi/dal/algo/correlation_distance/backend/cpu/compute_kernel_dense.cpp @@ -71,8 +71,38 @@ static result_t call_daal_kernel(const context_cpu& ctx, dal::detail::homogen_table_builder{}.reset(arr_values, row_count_x, row_count_y).build()); } +template +static result_t call_daal_kernel(const context_cpu& ctx, const descriptor_t& desc, const table& x) { + const std::int64_t row_count_x = x.get_row_count(); + + dal::detail::check_mul_overflow(row_count_x, row_count_x); + auto arr_values = array::empty(row_count_x * row_count_x); + + const auto daal_x = interop::convert_to_daal_table(x); + const auto daal_values = + interop::convert_to_daal_homogen_table(arr_values, row_count_x, row_count_x); + + daal::algorithms::Parameter param; + const daal::data_management::NumericTable* daal_input_tables[1] = { daal_x.get() }; + daal::data_management::NumericTable* daal_result_table[1] = { daal_values.get() }; + + interop::status_to_exception( + interop::call_daal_kernel(ctx, + 1, + daal_input_tables, + 1, + daal_result_table, + ¶m)); + + return result_t().set_values( + dal::detail::homogen_table_builder{}.reset(arr_values, row_count_x, row_count_x).build()); +} + template static result_t compute(const context_cpu& ctx, const descriptor_t& desc, const input_t& input) { + if (!input.get_y().has_data()) { + return call_daal_kernel(ctx, desc, input.get_x()); + } return call_daal_kernel(ctx, desc, input.get_x(), input.get_y()); } diff --git a/cpp/oneapi/dal/algo/correlation_distance/backend/gpu/compute_kernel_dense_dpc.cpp b/cpp/oneapi/dal/algo/correlation_distance/backend/gpu/compute_kernel_dense_dpc.cpp index 599652e2497..ab6a48bd495 100644 --- a/cpp/oneapi/dal/algo/correlation_distance/backend/gpu/compute_kernel_dense_dpc.cpp +++ b/cpp/oneapi/dal/algo/correlation_distance/backend/gpu/compute_kernel_dense_dpc.cpp @@ -37,7 +37,7 @@ static result_t compute(const context_gpu& ctx, const descriptor_t& desc, const auto& queue = ctx.get_queue(); const auto x = input.get_x(); - const auto y = input.get_y(); + const auto y = input.get_y().has_data() ? input.get_y() : input.get_x(); const std::int64_t x_row_count = x.get_row_count(); const std::int64_t y_row_count = y.get_row_count(); diff --git a/cpp/oneapi/dal/algo/correlation_distance/compute_types.cpp b/cpp/oneapi/dal/algo/correlation_distance/compute_types.cpp index 859ee9bf2f8..8abc2b5280a 100644 --- a/cpp/oneapi/dal/algo/correlation_distance/compute_types.cpp +++ b/cpp/oneapi/dal/algo/correlation_distance/compute_types.cpp @@ -23,6 +23,7 @@ template class detail::v1::compute_input_impl : public base { public: compute_input_impl(const table& x, const table& y) : x(x), y(y) {} + compute_input_impl(const table& x) : x(x) {} table x; table y; }; @@ -42,6 +43,9 @@ template compute_input::compute_input(const table& x, const table& y) : impl_(new compute_input_impl(x, y)) {} +template +compute_input::compute_input(const table& x) : impl_(new compute_input_impl(x)) {} + template const table& compute_input::get_x() const { return impl_->x; diff --git a/cpp/oneapi/dal/algo/correlation_distance/compute_types.hpp b/cpp/oneapi/dal/algo/correlation_distance/compute_types.hpp index c0754d789e9..00b8474bdf0 100644 --- a/cpp/oneapi/dal/algo/correlation_distance/compute_types.hpp +++ b/cpp/oneapi/dal/algo/correlation_distance/compute_types.hpp @@ -48,6 +48,10 @@ class compute_input : public base { /// Creates a new instance of the class with the given :literal:`x` and :literal:`y`. compute_input(const table& x, const table& y); + /// Creates a new instance of the class with the given :literal:`x` and a + /// default-constructed (empty) :literal:`y`. + compute_input(const table& x); + /// An $n \\times p$ table with the data x, where each row /// stores one feature vector. /// @remark default = table{} diff --git a/cpp/oneapi/dal/algo/correlation_distance/detail/compute_ops.hpp b/cpp/oneapi/dal/algo/correlation_distance/detail/compute_ops.hpp index 16c605652f7..e566b55d2a9 100644 --- a/cpp/oneapi/dal/algo/correlation_distance/detail/compute_ops.hpp +++ b/cpp/oneapi/dal/algo/correlation_distance/detail/compute_ops.hpp @@ -52,10 +52,8 @@ struct compute_ops { if (!input.get_x().has_data()) { throw domain_error(msg::input_x_is_empty()); } - if (!input.get_y().has_data()) { - throw domain_error(msg::input_y_is_empty()); - } - if (input.get_x().get_column_count() != input.get_y().get_column_count()) { + if (input.get_y().has_data() && + input.get_x().get_column_count() != input.get_y().get_column_count()) { throw invalid_argument(msg::input_x_cc_neq_y_cc()); } } @@ -65,7 +63,9 @@ struct compute_ops { const result_t& result) const { ONEDAL_ASSERT(result.get_values().has_data()); ONEDAL_ASSERT(input.get_x().get_row_count() == result.get_values().get_row_count()); - ONEDAL_ASSERT(input.get_y().get_row_count() == result.get_values().get_column_count()); + if (input.get_y().has_data()) { + ONEDAL_ASSERT(input.get_y().get_row_count() == result.get_values().get_column_count()); + } } template diff --git a/cpp/oneapi/dal/algo/correlation_distance/test/batch.cpp b/cpp/oneapi/dal/algo/correlation_distance/test/batch.cpp index 2f88e1d3feb..d1f03078757 100644 --- a/cpp/oneapi/dal/algo/correlation_distance/test/batch.cpp +++ b/cpp/oneapi/dal/algo/correlation_distance/test/batch.cpp @@ -66,6 +66,26 @@ class correlation_distance_batch_test check_result_values(x_data, y_data, result_values); } + void check_tables_equal(const table& lhs, const table& rhs) { + REQUIRE(lhs.get_row_count() == rhs.get_row_count()); + REQUIRE(lhs.get_column_count() == rhs.get_column_count()); + row_accessor lhs_acc{ lhs }; + row_accessor rhs_acc{ rhs }; + for (std::int64_t row = 0; row < lhs.get_row_count(); ++row) { + auto lhs_row = lhs_acc.pull({ row, row + 1 }); + auto rhs_row = rhs_acc.pull({ row, row + 1 }); + for (std::int64_t col = 0; col < lhs.get_column_count(); ++col) { + const Float l = lhs_row[col]; + const Float r = rhs_row[col]; + const auto rerr = + std::abs(l - r) / std::max({ double(1), std::abs(l), std::abs(r) }); + CAPTURE(row, col, l, r, rerr); + if (rerr > 1e-4) + FAIL(); + } + } + } + void check_result_values(const table& x_data, const table& y_data, const table& result_values) { const auto reference = compute_reference(x_data, y_data); @@ -187,4 +207,27 @@ TEMPLATE_LIST_TEST_M(correlation_distance_batch_test, this->general_checks(x_data, y_data, x_data_table_id, y_data_table_id); } +TEMPLATE_LIST_TEST_M(correlation_distance_batch_test, + "correlation_distance self equals x-vs-x and empty-y", + "[correlation_distance][integration][batch]", + correlation_distance_types) { + SKIP_IF(this->not_float64_friendly()); + + const te::dataframe x_data = + GENERATE_DATAFRAME(te::dataframe_builder{ 8, 4 }.fill_normal(0, 1, 7777)); + const auto x_data_table_id = this->get_homogen_table_id(); + const table x = x_data.get_table(this->get_policy(), x_data_table_id); + + const auto desc = this->get_descriptor(); + + const auto res_xx = this->compute(desc, x, x).get_values(); + const auto res_x = this->compute(desc, x).get_values(); + const auto res_empty_y = this->compute(desc, x, table{}).get_values(); + + INFO("compute(desc, x) matches compute(desc, x, x)"); + this->check_tables_equal(res_x, res_xx); + INFO("compute(desc, x, table{}) matches compute(desc, x, x)"); + this->check_tables_equal(res_empty_y, res_xx); +} + } // namespace oneapi::dal::correlation_distance::test