Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

boost::math::interpolators::pchip issue when using circular_buffer or circular_buffer_space optimized implementations and buffer capacity. #1248

Open
rodv92 opened this issue Mar 30, 2025 · 0 comments

Comments

@rodv92
Copy link

rodv92 commented Mar 30, 2025

Issue description :

When pushing back the initial values to the circular buffers of x,y before the call to the pchip constructor, if the number of data points
is less than the circular buffer capacity set during the circular buffers constructor call, subsequent push_backs calls of the pchip instance will eventually lead to :

a NaN at evaluation
that seems to coincide with the upper boundary of the elements of the initial circular buffer, even when some push_backs have been done after the call to the pchip constructor. , so that the evaluation does not lay at the boundary of the data anymore.

a segfault (for circular_buffer) or a malloc error (for circular_buffer_space_optimized) after a certain amount of push backs or evaluations.
Issue Reproduction:

Using a ramp function of the form y=x as it does not obfuscate the issue and does not involve cubic hermite logic.
If the capacity set here : boost::circular_buffer_space_optimized(20); is greater than the push back count (10) in the first for loop, the issue will appear. The greater the discrepancy between the allocated elements and the total push back counts, the greater the probability of segfault, that seems then to happens when the circular buffer cycles (here at index 20)

Seems the issue was already at least in boost 1.75

It doesn't seems that it is a big issue as capacity can be set at runtime to match the number of elements that one got to work with
before calling the pchip constructor, but maybe throwing an exception would be nice to prevent head scratching.
and maybe a note in the documentation : https://live.boost.org/doc/libs/1_87_0/libs/math/doc/html/math_toolkit/pchip.html

CODE TO REPRODUCE THE ISSUE :

#include <boost_1_82_0/circular_buffer.hpp>
#include <boost_1_82_0/math/interpolators/pchip.hpp>

using boost::math::interpolators::pchip;

#include <math.h>
#include <bits/stdc++.h>

#include <boost_1_82_0/circular_buffer.hpp>
#include <boost_1_82_0/math/interpolators/pchip.hpp>

using boost::math::interpolators::pchip;

#include <math.h>
#include <bits/stdc++.h>

int main()
{

boost::circular_buffer_space_optimized initial_x = boost::circular_buffer_space_optimized(20);
boost::circular_buffer_space_optimized initial_y = boost::circular_buffer_space_optimized(20);
// CAPACITY SET AT 20

// NUMBER OF PUSH BACKS BEFORE std::move and CONSTRUCTOR CALL = 10
for(uint32_t i=0; i<10; i++)
{
printf("push back count (before constructor call) i = %lu\n",i+1);

initial_x.push_back(double(i+0.5f));
initial_y.push_back(double(i+0.5f));

}

printf("before pchip constructor call\n");

//my_pchip = new pchip<boost::circular_buffer>(std::move(initial_x),std::move(initial_y));
boost::math::interpolators::pchip<boost::circular_buffer_space_optimized> my_pchip = pchip<boost::circular_buffer_space_optimized>(std::move(initial_x),std::move(initial_y));

printf("after pchip constructor call\n");

for(uint32_t i=10; i<100; i++)
{
printf("push back count (after constructor call) i = %lu\n",i+1);
my_pchip.push_back(double(i+0.5f),double(i+0.5f));
printf("eval at x = %f\n",double(i-2.f +0.3f));
double y = my_pchip(double(i-2.f +0.3f));
printf("y = %f\n",y);

}

return 0;
}

DEBUG OUTPUT 👍

push back count (before constructor call) i = 1
push back count (before constructor call) i = 2
push back count (before constructor call) i = 3
push back count (before constructor call) i = 4
push back count (before constructor call) i = 5
push back count (before constructor call) i = 6
push back count (before constructor call) i = 7
push back count (before constructor call) i = 8
push back count (before constructor call) i = 9
push back count (before constructor call) i = 10
before pchip constructor call
after pchip constructor call
push back count (after constructor call) i = 11
eval at x = 8.300000
y = 8.300000
push back count (after constructor call) i = 12
eval at x = 9.300000
y = nan
push back count (after constructor call) i = 13
eval at x = 10.300000
y = nan
push back count (after constructor call) i = 14
eval at x = 11.300000
y = 11.300000
push back count (after constructor call) i = 15
eval at x = 12.300000
y = 12.300000
push back count (after constructor call) i = 16
eval at x = 13.300000
y = 13.300000
push back count (after constructor call) i = 17
eval at x = 14.300000
y = 14.300000
push back count (after constructor call) i = 18
eval at x = 15.300000
y = 15.300000
push back count (after constructor call) i = 19

Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3ff0000000000000) at malloc.c:3102

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant