-
Notifications
You must be signed in to change notification settings - Fork 308
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
Add support for inserting new axes while slicing #570
Conversation
d075e2e
to
1e978ca
Compare
Exciting! I'll look closer later |
1e978ca
to
7e67f4a
Compare
I got to thinking – instead of having separate trait SliceArg {
// Number of dimensions that this slicing argument consumes in the input array.
type InDim: Dimension;
// Number of dimensions that this slicing argument produces in the output array.
type OutDim: Dimension;
fn next_in_dim<D>(&self, _: PhantomData<D>) -> PhantomData<D::Out>
where
D: Dimension + DimAdd<Self::InDim>,
{
PhantomData
}
fn next_out_dim<D>(&self, _: PhantomData<D>) -> PhantomData<D::Out>
where
D: Dimension + DimAdd<Self::OutDim>,
{
PhantomData
}
}
impl SliceArg for usize {
type InDim = Ix1;
type OutDim = Ix0;
}
impl SliceArg for NewAxis {
type InDim = Ix0;
type OutDim = Ix1;
}
impl SliceArg for Range<usize> {
type InDim = Ix1;
type OutDim = Ix1;
}
// ...
trait DimAdd<D: Dimension>: Dimension {
type Out: Dimension;
}
// ...
impl DimAdd<Ix2> for Ix3 {
type Out = Ix5;
}
// ...
impl DimAdd<IxDyn> for Ix3 {
type Out = IxDyn;
}
// ...
impl<D: Dimension> DimAdd<D> for IxDyn {
type Out = IxDyn;
}
// In s![] macro, to determine next input dimension use:
$crate::SliceArg::next_in_dim(&r, $in_dim)
// and to determine next output dimension use:
$crate::SliceArg::next_out_dim(&r, $out_dim) This has a couple of advantages:
|
@jturner314 DimAdd looks like a fine idea |
blas-tests/tests/oper.rs
Outdated
] | ||
}; | ||
|
||
let c = range_mat64(n, q).into_shape(cdim).unwrap(); | ||
|
||
{ | ||
let mut av = a.slice_mut(s![..;s1, ..;s2]); | ||
let c = c.slice(SliceInfo::<_, IxDyn>::new(cslice).unwrap().as_ref()); | ||
let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap()); |
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 needs to be fixed (in this PR or another), we can't design a library where SliceInfo::<_, IxDyn, IxDyn>::new(cslice).unwrap()
is a normal thing to say.
I don't keep up with IxDyn
, since I don't use it much.
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.
We could add
impl SliceInfo<Vec<AxisSliceInfo>, IxDyn, IxDyn> {
pub fn from_vec(indices: Vec<AxisSliceInfo>) -> Result<Self, ShapeError> {
SliceInfo::new(indices)
}
}
or
impl<T> SliceInfo<T, IxDyn, IxDyn>
where
T: AsRef<[AxisSliceInfo]>,
{
pub fn new_dyn(indices: T) -> Result<Self, ShapeError> {
SliceInfo::new(indices)
}
}
I don't really expect anyone to create a SliceInfo
instance directly, though, when they can just use the s![]
macro or call .slice_axis()
/.index_axis()
/.insert_axis()
on individual axes. Once we add support for Ellipsis
(i.e. ...
) in the s![]
macro, there are even fewer use cases for creating SliceInfo
directly.
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 just pushed a better solution. If we implement CanSlice<IxDyn>
for [AxisSliceInfo]
, then there's no need for the user to create a SliceInfo
instance here; they can just write &*cslice
. (Optionally, we could also implement CanSlice<IxDyn>
for Vec<AxisSliceInfo>
to avoid the need for the *
.)
We could also consider implementing CanSlice<IxDyn>
for [Slice]
and CanSlice<Dim<[usize; N]>>
for [Slice; N]
, although that would require modifying the CanSlice
trait somewhat (since it currently requires AsRef<[AxisSliceInfo]>
) and the slicing method implementations.
I do think this test is better written by slicing individual axes with .slice_axis*()
rather than combining the slicing information and calling .slice()
.
We have existing and new concepts and the dimensionality of each:
1D concepts are for a single axis and nD concepts pertain to the whole array. NewAxis already has the perfect name, so it's fine. We again need to bring some order to this and give them the best names. I'll come back to this. |
I'm sorry that I haven't come back to this, and the PR doesn't need to wait for me. |
4162aa5
to
da27047
Compare
I've rebased this off the latest The name |
6f441c8
to
c1afbbf
Compare
Rebased again to resolve conflict in |
Docs is a good reason to make CanSlice visible in the docs. Users depend on clicking on it to understand what it is. (MultiSlice should be visible by the same logic). Now, we don't have much of a module hierarchy in this crate. We're starting to miss it just to hide away lesser used things - suggestions welcome (with that said, I like the mostly-flat namespace). |
This isn't much more code and simplifies the logic somewhat.
This makes it visible in the docs, but the private marker trick prevents other crates from implementing it.
This makes it visible in the docs, but the private marker trick prevents other crates from implementing it.
This reduces how often an explicit `DimAdd` bound is necessary.
This reverts commit 3815462.
Thanks a lot 🎉 |
With this done, it's time to release 0.15. Just need to know if there are any last-minute cleanups needed or necessary. |
This is equivalent to NumPy's
np.newaxis
feature.A few notes:
CanSlice
trait has the additional benefit that.as_ref()
is no longer needed when slicing dynamic-dimensional arrays. (See Calling slice on a dynamic size array #501.)CanSlice
public because I don't see any reason (except for docs) to expose it._
instead of an expression that evaluates toNewAxis
to signal a new axis in thes![]
macro.Fixes #895