Skip to content

Commit 7a131ea

Browse files
committed
Merge branch 'lecture_09' of https://github.com/JuliaTeachingCTU/Scientific-Programming-in-Julia into lecture_09
2 parents 4c362bb + ee5941c commit 7a131ea

35 files changed

+1810
-941
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
*.log
2+
*.aux
3+
*.nav
4+
*.out
5+
*.snm
6+
*.toc
17
*.swp
28

39
/Manifest.toml

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ lecture_07 = [
8383

8484
lecture_08 = [
8585
"Lecture" => "./lecture_08/lecture.md"
86+
"Lab" => "./lecture_08/lab.md"
87+
"Homework" => "./lecture_08/hw.md"
8688
]
8789

8890
lecture_09 = [

docs/src/lecture_04/hw.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Homework 4: More Unit Tests
1+
# [Homework 4: More Unit Tests](@id hw4)
22

33

44
In this homework you will finish writing your unit tests for your `Ecosystem.jl`.

docs/src/lecture_07/hw.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# [Homework 7: Creating world in 3 days/steps](@id hw07)
22

3+
## How to submit
4+
Put all the code of inside `hw.jl`. Zip only this file (not its parent folder) and upload it to BRUTE. You should assume that only
5+
```julia
6+
using Ecosystem
7+
using Ecosystem.EcosystemCore
8+
```
9+
will be put before your file is executed, but do not include them in your solution. The version of `Ecosystem` pkg and consequently `EcosystemCore` should be the same as in [HW4](@ref hw4).
10+
311
```@raw html
412
<div class="admonition is-category-homework">
513
<header class="admonition-header">Homework (2 points)</header>
@@ -17,7 +25,7 @@ end
1725
`@add` should not be treated as a macro, but rather just as a syntax, that can be easily matched.
1826

1927
As this is not a small task let's break it into 3 steps
20-
1. Define method `default_config(::Type{T})` for each `T` in `Grass, Wolf,...`, which returns a tuple of default parameters for that particular agent.
28+
1. Define method `default_config(::Type{T})` for each `T` in `Grass, Wolf,...`, which returns a named tuple of default parameters for that particular agent (you can choose the default values however you like).
2129
2. Define method `_add_agents(max_id, count::Int, species::Type{<:Species})` and `_add_agents(max_id, count::Int, species::Type{<:AnimalSpecies}, sex::Type{<:Sex})` that return an array of `count` agents of species `species` with `id` going from `max_id+1` to `max_id+count`. Default parameters should be constructed with `default_config`.
2230
3. Define the underlying function `_ecosystem(ex)`, which parses the block expression and creates a piece of code that constructs the world.
2331

docs/src/lecture_07/lab.md

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# [Lab 07: Macros](@id macro_lab)
2-
A little reminder from the lecture, a macro in its essence is a function, which
2+
A little reminder from the [lecture](@ref macro_lecture), a macro in its essence is a function, which
33
1. takes as an input an expression (parsed input)
44
2. modifies the expressions in arguments
55
3. inserts the modified expression at the same place as the one that is parsed.
66

77
In this lab we are going to use what we have learned about manipulation of expressions and explore avenues of where macros can be useful
8-
- convenience (`@repeat n code`, `@show`)
8+
- convenience (`@repeat`, `@show`)
99
- performance critical code generation (`@poly`)
1010
- alleviate tedious code generation (`@species`, `@eats`)
1111
- just as a syntactic sugar (`@ecosystem`)
@@ -43,10 +43,10 @@ Testing it gives us the expected behavior
4343
@myshow xx = 1 + 1
4444
xx # should be defined
4545
```
46-
In this "simple" example, we had to use the following concepts mentioned already in the lecture:
47-
- `QuoteNode(ex)` is used to wrap the expression inside another layer of quoting, such that when it is interpolated into `:()` it stays being a piece of code instead of the value it represents - **TRUE QUOTING**
48-
- `esc(ex)` is used in case that the expression contains an assignment, that has to be evaluated in the top level module `Main` (we are `esc`aping the local context) - **ESCAPING**
49-
- `$(QuoteNode(ex))` and `$(esc(ex))` is used to evaluate an expression into another expression. **INTERPOLATION**
46+
In this "simple" example, we had to use the following concepts mentioned already in the [lecture](@ref macro_lecture):
47+
- `QuoteNode(ex)` is used to wrap the expression inside another layer of quoting, such that when it is interpolated into `:()` it stays being a piece of code instead of the value it represents - [**TRUE QUOTING**](@ref lec7_quotation)
48+
- `esc(ex)` is used in case that the expression contains an assignment, that has to be evaluated in the top level module `Main` (we are `esc`aping the local context) - [**ESCAPING**](@ref lec7_hygiene)
49+
- `$(QuoteNode(ex))` and `$(esc(ex))` is used to evaluate an expression into another expression. [**INTERPOLATION**](@ref lec7_quotation)
5050
- `local value = ` is used in order to return back the result after evaluation
5151

5252
Lastly, let's mention that we can use `@macroexpand` to see how the code is manipulated in the `@myshow` macro
@@ -83,7 +83,9 @@ _repeat(3, :(println("Hello!"))) # testing "macro" without defining it
8383
```
8484

8585
**HINTS**:
86-
- use `$` interpolation into a for loop expression
86+
- use `$` interpolation into a for loop expression; for example given `ex = :(1+x)` we can interpolate it into another expression `:($ex + y)` -> `:(1 + x + y)`
87+
- if unsure what gets interpolated use round brackets `:($(ex) + y)`
88+
- macro is a function that *creates* code that does what we want
8789

8890
**BONUS**:
8991
What happens if we call `@repeat 3 x = 2`? Is `x` defined?
@@ -123,12 +125,14 @@ Ideally we would like write some macro `@poly` that takes a polynomial in a math
123125
*Example usage*:
124126
```julia
125127
p = @poly x 3x^2+2x^1+10x^0 # the first argument being the independent variable to match
128+
p(2) # return the value
126129
```
127130

128131
However in order to make this happen, let's first consider much simpler case of creating the same but without the need for parsing the polynomial as a whole and employ the fact that macro can have multiple arguments separated by spaces.
129132

130133
```julia
131134
p = @poly 3 2 10
135+
p(2)
132136
```
133137

134138
```@raw html
@@ -138,10 +142,29 @@ p = @poly 3 2 10
138142
```
139143
Create macro `@poly` that takes multiple arguments and creates an anonymous function that constructs the unrolled code. Instead of directly defining the macro inside the macro body, create helper function `_poly` with the same signature that can be reused outside of it.
140144

145+
Recall Horner's method polynomial evaluation from previous [labs](@ref horner):
146+
```julia
147+
function polynomial(a, x)
148+
accumulator = a[end] * one(x)
149+
for i in length(a)-1:-1:1
150+
accumulator = accumulator * x + a[i]
151+
#= accumulator = muladd(x, accumulator, a[i]) =# # equivalent
152+
end
153+
accumulator
154+
end
155+
```
156+
141157
**HINTS**:
142158
- you can use `muladd` function as replacement for `ac * x + a[i]`
143-
- the expression should be built incrementally by nesting (try to write out the Horner's method[^1] to see it)
144-
- the order of coefficients has different order than in previous labs
159+
- think of the `accumulator` variable as the mathematical expression that is incrementally built (try to write out the Horner's method[^1] to see it)
160+
- you can nest expression arbitrarily
161+
- the order of coefficients has different order than in previous labs (going from )
162+
- use `evalpoly` to check the correctness
163+
```julia
164+
using Test
165+
p = @poly 3 2 10
166+
@test p(2) == evalpoly(2, [10,2,3]) # reversed coefficients
167+
```
145168

146169
[^1]: Explanation of the Horner schema can be found on [https://en.wikipedia.org/wiki/Horner%27s\_method](https://en.wikipedia.org/wiki/Horner%27s_method).
147170
```@raw html
@@ -151,6 +174,7 @@ Create macro `@poly` that takes multiple arguments and creates an anonymous func
151174
```
152175

153176
```@repl lab07_poly
177+
using InteractiveUtils #hide
154178
macro poly(a...)
155179
return _poly(a...)
156180
end
@@ -182,7 +206,7 @@ Moving on to the first/harder case, where we need to parse the mathematical expr
182206
```
183207
Create macro `@poly` that takes two arguments first one being the independent variable and second one being the polynomial written in mathematical notation. As in the previous case this macro should define an anonymous function that constructs the unrolled code.
184208
```julia
185-
julia> p = @poly x 3x^2 + 2x^1 + 10x^0 # the first argument being the independent variable to match
209+
julia> p = @poly x 3x^2+2x^1+10x^0 # the first argument being the independent variable to match
186210
```
187211

188212
**HINTS**:
@@ -262,7 +286,6 @@ end
262286
```
263287
Let's test it.
264288
```@repl lab07_poly
265-
using InteractiveUtils #hide
266289
p = @poly x 3x^2+2x^1+ 10
267290
p(2) == evalpoly(2, [10,2,3])
268291
@code_lowered p(2) # can show the generated code
@@ -357,6 +380,9 @@ Unfortunately the current version of `Ecosystem` and `EcosystemCore`, already co
357380
string.(hcat(["🌍", animal_species...], vcat(permutedims(species), em)))
358381
end
359382
eating_matrix()
383+
🌍 🐑 🐺 🌿 🍄
384+
🐑 ❌ ❌ ✅ ✅
385+
🐺 ✅ ❌ ❌ ❌
360386
```
361387

362388
```@raw html
@@ -369,14 +395,24 @@ Based on the following example syntax,
369395
@species Plant Broccoli 🥦
370396
@species Animal Rabbit 🐇
371397
```
372-
write macro `@species` inside `Ecosystem` pkg, which defines the abstract type, its show function and exports the type.
373-
374-
Define first helper function `_species` to inspect the macro's output. This is indispensable, as we are defining new types/constants and thus we would otherwise encountered errors during repeated evaluation (though only if the type signature changed).
398+
write macro `@species` inside `Ecosystem` pkg, which defines the abstract type, its show function and exports the type. For example `@species Plant Broccoli 🥦` should generate code:
399+
```julia
400+
abstract type Broccoli <: PlantSpecies end
401+
Base.show(io::IO,::Type{Broccoli}) = print(io,"🥦")
402+
export Broccoli
403+
```
404+
Define first helper function `_species` to inspect the macro's output. This is indispensable, as we are defining new types/constants and thus we may otherwise encounter errors during repeated evaluation (though only if the type signature changed).
405+
```julia
406+
_species(:Plant, :Broccoli, :🥦)
407+
_species(:Animal, :Rabbit, :🐇)
408+
```
375409

376410
**HINTS**:
377411
- use `QuoteNode` in the show function just like in the `@myshow` example
378-
- ideally these changes should be made inside the modified `Ecosystem` pkg provided in the lab (though not everything can be refreshed with `Revise`)
412+
- escaping `esc` is needed for the returned in order to evaluate in the top most module (`Ecosystem`/`Main`)
413+
- ideally these changes should be made inside the modified `Ecosystem` pkg provided in the lab (though not everything can be refreshed with `Revise`) - there is a file `ecosystem_macros.jl` just for this purpose
379414
- multiple function definitions can be included into a `quote end` block
415+
- interpolation works with any expression, e.g. `$(typ == :Animal ? AnimalSpecies : PlantSpecies)`
380416

381417
**BONUS**:
382418
Based on `@species` define also macros `@animal` and `@plant` with two arguments instead of three, where the species type is implicitly carried in the macro's name.
@@ -432,8 +468,9 @@ Define macro `@eats` inside `Ecosystem` pkg that assigns particular species thei
432468
where `Grass => 0.5` defines the behavior of the `eat!` function. The coefficient is used here as a multiplier for the energy balance, in other words the `Rabbit` should get only `0.5` of energy for a piece of `Grass`.
433469

434470
**HINTS**:
435-
- ideally these changes should be made inside the modified `Ecosystem` pkg provided in the lab (though not everything can be refreshed with `Revise`)
436-
- you can create an empty `quote end` block with `code = Expr(:block)` and push new expressions incrementally
471+
- ideally these changes should be made inside the modified `Ecosystem` pkg provided in the lab (though not everything can be refreshed with `Revise`) - there is a file `ecosystem_macros.jl` just for this purpose
472+
- escaping `esc` is needed for the returned in order to evaluate in the top most module (`Ecosystem`/`Main`)
473+
- you can create an empty `quote end` block with `code = Expr(:block)` and push new expressions into its `args` incrementally
437474
- use dispatch to create specific code for the different combinations of agents eating other agents (there may be catch in that we have to first `eval` the symbols before calling in order to know if they are animals or plants)
438475

439476
!!! note "Reminder of `EcosystemCore` `eat!` and `eats` functionality"
@@ -512,10 +549,10 @@ _eats(species, foodlist)
512549
```
513550

514551
---
515-
# Resources
552+
## Resources
516553
- macros in Julia [documentation](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros)
517554

518-
## `Type{T}` type selectors
555+
### `Type{T}` type selectors
519556
We have used `::Type{T}` signature[^2] at few places in the `Ecosystem` family of packages (and it will be helpful in the HW as well), such as in the `show` methods
520557
```julia
521558
Base.show(io::IO,::Type{World}) = print(io,"🌍")

docs/src/lecture_07/lecture.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ end
164164

165165
Observe that macro dispatch is based on the types of AST that are handed to the macro, not the types that the AST evaluates to at runtime.
166166

167-
## Notes on quotation
167+
## [Notes on quotation](@id lec7_quotation)
168168
In the previous lecture we have seen that we can *quote a block of code*, which tells the compiler to treat the input as a data and parse it. We have talked about three ways of quoting code.
169169
1. `:(quoted code)`
170170
2. Meta.parse(input_string)
@@ -239,7 +239,7 @@ Instead, when a macro is given an expression with $ in it, it assumes you're goi
239239
run(`touch $(filename)`)
240240
```
241241

242-
## Macro hygiene
242+
## [Macro hygiene](@id lec7_hygiene)
243243
Macro hygiene is a term coined in 1986. The problem it addresses is following: if you're automatically generating code, it's possible that you will introduce variable names in your generated code that will clash with existing variable names in the scope in which a macro is called. These clashes might cause your generated code to read from or write to variables that you should not be interacting with. A macro is hygienic when it does not interact with existing variables, which means that when macro is evaluated, it should not have any effect on the surrounding code.
244244

245245
By default, all macros in Julia are hygienic which means that variables introduced in the macro have automatically generated names, where Julia ensures they will not collide with user's variable. These variables are created by `gensym` function / macro.

docs/src/lecture_08/Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
graphs:
2+
pdflatex graphdiff_6.tex
3+
pdflatex graphdiff_7.tex
4+
pdflatex graphdiff_9.tex
5+
pdflatex graphdiff_14.tex
6+
pdf2svg graphdiff_6.pdf graphdiff_6.svg
7+
pdf2svg graphdiff_7.pdf graphdiff_7.svg
8+
pdf2svg graphdiff_9.pdf graphdiff_9.svg
9+
pdf2svg graphdiff_14.pdf graphdiff_14.svg
10+
rm graphdiff_6.pdf graphdiff_7.pdf graphdiff_9.pdf graphdiff_14.pdf

docs/src/lecture_08/Manifest.toml

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ git-tree-sha1 = "8756f9935b7ccc9064c6eef0bff0ad643df733a3"
188188
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
189189
version = "0.12.7"
190190

191+
[[FiniteDifferences]]
192+
deps = ["ChainRulesCore", "LinearAlgebra", "Printf", "Random", "Richardson", "StaticArrays"]
193+
git-tree-sha1 = "c56a261e1a5472f20cbd7aa218840fd203243319"
194+
uuid = "26cc04aa-876d-5657-8c51-4c34ba976000"
195+
version = "0.12.19"
196+
191197
[[FixedPointNumbers]]
192198
deps = ["Statistics"]
193199
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
@@ -411,9 +417,9 @@ uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
411417

412418
[[Libffi_jll]]
413419
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
414-
git-tree-sha1 = "761a393aeccd6aa92ec3515e428c26bf99575b3b"
420+
git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290"
415421
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
416-
version = "3.2.2+0"
422+
version = "3.2.2+1"
417423

418424
[[Libgcrypt_jll]]
419425
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
@@ -467,6 +473,13 @@ version = "1.3.5"
467473
deps = ["Libdl"]
468474
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
469475

476+
477+
[[LittleCMS_jll]]
478+
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg"]
479+
git-tree-sha1 = "110897e7db2d6836be22c18bffd9422218ee6284"
480+
uuid = "d3a379c0-f9a3-5b72-a4c0-6bf4d2e8af0f"
481+
version = "2.12.0+0"
482+
470483
[[LogExpFunctions]]
471484
deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
472485
git-tree-sha1 = "be9eef9f9d78cecb6f262f3c10da151a6c5ab827"
@@ -539,6 +552,13 @@ git-tree-sha1 = "7937eda4681660b4d6aeeecc2f7e1c81c8ee4e2f"
539552
uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051"
540553
version = "1.3.5+0"
541554

555+
556+
[[OpenJpeg_jll]]
557+
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libtiff_jll", "LittleCMS_jll", "Pkg", "libpng_jll"]
558+
git-tree-sha1 = "76374b6e7f632c130e78100b166e5a48464256f8"
559+
uuid = "643b3616-a352-519d-856d-80112ee9badc"
560+
version = "2.4.0+0"
561+
542562
[[OpenLibm_jll]]
543563
deps = ["Artifacts", "Libdl"]
544564
uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
@@ -606,6 +626,13 @@ git-tree-sha1 = "0d185e8c33401084cab546a756b387b15f76720c"
606626
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
607627
version = "1.23.6"
608628

629+
630+
[[Poppler_jll]]
631+
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "OpenJpeg_jll", "Pkg", "libpng_jll"]
632+
git-tree-sha1 = "e11443687ac151ac6ef6699eb75f964bed8e1faa"
633+
uuid = "9c32591e-4766-534b-9725-b71a8799265b"
634+
version = "0.87.0+2"
635+
609636
[[Preferences]]
610637
deps = ["TOML"]
611638
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
@@ -664,6 +691,13 @@ git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
664691
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
665692
version = "1.1.3"
666693

694+
695+
[[Richardson]]
696+
deps = ["LinearAlgebra"]
697+
git-tree-sha1 = "e03ca566bec93f8a3aeb059c8ef102f268a38949"
698+
uuid = "708f8203-808e-40c0-ba2d-98a6953ed40d"
699+
version = "1.4.0"
700+
667701
[[SHA]]
668702
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
669703

@@ -758,10 +792,30 @@ version = "1.6.0"
758792
deps = ["ArgTools", "SHA"]
759793
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
760794

795+
796+
[[Tectonic]]
797+
deps = ["Pkg"]
798+
git-tree-sha1 = "acf12eccb390a78653ee805cd527898f01f78a85"
799+
uuid = "9ac5f52a-99c6-489f-af81-462ef484790f"
800+
version = "0.6.1"
801+
761802
[[Test]]
762803
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
763804
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
764805

806+
807+
[[TikzGraphs]]
808+
deps = ["LaTeXStrings", "LightGraphs", "TikzPictures"]
809+
git-tree-sha1 = "48932ba660bc8cefc0aa9519ba79d63082aea892"
810+
uuid = "b4f28e30-c73f-5eaf-a395-8a9db949a742"
811+
version = "1.2.0"
812+
813+
[[TikzPictures]]
814+
deps = ["LaTeXStrings", "Poppler_jll", "Requires", "Tectonic"]
815+
git-tree-sha1 = "a08671c0979063a437378f6410bb75a465f3cd1c"
816+
uuid = "37f6aa50-8035-52d0-81c2-5a1d08754b2d"
817+
version = "3.4.1"
818+
765819
[[URIs]]
766820
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
767821
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"

docs/src/lecture_08/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
[deps]
22
ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2"
3+
FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000"
34
GraphRecipes = "bd48cda9-67a9-57be-86fa-5b3c104eda73"
45
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
56
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
7+
TikzGraphs = "b4f28e30-c73f-5eaf-a395-8a9db949a742"
8+
TikzPictures = "37f6aa50-8035-52d0-81c2-5a1d08754b2d"
69
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

0 commit comments

Comments
 (0)