Skip to content

Update readme #277

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

Merged
merged 2 commits into from
May 11, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 98 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
# OffsetArrays.jl

[![][action-img]][action-url]
[![][pkgeval-img]][pkgeval-url]
[![][codecov-img]][codecov-url]
[![][docs-stable-img]][docs-stable-url]
[![][docs-dev-img]][docs-dev-url]
| Documentation (stable) | Documentation (dev) | Unit Tests | Code coverage | PkgEval |
| :-: | :-: | :-: | :-: | :-: |
| [![][docs-stable-img]][docs-stable-url] | [![][docs-dev-img]][docs-dev-url] | [![][action-img]][action-url] | [![][codecov-img]][codecov-url] | [![][pkgeval-img]][pkgeval-url] |

## Introduction

OffsetArrays provides Julia users with arrays that have arbitrary
indices, similar to those found in some other programming languages
like Fortran.

An `OffsetArray` is a lightweight wrapper around an `AbstractArray` that shifts its indices.
Generally, indexing into an `OffsetArray` should be as performant as the parent array.

## Usage

You can construct such arrays as follows:
There are two ways to construct `OffsetArray`s: by specifying the axes of the array, or
by specifying its origin.

The first way to construct an `OffsetArray` by specifying its axes is:

```julia
OA = OffsetArray(A, axis1, axis2, ...)
Expand All @@ -30,20 +35,101 @@ julia> A = Float64.(reshape(1:15, 3, 5))
2.0 5.0 8.0 11.0 14.0
3.0 6.0 9.0 12.0 15.0

julia> OA = OffsetArray(A, -1:1, 0:4) # OA will have axes (-1:1, 0:4)
julia> axes(A) # indices of a Matrix start from 1 along each axis
(Base.OneTo(3), Base.OneTo(5))

julia> OA = OffsetArray(A, -1:1, 0:4) # OA will have the axes (-1:1, 0:4)
3×5 OffsetArray(::Matrix{Float64}, -1:1, 0:4) with eltype Float64 with indices -1:1×0:4:
1.0 4.0 7.0 10.0 13.0
2.0 5.0 8.0 11.0 14.0
3.0 6.0 9.0 12.0 15.0

julia> OA = OffsetArray(A, CartesianIndex(-1, 0):CartesianIndex(1, 4))
3×5 OffsetArray(::Matrix{Float64}, -1:1, 0:4) with eltype Float64 with indices -1:1×0:4:
[...]
julia> OA[-1,0]
1.0

julia> OA[1,4]
15.0
```

The second way to construct an `OffsetArray` is by specifying the origin, that is, the first index
along each axis. This is particularly useful if one wants, eg., arrays that are 0-indexed as opposed
to 1-indexed.

A convenient way to construct an `OffsetArray` this way is by using `OffsetArrays.Origin`:

```julia
julia> using OffsetArrays: Origin

julia> Origin(0)(A) # indices begin at 0 along all axes
3×5 OffsetArray(::Matrix{Float64}, 0:2, 0:4) with eltype Float64 with indices 0:2×0:4:
1.0 4.0 7.0 10.0 13.0
2.0 5.0 8.0 11.0 14.0
3.0 6.0 9.0 12.0 15.0

julia> Origin(2, 3)(A) # indices begin at 2 along the first axis and 3 along the second
3×5 OffsetArray(::Matrix{Float64}, 2:4, 3:7) with eltype Float64 with indices 2:4×3:7:
1.0 4.0 7.0 10.0 13.0
2.0 5.0 8.0 11.0 14.0
3.0 6.0 9.0 12.0 15.0
```

While the examples here refer to the common case where the parent arrays have indices starting at 1,
this is not necessary. An `OffsetArray` may wrap any array that has integer indices, irrespective of
where the indices begin.

julia> OA[-1,0], OA[1,4]
(1.0, 15.0)
## How to go back to 1-indexed arrays

Certain libraries, such as `LinearAlgebra`, require arrays to be indexed from 1. Passing an `OffsetArray`
with shifted indices would lead to an error here.

```julia
julia> A = Float64.(reshape(1:16, 4, 4));

julia> AO = Origin(0)(A);

julia> using LinearAlgebra

julia> Diagonal(AO)
ERROR: ArgumentError: offset arrays are not supported but got an array with index other than 1
```

The way to obtain a `1`-indexed array from an `OffsetArray` is by using `OffsetArrays.no_offset_view`.

An example of this is:

```julia
julia> OffsetArrays.no_offset_view(AO)
4×4 Matrix{Float64}:
1.0 5.0 9.0 13.0
2.0 6.0 10.0 14.0
3.0 7.0 11.0 15.0
4.0 8.0 12.0 16.0
```

This may now be passed to `LinearAlgebra`:

```julia
julia> D = Diagonal(OffsetArrays.no_offset_view(AO))
4×4 Diagonal{Float64, Vector{Float64}}:
1.0 ⋅ ⋅ ⋅
⋅ 6.0 ⋅ ⋅
⋅ ⋅ 11.0 ⋅
⋅ ⋅ ⋅ 16.0
```

If we want to restore the original indices of `AO`, we may wrap an `OffsetArray` around the `Diagonal` as:

```julia
julia> Origin(AO)(D)
4×4 OffsetArray(::Diagonal{Float64, Vector{Float64}}, 0:3, 0:3) with eltype Float64 with indices 0:3×0:3:
1.0 ⋅ ⋅ ⋅
⋅ 6.0 ⋅ ⋅
⋅ ⋅ 11.0 ⋅
⋅ ⋅ ⋅ 16.0
```
Comment on lines +112 to +129
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the specific 1-based case, I somehow feel the approach in #248 is more useful.

D = no_offset_view_apply(AO) do A
    # do all the normal linear algebra thing in 1-based case
    Diagonal(A)
end

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It certainly seems useful, might be worth pushing it along.


Here, `Origin(AO)` is able to automatically infer and use the indices of `AO`.

<!-- badges -->

[pkgeval-img]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/O/OffsetArrays.svg
Expand Down