From 7f9431bdcfe8f8917f7d07a6200b6fcafd371e94 Mon Sep 17 00:00:00 2001 From: Sam Morley <41870650+inakleinbottle@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:49:59 +0000 Subject: [PATCH] New test for value stream (#193) * Add test for tensor-valued stream with signature values This test verifies the construction and querying of a tensor-valued stream created from signature values of a sampled Brownian stream. It ensures the domain consistency and validates the terminal values using array comparison. * Fix incorrect sign in ldexp call for path_md resolution Corrected the sign in the exponent passed to ldexp, ensuring proper computation of the path metadata support. This resolves potential inaccuracies in interval computations caused by the incorrect exponent. * Fix type check for 'support' in kwargs_to_path_metadata Corrected the conditional logic to properly handle 'support' as an Interval. Added an explicit error throw when 'support' is not an Interval, ensuring clearer feedback for invalid input. * Fix argument order in tensor multiplication calls Reversed the order of arguments in tensor multiplication to ensure correct calculations. This change impacts methods using `p_increment_stream->signature` where the initial value must be multiplied first. * Return initial value for parameter equal to domain start. This change adds a condition to return the initial value when the input parameter matches the lower bound of the domain. It ensures correct behavior for edge-case input and prevents unnecessary computations. * Replaced the sligtly incorrect increment calculation The increment between two values is not quite the difference of their Lie values as previously used. Instead, it is the log of antipode(previous)*current at each step. * Refactor tensor-valued stream test for accuracy. Simplified stream initialization by removing unused resolution, refined initial value assertion, and adjusted query intervals to align with expected behavior. Improved test robustness by introducing an epsilon margin in interval checks. --- roughpy/src/args/kwargs_to_path_metadata.cpp | 4 +++- roughpy/src/streams/tensor_valued_stream.cpp | 15 ++++++++---- streams/src/tensor_valued_stream.cpp | 6 +++-- tests/streams/test_tensor_valued_stream.py | 24 ++++++++++++++++++++ 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/roughpy/src/args/kwargs_to_path_metadata.cpp b/roughpy/src/args/kwargs_to_path_metadata.cpp index 43ccd4658..85d2d095b 100644 --- a/roughpy/src/args/kwargs_to_path_metadata.cpp +++ b/roughpy/src/args/kwargs_to_path_metadata.cpp @@ -207,10 +207,12 @@ python::PyStreamMetaData python::kwargs_to_metadata(pybind11::kwargs& kwargs) if (kwargs.contains("support")) { auto support = kwargs_pop(kwargs, "support"); - if (!py::isinstance(support)) { + if (py::isinstance(support)) { md.support = intervals::RealInterval( support.cast() ); + } else { + RPY_THROW(py::type_error, "Support must be an Interval"); } } diff --git a/roughpy/src/streams/tensor_valued_stream.cpp b/roughpy/src/streams/tensor_valued_stream.cpp index 1275233dd..3e9224ac5 100644 --- a/roughpy/src/streams/tensor_valued_stream.cpp +++ b/roughpy/src/streams/tensor_valued_stream.cpp @@ -388,15 +388,20 @@ static PyObject* stvs_from_values(PyObject* cls, increment_data.reserve(data.size() - 1); param_t min_difference = std::numeric_limits::infinity(); - auto previous = ctx->tensor_to_lie(initial_value.log()); + // auto previous = ctx->tensor_to_lie(initial_value.log()); + FreeTensor previous = initial_value; for (Py_ssize_t i = 1; i < size; ++i) { param_t param_diff = data[i].first - data[i - 1].first; if (param_diff < min_difference) { min_difference = param_diff; } - auto current = ctx->tensor_to_lie(data[i].second.log()); + auto increment = previous.antipode().mul(data[i].second); + increment_data.emplace_back(data[i].first, ctx->tensor_to_lie(increment.log())); + previous = data[i].second; - increment_data.emplace_back(data[i].first, current.sub(previous)); - previous = std::move(current); + // auto current = ctx->tensor_to_lie(data[i].second.log()); + + // increment_data.emplace_back(data[i].first, current.sub(previous)); + // previous = std::move(current); } /* @@ -418,7 +423,7 @@ static PyObject* stvs_from_values(PyObject* cls, if (!path_md.support) { path_md.support = intervals::RealInterval( data.front().first, - data.back().first + ldexp(1., *path_md.resolution)); + data.back().first + ldexp(1., -*path_md.resolution)); } /* diff --git a/streams/src/tensor_valued_stream.cpp b/streams/src/tensor_valued_stream.cpp index fa1433c1b..9290d4f83 100644 --- a/streams/src/tensor_valued_stream.cpp +++ b/streams/src/tensor_valued_stream.cpp @@ -68,11 +68,13 @@ TensorValuedStream::StreamValue TensorValuedStream::value_at( RPY_THROW(std::invalid_argument, "param is not in the domain of this stream"); } + if (param == m_domain.inf()) { return m_initial_value; } + const intervals::RealInterval interval(m_domain.inf(), param); auto sig = p_increment_stream->signature(interval, *m_initial_value.context()); - return m_initial_value.context()->convert(sig.mul(m_initial_value), + return m_initial_value.context()->convert(m_initial_value.mul(sig), m_initial_value.storage_type()); } @@ -85,7 +87,7 @@ TensorValuedStream::StreamValue TensorValuedStream::terminal_value() const { auto sig = p_increment_stream->signature(m_domain, *m_initial_value.context()); - return m_initial_value.context()->convert(sig.mul(m_initial_value), + return m_initial_value.context()->convert(m_initial_value.mul(sig), m_initial_value.storage_type()); } diff --git a/tests/streams/test_tensor_valued_stream.py b/tests/streams/test_tensor_valued_stream.py index be5fa0efa..a62f87624 100644 --- a/tests/streams/test_tensor_valued_stream.py +++ b/tests/streams/test_tensor_valued_stream.py @@ -113,3 +113,27 @@ def test_tv_stream_from_values(): +def test_signature_value_stream(): + + ctx = rp.get_context(4, 3, rp.DPReal) + + base_stream = rp.BrownianStream.with_generator("pcg64", ctx=ctx, support=rp.RealInterval(0., 1.)) + ## sampled Brownian stream or level1 Brownian stream + + delta_t = 0.1 + + values = [ + (t, base_stream.signature(rp.RealInterval(0., t))) + for t in np.arange(0., 1., delta_t) + ] + + value_stream = rp.TensorValuedStream.from_values(values, ctx=ctx) + assert value_stream.initial_value() == values[0][1] + + epsilon = 0.03125 + for i in range(len(values)-1): + t = values[i][0] + sub_stream = value_stream.query(rp.RealInterval(t, t+delta_t+epsilon)) + + sig = sub_stream.terminal_value() + assert_array_almost_equal(sig, values[i+1][1])