-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Distinct Blueprints/Components type hierarchies.
This deep refactoring of the Framework addresses and fixes #139. It fixes the semantics of the components library (*e.g.* `OmegaFromRawEdges` and `OmegaFromAllometry` are *not* two separate components) while making it more flexible and future-proof. Take this opportunity to tick the numerous additional Framework `TODO`s, contributing to polishing the whole library API experience. Summarize changes in a fresh `CHANGELOG.md`, suggesting to issue v0.2.1.
- Loading branch information
Showing
164 changed files
with
17,253 additions
and
8,493 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
# Version 0.2.1 | ||
|
||
## Breaking changes (minor) | ||
|
||
- Components and blueprints are now two separate type hierachies. | ||
```julia-repl | ||
julia> Foodweb # The component. | ||
Foodweb (component for <internals>, expandable from: | ||
Matrix: boolean matrix of trophic links, | ||
Adjacency: adjacency list of trophic links, | ||
) | ||
julia> typeof(Foodweb) | ||
<Foodweb> (component type EcologicalNetworksDynamics._Foodweb) | ||
julia> fw = Foodweb.Adjacency([:a => :b]) # A blueprint for this component. | ||
blueprint for <Foodweb>: Adjacency { | ||
A: {a: {b}}, | ||
species: <implied blueprint for <Species>>, | ||
} | ||
julia> typeof(fw) | ||
Foodweb_.Adjacency (blueprint type for System{<internals>}) | ||
julia> fw isa Foodweb | ||
false | ||
julia> fw isa Foodweb.Adjacency | ||
true | ||
``` | ||
|
||
- Components are singletons types | ||
whose fields are blueprint types expanding into themselves. | ||
```julia | ||
Species # The component. | ||
Species.Names # A blueprint to expand from a list of names. | ||
Species.Number # A blueprint to expand from a species count. | ||
``` | ||
|
||
- Components can be directly called | ||
to transfer input to correct blueprint constructors | ||
```julia-repl | ||
julia> Species(5) isa Species.Number | ||
true | ||
julia> Species(["a", "b", "c"]) isa Species.Names | ||
true | ||
``` | ||
|
||
This comes with minor incompatible changes | ||
to the available set of blueprint constructor methods. | ||
For instance the redundant form | ||
`BodyMass(M = [1, 2])` is not supported anymore, | ||
but `BodyMass([1, 2])` does the same | ||
and `BodyMass(Z = 1.5)` still works as expected. | ||
|
||
- Model properties are now typically namespaced to ease future extensions. | ||
Equivalent `get_*` and `set_*!` methods may still exist | ||
but they are no longer exposed or recommended: | ||
use direct property accesses instead. | ||
```julia-repl | ||
julia> m = Model(Foodweb([:a => [:b, :c]])); | ||
julia> m.species # The namespace. | ||
Property space for '<internals>': .species | ||
.index | ||
.richness | ||
.label | ||
.names | ||
.number | ||
julia> m.species.number # (no more `m.n_species` or `get_n_species(m)`) | ||
3 | ||
julia> m.trophic # Another namespace. | ||
Property space for '<internals>': .trophic | ||
.levels | ||
.n_links | ||
.matrix | ||
.herbivory_matrix | ||
.carnivory_matrix | ||
.A | ||
julia> m.trophic.matrix | ||
3×3 EcologicalNetworksDynamics.TrophicMatrix: | ||
0 1 1 | ||
0 0 0 | ||
0 0 0 | ||
``` | ||
|
||
- Some property names have changed. The following list is not exhaustive, | ||
but new names can easily be discovered using REPL autocompletion | ||
or the `properties(m)` and `properties(m.prop)` methods: | ||
- `model.trophic_links` becomes `model.trophic.matrix`, | ||
because it does yield a matrix and not some collection of "links". | ||
The alias `model.A` is still available. | ||
- Likewise, | ||
`model.herbivorous_links` becomes `model.trophic.herbivory_matrix` *etc.* | ||
- Akward plurals like `model.body_masses` and `model.metabolic_classes` | ||
become `model.body_mass` and `model.metabolic_class`. | ||
|
||
- Julia allows linear indexing into 2D structures, | ||
but the package chooses instead to consider this a semantic flaw: | ||
```julia-repl | ||
julia> m = Model( | ||
Foodweb([:a => [:b, :c], :b => [:c, :d]]), | ||
Efficiency(:Miele2019; e_herbivorous = .1, e_carnivorous = .2), | ||
); | ||
m.e[5] | ||
ERROR: View error (EcologicalNetworksDynamics.EfficiencyRates): | ||
Edges data are 2-dimensional: | ||
cannot access trophic link data values with 1 index: [5]. | ||
``` | ||
|
||
## New features | ||
|
||
- Colored console display. | ||
```julia-repl | ||
julia> NutrientIntake() # (won't work in a .md file: try in your REPL) | ||
blueprint for <NutrientIntake>: NutrientIntake_ { | ||
r: <embedded blueprint for <GrowthRate>: Allometric { | ||
allometry: Allometry(p: (a: 1.0, b: -0.25), i: (), e: ()), | ||
}>, | ||
nodes: <embedded blueprint for <Nodes>: PerProducer { | ||
n: 1, | ||
}>, | ||
turnover: <embedded blueprint for <Turnover>: Flat { | ||
t: 0.25, | ||
}>, | ||
supply: <embedded blueprint for <Supply>: Flat { | ||
s: 4.0, | ||
}>, | ||
concentration: <embedded blueprint for <Concentration>: Flat { | ||
c: 0.5, | ||
}>, | ||
half_saturation: <embedded blueprint for <HalfSaturation>: Flat { | ||
h: 0.15, | ||
}>, | ||
} | ||
``` | ||
|
||
- Model properties available with `<tab>`-completion within the REPL. | ||
```julia-repl | ||
julia> m = Model(Foodweb([:a => [:b, :c]])); | ||
julia> m.trop|<tab> # -> m.trophic| | ||
julia> m.trophic.|<tab> | ||
A carnivory_matrix | ||
herbivory_matrix levels | ||
matrix n_links | ||
``` | ||
|
||
- Every blueprint *brought* by another is available as a brought field | ||
to be either *embedded*, *implied* or *unbrought*: | ||
```julia-repl | ||
julia> fw = Foodweb.Matrix([0 0; 1 0]) # Implied (brought if missing). | ||
blueprint for <Foodweb>: Matrix { | ||
A: 1 trophic link, | ||
species: <implied blueprint for <Species>>, | ||
} | ||
julia> fw.species = [:a, :b]; # Embedded (brought, erroring if already present). | ||
fw | ||
blueprint for <Foodweb>: Matrix { | ||
A: 1 trophic link, | ||
species: <embedded blueprint for <Species>: Names { | ||
names: [:a, :b], | ||
}>, | ||
} | ||
julia> fw.species = nothing; # Unbrought (error if missing). | ||
fw | ||
blueprint for <Foodweb>: Matrix { | ||
A: 1 trophic link, | ||
species: <no blueprint brought>, | ||
} | ||
``` | ||
|
||
- Every "leaf" "geometrical" model property *i.e.* a property whose futher | ||
model topology does not depend on or is not planned to depend on | ||
is now writeable. | ||
```julia-repl | ||
julia> m = Model(fw, BodyMass(2)); | ||
m.M[1] *= 10; | ||
m.M == [20, 2] | ||
true | ||
``` | ||
|
||
- Values are checked prior to expansion: | ||
```julia-repl | ||
julia> m = Model(fw, Efficiency(1.5)) | ||
ERROR: Blueprint value cannot be expanded: | ||
Not a value within [0, 1]: e = 1.5. | ||
``` | ||
|
||
- Efficiency from a matrix implies a Foodweb. | ||
```julia-repl | ||
julia> e = 0.5; | ||
m = Model(Efficiency([ | ||
0 e e | ||
0 0 e | ||
e 0 0 | ||
])); | ||
has_component(m, Foodweb) | ||
true | ||
julia> m.A | ||
3×3 EcologicalNetworksDynamics.TrophicMatrix: | ||
0 1 1 | ||
0 0 1 | ||
1 0 0 | ||
``` | ||
|
||
- Aggregated blueprints expansion is now clever enough | ||
to not error if two brought blueprints would bring the same component: | ||
```julia-repl | ||
julia> base = Model( | ||
Foodweb([:a => :b]), | ||
BodyMass(1), | ||
MetabolicClass(:all_invertebrates), | ||
) | ||
ni = NutrientIntake(nodes = 2; supply = [1, 2], turnover = [1, 2]); | ||
# 3 different specifications of Nutrients.Nodes. | ||
julia> ni.nodes | ||
<embedded blueprint for <Nutrients.Nodes>: Number { | ||
n: 2, | ||
}> | ||
julia> ni.supply.nutrients | ||
<implied blueprint for <Nutrients.Nodes>> | ||
julia> ni.turnover.nutrients | ||
<implied blueprint for <Nutrients.Nodes>> | ||
julia> base + ni; # But this still works fine. | ||
``` | ||
|
||
- Aggregated blueprints expansion is now clever enough | ||
to correctly figure a correct expansion order among brought blueprints: | ||
```julia-repl | ||
julia> ni = NutrientIntake(; turnover = [1, 2]); | ||
julia> base + ni; # `ni.turnover.nutrients` is expanded before `ni.supply`. | ||
``` |
Oops, something went wrong.