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

Cumulative map #506

Open
sebasv opened this issue Oct 23, 2018 · 1 comment
Open

Cumulative map #506

sebasv opened this issue Oct 23, 2018 · 1 comment

Comments

@sebasv
Copy link
Contributor

sebasv commented Oct 23, 2018

Like numpy.cumsum() or numpy.cumprod() but with map, mapv, map_inplace, map_into since unlike Numpy, in Rust we don't take a performance hit by supplying user functions. I imagine the signature to be something like

fn cum_mapv<F: Fn(T,T) -> T>(&self<T>, f: F) -> Self// T being the element type

// cumsum example:
let a = Array::linspace(0., 100., 100).cummapv(|acc, cur| acc + cur);

If this will be considered for inclusion, I can write up a PR!

@jturner314
Copy link
Member

I assume you're interested in summing over only one axis? If so, this seems reasonable:

fn accumulate_axis<F>(&self, axis: Axis, f: F) -> Array<A, D>
where
    A: Clone,
    F: FnMut(&A, &A) -> A,

This is similar to Python's itertools.accumulate. The changes from your proposal are adding an axis parameter, adding the A: Clone bound, changing the return type to Array, and changing F to take elements by reference to avoid unnecessary clones. It could be used basically the same way:

arr.accumulate_axis(Axis(1), ::std::ops::Add::add);
// or
arr.accumulate_axis(Axis(1), |acc, x| acc + x);

With this method, the first element of the result along each lane is always the clone of the first element of the input along that lane. I'm not quite sure what to call this method, but accumulate_axis seems pretty good.

Another related method would be this:

fn scan_axis<St, B, F>(&self, axis: Axis, initial_state: St, f: F) -> Array<B, D>
where
    F: FnMut(&mut St, &A) -> B,

This emulates std::iter::Iterator::scan. For a cumulative sum, it could be used like this:

arr.scan_axis(Axis(1), 0., |acc, &x| {
    *acc += x;
    *acc
});

It isn't quite as concise for computing a cumulative sum/product, but it is more flexible because the first element can be different between the input and result, and the element type can change.

I think both methods are useful and would be good additions to ndarray.

The tricky part of implementing these methods is initializing the result. (Ideally, the result array would be uninitialized initially. This requires unsafe code, and doing this without accidentally causing undefined behavior is tricky. Doing this correctly would be easier once #496 is merged.) For a first-pass implementation, though, we could sacrifice performance and use only safe code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants