From 00b9e6e4b7f57c09175c396576ac6e30f45a14ff 2019-03-19 18:20:49 From: Alexis Jeandet Date: 2019-03-19 18:20:49 Subject: [PATCH] New TimeSeries classes mostly usable from Python Signed-off-by: Alexis Jeandet --- diff --git a/external/TimeSeries b/external/TimeSeries index b844294..2146d47 160000 --- a/external/TimeSeries +++ b/external/TimeSeries @@ -1 +1 @@ -Subproject commit b8442947c778c24a35d33c5f8eed8548cbf9fead +Subproject commit 2146d47985f44d6914b224c3b99ebf9e16e87b3f diff --git a/external/pybind11 b/external/pybind11 index f7bc18f..25abf7e 160000 --- a/external/pybind11 +++ b/external/pybind11 @@ -1 +1 @@ -Subproject commit f7bc18f528bb35cd06c93d0a58c17e6eea3fa68c +Subproject commit 25abf7efba0b2990f5a6dfb0a31bc65c0f2f4d17 diff --git a/include/Data/SpectrogramTimeSerie.h b/include/Data/SpectrogramTimeSerie.h index bc736a4..930efd5 100644 --- a/include/Data/SpectrogramTimeSerie.h +++ b/include/Data/SpectrogramTimeSerie.h @@ -9,6 +9,8 @@ class SCIQLOP_CORE_EXPORT SpectrogramTimeSerie : public TimeSeries::TimeSerie { public: + using item_t = + decltype(TimeSeries::TimeSerie()[0]); SpectrogramTimeSerie() {} ~SpectrogramTimeSerie() = default; using TimeSerie::TimeSerie; diff --git a/src/pybind11_wrappers/CoreWrappers.cpp b/src/pybind11_wrappers/CoreWrappers.cpp index 84c7ceb..de5b9d3 100644 --- a/src/pybind11_wrappers/CoreWrappers.cpp +++ b/src/pybind11_wrappers/CoreWrappers.cpp @@ -27,6 +27,65 @@ namespace py = pybind11; using namespace std::chrono; +template +void copy_vector(py::array_t& t, py::array_t& values, T& dest_t, + U& dest_values) +{ + auto t_view = t.unchecked<1>(); + auto values_view = values.unchecked<2>(); + if constexpr(row_major) + { + for(std::size_t i = 0; i < t.size(); i++) + { + dest_t[i] = t_view[i]; + dest_values[i] = {values_view(i, 0), values_view(i, 1), + values_view(i, 2)}; + } + } + else + { + for(std::size_t i = 0; i < t.size(); i++) + { + dest_t[i] = t_view[i]; + dest_values[i] = {values_view(0, i), values_view(1, i), + values_view(2, i)}; + } + } +} + +template +void copy_scalar(py::array_t& t, py::array_t& values, T& dest_t, + U& dest_values) +{ + auto t_view = t.unchecked<1>(); + auto values_view = values.unchecked<1>(); + for(std::size_t i = 0; i < t.size(); i++) + { + dest_t[i] = t_view[i]; + dest_values[i] = values_view[i]; + } +} + +template +void copy_spectro(py::array_t& t, py::array_t& values, + T& dest_t, U& dest_values) +{ + auto t_view = t.unchecked<1>(); + auto values_view = values.unchecked<2>(); + const auto width = values.shape(0); + std::cout << "WIDTH" << width << std::endl; + for(std::size_t i = 0; i < t.size(); i++) + { + dest_t[i] = t_view[i]; + for(int j = 0; j < width; j++) + { + dest_values[i * width + j] = values_view(j, i); + std::cout << "dest_values[" << i * width + j << "] = values_view(" << j + << ", " << i << ") = " << values_view(j, i) << std::endl; + } + } +} + PYBIND11_MODULE(pysciqlopcore, m) { pybind11::bind_vector>(m, "VectorDouble"); @@ -156,6 +215,8 @@ PYBIND11_MODULE(pysciqlopcore, m) py::class_(m, "ITimeSerie") .def_property_readonly( "size", [](const TimeSeries::ITimeSerie& ts) { return ts.size(); }) + .def("__len__", + [](const TimeSeries::ITimeSerie& ts) { return ts.size(); }) .def_property_readonly( "shape", [](const TimeSeries::ITimeSerie& ts) { return ts.shape(); }) .def_property_readonly( @@ -172,13 +233,7 @@ PYBIND11_MODULE(pysciqlopcore, m) assert(t.size() == values.size()); ScalarTimeSerie::axis_t _t(t.size()); ScalarTimeSerie::axis_t _values(t.size()); - auto t_vew = t.unchecked<1>(); - auto values_vew = values.unchecked<1>(); - for(std::size_t i = 0; i < t.size(); i++) - { - _t[i] = t_vew[i]; - _values[i] = values_vew[i]; - } + copy_scalar(t, values, _t, _values); return ScalarTimeSerie(_t, _values); })) .def("__getitem__", @@ -202,14 +257,16 @@ PYBIND11_MODULE(pysciqlopcore, m) VectorTimeSerie::axis_t _t(t.size()); VectorTimeSerie::container_type _values(t.size()); - auto t_vew = t.unchecked<1>(); - auto values_vew = values.unchecked<2>(); - for(std::size_t i = 0; i < t.size(); i++) + if(values.shape()[0] == 3 && values.shape(1) != 3) { - _t[i] = t_vew[i]; - _values[i] = VectorTimeSerie::raw_value_type{ - values_vew(0, i), values_vew(1, i), values_vew(2, i)}; + copy_vector(t, values, _t, + _values); } + else + { + copy_vector(t, values, _t, _values); + } + return VectorTimeSerie(_t, _values); })) .def("__getitem__", @@ -221,10 +278,28 @@ PYBIND11_MODULE(pysciqlopcore, m) *(ts.begin() + key) = value; }); + py::class_(m, "SpectrogramTimeSerieItem") + .def("__getitem__", [](SpectrogramTimeSerie::item_t& self, + std::size_t key) { return self[key]; }); + py::class_( m, "SpectrogramTimeSerie") .def(py::init<>()) - .def(py::init>()); + .def(py::init>()) + .def(py::init([](py::array_t t, py::array_t values) { + assert(t.size() < values.size()); // TODO check geometry + SpectrogramTimeSerie::axis_t _t(t.size()); + SpectrogramTimeSerie::container_type< + SpectrogramTimeSerie::raw_value_type> + _values(values.size()); + copy_spectro(t, values, _t, _values); + std::vector shape; + shape.push_back(values.shape(1)); + shape.push_back(values.shape(0)); + return SpectrogramTimeSerie(_t, _values, shape); + })) + .def("__getitem__", + [](SpectrogramTimeSerie& ts, std::size_t key) { return ts[key]; }); py::class_>(m, "Variable2") .def(py::init()) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1f221e6..031cad2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -50,6 +50,11 @@ declare_test(TestVariableController2WithSync TestVariableController2WithSync Var declare_test(TestCatalogueController TestCatalogueController CatalogueController/TestCatalogueController.cpp "sciqlopcore;TestUtils;Qt5::Test") +add_executable(TestVariablesEmbed TestUtils/PyTestWrapperExe.cpp) +target_link_libraries(TestVariablesEmbed PRIVATE pybind11::embed) +add_test(NAME TestTestVariablesEmbed COMMAND TestVariablesEmbed) +target_compile_definitions(TestVariablesEmbed PRIVATE -DPYTEST_SCRIPT="${CMAKE_CURRENT_LIST_DIR}/TestVariables.py") +set_tests_properties(TestTestVariablesEmbed PROPERTIES ENVIRONMENT PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/../) find_package(PythonInterp 3 REQUIRED) diff --git a/tests/TestUtils/PyTestWrapperExe.cpp b/tests/TestUtils/PyTestWrapperExe.cpp new file mode 100644 index 0000000..3aa7153 --- /dev/null +++ b/tests/TestUtils/PyTestWrapperExe.cpp @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------------ +-- This file is a part of the SciQLOP Software +-- Copyright (C) 2019, Plasma Physics Laboratory - CNRS +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-------------------------------------------------------------------------------*/ +/*-- Author : Alexis Jeandet +-- Mail : alexis.jeandet@member.fsf.org +----------------------------------------------------------------------------*/ +#include +#include +#include +#include + +namespace py = pybind11; + +int main(int argc, char** argv) +{ + py::scoped_interpreter guard{}; + py::globals()["__file__"] = py::str(PYTEST_SCRIPT); + try + { + py::eval_file(PYTEST_SCRIPT); + } catch(py::error_already_set const& pythonErr) + { + std::cout << pythonErr.what(); + } + return 0; +} diff --git a/tests/TestVariables.py b/tests/TestVariables.py index 4ba87ff..15f3d43 100755 --- a/tests/TestVariables.py +++ b/tests/TestVariables.py @@ -1,10 +1,15 @@ import sys import os +if not hasattr(sys, 'argv') or len(sys.argv)==0: + sys.argv = [''] +current_script_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(current_script_path) import sciqlopqt import pysciqlopcore import numpy as np +import pandas as pds import datetime import time import unittest @@ -20,7 +25,7 @@ class TimeSeriesCtors(unittest.TestCase): @ddt.data( (pysciqlopcore.ScalarTimeSerie,10), (pysciqlopcore.VectorTimeSerie,10), - (pysciqlopcore.SpectrogramTimeSerie,[10,10]), + (pysciqlopcore.SpectrogramTimeSerie,[10,10]) ) def test_construct(self, case): ts = case[0](case[1]) @@ -35,10 +40,50 @@ class TimeSeriesData(unittest.TestCase): self.assertEqual(ts[0],123.) def test_build_ScalarTimeSerie_from_np_arrays(self): - ts = pysciqlopcore.ScalarTimeSerie(np.arange(10),np.zeros(10)) + ts = pysciqlopcore.ScalarTimeSerie(np.arange(10), np.arange(10)*10) + for i in range(len(ts)): + self.assertEqual(ts[i],i*10.) def test_build_VectorTimeSerie_from_np_arrays(self): - ts = pysciqlopcore.VectorTimeSerie(np.arange(10),np.zeros((3,10))) + v=np.ones((3,10)) + for i in range(3): + v[:][i] = np.arange(10)*10**i + ts = pysciqlopcore.VectorTimeSerie(np.arange(10), v) + for i in range(len(ts)): + self.assertEqual(ts[i].x,i) + self.assertEqual(ts[i].y,i*10.) + self.assertEqual(ts[i].z,i*100.) + + def test_build_VectorTimeSerie_from_np_arrays_row(self): + v=np.ones((10,3)) + for i in range(3): + v.transpose()[:][i] = np.arange(10)*10**i + ts = pysciqlopcore.VectorTimeSerie(np.arange(10), v) + for i in range(len(ts)): + self.assertEqual(ts[i].x,i) + self.assertEqual(ts[i].y,i*10.) + self.assertEqual(ts[i].z,i*100.) + + def test_build_VectorTimeSerie_from_np_dataframe(self): + df = pds.DataFrame(data=np.zeros((10,3)),index=np.arange(10)) + for i in range(3): + df[i] = np.arange(10)*10**i + ts = pysciqlopcore.VectorTimeSerie(df.index.values, df.values) + for i in range(len(ts)): + self.assertEqual(ts[i].x,i) + self.assertEqual(ts[i].y,i*10.) + self.assertEqual(ts[i].z,i*100.) + + def test_build_SpectrogramTimeSerie_from_np_arrays(self): + v=np.ones((4,10)) + for i in range(4): + v[:][i] = np.arange(10)*10**i + ts = pysciqlopcore.SpectrogramTimeSerie(np.arange(10), v) + for i in range(len(ts)): + for j in range(4): + print(f"ts[{i}][{j}] = " + str(ts[i][j])) + if __name__ == '__main__': - unittest.main() + unittest.main(exit=False) +