Skip to content

Commit df67ab7

Browse files
willtebbuttdevmotiontheogfgithub-actions[bot]st--
authored
Update Docs (#280)
* Improve ColVecs / RowVecs docstrings * Explain AbstractVectors reasoning * Improve MOInput docstring * Explain MOInput in docs in detail * Update userguide: * Move API docs around * Update kernelmatrix docs * Update README * Tweak style in userguide * Remove blank space * Mention reshape aesthetics * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Clarify API docs * Fix line lengths in docs * Fix line lengths in docs * Fix line lengths in docs * readme style Co-authored-by: David Widmann <[email protected]> * readme style Co-authored-by: David Widmann <[email protected]> * Fix readme comment * Type annotations for kernelmatrix Co-authored-by: David Widmann <[email protected]> * Type annotations for kernelmatrix_diag Co-authored-by: David Widmann <[email protected]> * julia -> jldoctest Co-authored-by: David Widmann <[email protected]> * Clarify API docs * Fix API typo Co-authored-by: David Widmann <[email protected]> * Fix API docs grammar Co-authored-by: David Widmann <[email protected]> * Vector -> AbstractVector in API docs Co-authored-by: David Widmann <[email protected]> * Remove redundant whitespace * Format userguide for consistency * Fix userguide grammar Co-authored-by: David Widmann <[email protected]> * Mention colprac in contributing section * Fix typo in api docs Co-authored-by: Théo Galy-Fajou <[email protected]> * Reference Alvarez review paper * Fix API docs typo Co-authored-by: Théo Galy-Fajou <[email protected]> * Add headings to input types section * Add some more context to API docs * Move nystrom and kernelpdmat to utils * Comment on utilities * Re-generate kernel heatmap * Finish off MOInput docstring * Tidy up kernelmatrix docstring style * transform -> composition * Simplify README example Co-authored-by: David Widmann <[email protected]> * Add filter to doctests * Fix formatting Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Add filter to doctests * Uncomment tests * Fix grammar Co-authored-by: st-- <[email protected]> * Clarify MO kernel explanation Co-authored-by: st-- <[email protected]> * Clarify reference Co-authored-by: st-- <[email protected]> * Grammar Co-authored-by: st-- <[email protected]> * Grammar Co-authored-by: st-- <[email protected]> * Phrasing Co-authored-by: st-- <[email protected]> * Typo Co-authored-by: st-- <[email protected]> * Title Case Co-authored-by: st-- <[email protected]> * Typo Co-authored-by: st-- <[email protected]> * Add Reference Co-authored-by: st-- <[email protected]> * Typo Co-authored-by: st-- <[email protected]> * Typo Co-authored-by: st-- <[email protected]> * Typo Co-authored-by: st-- <[email protected]> * Typo Co-authored-by: st-- <[email protected]> * Typo Co-authored-by: st-- <[email protected]> * Spacing Co-authored-by: st-- <[email protected]> * Grammar Co-authored-by: st-- <[email protected]> * Title Case Co-authored-by: st-- <[email protected]> * Consistently use title case in docs * Add note on compose * Fix typo in docstring Co-authored-by: st-- <[email protected]> * Link in MOInput docstring * Fix broken links * Move out abstract vector explanation * Design docs * Design on sidebar Co-authored-by: David Widmann <[email protected]> Co-authored-by: Théo Galy-Fajou <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: st-- <[email protected]>
1 parent a99a3e7 commit df67ab7

File tree

10 files changed

+456
-86
lines changed

10 files changed

+456
-86
lines changed

README.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,28 @@ The aim is to make the API as model-agnostic as possible while still being user-
1717
## Examples
1818

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

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

29-
# Set a matrix premultiplication on the data
30-
k₃ = transform(PolynomialKernel(c=2.0,d=2.0),LowRankTransform(randn(4,1)))
31-
K₃ = kernelmatrix(k₃,X,obsdim=1)
26+
# Set a function transformation on the data
27+
k₂ = Matern32Kernel() FunctionTransform(sin)
28+
K₂ = kernelmatrix(k₂, x)
3229

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

37-
plot(heatmap.([K₁,K₂,K₃,K₄],yflip=true,colorbar=false)...,layout=(2,2),title=["K₁" "K₂" "K₃" "K₄"])
34+
# Add and sum kernels
35+
k₄ = 0.5 * SqExponentialKernel() * LinearKernel(; c=0.5) + 0.4 * k₂
36+
K₄ = kernelmatrix(k₄, x)
37+
38+
plot(
39+
heatmap.([K₁, K₂, K₃, K₄]; yflip=true, colorbar=false)...;
40+
layout=(2, 2), title=["K₁" "K₂" "K₃" "K₄"],
41+
)
3842
```
3943
<p align=center>
4044
<img src="docs/src/assets/heatmap_combination.png" width=400px>
@@ -43,10 +47,9 @@ The aim is to make the API as model-agnostic as possible while still being user-
4347
## Packages goals (by priority)
4448
- Ensure AD Compatibility (already the case for Zygote, ForwardDiff)
4549
- Toeplitz Matrices compatibility
46-
- BLAS backend
4750

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

5053
## Issues/Contributing
5154

52-
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).
55+
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).

docs/make.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,15 @@ makedocs(;
3333
"create_kernel.md",
3434
"API" => "api.md",
3535
"Examples" => "example.md",
36+
"Design" => "design.md",
3637
],
3738
strict=true,
3839
checkdocs=:exports,
40+
doctestfilters=[
41+
r"{([a-zA-Z0-9]+,\s?)+[a-zA-Z0-9]+}",
42+
r"(Array{[a-zA-Z0-9]+,\s?1}|Vector{[a-zA-Z0-9]+})",
43+
r"(Array{[a-zA-Z0-9]+,\s?2}|Matrix{[a-zA-Z0-9]+})",
44+
],
3945
)
4046

4147
deploydocs(;

docs/src/api.md

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,78 @@
11
# API Library
22

3-
---
4-
```@contents
5-
Pages = ["api.md"]
6-
```
7-
83
```@meta
94
CurrentModule = KernelFunctions
105
```
116

127
## Functions
138

9+
The KernelFunctions API comprises the following four functions.
1410
```@docs
1511
kernelmatrix
1612
kernelmatrix!
1713
kernelmatrix_diag
1814
kernelmatrix_diag!
19-
kernelpdmat
20-
nystrom
2115
```
2216

23-
## Utilities
17+
## Input Types
18+
19+
The above API operates on collections of inputs.
20+
All collections of inputs in KernelFunctions.jl are represented as `AbstractVector`s.
21+
To understand this choice, please see the [design notes on collections of inputs](@ref why_abstract_vectors).
22+
The length of any such `AbstractVector` is equal to the number of inputs in the collection.
23+
For example, this means that
24+
```julia
25+
size(kernelmatrix(k, x)) == (length(x), length(x))
26+
```
27+
is always true, for some `Kernel` `k`, and `AbstractVector` `x`.
28+
29+
### Univariate Inputs
30+
31+
If each input to your kernel is `Real`-valued, then any `AbstractVector{<:Real}` is a valid
32+
representation for a collection of inputs.
33+
More generally, it's completely fine to represent a collection of inputs of type `T` as, for
34+
example, a `Vector{T}`.
35+
However, this may not be the most efficient way to represent collection of inputs.
36+
See [Vector-Valued Inputs](@ref) for an example.
2437

38+
39+
### Vector-Valued Inputs
40+
41+
We recommend that collections of vector-valued inputs are stored in an
42+
`AbstractMatrix{<:Real}` when possible, and wrapped inside a `ColVecs` or `RowVecs` to make
43+
their interpretation clear:
2544
```@docs
2645
ColVecs
2746
RowVecs
47+
```
48+
These types are specialised upon to ensure good performance e.g. when computing Euclidean distances between pairs of elements.
49+
The benefit of using this representation, rather than using a `Vector{Vector{<:Real}}`, is that
50+
optimised matrix-matrix multiplication functionality can be utilised when computing
51+
pairwise distances between inputs, which are needed for `kernelmatrix` computation.
52+
53+
### Inputs for Multiple Outputs
54+
55+
KernelFunctions.jl views multi-output GPs as GPs on an extended input domain.
56+
For an explanation of this design choice, see [the design notes on multi-output GPs](@ref inputs_for_multiple_outputs).
57+
58+
An input to a multi-output `Kernel` should be a `Tuple{T, Int}`, whose first element specifies a location in the domain of the multi-output GP, and whose second element specifies which output the inputs corresponds to.
59+
The type of collections of inputs for multi-output GPs is therefore `AbstractVector{<:Tuple{T, Int}}`.
60+
61+
KernelFunctions.jl provides the following type or situations in which all outputs are observed all of the time:
62+
```@docs
2863
MOInput
29-
NystromFact
3064
```
65+
As with [`ColVecs`](@ref) and [`RowVecs`](@ref) for vector-valued input spaces, this
66+
type enables specialised implementations of e.g. [`kernelmatrix`](@ref) for
67+
[`MOInput`](@ref)s in some situations.
3168

32-
## Index
69+
To find out more about the background, read this [review of kernels for vector-valued functions](https://arxiv.org/pdf/1106.6251.pdf).
3370

34-
```@index
35-
Pages = ["api.md"]
36-
Module = ["KernelFunctions"]
37-
Order = [:type, :function]
71+
## Utilities
72+
73+
KernelFunctions also provides miscellaneous utility functions.
74+
```@docs
75+
kernelpdmat
76+
nystrom
77+
NystromFact
3878
```
-5.12 KB
Loading

docs/src/design.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Design
2+
3+
## [Why AbstractVectors Everywhere?](@id why_abstract_vectors)
4+
5+
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.
6+
7+
#### Unique Ordering
8+
9+
There must be a clearly-defined first, second, etc element of an input collection.
10+
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.
11+
12+
Moreover, ordering guarantees that if you permute the collection of inputs, the ordering of the rows and columns of the `kernelmatrix` are correspondingly permuted.
13+
14+
#### Generality
15+
16+
There must be no restriction on the domain of the input.
17+
Collections of `Real`s, vectors, graphs, finite-dimensional domains, or really anything else that you fancy should be straightforwardly representable.
18+
Moreover, whichever input class is chosen should not prevent optimal performance from being obtained.
19+
20+
#### Unambiguously-Defined Length
21+
22+
Knowing the length of a collection of inputs is important.
23+
For example, a well-defined length guarantees that the size of the output of `kernelmatrix`,
24+
and related functions, are predictable.
25+
It also makes it possible to perform internal error-checking that ensures that e.g. there
26+
are the same number of inputs in two collections of inputs.
27+
28+
29+
30+
### AbstractMatrices Do Not Cut It
31+
32+
Notably, while `AbstractMatrix` objects are often used to represent collections of vector-valued
33+
inputs, they do _not_ immediately satisfy these properties as it is unclear whether a matrix
34+
of size `P x Q` represents a collection of `P` `Q`-dimensional inputs (each row is an
35+
input), or `Q` `P`-dimensional inputs (each column is an input).
36+
37+
Moreover, they occassionally add some aesthetic inconvenience.
38+
For example, a collection of `Real`-valued inputs, which might be straightforwardly
39+
represented as an `AbstractVector{<:Real}`, must be reshaped into a matrix.
40+
41+
There are two commonly used ways to partly resolve these shortcomings:
42+
43+
#### Resolution 1: Specify a Convention
44+
45+
One way that these shortcomings can be partly resolved is by specifying a convention that
46+
everyone adheres to regarding the interpretation of rows vs columns.
47+
However, opinions about the choice of convention are often surprisingly strongly held, and
48+
users regularly have to remind themselves _which_ convention has been chosen.
49+
While this resolves the ordering problem, and in principle defines the "length" of a
50+
collection of inputs, `AbstractMatrix`s already have a `length` defined in Julia, which
51+
would generally disagree with our internal notion of `length`.
52+
This isn't a show-stopper, but it isn't an especially clean situation.
53+
54+
There is also the opportunity for some kinds of silent bugs.
55+
For example, if an input matrix happens to be square because the number of input dimensions
56+
is the same as the number of inputs, it would be hard to know whether the correct
57+
`kernelmatrix` has been computed.
58+
This kind of bug seems unlikely, but it exists regardless.
59+
60+
Finally, suppose that your inputs are some type `T` that is not simply a vector of real
61+
numbers, say a graph.
62+
In this situation, how should a collection of inputs be represented?
63+
A `N x 1` or `1 x N` matrix is the only obvious candidate, but the additional singular
64+
dimension seems somewhat redundant.
65+
66+
#### Resolution 2: Always Specify An `obsdim` Argument
67+
68+
Another way to partly resolve these problems is to not commit to a convention, and instead
69+
to propagate some additional information through the codebase that specifies how the input
70+
data is to be interpretted.
71+
For example, a kernel `k` that represents the sum of two other kernels might implement
72+
`kernelmatrix` as follows:
73+
```julia
74+
function kernelmatrix(k::KernelSum, x::AbstractMatrix; obsdim=1)
75+
return kernelmatrix(k.kernels[1], x; obsdim=obsdim) +
76+
kernelmatrix(k.kernels[2], x; obsdim=obsdim)
77+
end
78+
```
79+
While this prevents this package from having to pre-specify a convention, it doesn't resolve
80+
the `length` issue, or the issue of representing collections of inputs which aren't
81+
immediately represented as vectors.
82+
Moreover, it complicates the internals; in contrast, consider what this function looks like
83+
with an `AbstractVector`:
84+
```julia
85+
function kernelmatrix(k::KernelSum, x::AbstractVector)
86+
return kernelmatrix(k.kernels[1], x) + kernelmatrix(k.kernels[2], x)
87+
end
88+
```
89+
This code is clearer (less visual noise), and has removed a possible bug -- if the
90+
implementer of `kernelmatrix` forgets to pass the `obsdim` kwarg into each subsequent
91+
`kernelmatrix` call, it's possible to get the wrong answer.
92+
93+
This being said, we do support matrix-valued inputs -- see
94+
[Why We Have Support for Both](@ref).
95+
96+
97+
### AbstractVectors
98+
99+
Requiring all collections of inputs to be `AbstractVector`s resolves all of these problems,
100+
and ensures that the data is self-describing to the extent that KernelFunctions.jl requires.
101+
102+
Firstly, the question of how to interpret the columns and rows of a matrix of inputs is
103+
resolved.
104+
Users _must_ wrap matrices which represent collections of inputs in either a `ColVecs` or
105+
`RowVecs`, both of which have clearly defined semantics which are hard to confuse.
106+
107+
By design, there is also no discrepancy between the number of inputs in the collection, and
108+
the `length` function -- the `length` of a `ColVecs`, `RowVecs`, or `Vector{<:Real}` is
109+
equal to the number of inputs.
110+
111+
There is no loss of performance.
112+
113+
A collection of `N` `Real`-valued inputs can be represented by an
114+
`AbstractVector{<:Real}` of `length` `N`, rather than needing to use an
115+
`AbstractMatrix{<:Real}` of size either `N x 1` or `1 x N`.
116+
The same can be said for any other input type `T`, and new subtypes of `AbstractVector` can
117+
be added if particularly efficient ways exist to store collections of inputs of type `T`.
118+
A good example of this in practice is using `Tuple{S, Int}`, for some input type `S`, as the
119+
[Inputs for Multiple Outputs](@ref).
120+
121+
This approach can also lead to clearer user code.
122+
A user need only wrap their inputs in a `ColVecs` or `RowVecs` once in their code, and this
123+
specification is automatically re-used _everywhere_ in their code.
124+
In this sense, it is straightforward to write code in such a way that there is one unique
125+
source of "truth" about the way in which a particular data set should be interpreted.
126+
Conversely, the `obsdim` resolution requires that the `obsdim` keyword argument is passed
127+
around with the data _every_ _single_ _time_ that you use it.
128+
129+
The benefits of the `AbstractVector` approach are likely most strongly felt when writing a substantial amount of code on top of KernelFunctions.jl -- in the same way that using
130+
`AbstractVector`s inside KernelFunctions.jl removes the need for large amounts of keyword
131+
argument propagation, the same will be true of other code.
132+
133+
134+
135+
136+
### Why We Have Support for Both
137+
138+
In short: many people like matrices, and are familiar with `obsdim`-style keyword
139+
arguments.
140+
141+
All internals are implemented using `AbstractVector`s though, and the `obsdim` interface
142+
is just a thin layer of utility functionality which sits on top of this.
143+
144+
145+
146+
147+
148+
## [Kernels for Multiple-Outputs](@id inputs_for_multiple_outputs)
149+
150+
There are two equally-valid perspectives on multi-output kernels: they can either be treated
151+
as matrix-valued kernels, or standard kernels on an extended input domain.
152+
Each of these perspectives are convenient in different circumstances, but the latter
153+
greatly simplifies the incorporation of multi-output kernels in KernelFunctions.
154+
155+
More concretely, let `k_mat` be a matrix-valued kernel, mapping pairs of inputs of type `T` to matrices of size `P x P` to describe the covariance between `P` outputs.
156+
Given inputs `x` and `y` of type `T`, and integers `p` and `q`, we can always find an
157+
equivalent standard kernel `k` mapping from pairs of inputs of type `Tuple{T, Int}` to the
158+
`Real`s as follows:
159+
```julia
160+
k((x, p), (y, q)) = k_mat(x, y)[p, q]
161+
```
162+
This ability to treat multi-output kernels as single-output kernels is very helpful, as it
163+
means that there is no need to introduce additional concepts into the API of
164+
KernelFunctions.jl, just additional kernels!
165+
This in turn simplifies downstream code as they don't need to "know" about the existence of
166+
multi-output kernels in addition to standard kernels. For example, GP libraries built on
167+
top of KernelFunctions.jl just need to know about `Kernel`s, and they get multi-output
168+
kernels, and hence multi-output GPs, for free.
169+
170+
Where there is the need to specialise _implementations_ for multi-output kernels, this is
171+
done in an encapsulated manner -- parts of KernelFunctions that have nothing to do with
172+
multi-output kernels know _nothing_ about the existence of multi-output kernels.

0 commit comments

Comments
 (0)