diff --git a/cpp/src/arrow/CMakeLists.txt b/cpp/src/arrow/CMakeLists.txt index 660d052fd2d..af50588a856 100644 --- a/cpp/src/arrow/CMakeLists.txt +++ b/cpp/src/arrow/CMakeLists.txt @@ -246,6 +246,7 @@ if(ARROW_COMPUTE) compute/kernels/mean.cc compute/kernels/sort_to_indices.cc compute/kernels/sum.cc + compute/kernels/add.cc compute/kernels/take.cc compute/kernels/isin.cc compute/kernels/util_internal.cc diff --git a/cpp/src/arrow/compute/kernels/CMakeLists.txt b/cpp/src/arrow/compute/kernels/CMakeLists.txt index 0f5fd72c4e2..a54fd835193 100644 --- a/cpp/src/arrow/compute/kernels/CMakeLists.txt +++ b/cpp/src/arrow/compute/kernels/CMakeLists.txt @@ -23,6 +23,7 @@ add_arrow_test(hash_test PREFIX "arrow-compute") add_arrow_test(isin_test PREFIX "arrow-compute") add_arrow_test(sort_to_indices_test PREFIX "arrow-compute") add_arrow_test(util_internal_test PREFIX "arrow-compute") +add_arrow_test(add-test PREFIX "arrow-compute") add_arrow_benchmark(sort_to_indices_benchmark PREFIX "arrow-compute") # Aggregates diff --git a/cpp/src/arrow/compute/kernels/add-test.cc b/cpp/src/arrow/compute/kernels/add-test.cc new file mode 100644 index 00000000000..89d2c90882d --- /dev/null +++ b/cpp/src/arrow/compute/kernels/add-test.cc @@ -0,0 +1,106 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include +#include +#include +#include +#include + +#include + +#include "arrow/array.h" +#include "arrow/compute/kernel.h" +#include "arrow/compute/kernels/add.h" +#include "arrow/compute/test_util.h" +#include "arrow/type.h" +#include "arrow/type_traits.h" +#include "arrow/util/checked_cast.h" + +#include "arrow/testing/gtest_common.h" +#include "arrow/testing/gtest_util.h" +#include "arrow/testing/random.h" + +namespace arrow { +namespace compute { + +template +class TestArithmeticKernel : public ComputeFixture, public TestBase { + private: + void AssertAddArrays(const std::shared_ptr lhs, const std::shared_ptr rhs, + const std::shared_ptr expected) { + std::shared_ptr actual; + ASSERT_OK(arrow::compute::Add(&this->ctx_, *lhs, *rhs, &actual)); + ASSERT_OK(actual->Validate()); + AssertArraysEqual(*expected, *actual); + } + + protected: + virtual void AssertAdd(const std::string& lhs, const std::string& rhs, + const std::string& expected) { + auto type = TypeTraits::type_singleton(); + AssertAddArrays(ArrayFromJSON(type, lhs), ArrayFromJSON(type, rhs), + ArrayFromJSON(type, expected)); + } +}; + +template +class TestArithmeticKernelForReal : public TestArithmeticKernel {}; +TYPED_TEST_CASE(TestArithmeticKernelForReal, RealArrowTypes); + +template +class TestArithmeticKernelForIntegral : public TestArithmeticKernel {}; +TYPED_TEST_CASE(TestArithmeticKernelForIntegral, IntegralArrowTypes); + +TYPED_TEST(TestArithmeticKernelForReal, SortReal) { + this->AssertAdd("[]", "[]", "[]"); + + this->AssertAdd("[3.4, 2.6, 6.3]", "[1, 0, 2]", "[4.4, 2.6, 8.3]"); + + this->AssertAdd("[1.1, 2.4, 3.5, 4.3, 5.1, 6.8, 7.3]", "[0, 1, 2, 3, 4, 5, 6]", + "[1.1, 3.4, 5.5, 7.3, 9.1, 11.8, 13.3]"); + + this->AssertAdd("[7, 6, 5, 4, 3, 2, 1]", "[6, 5, 4, 3, 2, 1, 0]", + "[13, 11, 9, 7, 5, 3, 1]"); + + this->AssertAdd("[10.4, 12, 4.2, 50, 50.3, 32, 11]", "[2, 0, 6, 1, 5, 3, 4]", + "[12.4, 12, 10.2, 51, 55.3, 35, 15]"); + + this->AssertAdd("[null, 1, 3.3, null, 2, 5.3]", "[1, 4, 2, 5, 0, 3]", + "[null, 5, 5.3, null, 2, 8.3]"); +} + +TYPED_TEST(TestArithmeticKernelForIntegral, SortIntegral) { + this->AssertAdd("[]", "[]", "[]"); + + this->AssertAdd("[3, 2, 6]", "[1, 0, 2]", "[4, 2, 8]"); + + this->AssertAdd("[1, 2, 3, 4, 5, 6, 7]", "[0, 1, 2, 3, 4, 5, 6]", + "[1, 3, 5, 7, 9, 11, 13]"); + + this->AssertAdd("[7, 6, 5, 4, 3, 2, 1]", "[6, 5, 4, 3, 2, 1, 0]", + "[13, 11, 9, 7, 5, 3, 1]"); + + this->AssertAdd("[10, 12, 4, 50, 50, 32, 11]", "[2, 0, 6, 1, 5, 3, 4]", + "[12, 12, 10, 51, 55, 35, 15]"); + + this->AssertAdd("[null, 1, 3, null, 2, 5]", "[1, 4, 2, 5, 0, 3]", + "[null, 5, 5, null, 2, 8]"); +} + +} // namespace compute +} // namespace arrow diff --git a/cpp/src/arrow/compute/kernels/add.cc b/cpp/src/arrow/compute/kernels/add.cc new file mode 100644 index 00000000000..19eb153b5cd --- /dev/null +++ b/cpp/src/arrow/compute/kernels/add.cc @@ -0,0 +1,131 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "arrow/compute/kernels/add.h" +#include "arrow/builder.h" +#include "arrow/compute/context.h" +#include "arrow/type_traits.h" + +namespace arrow { +namespace compute { + +template +class AddKernelImpl : public AddKernel { + private: + using ArrayType = typename TypeTraits::ArrayType; + std::shared_ptr result_type_; + + Status Add(FunctionContext* ctx, const std::shared_ptr& lhs, + const std::shared_ptr& rhs, std::shared_ptr* result) { + NumericBuilder builder; + RETURN_NOT_OK(builder.Reserve(lhs->length())); + for (int i = 0; i < lhs->length(); i++) { + if (lhs->IsNull(i) || rhs->IsNull(i)) { + builder.UnsafeAppendNull(); + } else { + builder.UnsafeAppend(lhs->Value(i) + rhs->Value(i)); + } + } + return builder.Finish(result); + } + + public: + explicit AddKernelImpl(std::shared_ptr result_type) + : result_type_(result_type) {} + + Status Call(FunctionContext* ctx, const Datum& lhs, const Datum& rhs, + Datum* out) override { + if (!lhs.is_array() || !rhs.is_array()) { + return Status::Invalid("AddKernel expects array values"); + } + if (lhs.length() != rhs.length()) { + return Status::Invalid("AddKernel expects arrays with the same length"); + } + auto lhs_array = lhs.make_array(); + auto rhs_array = rhs.make_array(); + std::shared_ptr result; + RETURN_NOT_OK(this->Add(ctx, lhs_array, rhs_array, &result)); + *out = result; + return Status::OK(); + } + + std::shared_ptr out_type() const override { return result_type_; } + + Status Add(FunctionContext* ctx, const std::shared_ptr& lhs, + const std::shared_ptr& rhs, std::shared_ptr* result) override { + auto lhs_array = std::static_pointer_cast(lhs); + auto rhs_array = std::static_pointer_cast(rhs); + return Add(ctx, lhs_array, rhs_array, result); + } +}; + +Status AddKernel::Make(const std::shared_ptr& value_type, + std::unique_ptr* out) { + AddKernel* kernel; + switch (value_type->id()) { + case Type::UINT8: + kernel = new AddKernelImpl(value_type); + break; + case Type::INT8: + kernel = new AddKernelImpl(value_type); + break; + case Type::UINT16: + kernel = new AddKernelImpl(value_type); + break; + case Type::INT16: + kernel = new AddKernelImpl(value_type); + break; + case Type::UINT32: + kernel = new AddKernelImpl(value_type); + break; + case Type::INT32: + kernel = new AddKernelImpl(value_type); + break; + case Type::UINT64: + kernel = new AddKernelImpl(value_type); + break; + case Type::INT64: + kernel = new AddKernelImpl(value_type); + break; + case Type::FLOAT: + kernel = new AddKernelImpl(value_type); + break; + case Type::DOUBLE: + kernel = new AddKernelImpl(value_type); + break; + default: + return Status::NotImplemented("Arithmetic operations on ", *value_type, " arrays"); + } + out->reset(kernel); + return Status::OK(); +} + +Status Add(FunctionContext* ctx, const Array& lhs, const Array& rhs, + std::shared_ptr* result) { + Datum result_datum; + std::unique_ptr kernel; + ARROW_RETURN_IF( + !lhs.type()->Equals(rhs.type()), + Status::Invalid("Array types should be equal to use arithmetic kernels")); + RETURN_NOT_OK(AddKernel::Make(lhs.type(), &kernel)); + RETURN_NOT_OK(kernel->Call(ctx, Datum(lhs.data()), Datum(rhs.data()), &result_datum)); + *result = result_datum.make_array(); + return Status::OK(); +} + +} // namespace compute +} // namespace arrow diff --git a/cpp/src/arrow/compute/kernels/add.h b/cpp/src/arrow/compute/kernels/add.h new file mode 100644 index 00000000000..19991aa4473 --- /dev/null +++ b/cpp/src/arrow/compute/kernels/add.h @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include + +#include "arrow/compute/kernel.h" +#include "arrow/status.h" +#include "arrow/util/visibility.h" + +namespace arrow { + +class Array; + +namespace compute { + +class FunctionContext; + +/// \brief Summarizes two arrays. +/// +/// Summarizes two arrays with the same length. +/// The output is an array with same length and type as input. +/// Types of both input arrays should be equal +/// +/// For example given lhs = [1, null, 3], rhs = [4, 5, 6], the output +/// will be [5, null, 7] +/// +/// \param[in] ctx the FunctionContext +/// \param[in] lhs the first array +/// \param[in] rhs the second array +/// \param[out] result the sum of first and second arrays + +ARROW_EXPORT +Status Add(FunctionContext* ctx, const Array& lhs, const Array& rhs, + std::shared_ptr* result); + +/// \brief BinaryKernel implementing Add operation +class ARROW_EXPORT AddKernel : public BinaryKernel { + public: + /// \brief BinaryKernel interface + /// + /// delegates to subclasses via Add() + Status Call(FunctionContext* ctx, const Datum& lhs, const Datum& rhs, + Datum* out) override = 0; + + /// \brief output type of this kernel + std::shared_ptr out_type() const override = 0; + + /// \brief single-array implementation + virtual Status Add(FunctionContext* ctx, const std::shared_ptr& lhs, + const std::shared_ptr& rhs, + std::shared_ptr* result) = 0; + + /// \brief factory for Add + /// + /// \param[in] value_type constructed AddKernel + /// \param[out] out created kernel + static Status Make(const std::shared_ptr& value_type, + std::unique_ptr* out); +}; +} // namespace compute +} // namespace arrow