Fixed #1111 Add branching logic for sdp - #1200
Conversation
|
Review these changes at https://app.gitnotebooks.com/stumpy-dev/stumpy/pull/1200 |
|
As discussed previously in this comment, I am planning to replace |
Doesn't this mean that every time somebody passes in the |
Right! A user often uses a certain system. So, users probably would like to set the (1) Config variable: But... we are not actually using I decided the last approach, i.e. closure, and I've pushed the code. What do you think? |
| * index 0: (LB_m, UB_m) | ||
| * index 1: (LB_n, UB_n) | ||
| * index 2: func | ||
| where, fucn is the sdp function for computing |
| sliding_dot_product : callable | ||
| A callable object that computes the sliding dot product between ``Q`` | ||
| and ``T`` using different methods based on len(Q) and len(T). It | ||
| internally checks the boundary in `boundaries` and choose a function |
| if m == n: | ||
| return np.array([np.dot(Q, T)]) | ||
|
|
||
| for item in boundaries: |
There was a problem hiding this comment.
There's just something about this that doesn't sit right with me. Here's what I am seeing:
- I get that when we do
sliding_dot_product = make_sliding_dot_product()(let's refer to this left side assdp1), you are settingsliding_dot_productto thesliding_dot_productfunction that is being returned from inside of the closure (let's refer to this inside-of-the-closure-function assdp2) - However, the contents of the
sliding_dot_productfunction inside of the closure (sdp2) are never executed until you callsdp1. - This means that if you have many
QandTpairs, it is performing boundary comparisons every time rather than performing anO(1)lookup - Similarly, if you have many boundaries to check, then this would be slow
So, while the logic is sound, this point:
Doesn't this mean that every time somebody passes in the boundaries then the sets of bounds get loaded every time you call this function?
is still relevant. Yes, the sets of boundaries are supplied/embedded once into sdp2 BUT each call to sdp2 triggers a slow iterative search. Is there a cheap time AND space efficient way to create an O(1) function lookup given m and n? This for loop isn't the right approach. If we have an O(1) function lookup then it's really lightweight and I wouldn't mind going the config route because 99.9% of users will use our default.
Having said that, are we going to have a way for users to run a function and come up with boundaries and functions that are best suited for their hardware?
There was a problem hiding this comment.
So, while the logic is sound, this point:
Doesn't this mean that every time somebody passes in the boundaries then the sets of bounds get loaded every time you call this function?
is still relevant. Yes, the sets of boundaries are supplied/embedded once into
sdp2BUT each call tosdp2triggers a slow iterative search.
Right, I misunderstood your point before.
Is there a cheap time AND space efficient way to create an O(1) function lookup given
mandn? This for loop isn't the right approach. If we have anO(1)function lookup then it's really lightweight
Let's replace that for-loop with choose_sdp_func, a function that gets m and n as inputs and return a sdp function as output, and also let's get rid of closure. So, we can have a regular function like this:
def sliding_dot_product(Q, T, choose_sdp_func=None):
if m == n:
return np.array([np.dot(Q, T)])
if choose_sdp_func is None:
# STUMPY DEFAULT
sdp_func = choose_sdp_func(m, n) # branching logic
return sdp_func(Q, T)
Is it a bad design? This allows [advanced] users to use different ways to implement branching logic for their choose_sdp_func function:
- A dictionary, keyed by
(m,n), and the values are sdp functions. It is O(1) - Or, if-else logic
- Or, boundaries.
- etc
We can always add a caching mechanism on top of the function choose_sdp_func to make sure it returns output in O(1) for a repeated input (m, n). Regarding STUMPY DEFAULT for choose_sdp_func, we can go with a simple if-else logic.
As a side, regarding the following question you raised before in another comment:
the question remains, how do we allow the flexibility to swap out or modify the if/else logic in that case as well? We should think about this..
I think the proposal should address that
Having said that, are we going to have a way for users to run a function and come up with boundaries and functions that are best suited for their hardware?
Finding "boundaries" is tricky. I have a narrower scope (and, tbh, simpler solution 😅) in mind... a function that gets (m, n) and a list of sdp functions as input, and returns the best one for the provided (m, n).
There was a problem hiding this comment.
Finding "boundaries" is tricky
Okay, I think it might be tricky for most users too! So, maybe they appreciate if we just come up with a function that helps them identify boundaries approximately. For instance, we just explore cases where m and n are exact power of two.
There was a problem hiding this comment.
I am mostly interested in what the STUMPY_DEFAULT choose_sdp_func would look like and how efficient it would be?
See #1111
Pull Request Checklist
Below is a simple checklist but please do not hesitate to ask for assistance!
black(i.e.,python -m pip install blackorconda install -c conda-forge black)flake8(i.e.,python -m pip install flake8orconda install -c conda-forge flake8)pytest-cov(i.e.,python -m pip install pytest-covorconda install -c conda-forge pytest-cov)black --exclude=".*\.ipynb" --extend-exclude=".venv" --diff ./in the root stumpy directoryflake8 --extend-exclude=.venv ./in the root stumpy directory./setup.sh dev && ./test.shin the root stumpy directory and ensured that all tests are passing locallyPlease do not commit any code to avoid/circumvent a failing test and, instead, engage in a discussion (below) to determine the best course of action.
Only request a review after the checklist above is fully completed!
This is to address PR 4 as described in #1118 (comment). I've copied the corresponding notes below: