-
Notifications
You must be signed in to change notification settings - Fork 175
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
Piecewise param #1355
Piecewise param #1355
Conversation
I'm 👍 on the param in general; it's not very long so it doesn't add much extra complexity. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main content of this PR is great, I think storing the data
dict in new param makes sense and simplifies the implementation! There are a few stylistic things that I've commented about inline that need to be addressed though. Additionally, it would be good to rebase this branch to master
, as the Piecewise
process has changed a bit in master compared to where this branch started.
nengo/params.py
Outdated
@@ -350,6 +350,35 @@ def coerce(self, instance, value): | |||
return super(DictParam, self).coerce(instance, value) | |||
|
|||
|
|||
class PiecewiseDataParam(DictParam): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put this class in processes.py
instead of params.py
? This is what we normally do with params that apply only to a single class (e.g., PrePostParam
in connection.py
).
nengo/params.py
Outdated
def coerce(self, instance, data): | ||
data = super(PiecewiseData, self).coerce(instance, data) | ||
|
||
self.interpolatable = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to keep track of this in the param, rather than in the piecewise class. However, for clarity, we define all instance variables in the __init__
method, even though there's no other reason to implement __init__
here (though you could additionally make the constructor easier by fixing some parameters like readonly
).
So yeah, define this in __init__
(probably defaulting to False
?) and then change it in this method (which I think is not done at the moment).
nengo/params.py
Outdated
for time, value in data.iteritems(): | ||
if not is_number(time): | ||
raise ValidationError("Keys must be times (floats or ints), not %r" | ||
% type(time).__name__, attr='data') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should also pass obj=instance
here.
nengo/params.py
Outdated
# make sure this is the same length as previous items | ||
if length != output_length and output_length is not None: | ||
raise ValidationError("time %g has %d items instead of %d" % | ||
(time, length, output_length), attr='data') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
obj=instance
here too
nengo/processes.py
Outdated
@@ -4,7 +4,7 @@ | |||
from nengo.base import Process | |||
from nengo.dists import DistributionParam, Gaussian | |||
from nengo.exceptions import ValidationError | |||
from nengo.params import BoolParam, DictParam, NdarrayParam, NumberParam | |||
from nengo.params import BoolParam, DictParam, EnumParam, NdarrayParam, NumberParam, PiecewiseDataParam |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is too long, should be < 80 characters.
nengo/processes.py
Outdated
return False | ||
return True | ||
|
||
def _output_length(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also be a property of the param
nengo/processes.py
Outdated
|
||
Parameters | ||
---------- | ||
tp : time points of the function where values are specified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will have to be updated, as tp
and yp
are no longer attributes of the param.
nengo/tests/test_processes.py
Outdated
def test_function(self, Simulator): | ||
f = self.run_sim({0: np.sin, 0.5: np.cos}, 'zero', Simulator) | ||
|
||
assert np.allclose(f[0], [np.sin(0.001)]) # t = 0.001 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I did in the other other tests, we should opt for passing in things like t == 0.001
rather than passing in 0
and commenting the actual time (which is more understandable to the reader).
nengo/tests/test_processes.py
Outdated
assert np.allclose(f[999], [np.cos(1.0)]) # t = 1.0 | ||
assert np.allclose(f[1499], [np.cos(1.5)]) # t = 1.5 | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inside a class, PEP8 says to leave one blank line between methods, not more than one.
nengo/tests/test_processes.py
Outdated
assert np.allclose(f[499], [1]) # t = 0.5 | ||
assert np.allclose(f[749], [0.5]) # t = 0.75 | ||
assert np.allclose(f[999], [0]) # t = 1.0 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, PEP8 says no extra newlines at the end of a file.
8e2137f
to
47af365
Compare
nengo/processes.py
Outdated
default_size_in=0, default_size_out=self.yp[0].size, **kwargs) | ||
default_size_in=0, default_size_out=self._shape_out()[0], **kwargs) | ||
|
||
def _interpolatable(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had initially thought of tracking this in the PiecewiseDataParam
class, but didn't know if we could retract any info from the descriptor class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah you're right, it is difficult to access attributes of descriptor classes. Never mind!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed a commit, mostly to remove the underscored methods. With this, LGTM!
Note that there were some issues with the rebase; specifically, master contains a modified version of da80a22, cfe51f3, and 7c5206a so those commits should not have been included post-rebase. I'll fix this in the merge (which will be easy because most of the issues were fixed post-rebase), but just wanted to point it out for future rebasing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed a commit with some minor suggestions:
np.ravel(...)
will implicitly cast its arguments to np
arrays if necessary, so we can be a bit more
parsimonious thannp.asarray(...).ravel()
- The
size_out
property seemed like it was doing a lot
of work to compute the size out, so I was trying to
think of ways to simplify it. I'm ambivalent about this
solution; it's nice and simple, but a bit weird to have
parameters be setting attributes on their instance. I'd
be fine if people wanted to reject this change and
just leave it the way it was, just putting it out there as
an option.
I like the ravel change, and setting a property on the instance is done in some Nengo objects... but I think if we're going to set it on the instance, we should add a |
In doing so, I also added the PiecewiseDataParam class, rather than stripping out tp and yp when the process is made.
9e2b946
to
b44a1f5
Compare
Thanks for the changes! It looks neater now 😄 Didn't know we could modify the instance from the descriptor....that's actually pretty cool. I was also thinking it'd be to nice to compute things once in the descriptor, but I'll go along with @tbekolay |
|
Motivation and context:
piecewise() function accepts a dictionary of time points and corresponding function/constants. We want to turn this into be a process. Thus we need a new parameter type(s) used to define a piecewise function.
Previous PR used two NdArrays, tp and yp to define a piecewise function. This limits users from defining a piecewise function that includes non-constant subdomains (i.e. sin() from 0.5 < t < 1). The following are two options I considered:
Option 1: use NdArray for tp, and add a parameter NumericalSeries, which represents a list of either numerical constants OR callable functions whose output dimentions are the same
Option 2 (chosen): Add a dictionary parameter who keys are time points and values are either callable functions or numerical constants
Both parameters would be specific to piecewise process. I chose option 2 to maintain previous interface of piecewise() function
Interactions with other PRs:
This change includes changes from #1100. Addition of PiecewiseDataParam is included.
How has this been tested?
Added new unit tests andran them
How long should this take to review?
Where should a reviewer start?
Please have a look at the first PR. Then, have a look at PiecewiseDataParam to see if the logic makes sense, as well as the choice of such parameter type.
Types of changes:
Checklist:
Still to do: