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

Provide a range for basis #191

Merged
merged 38 commits into from
Jul 27, 2024
Merged

Provide a range for basis #191

merged 38 commits into from
Jul 27, 2024

Conversation

BalzaniEdoardo
Copy link
Collaborator

@BalzaniEdoardo BalzaniEdoardo commented Jul 16, 2024

Setting basis range

This PR addresses #178, allowing to set a fixed range for the basis.

This is a useful when,

  1. We want to discard outliers, for example position tracking artifacts or similar
  2. We want to fit the same model with the same basis configuration over different runs of an experiment.

The vmin/vmax are set at initialization of the basis, so that multiple calls to Basis.compute_features will return same result every time and do not require extra arguments.

vmin and vmax are get only properties.

Behavior of (__call__ and compute_features):

  • The default behavior is unchanged. If vmin/vmax are not provided, they are set to the nanmin/nanmax respectively when the samples are provided.
  • If vmin/vmax are provided, the range starts/ends at vmin/vmax. Any sample out of range will return NaN.

Behavior ofevaluate_on_grid (differs since it does not receive any sample):

  • if both vmin/vmax are provided, evaluates on an equispaced grid between vmin and vmax.
  • If only vmin is provided, evaluates on an equispaced grid between vmin and vmin + 1.
  • If only vmax is provided, evaluates on an equispaced grid between vmax - 1 and vmax.
  • If no value is provided, evaluates on an equispaced grid between 0 and 1.
from nemos.basis import BSplineBasis
import numpy as np


x = np.arange(10)
basis = BSplineBasis(5, vmin=1, vmax=8)

# this uses the values passed at initializaiton vmin=1, vmax=8
y = basis.compute_features(x)
y = basis(x)

@BalzaniEdoardo
Copy link
Collaborator Author

BalzaniEdoardo commented Jul 16, 2024

TODO

  • Add documentation in background

@codecov-commenter
Copy link

codecov-commenter commented Jul 16, 2024

Codecov Report

Attention: Patch coverage is 96.92308% with 2 lines in your changes missing coverage. Please review.

Project coverage is 97.10%. Comparing base (fceea56) to head (e3d4a32).
Report is 90 commits behind head on development.

Files Patch % Lines
src/nemos/basis.py 96.55% 2 Missing ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##           development     #191      +/-   ##
===============================================
- Coverage        97.23%   97.10%   -0.13%     
===============================================
  Files               15       18       +3     
  Lines             1484     1626     +142     
===============================================
+ Hits              1443     1579     +136     
- Misses              41       47       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@BalzaniEdoardo BalzaniEdoardo marked this pull request as ready for review July 18, 2024 20:10
Copy link
Member

@billbrod billbrod left a comment

Choose a reason for hiding this comment

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

Basic ideas here all look good to me, but I've got some higher level questions:

  • Not sure how I feel about setting just one of vmin/vmax. Picking vmin+1 / vmax-1 feels very arbitrary. why not require both? we could do that by changing the argument to vrange and requiring it to be a 2-tuple
  • this PR pulls the eval and conv modes of the basis further apart. do we want to consider making them separate classes? or something else, but it feels weird to have fully half of the arguments be ignored in one mode and the other half in the other mode.
  • not necessarily part of this PR, but: I don't know how to pull up the docstring of __call__. what comes up is the docstring of the class. that seems ... like a problem. if I'm missing something, let me know, otherwise I can create an issue
  • for the raised cosine basis objects, they only give reasonable values on the interval [0,1]. So vmin/vmax should lie within that interval as well.
  • for those objects, it's weird that the log never rescales and the linear can optionally do so. shouldn't they both always rescale? did we discuss this at some point? the other basis objects never rescale, right? but it feels like rescaling means something different

docs/background/plot_01_1D_basis_function.py Outdated Show resolved Hide resolved
docs/background/plot_01_1D_basis_function.py Show resolved Hide resolved
src/nemos/validation.py Outdated Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Show resolved Hide resolved
sample_pts, knot_locs, order=self.order, der=0, outer_ok=False
)
else:
basis_eval = np.full((sample_pts.shape[0], self.n_basis_funcs), np.nan)
Copy link
Member

Choose a reason for hiding this comment

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

I'm confused as how this can be hit, if we raise a ValueError if all sample points lie outside vmin/vmax

Copy link
Member

Choose a reason for hiding this comment

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

(note this shows up in multiple basis objects)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yep, It wasn't the case at the beginning but then I changed my mind and this is a leftover

-------
basis_funcs :
Raised cosine basis functions, shape (n_samples, n_basis_funcs).
rescale_samples :
Copy link
Member

Choose a reason for hiding this comment

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

why is this only an option for this basis?

Copy link
Member

Choose a reason for hiding this comment

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

I realize this isn't new, but it feels odd, given the new behavior

Copy link
Member

Choose a reason for hiding this comment

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

also the docstring says that it rescales to 0,1, but now it depends on vmin/vmax

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed, and added a private flag at init that define if the RaisedCosineLinear should rescale or not (if called by the subclass it should not, otherwise it always have to)

@BalzaniEdoardo
Copy link
Collaborator Author

Basic ideas here all look good to me, but I've got some higher level questions:

* Not sure how I feel about setting just one of vmin/vmax. Picking vmin+1 / vmax-1 feels very arbitrary. why not require both? we could do that by changing the argument to `vrange` and requiring it to be a 2-tuple

This has been addressed (bounds is the name of the variable)

* this PR pulls the eval and conv modes of the basis further apart. do we want to consider making them separate classes? or something else, but it feels weird to have fully half of the arguments be ignored in one mode and the other half in the other mode.

We should discuss this with Sofia and @gviejo. But definitely not something we should tackle in this PR.

* not necessarily part of this PR, but: I don't know how to pull up the docstring of `__call__`. what comes up is the docstring of the class. that seems ... like a problem. if I'm missing something, let me know, otherwise I can create an issue

Interesting...

* for the raised cosine basis objects, they only give reasonable values on the interval [0,1]. So vmin/vmax should lie within that interval as well.

This has been resolved. RaisedCosineLinear should always be called with the rescale_sample flag set to true. The Log instead must call the super class call with the rescale_sample set to False.

* for those objects, it's weird that the log never rescales and the linear can optionally do so. shouldn't they both always rescale? did we discuss this at some point? the other basis objects never rescale, right? but it feels like rescaling means something different

They both have a single possible behavior, I was exposing the parameter by mistake.

@BalzaniEdoardo BalzaniEdoardo requested a review from billbrod July 19, 2024 22:04
Copy link
Member

@billbrod billbrod left a comment

Choose a reason for hiding this comment

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

I have a couple points dropped throughout that I think need addressing, basically juts by adding a comment, but then I think this is good to go. I think using a single bounds argument is much cleaner.

src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Show resolved Hide resolved
src/nemos/basis.py Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
src/nemos/basis.py Outdated Show resolved Hide resolved
BalzaniEdoardo and others added 12 commits July 27, 2024 12:20
Co-authored-by: William F. Broderick <billbrod@gmail.com>
Co-authored-by: William F. Broderick <billbrod@gmail.com>
Co-authored-by: William F. Broderick <billbrod@gmail.com>
Co-authored-by: William F. Broderick <billbrod@gmail.com>
Co-authored-by: William F. Broderick <billbrod@gmail.com>
@BalzaniEdoardo BalzaniEdoardo merged commit 8784eac into development Jul 27, 2024
11 checks passed
@BalzaniEdoardo BalzaniEdoardo deleted the min_max_basis branch July 27, 2024 17:09
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.

4 participants