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

Update Docs #280

Merged
merged 97 commits into from
May 10, 2021
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
d01313d
Improve ColVecs / RowVecs docstrings
willtebbutt Apr 17, 2021
f6f8803
Explain AbstractVectors reasoning
willtebbutt Apr 17, 2021
c9885fb
Improve MOInput docstring
willtebbutt Apr 17, 2021
6800d50
Explain MOInput in docs in detail
willtebbutt Apr 17, 2021
1573663
Update userguide:
willtebbutt Apr 17, 2021
3c3c264
Move API docs around
willtebbutt Apr 17, 2021
24732d5
Update kernelmatrix docs
willtebbutt Apr 17, 2021
2cdb28e
Update README
willtebbutt Apr 17, 2021
8f19846
Tweak style in userguide
willtebbutt Apr 17, 2021
4a5cdf1
Remove blank space
willtebbutt Apr 17, 2021
67aaaa8
Mention reshape aesthetics
willtebbutt Apr 17, 2021
3754d21
Clarify API docs
willtebbutt Apr 17, 2021
ce6feed
Clarify API docs
willtebbutt Apr 17, 2021
2016c1b
Clarify API docs
willtebbutt Apr 17, 2021
f66b87d
Clarify API docs
willtebbutt Apr 17, 2021
bdf5314
Clarify API docs
willtebbutt Apr 17, 2021
838ae45
Clarify API docs
willtebbutt Apr 17, 2021
ed4a380
Clarify API docs
willtebbutt Apr 17, 2021
9d38667
Clarify API docs
willtebbutt Apr 17, 2021
ce52014
Clarify API docs
willtebbutt Apr 17, 2021
49a975c
Clarify API docs
willtebbutt Apr 17, 2021
5436c62
Clarify API docs
willtebbutt Apr 17, 2021
bf707e0
Clarify API docs
willtebbutt Apr 17, 2021
89af4a1
Clarify API docs
willtebbutt Apr 17, 2021
f31fbee
Clarify API docs
willtebbutt Apr 17, 2021
b2fe49d
Clarify API docs
willtebbutt Apr 17, 2021
b22d3ab
Clarify API docs
willtebbutt Apr 17, 2021
31583a4
Clarify API docs
willtebbutt Apr 17, 2021
9e996a5
Clarify API docs
willtebbutt Apr 17, 2021
25f81b4
Clarify API docs
willtebbutt Apr 17, 2021
cacbec9
Fix line lengths in docs
willtebbutt Apr 17, 2021
1b5a809
Fix line lengths in docs
willtebbutt Apr 17, 2021
dec2203
Fix line lengths in docs
willtebbutt Apr 17, 2021
6cb333e
readme style
willtebbutt Apr 18, 2021
ef4829d
readme style
willtebbutt Apr 18, 2021
400eb3e
Fix readme comment
willtebbutt Apr 18, 2021
f966503
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 18, 2021
d694970
Type annotations for kernelmatrix
willtebbutt Apr 18, 2021
96982ae
Type annotations for kernelmatrix_diag
willtebbutt Apr 18, 2021
da5cc78
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 18, 2021
46f006b
julia -> jldoctest
willtebbutt Apr 18, 2021
ab59d8c
Clarify API docs
willtebbutt Apr 18, 2021
747af25
Fix API typo
willtebbutt Apr 18, 2021
770b183
Fix API docs grammar
willtebbutt Apr 18, 2021
02f23be
Vector -> AbstractVector in API docs
willtebbutt Apr 18, 2021
dbf5636
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 18, 2021
69c3544
Remove redundant whitespace
willtebbutt Apr 18, 2021
5d7b02d
Format userguide for consistency
willtebbutt Apr 18, 2021
d4573fc
Fix userguide grammar
willtebbutt Apr 18, 2021
5d52ca6
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 18, 2021
84932aa
Mention colprac in contributing section
willtebbutt Apr 18, 2021
913d76d
Fix typo in api docs
willtebbutt Apr 18, 2021
e004883
Reference Alvarez review paper
willtebbutt Apr 18, 2021
e8e5b58
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 18, 2021
515df7b
Fix API docs typo
willtebbutt Apr 18, 2021
c8db3e3
Add headings to input types section
willtebbutt Apr 18, 2021
d164d6d
Add some more context to API docs
willtebbutt Apr 18, 2021
69f6a3d
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 18, 2021
89c134a
Move nystrom and kernelpdmat to utils
willtebbutt Apr 18, 2021
bb54876
Comment on utilities
willtebbutt Apr 18, 2021
0f17fa5
Re-generate kernel heatmap
willtebbutt Apr 18, 2021
cd13ee2
Finish off MOInput docstring
willtebbutt Apr 18, 2021
8113ea2
Tidy up kernelmatrix docstring style
willtebbutt Apr 18, 2021
ccf3a0d
transform -> composition
willtebbutt Apr 18, 2021
214a2a3
Simplify README example
willtebbutt Apr 18, 2021
58fd7dd
Add filter to doctests
willtebbutt Apr 18, 2021
52e19b7
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 18, 2021
dce7283
Fix formatting
willtebbutt Apr 18, 2021
a4ea373
Add filter to doctests
Apr 19, 2021
1f5895b
Uncomment tests
Apr 19, 2021
2062dda
Fix grammar
willtebbutt Apr 20, 2021
742794c
Clarify MO kernel explanation
willtebbutt Apr 20, 2021
5ecfbdb
Clarify reference
willtebbutt Apr 20, 2021
5969a98
Grammar
willtebbutt Apr 20, 2021
3502a91
Grammar
willtebbutt Apr 20, 2021
2188e98
Phrasing
willtebbutt Apr 20, 2021
a0b95fa
Typo
willtebbutt Apr 20, 2021
9c4c3da
Title Case
willtebbutt Apr 20, 2021
0deb047
Typo
willtebbutt Apr 20, 2021
c869610
Add Reference
willtebbutt Apr 20, 2021
442488b
Typo
willtebbutt Apr 20, 2021
0d444b5
Typo
willtebbutt Apr 20, 2021
60f4b6f
Typo
willtebbutt Apr 20, 2021
74f04b8
Typo
willtebbutt Apr 20, 2021
4f9d11f
Typo
willtebbutt Apr 20, 2021
d5ed728
Spacing
willtebbutt Apr 20, 2021
2e932e3
Grammar
willtebbutt Apr 20, 2021
ac576cd
Title Case
willtebbutt Apr 20, 2021
6ce271d
Consistently use title case in docs
willtebbutt Apr 20, 2021
46386c6
Add note on compose
willtebbutt Apr 20, 2021
bacdca6
Fix typo in docstring
willtebbutt Apr 20, 2021
4a450c6
Link in MOInput docstring
willtebbutt Apr 20, 2021
9548024
Merge branch 'wct/docs-update' of https://github.com/JuliaGaussianPro…
willtebbutt Apr 20, 2021
5a429cf
Fix broken links
willtebbutt May 1, 2021
235f5ac
Move out abstract vector explanation
willtebbutt May 1, 2021
09dd5f9
Design docs
willtebbutt May 1, 2021
04316c8
Design on sidebar
willtebbutt May 7, 2021
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
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,28 @@ The aim is to make the API as model-agnostic as possible while still being user-
## Examples

```julia
X = reshape(collect(range(-3.0,3.0,length=100)),:,1)
# Set simple scaling of the data
k₁ = SqExponentialKernel()
K₁ = kernelmatrix(k₁,X,obsdim=1)
x = range(-3.0, 3.0; length=100)

# Set a function transformation on the data
k₂ = TransformedKernel(Matern32Kernel(),FunctionTransform(x->sin.(x)))
K₂ = kernelmatrix(k₂,X,obsdim=1)
# A simple standardised squared-exponential / exponentiated-quadratic kernel.
k₁ = SqExponentialKernel()
K₁ = kernelmatrix(k₁, x)

# Set a matrix premultiplication on the data
k₃ = transform(PolynomialKernel(c=2.0,d=2.0),LowRankTransform(randn(4,1)))
K₃ = kernelmatrix(k₃,X,obsdim=1)
# Set a function transformation on the data
k₂ = Matern32Kernel() FunctionTransform(sin)
K₂ = kernelmatrix(k₂, x)

# Add and sum kernels
k₄ = 0.5*SqExponentialKernel()*LinearKernel(c=0.5) + 0.4*k₂
K₄ = kernelmatrix(k₄,X,obsdim=1)
# Set a matrix premultiplication on the data
k₃ = PolynomialKernel(; c=2.0, degree=2) LinearTransform(randn(4, 1))
K₃ = kernelmatrix(k₃, x)

plot(heatmap.([K₁,K₂,K₃,K₄],yflip=true,colorbar=false)...,layout=(2,2),title=["K₁" "K₂" "K₃" "K₄"])
# Add and sum kernels
k₄ = 0.5 * SqExponentialKernel() * LinearKernel(; c=0.5) + 0.4 * k₂
K₄ = kernelmatrix(k₄, x)

plot(
heatmap.([K₁, K₂, K₃, K₄]; yflip=true, colorbar=false)...;
layout=(2, 2), title=["K₁" "K₂" "K₃" "K₄"],
)
```
<p align=center>
<img src="docs/src/assets/heatmap_combination.png" width=400px>
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -43,10 +47,9 @@ The aim is to make the API as model-agnostic as possible while still being user-
## Packages goals (by priority)
- Ensure AD Compatibility (already the case for Zygote, ForwardDiff)
- Toeplitz Matrices compatibility
- BLAS backend

Directly inspired by the [MLKernels](https://github.com/trthatcher/MLKernels.jl) package.

## Issues/Contributing

If you notice a problem or would like to contribute by adding more kernel functions or features please [submit an issue](https://github.com/JuliaGaussianProcesses/KernelFunctions.jl/issues).
If you notice a problem or would like to contribute by adding more kernel functions or features please [submit an issue](https://github.com/JuliaGaussianProcesses/KernelFunctions.jl/issues), or open a PR (please see the [ColPrac](https://github.com/SciML/ColPrac) contribution guidelines).
5 changes: 5 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ makedocs(;
],
strict=true,
checkdocs=:exports,
doctestfilters = [
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
r"{([a-zA-Z0-9]+,\s?)+[a-zA-Z0-9]+}",
r"(Array{[a-zA-Z0-9]+,\s?1}|Vector{[a-zA-Z0-9]+})",
r"(Array{[a-zA-Z0-9]+,\s?2}|Matrix{[a-zA-Z0-9]+})",
],
)

deploydocs(;
Expand Down
223 changes: 218 additions & 5 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,236 @@ Pages = ["api.md"]
CurrentModule = KernelFunctions
```

## Input Types

All collections of inputs in KernelFunctions.jl are represented as `AbstractVector`s.
The length of any such `AbstractVector` is equal to the number of inputs in the collection.
For example, this means that
```julia
size(kernelmatrix(k, x)) == (length(x), length(x))
```
is always true, for some `Kernel` `k`, and `AbstractVector` `x`.

### Univariate Inputs

If each input to your kernel is `Real`-valued, then any `AbstractVector{<:Real}` is a valid
representation for a collection of inputs.
More generally, it's completely fine to represent a collection of inputs of type `T` as, for
example, a `Vector{T}`.
However, this may not be the most efficient way to represent collection of inputs.
See [Vector-Valued Inputs](@ref) for an example.

### Vector-Valued Inputs

We recommend that collections of vector-valued inputs are stored in an
`AbstractMatrix{<:Real}` when possible, and wrapped inside a `ColVecs` or `RowVecs` to make
their interpretation clear:
```@docs
ColVecs
RowVecs
```
These types are specialised upon when e.g. computing Euclidean distances between pairs of elements to ensure good performance.
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
The benefit of using this representation, rather a `Vector{Vector{<:Real}}`, is that
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
optimised matrix-matrix multiplication functionality can be utilised when computing
pairwise distances between inputs, which are needed for `kernelmatrix` computation.

### Inputs for Multiple Outputs

There are two equally-valid perspectives on multi-output kernels: they can either be treated
as matrix-valued kernels, or standard kernels on an extended input domain.
Each of these perspectives are convenient in different circumstances, but the latter
greatly simplifies the incorporation of multi-output kernels in KernelFunctions.

More concretely, let `k_mat` be a matrix-valued kernel, mapping pairs of inputs of type `T` to matrices of size `P x P`.
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
Given inputs `x` and `y` of type `T`, and integers `p` and `q`, we can always find an
equivalent standard kernel `k` mapping from pairs of inputs of type `Tuple{T, Int}` to the
`Real`s as follows:
```julia
k((x, p), (y, q)) = k_mat(x, y)[p, q]
```
This ability to treat multi-output kernels as single-output kernels is very helpful, as it
means that there is no need to introduce additional concepts into the API of
KernelFunctions.jl, just additional kernels!
This in turn simplifies downstream code as they don't need to "know" about the existence of
multi-output kernels in addition to standard kernels. For example, GP libraries built on
top of KernelFunctions.jl just need to know about `Kernel`s, and they get multi-output
kernels, and hence multi-output GPs, for free.

Where there is the need to specialise _implementations_ for multi-output kernels, this is
done in an encapsulated manner -- parts of KernelFunctions that have nothing to do with
multi-output kernels know _nothing_ about the existence of multi-output kernels.

Multi-output kernels in KernelFunctions.jl do support collection of inputs of
type `AbstractVector{Tuple{T, Int}}`, we provide the `MOInput` type to simplify constructing inputs
for situations in which all outputs are observed all of the time:
Copy link
Member

Choose a reason for hiding this comment

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

I didn't quite get what you're trying to say here, could you explain so we can iterate on it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, yeah, this really isn't clear.

Perhaps my intent is clearer in the following:

A collection of inputs to a multi-output kernel must be a subtype of `AbstractVector{Tuple{T, Int}}`.
For example,
```julia
[(5.0, 2), (4.0, 3)]

which contains the 2nd output at location 5.0 and 3rd output at location 4.0.
We provide the MOInput type to simplify constructing collections of inputs comprising each output at each location:

In conjunction with the docstring, maybe this is clearer?

I wonder whether we need better terminology. I'm trying to refer to both a tuple `(x, p)` as an input, and `x` itself as an input. I keep wanting to write things like "a collection of inputs where each output is observed at each input", where "inputs" corresponds to a collection of tuples, and "input" refers to the first element of each tuple 😬 . Any thoughts?

```@docs
MOInput
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
```
As with [`ColVecs`](@ref) and [`RowVecs`](@ref) for vector-valued input spaces, this
type enables specialised implementations of e.g. [`kernelmatrix`](@ref) for
[`MOInput`](@ref)s.

See [here](https://arxiv.org/pdf/1106.6251.pdf) for a review of kernels for vector-valued
functions.
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved


## Why AbstractVectors Everywhere?

To understand the advantages of using `AbstractVector`s everywhere to represent collections of inputs, first consider the following properties that it is desirable for a collection of inputs to satisfy.

#### Unique Ordering

There must be a clearly-defined first, second, etc element of an input collection.
If this were not the case, it would not be possible to determine a unique mapping between a collection of inputs and the output of `kernelmatrix`, as it would not be clear what order the rows and columns of the output should appear in.

Moreover, ordering guarantees that if you permute the collection of inputs, the ordering of the rows and columns of the `kernelmatrix` are correspondingly permuted.

#### Generality

There must be no restriction on the domain of the input.
Collections of `Real`s, vectors, graphs, finite-dimensional domains, or really anything else that you fancy should be straightforwardly representable.
Moreover, whichever input class is chosen should not prevent optimal performance from being obtained.

#### Unambiguously-Defined Length

Knowing the length of a collection of inputs is important.
For example, a well-defined length guarantees that the size of the output of `kernelmatrix`,
and related functions, are predictable.
It also makes it possible to perform internal error-checking that ensures that e.g. there
are the same number of inputs in two collections of inputs.



### AbstractMatrices do not cut it

Notably, while `AbstractMatrix`s are often used to represent collections of vector-valued
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
inputs, they do _not_ immediately satisfy these properties as it is unclear whether a matrix
of size `P x Q` represents a collection of `P` `Q`-dimensional inputs (each row is an
input), or `Q` `P`-dimensional inputs (each column is an input).

Moreover, they occassionally add some aesthetic inconvenience.
For example, a collection of `Real`-valued inputs which might be straightforwardly
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
represented as an `AbstractVector{<:Real}`, must be reshaped into a matrix.

Below we discuss a couple of things that are often done that partly resolve these shortcomings.
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved

#### Resolution 1: Specify a convention
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved

One way that these shortcomings can be partly resolved is by specifying a convention that
everyone adheres to regarding the interpretation of rows vs columns.
However, opinions about the choice of convention are often surprisingly strongly held, and
users regularly have to remind themselves _which_ convention has been chosen.
While this resolves the ordering problem, and in principle defines the "length" of a
collection of inputs, `AbstractMatrix`s already have a `length` defined in Julia, which
would generally disagree with our internal notion of `length`.
This isn't a show-stopper, but it isn't an especially clean situation.

There is also the opportunity for some kinds of silent bugs.
For example, if an input matrix happens to be square because the number of input dimensions
is the same as the number of inputs, it would be hard to know whether the correct
`kernelmatrix` has been computed.
This kind of bug seems unlikely, but it exists regardless.

Finally, suppose that your inputs are some type `T` that is not simply a vector of real
numbers, say a graph.
In this situation, how should a collection of inputs be represented?
A `N x 1` or `1 x N` matrix is the only obvious candidate, but the addition singular
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
dimension seems somewhat redundant.

#### Resolution 2: Always specify an `obsdim` argument

Another way to partly resolve these problems is to not commit to a convention, and instead
to propagate some additional information through the codebase that specifies how the input
data is to be interpretted.
For example, a kernel `k` that represents the sum of two other kernels might implement
`kernelmatrix` as follows:
```julia
function kernelmatrix(k::KernelSum, x::AbstractMatrix; obsdim=1)
return kernelmatrix(k.kernels[1], x; obsdim=obsdim) +
kernelmatrix(k.kernels[2], x; obsdim=obsdim)
end
```
While this prevents this package from having to pre-specify a convention, it doesn't resolve
the `length` issue, or the issue of representing collections of inputs which aren't
immediately represented as vectors.
Moreover, it complicates the internals; in contrast, consider what this function looks like
with an `AbstractVector`:
```julia
function kernelmatrix(k::KernelSum, x::AbstractVector)
return kernelmatrix(k.kernels[1], x) + kernelmatrix(k.kernels[2], x)
end
```
This code is clearer (less visual noise), and has removed a possible bug -- if the
implementer of `kernelmatrix` forgets to pass the `obsdim` kwarg into each subsequent
`kernelmatrix` call, it's possible to get the wrong answer.

willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
This being said, we do support matrix-valued inputs -- see
[Why we have support for both](@ref).


### AbstractVectors

Requiring all collections of inputs to be `AbstractVector`s resolves all of these problems,
and ensures that the data is self-describing to the extent that KernelFunctions.jl requires.

Firstly, the question of how to interpret the columns and rows of a matrix of inputs is
resolved.
Users _must_ wrap matrix which represent collections of inputs in either a `ColVecs` or
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
`RowVecs`, both of which have clearly defined semantics which are hard to confuse.

By design, there is also no discrepancy between the number of inputs in the collection, and
the `length` function -- the `length` of a `ColVecs`, `RowVecs`, or `Vector{<:Real}` is
equal to the number of inputs.

There is no loss of performance.

A collection of `N` `Real`-valued inputs can be represented by an
`AbstractVector{<:Real}` of `length` `N`, rather than needing to use an
`AbstractMatrix{<:Real}` of size either `N x 1` or `1 x N`.
The same can be said for any other input type `T`, and new subtypes of `AbstractVector` can
be added if particularly efficient ways exist to store collections of inputs of type `T`.
A good example of this in practice using `Tuple{S, Int}`, for some input type `S`, as the
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
input type for "multi-output" GPs.
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved

This approach can also lead to clearer user code.
A user need only wrap their inputs in a `ColVecs` or `RowVecs` once in their code, and this
specification is automatically re-used _everywhere_ in their code.
In this sense, it is straightforward to write code in such a way that there is one unique
source of "truth" about the way in which a particular data set should be interpretted.
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
Conversely, the `obsdim` resolution requires that the `obsdim` keyword argument is passed
around with the data _every_ _single_ _time_ that you use it.

The benefits of the `AbstractVector` approach are likely most strongly felt when writing a substantial amount of code on top of KernelFunctions -- in the same way that using
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
`AbstractVector`s inside KernelFunctions.jl removes the need for large amounts of keyword
argument propagation, the same will be true of other code.




### Why we have support for both
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved

In short: many people like matrices, and are familiar with `obsdim`-style keyword
arguments.

All internals are implemented using `AbstractVector`s though, and the `obsdim` interface
is just a thin layer of utility functionality which sits on top of this.

## Functions

The KernelFunctions API comprises the following four functions.
```@docs
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
kernelmatrix
kernelmatrix!
kernelmatrix_diag
kernelmatrix_diag!
kernelpdmat
nystrom
```

## Utilities

KernelFunctions also provides some utility functions.
```@docs
ColVecs
RowVecs
MOInput
kernelpdmat
nystrom
NystromFact
```

Expand Down
Binary file modified docs/src/assets/heatmap_combination.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading