Skip to content

Commit 405aa68

Browse files
committed
Update README.md
1 parent aba69ad commit 405aa68

File tree

1 file changed

+37
-6
lines changed

1 file changed

+37
-6
lines changed

README.md

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The primitives in this package support downstream packages such as [Einops.jl](h
1111

1212
## Motivation
1313

14-
Julia infamously struggles with "double wrappers", particularly on GPUs, triggering generic fallbacks that use scalar indexing. This can make users particularly wary when working with views and lazy permutations. In the case of `reshape`, the *structure* of the new shape relative to the old shape is completely neglected, and an array may for example become a reshape of a view:
14+
Julia infamously struggles with "double wrappers", particularly on GPUs, triggering generic fallbacks that use scalar indexing. This can make users wary when working with views and lazy permutations. In the case of `reshape`, the *structure* of the new shape relative to the old shape is completely neglected, and an array may for example become a reshape of a view:
1515

1616
```julia
1717
julia> x = rand(3, 4, 2);
@@ -25,7 +25,9 @@ julia> reshape(y, size(y, 1), :) isa Base.ReshapedArray
2525
true
2626
```
2727

28-
We use `size(y, 1)` in our reshape, but despite preserving the first dimension (the one dimension only partially sliced) it evaluates to an integer at runtime, and Julia has no way of knowing that it represents preserving the first dimension. The size could in theory be constant-propagated [if the size wasn't dynamic](https://github.com/JuliaArrays/FixedSizeArrays.jl), [or if the size is embedded in the type](https://github.com/JuliaArrays/StaticArrays.jl), but even then, integers alone are not useful once passed through `reshape`.
28+
We use `size(y, 1)` in our reshape, but despite preserving the first dimension (the one dimension only partially sliced) it evaluates to an integer at runtime, and Julia has no way of knowing that it represents preserving the first dimension.
29+
30+
The size could in theory be constant-propagated [if the size wasn't dynamic](https://github.com/JuliaArrays/FixedSizeArrays.jl), or [if the size is embedded in the type](https://github.com/JuliaArrays/StaticArrays.jl). But even then, integers alone are not useful once passed through `reshape`.
2931

3032
Rewrap provides types like `Keep`, `Merge`, and `Split` that encode reshape structure at compile-time, enabling rewrapping optimizations.
3133

@@ -74,8 +76,38 @@ The view is commuted past the reshape: the parent array gets reshaped first, the
7476

7577
## Features
7678

77-
- `..` (from [EllipsisNotation.jl](https://github.com/SciML/EllipsisNotation.jl)) is replaced with `Keep(..)` when passed to `reshape`.
78-
- `:` can be used like normal, but under the hood it gets replaced by `Merge(..)` when passed to `reshape`.
79+
### Local Reshape Operations
80+
81+
These operations are passed to `reshape` and consume/emit a specific number of dimensions:
82+
83+
| Operation | Description |
84+
|-----------|-------------|
85+
| `Keep(N)` | Keep `N` dimensions unchanged (default: 1). `..` becomes `Keep(..)`. |
86+
| `Merge(N)` | Merge `N` dimensions into one. `:` becomes `Merge(..)`. |
87+
| `Split(N, sizes)` | Split `N` dimensions into multiple, with sizes as a tuple of integers and at most one `:`. |
88+
| `Squeeze(N)` | Remove `N` singleton dimensions (default: 1). |
89+
| `Unsqueeze(M)` | Add `M` singleton dimensions (default: 1). |
90+
| `Resqueeze(N => M)` | Turn `N` singleton dimensions into `M` singleton dimensions. |
91+
92+
### Global Axis Operations
93+
94+
These are callable structs that transform arrays:
95+
96+
| Operation | Description |
97+
|-----------|-------------|
98+
| `Reshape(ops, x)` | Apply a tuple of local reshape operations to array `x`. |
99+
| `Permute(perm)` | Permute axes, unwrapping existing `PermutedDimsArray` when possible. |
100+
| `Reduce(f; dims)` | Reduce over dimensions, unwrapping lazy permutes when only reduced dims were permuted. |
101+
| `Repeat(repeats)` | Repeat array along dimensions, pushing through `PermutedDimsArray` to avoid scalar indexing. |
102+
103+
### Enhanced Base Functions
104+
105+
Rewrap also provides optimized versions of common operations:
106+
107+
- `Rewrap.reshape(x, ops...)` — reshape with full Rewrap semantics (no type piracy concerns)
108+
- `Base.reshape(x, ops...)` — reshape with Rewrap semantics (must include a `LocalReshape`)
109+
- `Rewrap.dropdims(x; dims)` — drop singleton dimensions while preserving wrapper types
110+
- `Rewrap.vec(x)` — flatten to vector, preserving views when possible
79111

80112
## Limitations
81113

@@ -86,7 +118,6 @@ julia> reshape(z, Keep(), :)
86118
1 3 5 13 15 17
87119
2 4 6 14 16 18
88120
```
89-
- Direct arguments of reshape can not be integers when an axis operation is present.
90121
- `..` and `:` alone won't use Rewrap.jl, as defining such methods would be type piracy. In these cases, `Keep(..)` and `Merge(..)` should be used instead.
91122

92123
## Installation
@@ -98,4 +129,4 @@ Pkg.add("Rewrap")
98129

99130
## Contributing
100131

101-
At the moment, Rewrap explicitly defines optimizations in big codegen monoliths for generated function specializations, making the source code hard to parse. Ideally it would use a more modular approach. If you have any ideas or suggestions, please feel free to open an issue or a pull request.
132+
Rewrap uses generated functions with compile-time type analysis to produce specialized code for each reshape pattern. We welcome ideas for making the implementation more modular please feel free to open an issue or pull request.

0 commit comments

Comments
 (0)