Skip to content

Jerk sliding slide#117

Merged
pavelkomarov merged 9 commits intomasterfrom
jerk-sliding-slide
Jul 1, 2025
Merged

Jerk sliding slide#117
pavelkomarov merged 9 commits intomasterfrom
jerk-sliding-slide

Conversation

@pavelkomarov
Copy link
Collaborator

@pavelkomarov pavelkomarov commented Jul 1, 2025

Attempting to use slide_function with jerk_sliding in the spirit of #69.

Essentially the old code was using these kernels
Screenshot 2025-06-30 at 18 05 33

which when normalized get weighted like this:
Screenshot 2025-06-30 at 18 05 15

(This isn't exactly the same as with the slide_function, because it starts with the kernel half-in the domain and goes until it's half-out of the domain, so you always have more than one solution contributing to a region. But that figure looked cool.)

@pavelkomarov
Copy link
Collaborator Author

pavelkomarov commented Jul 1, 2025

Okay, I had a systematic problem where I was not passing through dt properly. Solved in #118 investigation.

I'm finding 1000 is awfully big, that I can do better with a smaller window of 100. Here is the old solution with 1000 -> 100, 200->20, 400->40:

Screenshot 2025-06-30 at 20 06 09

And here is with the transformation to use the sliding function from the utilities as in this PR:

Screenshot 2025-06-30 at 20 03 15

Looking identical.

Really the window size and stride could be made parameters like they are for things in the linear module.

ttt = np.linspace(0, 3, 201) # for testing jerk_sliding, which requires > 1001 points
np.random.seed(7) # for repeatability of the test, so we don't get random failures
noise = 0.05*np.random.randn(*t.shape)
long_noise = 0.05*np.random.randn(*ttt.shape)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I define noise up here before anything runs for consistency. Otherwise I was finding I'd get different answers when running a test on its own versus with everyone else.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up adding window_size to jerk_sliding so that I could test it with the same t and noise as everyone else.

(smooth_acceleration, {'gamma':2, 'window_size':5}), (smooth_acceleration, [2, 5])
# TODO (jerk_sliding), because with the test cases here (len < 1000) it would just be a duplicate of jerk
(smooth_acceleration, {'gamma':2, 'window_size':5}), (smooth_acceleration, [2, 5]),
(jerk_sliding, {'gamma':1e2, 'solver':'CLARABEL'}), (jerk_sliding, [1e2], {'solver':'CLARABEL'})
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OSQP solver was giving me trouble in the CI again.

request.config.plots = defaultdict(lambda: pyplot.subplots(6, 2, figsize=(12,7))) # 6 is len(test_funcs_and_derivs)

def pytest_sessionfinish(session, exitstatus):
if not hasattr(session.config, 'plots'): return
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Occasionally I'd get a failure while testing about this not being able to plot when that wasn't at all relevant. The cause was always a syntax error in the test code file, but this would obfuscate that with a different confusing error. Now we fall through to highlight the syntax error.

from ..finite_difference import first_order, second_order
# Function aliases for testing cases where parameters change the behavior in a big way
def iterated_first_order(*args, **kwargs): return first_order(*args, **kwargs)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I briefly had some other vectors up here, but switching between them in the test is annoying, so I've tried to keep everyone on this one t and dt.

kernel = utility.gaussian_kernel(9)

x_hat, dxdt_hat = utility.slide_function(identity, x, 0.1, kernel, step_size=2)
x_hat, dxdt_hat = utility.slide_function(identity, x, 0.1, kernel, stride=2)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to rename this parameter. I like stride better, because dt is a step size. Less ambiguous.

dxdt_hat = y/dt # y only holds the dx values; to get deriv scale by dt
x_hat = np.cumsum(y) + integration_constants.value[order-1] # smoothed data

dxdt_hat = (dxdt_hat[0:-1] + dxdt_hat[1:])/2 # take first order FD to smooth a touch
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for that little 0.



def jerk_sliding(x, dt, params=None, options=None, gamma=None, solver=None):
def jerk_sliding(x, dt, params=None, options=None, gamma=None, solver=None, window_size=101):
Copy link
Collaborator Author

@pavelkomarov pavelkomarov Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the window_size parameter to match methods from the linear module. I have not, however, added the stride/step_size. We could, but I've just kept to striding by the ramp width.

gamma = params[0] if isinstance(params, list) else params
if options != None:
if 'solver' in options: solver = options['solver']
if 'window_size' in options: window_size = options['window_size']
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've allowed users to pass this via the old way too, just so I can be consistent in my tests.

raise ValueError("`gamma` must be given.")

window_size = 1000
stride = 200
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard-coding these makes this method less usable.

from pynumdiff.finite_difference import second_order
return second_order(x, dt)
ramp = window_size//5
kernel = np.hstack((np.arange(1, ramp+1)/ramp, np.ones(window_size - 2*ramp), (np.arange(1, ramp+1)/ramp)[::-1]))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kernel isn't exactly the same as before, but it's basically the same shape, the same in spirit.

@pavelkomarov
Copy link
Collaborator Author

Okay, based on the identicalness of before-and-after and the correctness of the tests, I'm saying this one is a correct PR. I've added to the interface to make this thing easier to test and more flexible to use. Happy with it. Going to act unilaterally again and say this is a positive change and merge.

@pavelkomarov pavelkomarov merged commit e483a7c into master Jul 1, 2025
1 check passed
@pavelkomarov pavelkomarov deleted the jerk-sliding-slide branch July 1, 2025 04:30
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

Successfully merging this pull request may close these issues.

1 participant