-
Notifications
You must be signed in to change notification settings - Fork 105
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
MAINT: rename TensorGrid->RectGrid and remove RegularGrid #841
Conversation
[5.0], | ||
[2.0], | ||
min_pt=[-1.0, 1.0, 4.0, 2.0], max_pt=[-0.5, 3.0, 5.0, 3.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.
This now became a uniform partition and printed out an enormous line, had to remove it.
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 dont get it, why did it change and how does it look now?
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.
Now by definition, any axis with two cells or less is uniform, which is kind of hard to argue against. The printed return value was roughly
uniform_partition([-1.0, 1.0, 4.0, 2.0], [-1.0, 1.0, 4.0, 2.0], (1, 1, 1, 1), nodes_on_bdry=((True, False), False, (False, True), (True, False))
modulo error in the booleans.
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 see why that was removed.
dpart_0 = odl.RectPartition(odl.IntervalProd(0, 0), odl.TensorGrid([0])) | ||
geom_p2d = odl.tomo.Parallel2dGeometry(apart, dpart=dpart_0) | ||
with pytest.raises(ValueError): | ||
odl.tomo.astra_projection_geometry(geom_p2d) |
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 check no longer made sense since the grid was in fact uniform, hence the error condition was gone.
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.
Well, perhaps we should change the grid then?
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.
Too lazy :-)
Okay I'll do it. I had to get it out because gates close at 7, didn't want to be locked in.
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.
Great change. Minor review and go ahead.
.. math:: | ||
G = \{(-1, 0), (-1, 1), (0, 0), (0, 1), (2, 0), (2, 1)\} | ||
|
||
Here is a graphical representation:: |
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.
Does this typeset well?
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 think it looks decent. It's treated as code, so it uses a monospace font.
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.
Nice then!
if not self.is_uniform: | ||
raise NotImplementedError('`stride` not defined for non-uniform ' | ||
'grid') | ||
strd = np.zeros(self.ndim) |
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.
might as well write out stride instead of strd
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.
Hesitant to shadow the function name, feels wrong.
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 function name isn't shadowed by it since it wasn't available in the first place? The only way to access it is via self.stride
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.
Okay, "shadow" was the wrong term, "simultaneously highlighted by the editor" would be more precise.
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 guess editor confusion is also a reason, stay with this then.
@property | ||
def stride(self): | ||
"""Step per axis between neighboring points of a uniform grid. | ||
|
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.
raises ...
True | ||
""" | ||
# Optimization for some common cases | ||
if other is self: | ||
return True | ||
if not (isinstance(other, TensorGrid) and | ||
if not (isinstance(other, RectGrid) and |
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.
Why is this if statement still there?
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.
To return False
for wrong type instead of raising an AttributeError
?
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.
well any non rect grid should be false, I was thinking about the following lines but perhaps I missed the logic (hard to read with changelogs)
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 would probably be easier to read as
if not isinstance(other, RectGrid) or
not ...
It's easy to mismatch parens I guess.
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.
Well
if not isinstance(other, RectGrid):
return False
# other logic here
is our standard pattern.
# vec_s. If there is no almost zero entry in each row, | ||
# return False. | ||
vec_o_mg, vec_s_mg = sparse_meshgrid(vec_o, vec_s) | ||
if not np.all(np.any(np.abs(vec_s_mg - vec_o_mg) <= atol, |
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.
np allclose?
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.
Oh well yeah, that was some old code I just copied.
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.
No, allclose
is not suitable since the two vectors are broadcast and form a matrix (like a distance matrix). Then it is checked that in each column there is at least one entry close enough to zero.
AFAICS the only thing I can do is replace the boolean abs expression with np.isclose
.
|
||
def insert(self, index, other): | ||
"""Insert another regular grid before the given index. | ||
def uniform_grid(min_pt, max_pt, shape): |
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.
Good addition.
return '{}({})'.format(self.__class__.__name__, inner_str) | ||
coord_vecs = [np.linspace(mi, ma, num, endpoint=True, dtype=np.float64) | ||
for mi, ma, num in zip(min_pt, max_pt, shape)] | ||
return RectGrid(*coord_vecs) | ||
|
||
|
||
def uniform_sampling_fromintv(intv_prod, shape, nodes_on_bdry=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.
uniform_grid_fromintv now I guess
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.
Better name eh? I'll change it.
@@ -1264,7 +1168,7 @@ def uniform_sampling_fromintv(intv_prod, shape, nodes_on_bdry=True): | |||
gmin.append(xmin + (xmax - xmin) / (2 * n)) | |||
gmax.append(xmax - (xmax - xmin) / (2 * n)) | |||
|
|||
return RegularGrid(gmin, gmax, shape) | |||
return uniform_grid(gmin, gmax, shape) | |||
|
|||
|
|||
def uniform_sampling(min_pt, max_pt, shape, nodes_on_bdry=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.
How does this compare to uniform_grid?
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 uses the interval product min/max, while the other uses the grid min/max directly. Subtle difference.
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.
Oh god you're right, but with nodes_on_bdry=True they are equal, no?
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.
Oh yes, of course. So perhaps one of them can go. I'll look into it.
[5.0], | ||
[2.0], | ||
min_pt=[-1.0, 1.0, 4.0, 2.0], max_pt=[-0.5, 3.0, 5.0, 3.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.
I dont get it, why did it change and how does it look now?
dpart_0 = odl.RectPartition(odl.IntervalProd(0, 0), odl.TensorGrid([0])) | ||
geom_p2d = odl.tomo.Parallel2dGeometry(apart, dpart=dpart_0) | ||
with pytest.raises(ValueError): | ||
odl.tomo.astra_projection_geometry(geom_p2d) |
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.
Well, perhaps we should change the grid then?
5ca2195
to
61a29db
Compare
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.
Minor comments, go ahead after.
""" | ||
|
||
from __future__ import absolute_import | ||
|
||
__version__ = '0.5.4.dev0' | ||
__all__ = ('diagnostics', 'discr', 'operator', 'set', 'space', 'solvers', | ||
'tomo', 'trafos', 'util', 'phantom', 'deform', 'ufunc_ops') | ||
__all__ = () |
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.
Are we sure all of them should be removed? How about stuff like util that is not default imported?
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 understand it, the imports further down put all the subpackages in the module dictionary, so that's not the purpose of adding them to __all__
anyway. The __all__
here is only effective if someone does from odl import *
, and then I guess we don't want the subpackages themselves to be included, only their __all__
contents. Or do we?
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.
Well if someone does from odl import all
I guess they should get something called util
? Otherwise they cant access everything. For subpackages that are included via import *
, we don't need to import them specifically.
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.
Hm, right. But that basically means that everything has to stay, since the same applies to the other subpackages. Currently you get everything from the subpackages' __all__
but the other stuff is not accessible, at least not directly.
That also means that we have to be careful not to include any module or package into __all__
that conflicts with another name somewhere in the library, or at least make sure that they're not imported into the same namespace.
the product of these lengths. This class makes use of that | ||
sparse storage scheme. | ||
|
||
See ``Notes`` for details. |
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.
Where did notes go?
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.
Edit: Into Init, i'd keep it at class level.
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.
But then in IDEs you get class docstring -> Notes -> Parameters, which IMO is not the best possible order. As I've argued earlier, and that's of course debatable, I think parameters should come up as quickly as possible, with all the extra stuff afterwards. That's why I moved the notes into __init__
after parameters and examples.
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.
Still not convinced that we should change the logical structure of the doc in order to please a specific IDE typesetting, other IDEs may do this "properly" (i.e. distinguish between a class and its initializer in a better way than spyder). Very few will read this anyway.
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.
To continue on the above, this is mostly dev doc and for developers those notes are very important, more so than the init doc.
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.
That may be true, but how often is that important compared to the parameter explanations?
The math notes are important to understand what this represents, and that's usually when you read the doc for the first time. After that, you perhaps occasionally check the math but not often.
On the other hand, the parameters are important on a day-to-day basis when you use the function or class in practice. Of course the signature is printed first (usually, sometimes it doesn't work but that's nothing to count in), but when there are kwargs
or if you need details about the parameters you need that section. And then at least I find it annoying if there are too many other things in the way that make me scroll every time I need info about the arguments.
So even from a dev perspective I feel that in the long run, parameters are more important than other details in the notes.
other IDEs may do this "properly" (i.e. distinguish between a class and its initializer in a better way than spyder)
Not sure what a better way would look like, or how other IDEs handle this. To me it seems reasonable to show the class docstring and the initialization doc if a user asks for documentation on a class.
The alternative would be to let the user choose, with pop-up menus like (apparently) in PyCharm. I personally find that annoying.
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.
Made a new issue #854.
# Uniformity, setting True in degenerate axes | ||
diffs = [np.diff(v) for v in self.coord_vectors] | ||
self.__is_uniform_byaxis = tuple( | ||
True if diff.size == 0 else np.allclose(diff, diff[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.
diff.size == 0 or np.allclose(diff, diff[0])
>>> rg = uniform_grid([-1.5, -1], [-0.5, 3], (2, 3)) | ||
>>> rg.stride | ||
array([ 1., 2.]) | ||
|
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.
remove blankline
np.all(self.max_pt <= other.max_pt + atol)): | ||
if not isinstance(other, RectGrid): | ||
return False | ||
if not(np.all(self.shape <= other.shape) and |
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.
little reason to use np.all for length ~3 arrays, more readable with simply all (and probably faster for this small case)
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's also wrong since the statement in parenthesis is already a boolean (compares tuples).
IntervalProd.squeeze | ||
""" | ||
newset = self.set[self.grid.inondeg] | ||
nondegen_indcs = np.where(self.grid.nondegen_byaxis)[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.
np.flatnonzero to skip the [0]
@@ -102,7 +101,7 @@ def ndim(self): | |||
@property | |||
def true_ndim(self): | |||
"""Number of non-degenerate (positive-length) intervals.""" | |||
return len(self.inondeg) | |||
return sum(self.nondegen_byaxis.astype(int)) |
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.
return np.count_nonzero(self.nondegen_byaxis)
@@ -431,7 +432,7 @@ def measure(self, ndim=None): | |||
elif ndim > self.true_ndim: | |||
return 0.0 | |||
else: | |||
return np.prod((self.max_pt - self.min_pt)[self.inondeg]) | |||
return np.prod(self.extent()[self.nondegen_byaxis]) |
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.
Remind me, why is extent not a property?
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.
Probably because the thought was that for some grid or domain structures in the future, this might be costly to compute. If we're sure that it's always gonna be cheap, we can make it a property (but then what to do with min
and max
which are methods, too?).
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.
min
and max
are methods in order to conform to the numpy interface, which allows these to be used with numpy in a duck-typing style. Otherwise we provide min_pt
etc which do the same.
I'll propose a change.
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.
Issue: #847
- Similarly uniform_sampling_fromintv->uniform_grid_fromintv - Remove old uniform_grid
61a29db
to
799f2e5
Compare
Closes #670.
Also some maintenance changes regarding the
ideg
andinondeg
properties, now replaced by a singlenondegen_byaxis
property that returns a full boolean array.