diff --git a/algebra/src/CMakeLists.txt b/algebra/src/CMakeLists.txt index 91826ae6..bace7645 100644 --- a/algebra/src/CMakeLists.txt +++ b/algebra/src/CMakeLists.txt @@ -7,8 +7,11 @@ add_subdirectory(libalgebra_lite_internal) if (ROUGHPY_BUILD_TESTS) add_executable(test_algebra - test_lie.cpp + tensor_fixture.cpp + tensor_fixture.h + test_dense_tensor.cpp test_free_tensor.cpp + test_lie.cpp ) target_link_libraries(test_algebra PRIVATE RoughPy::Algebra GTest::gtest) diff --git a/algebra/src/tensor_fixture.cpp b/algebra/src/tensor_fixture.cpp new file mode 100644 index 00000000..12b037a6 --- /dev/null +++ b/algebra/src/tensor_fixture.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2024 RoughPy Developers. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "tensor_fixture.h" + +namespace rpy { +namespace algebra { +namespace testing { + +void TensorFixture::SetUp() +{ + auto rational_poly_tpo = scalars::ScalarType::of(); + if (!rational_poly_tpo) { + GTEST_FAIL(); + } + + rational_poly_tp = *rational_poly_tpo; + context = rpy::algebra::get_context(width, depth, rational_poly_tp); +} + +} // namespace testing +} // namespace algebra +} // namespace rpy diff --git a/algebra/src/tensor_fixture.h b/algebra/src/tensor_fixture.h new file mode 100644 index 00000000..357fabb1 --- /dev/null +++ b/algebra/src/tensor_fixture.h @@ -0,0 +1,117 @@ +// Copyright (c) 2024 RoughPy Developers. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef ROUGHPY_ALGEBRA_SRC_TENSOR_FIXTURE_H +#define ROUGHPY_ALGEBRA_SRC_TENSOR_FIXTURE_H + +#include + +#include "roughpy/core/ranges.h" +#include "roughpy/core/types.h" +#include "roughpy/algebra/context.h" +#include "roughpy/scalars/scalar_types.h" + +namespace rpy { +namespace algebra { +namespace testing { + +//! FIXME docs +//! +class TensorFixture : public ::testing::Test +{ +protected: + const deg_t width = 2; + const deg_t depth = 5; + const scalars::ScalarType* rational_poly_tp; + rpy::algebra::context_pointer context; + +protected: + void SetUp() override; + +public: + //! Create free tensor with all coeffs 1 of width and depth and given char + RPY_NO_DISCARD FreeTensor make_ones_tensor( + char indeterminate_char + ) const + { + FreeTensor result = make_tensor([indeterminate_char](size_t i) { + auto key = scalars::indeterminate_type(indeterminate_char, i); + auto coeff = scalars::rational_poly_scalar(key, scalars::rational_scalar_type(1)); + return coeff; + }); + return result; + } + + //! Create free tensor with all coeffs N of width and depth and given char + RPY_NO_DISCARD FreeTensor make_ns_tensor( + char indeterminate_char, + scalars::rational_scalar_type n + ) const + { + FreeTensor result = make_tensor([indeterminate_char, n](size_t i) { + auto key = scalars::indeterminate_type(indeterminate_char, i); + auto coeff = scalars::rational_poly_scalar(key, n); + return coeff; + }); + return result; + } + + //! Create free tensor with each coeff constructed from make_coeff_fn + //! defaulting to default tensor data size. Lambda signature is: + //! make_coeff_fn(size_t index) -> scalars::rational_poly_scalar + template + RPY_NO_DISCARD FreeTensor make_tensor( + MakeCoeffFn&& make_coeff_fn + ) const + { + using namespace rpy::scalars; + + // Construct and allocate a rational polynomial + VectorConstructionData cons_data{ + KeyScalarArray(rational_poly_tp), + VectorType::Dense + }; + const dimn_t size = context->tensor_size(depth); + cons_data.data.allocate_scalars(size); + + // Delegate the construction of each basis with coefficient + auto slice = cons_data.data.as_mut_slice(); + for (auto&& [i, coeff] : views::enumerate(slice)) { + coeff = make_coeff_fn(i); + } + + FreeTensor result = context->construct_free_tensor(cons_data); + return result; + } +}; + +} // namespace testing +} // namespace algebra +} // namespace rpy + +#endif // ROUGHPY_ALGEBRA_SRC_TENSOR_FIXTURE_H diff --git a/algebra/src/test_dense_tensor.cpp b/algebra/src/test_dense_tensor.cpp new file mode 100644 index 00000000..21f813c1 --- /dev/null +++ b/algebra/src/test_dense_tensor.cpp @@ -0,0 +1,282 @@ +// Copyright (c) 2024 RoughPy Developers. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "tensor_fixture.h" + +namespace { + +using namespace rpy; +using namespace rpy::algebra; +using namespace rpy::algebra::testing; +using namespace scalars; + +using DenseTensorFixture = TensorFixture; + +} // namespace + +TEST_F(DenseTensorFixture, test_construct_from_context) +{ + FreeTensor t(context); + ASSERT_EQ(t.context(), context); + ASSERT_EQ(t.width(), 2); + ASSERT_EQ(t.depth(), 5); +} + +TEST_F(DenseTensorFixture, test_copy_construct) +{ + FreeTensor original = make_ns_tensor('x', 3); + FreeTensor t(original); + ASSERT_EQ(t, original); +} + +TEST_F(DenseTensorFixture, test_move_construct) +{ + FreeTensor original = make_ns_tensor('x', 5); + FreeTensor expected = original; // Copy assert compare value before move + FreeTensor t(std::move(original)); + ASSERT_EQ(original.context(), nullptr); // Original's impl invalidated + ASSERT_EQ(t, expected); +} + +TEST_F(DenseTensorFixture, test_copy_assign) +{ +} + +TEST_F(DenseTensorFixture, test_move_assign) +{ +} + +TEST_F(DenseTensorFixture, test_context) +{ +} + +TEST_F(DenseTensorFixture, test_basis_type) +{ +} + +TEST_F(DenseTensorFixture, test_basis) +{ +} + +TEST_F(DenseTensorFixture, test_borrow) +{ +} + +TEST_F(DenseTensorFixture, test_borrow_mut) +{ +} + +TEST_F(DenseTensorFixture, test_add) +{ + FreeTensor lhs = make_ns_tensor('x', 2); + FreeTensor rhs = make_ns_tensor('y', 3); + FreeTensor result = lhs.add(rhs); + + FreeTensor expected = make_tensor([](size_t i) { + auto lhs_coeff = rational_poly_scalar(indeterminate_type('x', i), 2); + auto rhs_coeff = rational_poly_scalar(indeterminate_type('y', i), 3); + return lhs_coeff + rhs_coeff; + }); + + ASSERT_EQ(result, expected); +} + +TEST_F(DenseTensorFixture, test_sub) +{ + FreeTensor lhs = make_ns_tensor('x', 3); + FreeTensor rhs = make_ns_tensor('y', 5); + FreeTensor result = lhs.sub(rhs); + + FreeTensor expected = make_tensor([](size_t i) { + auto lhs_coeff = rational_poly_scalar(indeterminate_type('x', i), 3); + auto rhs_coeff = rational_poly_scalar(indeterminate_type('y', i), 5); + return lhs_coeff - rhs_coeff; + }); + + ASSERT_EQ(result, expected); +} + +TEST_F(DenseTensorFixture, test_mul) +{ +} + +TEST_F(DenseTensorFixture, test_dimension) +{ +} + +TEST_F(DenseTensorFixture, test_size) +{ +} + +TEST_F(DenseTensorFixture, test_is_zero) +{ +} + +TEST_F(DenseTensorFixture, test_width) +{ +} + +TEST_F(DenseTensorFixture, test_depth) +{ +} + +TEST_F(DenseTensorFixture, test_degree) +{ +} + +TEST_F(DenseTensorFixture, test_storage_type) +{ +} + +TEST_F(DenseTensorFixture, test_coeff_type) +{ +} + +TEST_F(DenseTensorFixture, test_operator_index) +{ +} + +TEST_F(DenseTensorFixture, test_operator_const_index) +{ +} + +TEST_F(DenseTensorFixture, test_iterators) +{ + // Tensor has key values 0..N for N basis + FreeTensor t = make_tensor([](size_t i) { + auto key = indeterminate_type('x', i); + auto coeff = rational_poly_scalar(key, 1) * i; + return coeff; + }); + + // Check that each value is 0..N as expected + size_t expected_index = 0; + for (auto it = t.begin(); it != t.end(); ++it) { + ASSERT_EQ(it->key(), expected_index); + // TODO check it->value() is expected_index + ++expected_index; + } + ASSERT_EQ(expected_index, t.size()); +} + +TEST_F(DenseTensorFixture, test_dense_data) +{ +} + +TEST_F(DenseTensorFixture, test_uminus) +{ + FreeTensor lhs = make_ones_tensor('x'); + FreeTensor result = lhs.uminus(); + + FreeTensor expected = make_tensor([](size_t i) { + auto key = indeterminate_type('x', i); + auto coeff = rational_poly_scalar(key, 1); + return -coeff; + }); + + ASSERT_EQ(result, expected); +} + +TEST_F(DenseTensorFixture, test_smul) +{ + FreeTensor lhs = make_ns_tensor('x', 5); + Scalar rhs(7); + FreeTensor result = lhs.smul(rhs); + + FreeTensor expected = make_tensor([](size_t i) { + auto key = indeterminate_type('x', i); + auto coeff = rational_poly_scalar(key, 5); + return coeff * 7; + }); + + ASSERT_EQ(result, expected); +} + +TEST_F(DenseTensorFixture, test_sdiv) +{ +} + +TEST_F(DenseTensorFixture, test_add_inplace) +{ +} + +TEST_F(DenseTensorFixture, test_sub_inplace) +{ +} + +TEST_F(DenseTensorFixture, test_mul_inplace) +{ +} + +TEST_F(DenseTensorFixture, test_smul_inplace) +{ +} + +TEST_F(DenseTensorFixture, test_sdiv_inplace) +{ +} + +TEST_F(DenseTensorFixture, test_add_scal_mul) +{ +} + +TEST_F(DenseTensorFixture, test_sub_scal_mul) +{ +} + +TEST_F(DenseTensorFixture, test_add_scal_div) +{ +} + +TEST_F(DenseTensorFixture, test_sub_scal_div) +{ +} + +TEST_F(DenseTensorFixture, test_add_mul) +{ +} + +TEST_F(DenseTensorFixture, test_sub_mul) +{ +} + +TEST_F(DenseTensorFixture, test_mul_smul) +{ +} + +TEST_F(DenseTensorFixture, test_mul_sdiv) +{ +} + +TEST_F(DenseTensorFixture, test_operator_equals) +{ +} + +TEST_F(DenseTensorFixture, test_print) +{ +} diff --git a/algebra/src/test_free_tensor.cpp b/algebra/src/test_free_tensor.cpp index fd5d4215..5392a826 100644 --- a/algebra/src/test_free_tensor.cpp +++ b/algebra/src/test_free_tensor.cpp @@ -1,86 +1,56 @@ +// Copyright (c) 2024 RoughPy Developers. All rights reserved. // -// Created by sammorley on 22/11/24. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: // +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#include -#include - -#include - -#include -#include - -#include -#include +#include "tensor_fixture.h" +#include "roughpy/core/ranges.h" +#include "roughpy/algebra/free_tensor.h" +namespace { using namespace rpy; using namespace rpy::algebra; +using namespace rpy::algebra::testing; +using FreeTensorFixture = TensorFixture; -namespace { - -class FreeTensorTests : public ::testing::Test { - deg_t width = 2; - deg_t depth = 5; - const scalars::ScalarType* rational_tp; - context_pointer context; - -protected: - void SetUp() override; - -public: - - - - RPY_NO_DISCARD - FreeTensor generic_tensor(char indeterminate_char) const; - -}; +} // namespace -void FreeTensorTests::SetUp() +TEST_F(FreeTensorFixture, TestStreamOut) { - auto tpo = scalars::ScalarType::of(); - if (!tpo) { - GTEST_FAIL(); - } - rational_tp = *tpo; - context = get_context(width, depth, rational_tp); -} - -FreeTensor FreeTensorTests::generic_tensor(char indeterminate_char) const -{ - const auto size = context->tensor_size(depth); - - - VectorConstructionData cons_data{ - scalars::KeyScalarArray(rational_tp), - VectorType::Dense - }; - - cons_data.data.allocate_scalars(static_cast(size)); - - auto slice = cons_data.data.as_mut_slice(); - for (auto&& [i, elt] : views::enumerate(slice)) { - scalars::indeterminate_type ind(indeterminate_char, i); - elt = scalars::rational_poly_scalar(ind, scalars::rational_scalar_type(1)); - } - - return context->construct_free_tensor(cons_data); -} - -} - - -TEST_F(FreeTensorTests, TestStreamOut) -{ - auto tensor = generic_tensor('x'); + auto tensor = make_ones_tensor('x'); std::stringstream ss; ss << tensor; - const string expected = "{ { 1(x0) }() { 1(x1) }(1)"; + const std::string expected = "{ { 1(x0) }() { 1(x1) }(1)"; auto first_term = ss.str().substr(0, expected.size()); EXPECT_EQ(expected, first_term); -} \ No newline at end of file +} diff --git a/algebra/src/test_lie.cpp b/algebra/src/test_lie.cpp index 0f74bc72..ab056614 100644 --- a/algebra/src/test_lie.cpp +++ b/algebra/src/test_lie.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2023 RoughPy Developers. All rights reserved. +// Copyright (c) 2024 RoughPy Developers. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: @@ -26,14 +26,18 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -// -// Created by sam on 13/03/23. -// #include -#include "ContextFixture.h" +#include "tensor_fixture.h" + +namespace { + +using LieFixture = rpy::algebra::testing::TensorFixture; -using LieTests = rpy::algebra::testing::ContextFixture; +} // namespace -TEST_F(LieTests, LieAdditionDenseData) {} +TEST_F(LieFixture, LieAdditionDenseData) +{ + // FIXME tests required +}