Skip to content

Commit d68c4c7

Browse files
Merge pull request #55 from SciML/docs
add docs
2 parents eb5f952 + 2f3e43e commit d68c4c7

12 files changed

+404
-252
lines changed

.github/workflows/Documentation.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Documentation
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- 'release-'
8+
tags: '*'
9+
pull_request:
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v2
16+
- uses: julia-actions/setup-julia@latest
17+
with:
18+
version: '1'
19+
- name: Install dependencies
20+
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
21+
- name: Build and deploy
22+
env:
23+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token
24+
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key
25+
run: julia --project=docs/ docs/make.jl

README.md

Lines changed: 0 additions & 252 deletions
Original file line numberDiff line numberDiff line change
@@ -70,255 +70,3 @@ tissue1 = construct(Tissue, deepcopy([population, population2, population3])) #
7070
tissue2 = construct(Tissue, deepcopy([population2, population, population3]))
7171
embryo = construct(Embryo, deepcopy([tissue1, tissue2])) # Make an embryo from Tissues
7272
```
73-
74-
## Human readable printing of the embryo structure
75-
```julia
76-
print_human_readable(embryo)
77-
# +|Tissue; |Tissue
78-
# +|Popula; |Popula; |Popula; +|Popula; |Popula; |Popula
79-
# +Cell; Cell; Cell; +Cell; Cell; Cell; +Cell; Cell; Cell; +Cell; Cell; Cell; +Cell; Cell; Cell; +Cell; Cell; Cell
80-
81-
print_human_readable(embryo;NcharPerName=2)
82-
# +|Ti; |Ti
83-
# +|Po; |Po; |Po; +|Po; |Po; |Po
84-
# +Ce; Ce; Ce; +Ce; Ce; Ce; +Ce; Ce; Ce; +Ce; Ce; Ce; +Ce; Ce; Ce; +Ce; Ce; Ce
85-
```
86-
Here, if the 'AbstractMultiScaleArrayLeaf's contain several fields, you can specify them with fields = [field1,field2,...]
87-
```julia
88-
print_human_readable(embryo;NcharPerName=2,fields=[:values])
89-
# +|Ti; |Ti
90-
# +|Po; |Po; |Po; +|Po; |Po; |Po
91-
# +va: [1.0, 2.0, 3.0]; va: [3.0, 2.0, 5.0]; va: [4.0, 6.0]; +va: [1.0, 2.0, 3.0]; va: [3.0, 2.0, 5.0]; va: [4.0, 6.0]; +va: [1.0, 2.0, 3.0]; va: [3.0, 2.0, 5.0]; va: [4.0, 6.0]; +va: [1.0, 2.0, 3.0]; va: [3.0, 2.0, 5.0]; va: [4.0, 6.0]; +va: [1.0, 2.0, 3.0]; va: [3.0, 2.0, 5.0]; va: [4.0, 6.0]; +va: [1.0, 2.0, 3.0]; va: [3.0, 2.0, 5.0]; va: [4.0, 6.0]
92-
```
93-
if your screen is small, then print a sub-part of the AbstractMultiScaleArray:
94-
```julia
95-
print_human_readable(embryo.nodes[1].nodes[1];fields=[:values])
96-
# +values: [1.0, 2.0, 3.0]; values: [3.0, 2.0, 5.0]; values: [4.0, 6.0]
97-
```
98-
99-
## Indexing and Iteration
100-
101-
The head node then acts as the king. It is designed to have functionality which
102-
mimics a vector in order for usage in DifferentialEquations or Optim. So for example
103-
104-
```julia
105-
embryo[12]
106-
```
107-
108-
returns the "12th protein", counting by Embryo > Tissue > Population > Cell in order
109-
of the vectors. The linear indexing exists for every `AbstractMultiScaleArray`.
110-
These types act as full linear vectors, so standard operations do the sensical
111-
operations:
112-
113-
```julia
114-
embryo[10] = 4.0 # changes protein concentration 10
115-
embryo[2,3,1] # Gives the 1st cell in the 3rd population of the second tissue
116-
embryo[:] # generates a vector of all of the protein concentrations
117-
eachindex(embryo) # generates an iterator for the indices
118-
```
119-
120-
Continuous models can thus be written at the protein level and will work seamlessly
121-
with DifferentialEquations or Optim which will treat it like a vector of protein concentrations.
122-
Using the iterators, note that we can get each cell population by looping through
123-
2 levels below the top, so
124-
125-
```julia
126-
for cell in level_iter(embryo,3)
127-
# Do something with the cells!
128-
end
129-
```
130-
131-
or the multiple level iter, which is the one generally used in
132-
DifferentialEquations.jl functions:
133-
134-
```julia
135-
for (cell, dcell) in LevelIter(3,embryo, dembryo)
136-
# If these are similar structures, `cell` and `dcell` are the similar parts
137-
cell_ode(dcell,cell,p,t)
138-
end
139-
```
140-
141-
`LevelIterIdx` can give the indices along with iteration:
142-
143-
```julia
144-
for (cell, y, z) in LevelIterIdx(embryo, 3)
145-
# cell = embryo[y:z]
146-
end
147-
```
148-
149-
However, the interesting behavior comes from event handling. Since `embryo` will be the
150-
"vector" for the differential equation or optimization problem, it will be the value
151-
passed to the event handling. MultiScaleArrays includes behavior for changing the
152-
structure. For example:
153-
154-
```julia
155-
tissue3 = construct(Tissue, deepcopy([population, population2]))
156-
add_node!(embryo, tissue3) # Adds a new tissue to the embryo
157-
remove_node!(embryo, 2, 1) # Removes population 1 from tissue 2 of the embryo
158-
```
159-
160-
Combined with event handling, this allows for dynamic structures to be derived from
161-
low level behaviors.
162-
163-
## Heterogeneous Nodes via Tuples
164-
165-
Note that tuples can be used as well. This allows for type-stable broadcasting with
166-
heterogeneous nodes. This could be useful for mixing types
167-
inside of the nodes. For example:
168-
169-
```julia
170-
struct PlantSettings{T} x::T end
171-
struct OrganParams{T} y::T end
172-
173-
struct Organ{B<:Number,P} <: AbstractMultiScaleArrayLeaf{B}
174-
values::Vector{B}
175-
name::Symbol
176-
params::P
177-
end
178-
179-
struct Plant{B,S,N<:Tuple{Vararg{<:Organ{<:Number}}}} <: AbstractMultiScaleArray{B}
180-
nodes::N
181-
values::Vector{B}
182-
end_idxs::Vector{Int}
183-
settings::S
184-
end
185-
186-
struct Community{B,N<:Tuple{Vararg{<:Plant{<:Number}}}} <: AbstractMultiScaleArray{B}
187-
nodes::N
188-
values::Vector{B}
189-
end_idxs::Vector{Int}
190-
end
191-
192-
mutable struct Scenario{B,N<:Tuple{Vararg{<:Community{<:Number}}}} <: AbstractMultiScaleArrayHead{B}
193-
nodes::N
194-
values::Vector{B}
195-
end_idxs::Vector{Int}
196-
end
197-
198-
organ1 = Organ([1.1,2.1,3.1], :Shoot, OrganParams(:grows_up))
199-
organ2 = Organ([4.1,5.1,6.1], :Root, OrganParams("grows down"))
200-
organ3 = Organ([1.2,2.2,3.2], :Shoot, OrganParams(true))
201-
organ4 = Organ([4.2,5.2,6.2], :Root, OrganParams(1//3))
202-
plant1 = construct(Plant, (deepcopy(organ1), deepcopy(organ2)), Float64[], PlantSettings(1))
203-
plant2 = construct(Plant, (deepcopy(organ3), deepcopy(organ4)), Float64[], PlantSettings(1.0))
204-
community = construct(Community, (deepcopy(plant1), deepcopy(plant2), ))
205-
scenario = construct(Scenario, (deepcopy(community),))
206-
```
207-
208-
(of course at the cost of mutability).
209-
210-
## Idea
211-
212-
The idea behind MultiScaleArrays is simple. The `*DiffEq` solvers (OrdinaryDiffEq.jl,
213-
StochasticDiffEq.jl, DelayDiffEq.jl, etc.) and native optimization packages like
214-
Optim.jl in their efficient in-place form all work with any Julia-defined
215-
`AbstractArray` which has a linear index. Thus, to define our multiscale model,
216-
we develop a type which has an efficient linear index. One can think of representing
217-
cells with proteins as each being an array with values for each protein. The linear
218-
index of the multiscale model would be indexing through each protein of each cell.
219-
With proper index overloads, one can define a type such that `a[i]` does just that,
220-
and thus it will work in the differential equation solvers. MultiScaleArrays.jl
221-
takes that further by allowing one to recursively define an arbitrary `n`-level
222-
hierarchical model which has efficient indexing structures. The result is a type
223-
which models complex behavior, but the standard differential equation solvers will
224-
work directly and efficiently on this type, making it easy to develop novel models
225-
without having to re-develop advanced adaptive/stiff/stochastic/etc. solving
226-
techniques for each new model.
227-
228-
## Defining A MultiScaleModel: The Interface
229-
230-
The required interface is as follows. Leaf types must extend AbstractMultiScaleArrayLeaf, the
231-
highest level of the model or the head extends MultiScaleModelHead, and all
232-
intermediate types extend AbstractMultiScaleArray. The leaf has an array `values::Vector{B}`.
233-
Each type above then contains three fields:
234-
235-
- `nodes::Vector{T}`
236-
- `values::Vector{B}`
237-
- `end_idxs::Vector{Int}`
238-
239-
Note that the ordering of the fields matters.
240-
`B` is the `BottomType`, which has to be the same as the eltype for the array
241-
in the leaf types. `T` is another `AbstractMultiScaleArray`. Thus at each level,
242-
an` AbstractMultiScaleArray` contains some information of its own (`values`), the
243-
next level down in the heirarchy (`nodes`), and caching for indices (`end_idxs`).
244-
You can add and use extra fields as you please, and even make the types immutable.
245-
246-
## The MultiScaleModel API
247-
248-
The resulting type acts as an array. A leaf type `l` acts exactly as an array
249-
with `l[i] == l.values[i]`. Higher nodes also act as a linear array. If `ln` is level
250-
`n` in the heirarchy, then `ln.nodes` is the vector of level `n-1` objects, and `ln.values`
251-
are its "intrinsic values". There is an indexing scheme on `ln`, where:
252-
253-
- `ln[i,j,k]` gets the `k`th `n-3` object in the `j`th `n-2` object in the `i`th level `n-1`
254-
object. Of course, this recurses for the whole hierarchy.
255-
- `ln[i]` provides a linear index through all `.nodes` and `.values` values in every lower
256-
level and `ln.values` itself.
257-
258-
Thus `typeof(ln) <: AbstractVector{B}` where `B` is the eltype of its leaves and
259-
all `.values`'s.
260-
261-
In addition, iterators are provided to make it easy to iterate through levels.
262-
For `h` being the head node, `level_iter(h,n)` iterates through all level objects
263-
`n` levels down from the top, while `level_iter_idx(h,n)` is an enumeration
264-
`(node,y,z)` where `node` are the `n`th from the head objects, with `h[y:z]` being
265-
the values it holds in the linear indexing.
266-
267-
### Extensions
268-
269-
Note that this only showed the most basic MultiScaleArray. These types can be
270-
extended as one pleases. For example, we can change the definition of the cell
271-
to have:
272-
273-
```julia
274-
struct Cell{B} <: AbstractMultiScaleArrayLeaf{B}
275-
values::Vector{B}
276-
celltype::Symbol
277-
end
278-
```
279-
280-
Note that the ordering of the fields matters here: the extra fields must come
281-
after the standard fields (so for a leaf it comes after `values`, for a standard
282-
multiscale array it would come after `nodes,values,end_idxs`).
283-
Then we'd construct cells with
284-
`cell3 = Cell([3.0; 2.0; 5.0], :BCell)`, and can give it a cell type.
285-
This information is part of the call, so
286-
287-
```julia
288-
for (cell, y, z) in level_iter_idx(embryo, 2)
289-
f(t, cell, @view embryo[y:z])
290-
end
291-
```
292-
293-
can allow one to check the `cell.celltype` in `f` an apply a different ODE depending
294-
on the cell type. You can add fields however you want, so you can use them
295-
to name cells and track lineages.
296-
297-
Showing the use of `values`, you just pass it to the constructor. Let's pass it an array
298-
of 3 values:
299-
300-
```julia
301-
tissue = construct(Tissue, deepcopy([population; population2]), [0.0; 0.0; 0.0])
302-
```
303-
304-
We can selectively apply some function on these `values` via:
305-
306-
```julia
307-
for (tissue, y, z) in level_iter_idx(embryo, 1)
308-
f(t, tissue, @view embryo[y:z])
309-
end
310-
```
311-
312-
and mutate `tis.values` in `f`. For example, we could have
313-
314-
```julia
315-
function f(du, tissue::Tissue, p, t)
316-
du .+= randn(3)
317-
end
318-
```
319-
320-
applies normal random numbers to the three values. We could use this to add to the
321-
model the fact that `tissue.values[1:3]` are the tissue's position, and `f` would then be
322-
adding Brownian motion.
323-
324-
Of course, you can keep going and kind of do whatever you want. The power is yours!

docs/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build/
2+
site/

docs/Project.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[deps]
2+
MultiScaleArrays = "f9640e96-87f6-5992-9c3b-0743c6a49ffa"
3+
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
4+
5+
[compat]
6+
Documenter = "0.27"

docs/make.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Documenter, MultiScaleArrays
2+
3+
include("pages.jl")
4+
5+
makedocs(
6+
sitename="MultiScaleArrays.jl",
7+
authors="Chris Rackauckas",
8+
modules=[MultiScaleArrays],
9+
clean=true,doctest=false,
10+
format = Documenter.HTML(analytics = "UA-90474609-3",
11+
assets = ["assets/favicon.ico"],
12+
canonical="https://multiscalearrays.sciml.ai/stable/"),
13+
pages=pages
14+
)
15+
16+
deploydocs(
17+
repo = "github.com/SciML/MultiScaleArrays.jl.git";
18+
push_preview = true
19+
)

docs/pages.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Put in a separate page so it can be used by SciMLDocs.jl
2+
3+
pages=[
4+
"Home" => "index.md",
5+
"multiscalearray.md"
6+
]

docs/src/assets/favicon.ico

1.36 KB
Binary file not shown.

docs/src/assets/logo.png

26 KB
Loading

0 commit comments

Comments
 (0)