libmir/numir

Squeeze Does Not Match Numpy Functionality

jmh530 opened this issue · 4 comments

The unittest for squeeze

    assert(iota(1, 3, 4).slice.squeeze!0.shape == [3, 4]);
    assert(iota(3, 1, 4).slice.squeeze!1.shape == [3, 4]);
    assert(iota(3, 4, 1).slice.squeeze!(-1).shape == [3, 4]);

shows it being used with specific values for a.

The way Numpy's squeeze works is that it removes all 1-dimensional indices, or a subset.

This suggests that to match Numpy's functionality would require additional overloads, such as

auto squeeze(S)(S s) pure

that would remove all and

auto squeeze(Slice sl, S)(S s) pure

where sl is a slice indicative of a subset. Admittedly the first is more important than the second.

The first one is difficult to implement because there is a dynamic return type mismatch of packs as follows:

auto squeeze(S s) pure // if S.packs = [N]
{
    foreach (i, n; s.shape) {
        if (n == 1) {
            return squeeze(s, i).squeeze; // packs = [M] where M < N
        }
    }
    return s; // packs = [N]
}

but I think there is another way if you know the squeezed shape at compile time.

Slice!(k, squeezed, iterator) squeeze(size_t[] squeezed, SliceKind k, size_t[] packs, iterator)(Slice!(k, packs, iterator) s) pure;

// usage
auto a = iota(1, 2, 3);
auto b = squeeze!([2])(a);

in addition, this interface confuses with your second request.

@ShigekiKarita The tricky part is that it returns a view of a rather than returning a new slice, in which case you could just use reshape.

Perhaps @9il has any suggestions?

9il commented

Lets make squeeze accept only one CT parameter - the number of the result dimensions.
Algorithm should copy all non 1dimensions from back to front and fill remaining dimensions with 1 . Contiguous -> Contiguous, other -> Universal

squeeze in mir-algorithm has this functionality.