diff --git a/.gitignore b/.gitignore index 8974617..b22c20f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ compile_commands.json CTestTestfile.cmake install_manifest.txt Makefile + +*.png diff --git a/CMakeLists.txt b/CMakeLists.txt index f0927c4..5bcd83d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,12 @@ ELSE() ADD_DEFINITIONS(-DUSE_FAKE_CUDA_RUNTIME) ENDIF() +IF(APPLE) + ADD_DEFINITIONS(-DHAVE_STD_CPP_FS) +ELSE() + LINK_LIBRARIES(stdc++fs) +ENDIF() + IF(BUILD_TESTS) ENABLE_TESTING() INCLUDE(cmake/tests.cmake) diff --git a/CODE_OF_CONDUCT b/CODE_OF_CONDUCT new file mode 100644 index 0000000..a889595 --- /dev/null +++ b/CODE_OF_CONDUCT @@ -0,0 +1,8 @@ +"All things were made of numbers." +Pythagoras (c. 570 BC ~ c. 495 BC) + +"Beauty is the first test: there is no permanent place in the world for ugly mathematics." +G. H. Hardy (1877 - 1947) + +"I, in any case, am convinced that He does not play dice." +Albert Einstein (1879 - 1955) diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 93e847c..cc5d497 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -1,8 +1,10 @@ INCLUDE(${CMAKE_SOURCE_DIR}/cmake/gtest.cmake) -FUNCTION(ADD_GTEST target) +FUNCTION(ADD_UNIT_TEST target) ADD_EXECUTABLE(${target} ${ARGN} tests/main.cpp) TARGET_USE_GTEST(${target}) + TARGET_INCLUDE_DIRECTORIES(${target} + PRIVATE ${CMAKE_SOURCE_DIR}/tests/include) TARGET_LINK_LIBRARIES(${target} stdtensor) IF(HAVE_CUDA) TARGET_LINK_LIBRARIES(${target} cudart) @@ -13,9 +15,6 @@ ENDFUNCTION() FILE(GLOB tests tests/test_*.cpp) FOREACH(t ${tests}) GET_FILENAME_COMPONENT(name ${t} NAME_WE) - STRING(REPLACE "_" - "-" - name - ${name}) - ADD_GTEST(${name} ${t}) + STRING(REPLACE "_" "-" name ${name}) + ADD_UNIT_TEST(${name} ${t}) ENDFOREACH() diff --git a/examples/example_opencv.cpp b/examples/example_opencv.cpp index 08bb6e7..1543c2e 100644 --- a/examples/example_opencv.cpp +++ b/examples/example_opencv.cpp @@ -8,8 +8,10 @@ using bmp_t = ttl::matrix; void save_bmp(const bmp_t &bmp) { - const cv::Mat img(cv::Size(bmp.shape().dims()[1], bmp.shape().dims()[0]), - CV_8UC(3), (void *)bmp.data()); + uint32_t h, w; + std::tie(h, w) = bmp.dims(); + // const auto [h, w] = bmp.dims(); // c++17 + const cv::Mat img(cv::Size(w, h), CV_8UC(3), bmp.data()); cv::imwrite("i.png", img); } diff --git a/include/stdtensor b/include/stdtensor deleted file mode 100644 index 907b016..0000000 --- a/include/stdtensor +++ /dev/null @@ -1,3 +0,0 @@ -// For backward compatibility -#pragma once -#include diff --git a/include/ttl/algorithm b/include/ttl/algorithm index f86bcfc..0e3ef98 100644 --- a/include/ttl/algorithm +++ b/include/ttl/algorithm @@ -6,6 +6,7 @@ namespace ttl { using ttl::internal::argmax; using ttl::internal::cast; +using ttl::internal::chebyshev_distenace; using ttl::internal::fill; using ttl::internal::hamming_distance; using ttl::internal::max; diff --git a/include/ttl/bits/flat_tensor_mixin.hpp b/include/ttl/bits/flat_tensor_mixin.hpp index 96991fe..26c4a42 100644 --- a/include/ttl/bits/flat_tensor_mixin.hpp +++ b/include/ttl/bits/flat_tensor_mixin.hpp @@ -46,6 +46,12 @@ class flat_tensor_mixin using shape_type = S; using device_type = D; + rank_t rank() const { return shape_.rank(); } + + Dim size() const { return shape_.size(); } + + const auto &dims() const { return shape_.dims(); } + size_t data_size() const { return shape_.size() * sizeof(R); } const S &shape() const { return shape_; } diff --git a/include/ttl/bits/raw_tensor_mixin.hpp b/include/ttl/bits/raw_tensor_mixin.hpp index 1f21b00..b7578c0 100644 --- a/include/ttl/bits/raw_tensor_mixin.hpp +++ b/include/ttl/bits/raw_tensor_mixin.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include @@ -86,6 +87,14 @@ class raw_tensor_mixin return reinterpret_cast(data_.get()); } + template + auto typed() const + { + using Access = typename basic_access_traits::type; + using T = basic_tensor, D, Access>; + return T(data(), shape_); + } + template basic_tensor, D, A1> ranked_as() const { diff --git a/include/ttl/bits/std_access_traits.hpp b/include/ttl/bits/std_access_traits.hpp new file mode 100644 index 0000000..16fefaf --- /dev/null +++ b/include/ttl/bits/std_access_traits.hpp @@ -0,0 +1,29 @@ +#pragma once + +namespace ttl +{ +namespace internal +{ +struct owner; +struct readwrite; +struct readonly; + +template +struct basic_access_traits; + +template <> +struct basic_access_traits { + using type = readwrite; +}; + +template <> +struct basic_access_traits { + using type = readwrite; +}; + +template <> +struct basic_access_traits { + using type = readonly; +}; +} // namespace internal +} // namespace ttl diff --git a/include/ttl/bits/std_host_tensor_algo.hpp b/include/ttl/bits/std_host_tensor_algo.hpp index 78aa35b..970c80a 100644 --- a/include/ttl/bits/std_host_tensor_algo.hpp +++ b/include/ttl/bits/std_host_tensor_algo.hpp @@ -37,6 +37,19 @@ Dim hamming_distance(const basic_host_tensor_view &x, std::not_equal_to()); } +template +R chebyshev_distenace(const basic_host_tensor_view &x, + const basic_host_tensor_view &y) +{ + return std::inner_product( + x.data(), x.data_end(), y.data(), static_cast(0), + [](R a, R d) { return std::max(a, d); }, + [](R x, R y) { + // FIXME: make sure it is commutative for floats + return x > y ? x - y : y - x; + }); +} + template R max(const basic_host_tensor_view &t) { diff --git a/include/ttl/bits/std_tensor_mixin.hpp b/include/ttl/bits/std_tensor_mixin.hpp index 7d7dfd8..0d98fd1 100644 --- a/include/ttl/bits/std_tensor_mixin.hpp +++ b/include/ttl/bits/std_tensor_mixin.hpp @@ -47,6 +47,12 @@ class basic_scalar_mixin data_ptr data_end() const { return data_.get() + 1; } S shape() const { return S(); } + + data_ref at() const + { // FIXME: support other devices + static_assert(std::is_same::value, ""); + return data_.get()[0]; + } }; template diff --git a/include/ttl/bits/std_tensor_traits.hpp b/include/ttl/bits/std_tensor_traits.hpp index ba2fa57..2f93ca8 100644 --- a/include/ttl/bits/std_tensor_traits.hpp +++ b/include/ttl/bits/std_tensor_traits.hpp @@ -1,15 +1,12 @@ #pragma once #include +#include #include namespace ttl { namespace internal { -struct owner; -struct readwrite; -struct readonly; - template using own_ptr = std::unique_ptr>; @@ -45,7 +42,7 @@ struct basic_tensor_traits { using ref_type = R &; using Data = own_ptr; - using Access = readwrite; + using Access = readwrite; // FIXME: use basic_access_traits }; template diff --git a/tests/include/ttl/filesystem b/tests/include/ttl/filesystem new file mode 100644 index 0000000..0c61203 --- /dev/null +++ b/tests/include/ttl/filesystem @@ -0,0 +1,21 @@ +// -*- mode: c++ -*- +#pragma once + +#ifdef HAVE_STD_CPP_FS +#include + +namespace std +{ +namespace filesystem = std::__fs::filesystem; +} + +#else + +#include + +namespace std +{ +namespace filesystem = std::experimental::filesystem; +} + +#endif diff --git a/tests/test_algo.cpp b/tests/test_algo.cpp index ec28758..f6e12af 100644 --- a/tests/test_algo.cpp +++ b/tests/test_algo.cpp @@ -8,7 +8,7 @@ TEST(tensor_algo_test, test_argmax) using R = float; ttl::tensor t(10); std::iota(t.data(), t.data_end(), 1); - ASSERT_EQ(static_cast(9), ttl::argmax(view(t))); + ASSERT_EQ(static_cast(9), ttl::argmax(ttl::view(t))); } TEST(tensor_algo_test, test_cast) @@ -23,9 +23,9 @@ TEST(tensor_algo_test, test_cast) }); ttl::tensor y(n); - ttl::cast(view(x), ref(y)); + ttl::cast(ttl::view(x), ttl::ref(y)); - ASSERT_EQ(5, ttl::sum(view(y))); + ASSERT_EQ(5, ttl::sum(ttl::view(y))); } TEST(tensor_algo_test, test_fill) @@ -33,12 +33,12 @@ TEST(tensor_algo_test, test_fill) { using R = int; ttl::tensor t(10); - ttl::fill(ref(t), 1); + ttl::fill(ttl::ref(t), 1); } { using R = float; ttl::tensor t(10); - ttl::fill(ref(t), static_cast(1.1)); + ttl::fill(ttl::ref(t), static_cast(1.1)); } } @@ -47,11 +47,26 @@ TEST(tensor_algo_test, test_hamming_distance) using R = int; int n = 0xffff; ttl::tensor x(n); - ttl::fill(ref(x), -1); + ttl::fill(ttl::ref(x), -1); ttl::tensor y(n); - ttl::fill(ref(y), 1); + ttl::fill(ttl::ref(y), 1); ASSERT_EQ(static_cast(n), - ttl::hamming_distance(view(x), view(y))); + ttl::hamming_distance(ttl::view(x), ttl::view(y))); +} + +TEST(tensor_algo_test, chebyshev_distenace) +{ + using R = int; + int n = 0xffff; + ttl::tensor x(n); + ttl::tensor y(n); + std::iota(x.data(), x.data_end(), 1); + std::iota(y.data(), y.data_end(), 1); + ASSERT_EQ(static_cast(0), + ttl::chebyshev_distenace(ttl::view(x), ttl::view(y))); + std::reverse(y.data(), y.data_end()); + ASSERT_EQ(static_cast(n - 1), + ttl::chebyshev_distenace(ttl::view(x), ttl::view(y))); } TEST(tensor_algo_test, test_summaries_int) @@ -60,10 +75,10 @@ TEST(tensor_algo_test, test_summaries_int) const int n = 10; ttl::tensor x(n); std::iota(x.data(), x.data_end(), -5); - ASSERT_EQ(-5, ttl::min(view(x))); - ASSERT_EQ(4, ttl::max(view(x))); - ASSERT_EQ(-5, ttl::sum(view(x))); - ASSERT_EQ(0, ttl::mean(view(x))); + ASSERT_EQ(-5, ttl::min(ttl::view(x))); + ASSERT_EQ(4, ttl::max(ttl::view(x))); + ASSERT_EQ(-5, ttl::sum(ttl::view(x))); + ASSERT_EQ(0, ttl::mean(ttl::view(x))); } TEST(tensor_algo_test, test_summaries_float) @@ -72,8 +87,8 @@ TEST(tensor_algo_test, test_summaries_float) const int n = 10; ttl::tensor x(n); std::iota(x.data(), x.data_end(), -5); - ASSERT_EQ(-5, ttl::min(view(x))); - ASSERT_EQ(4, ttl::max(view(x))); - ASSERT_EQ(-5, ttl::sum(view(x))); - ASSERT_EQ(-0.5, ttl::mean(view(x))); + ASSERT_EQ(-5, ttl::min(ttl::view(x))); + ASSERT_EQ(4, ttl::max(ttl::view(x))); + ASSERT_EQ(-5, ttl::sum(ttl::view(x))); + ASSERT_EQ(-0.5, ttl::mean(ttl::view(x))); } diff --git a/tests/test_include.cpp b/tests/test_include_ttl_tensor.cpp similarity index 86% rename from tests/test_include.cpp rename to tests/test_include_ttl_tensor.cpp index 6af1611..8744fe1 100644 --- a/tests/test_include.cpp +++ b/tests/test_include_ttl_tensor.cpp @@ -1,6 +1,6 @@ #include "testing.hpp" -#include +#include using ttl::tensor; using ttl::tensor_ref; diff --git a/tests/_test_loc.cpp b/tests/test_loc.cpp similarity index 92% rename from tests/_test_loc.cpp rename to tests/test_loc.cpp index 34aa019..ef5b00f 100644 --- a/tests/_test_loc.cpp +++ b/tests/test_loc.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include #include "testing.hpp" @@ -20,7 +20,6 @@ int loc(const char *filename) TEST(test_loc, test1) { - std::string path = "/path/to/directory"; int tot = 0; int n = 0; for (const auto &entry : fs::directory_iterator("include/ttl/bits")) { diff --git a/tests/test_raw_tensor.cpp b/tests/test_raw_tensor.cpp index 3e9638d..674dcdb 100644 --- a/tests/test_raw_tensor.cpp +++ b/tests/test_raw_tensor.cpp @@ -93,7 +93,8 @@ TEST(raw_tensor_test, test_convert) } } -template void test_raw_accessors(const T &t) +template +void test_raw_accessors(const T &t) { t.shape(); t.value_type(); @@ -124,3 +125,38 @@ TEST(raw_tensor_test, test_data) test_raw_accessors(rr); test_raw_accessors(rv); } + +#include + +TEST(raw_tensor_test, test_convert_to_flat) +{ + using ttl::experimental::raw_tensor; + using ttl::experimental::raw_tensor_ref; + using ttl::experimental::raw_tensor_view; + using encoder = raw_tensor::encoder_type; + using raw_shape = raw_tensor::shape_type; + + raw_tensor rt(encoder::value(), 1, 2, 3); + { + static_assert( + std::is_same()), + ttl::experimental::flat_tensor_ref>::value, + ""); + ttl::experimental::flat_tensor_ref ft = rt.typed(); + ASSERT_EQ(ft.size(), static_cast(6)); + } + { + const raw_tensor_ref rtr(rt); + static_assert( + std::is_same()), + ttl::experimental::flat_tensor_ref>::value, + ""); + } + { + const raw_tensor_view rtv(rt); + static_assert( + std::is_same()), + ttl::experimental::flat_tensor_view>::value, + ""); + } +} diff --git a/tests/test_shape.cpp b/tests/test_shape.cpp index 2464542..16b54c0 100644 --- a/tests/test_shape.cpp +++ b/tests/test_shape.cpp @@ -17,8 +17,11 @@ void test_shape(dim_t h, dim_t w) for (dim_t j = 0; j < w; ++j) { ASSERT_EQ(s.offset(i, j), k); { - dim_t u, v; - std::tie(u, v) = s.expand(k); + // dim_t u, v; + // std::tie(u, v) = s.expand(k); + const auto coords = s.expand(k); + const auto u = std::get<0>(coords); + const auto v = std::get<1>(coords); ASSERT_EQ(i, u); ASSERT_EQ(j, v); } diff --git a/tests/test_tensor.cpp b/tests/test_tensor.cpp index e7890d4..69b01f9 100644 --- a/tests/test_tensor.cpp +++ b/tests/test_tensor.cpp @@ -189,52 +189,67 @@ auto create_tensor_func() TEST(tensor_test, return_tensor) { auto t = create_tensor_func(); } -template -R read_tensor_func(const tensor &t, int i, int j) +template +R read_tensor_func(const tensor &t, I... is) { - const R x = t.at(i, j); + const R x = t.at(is...); return x; } -template -R read_tensor_ref_func(const tensor_ref &t, int i, int j) +template +R read_tensor_ref_func(const tensor_ref &t, I... is) { - const R x = t.at(i, j); + const R x = t.at(is...); return x; } -template -R read_tensor_view_func(const tensor_view &t, int i, int j) +template +R read_tensor_view_func(const tensor_view &t, I... is) { - const R x = t.at(i, j); + const R x = t.at(is...); return x; } TEST(tensor_test, test_read_access) { - tensor t(2, 2); + { + tensor t; + t.data()[0] = 1; + ASSERT_EQ(t.at(), 1); - t[0][0] = 1; - ASSERT_EQ(1, read_tensor_func(t, 0, 0)); + t.at() = 2; + tensor_ref r = ttl::ref(t); + ASSERT_EQ(r.at(), 2); - t[0][0] = 2; - ASSERT_EQ(2, read_tensor_ref_func(ref(t), 0, 0)); + r.at() = 3; + tensor_view v = ttl::view(t); + ASSERT_EQ(v.at(), 3); + } + { + tensor t(2, 2); - t[0][0] = 3; - ASSERT_EQ(3, read_tensor_view_func(view(t), 0, 0)); + t[0][0] = 1; + ASSERT_EQ(1, read_tensor_func(t, 0, 0)); - { - auto v = view(t); - auto p = v.at(0, 0); - p += 1; - UNUSED(p); + t[0][0] = 2; + ASSERT_EQ(2, read_tensor_ref_func(ref(t), 0, 0)); + + t[0][0] = 3; ASSERT_EQ(3, read_tensor_view_func(view(t), 0, 0)); - } - { - auto &p = t.at(0, 0); - p += 1; - UNUSED(p); - ASSERT_EQ(4, read_tensor_view_func(view(t), 0, 0)); + + { + auto v = view(t); + auto p = v.at(0, 0); + p += 1; + UNUSED(p); + ASSERT_EQ(3, read_tensor_view_func(view(t), 0, 0)); + } + { + auto &p = t.at(0, 0); + p += 1; + UNUSED(p); + ASSERT_EQ(4, read_tensor_view_func(view(t), 0, 0)); + } } }