-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathjulia-manual.jl
1868 lines (1509 loc) · 73.4 KB
/
julia-manual.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
### A Pluto.jl notebook ###
# v0.15.1
using Markdown
using InteractiveUtils
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
quote
local el = $(esc(element))
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
el
end
end
# ╔═╡ 5667c9d7-46ce-47ce-bfb7-5851cf8e80a7
using LinearAlgebra, BenchmarkTools
# ╔═╡ b4c31904-2384-425d-a12b-5f3c145bc5fb
using PrettyTables
# ╔═╡ 1cbe8ec9-a704-4912-98d2-69385c773627
using Plots
# ╔═╡ 09a3f57a-db03-4bf4-9129-ab4a0254604e
begin
using PlutoUI
PlutoUI.TableOfContents(title = "Contents")
end
# ╔═╡ 58a85b40-e086-11eb-1471-a3d9d5596625
md"""
# Introduction to Julia for CATAM
"""
# ╔═╡ b695a54f-da17-4acf-aa30-5fb2344df48e
md"""
## About this manual
This manual is intended as an introduction to the programming language [Julia](https://julialang.org/) for use for [CATAM](https://www.maths.cam.ac.uk/undergrad/catam). Some of the advantages of Julia over other programming languages can be found in the section [Learning Julia](#ffcdf202-a14d-417a-9a82-bf3e5ba38972). The headline advantage that it has over the likes of perennial favourites Matlab and Python is [speed](#d508655f-663b-4baa-af93-d0cbd5935a97), which can be critical in some projects, and remains handy for all others. This is rare among languages with the intuitive and wide-ranging syntax that Julia provides.
This manual is perfectly viewable in web-page form, and you will be able to copy code from the manual into Julia to test/edit it. No code which is used in creating the outputs seen in this document is hidden.
However, the interactivity built into the Pluto notebook in which this is written is lost. To enable this:
- Download the notebook as a *.jl* file by clicking *`Edit or run this notebook`* in the top right, and clicking the download button
- Download and install Julia as detailed in the section [Installing Julia](#45aa5e5d-b518-4f28-ab45-365a9c3d4ee1) (note that no editor, e.g. VSCode, is needed)
- Download the Pluto package as detailed in the section [Pluto notebooks](#3cb29926-3332-429b-b8b6-0b95113b388b)
- Open the notebook which you have downloaded in Pluto
A more detailed guide to downloading Julia and Pluto, with images, is [here](https://medium.com/swlh/a-guide-to-building-reactive-notebooks-for-scientific-computing-with-julia-and-pluto-jl-1a2c0c455d51)
"""
# ╔═╡ 45aa5e5d-b518-4f28-ab45-365a9c3d4ee1
md"""
## Installing Julia
To use Julia, two elements are required: Julia itself, and an editor to allow you to write and run your programs.
Julia is a standalone program downloaded from <https://julialang.org/downloads/>
- For most Windows users, the version to download will be the 64-bit (installer). Once downloaded, run the installer, making sure to check the **Add Julia to PATH** box (which will allow the editor to find it)
- For Mac users there is no such paralysis of choice, the single Mac download link is the one you want
- For Linux users, you know what you're doing anyway
Additional platform specific information can be found at <https://julialang.org/downloads/platform/>
An editor is technically not essential, although it makes programming immeasuarably faster and easier. This manual will use the editor VSCode, which is developed by Microsoft but available on Mac and Linux as well as Windows. This is downloadable from <https://code.visualstudio.com/Download>
The third and final step is to open VSCode and install the Julia extension which allows it to recognise and run Julia code. This is found in the Extensions tab (accessible via **View** > **Extensions**, or `Ctrl` + `Shift` + `X`, or as the fifth symbol on the left sidebar). Search for Julia, and click Install. Once the extension has installed, restart VSCode, and it will be ready to use. Documentation for the extension is found at <https://www.julia-vscode.org/docs/dev/>, which provides (at the time of writing) limited information on using the extension.
Many alternatives to VSCode exist, as detailed in the **Editors and IDEs** section of <https://julialang.org/>. If you already have an editor of choice, it is likely that a Julia syntax-highlighting or even REPL extension exists for it.
"""
# ╔═╡ 6dab02d3-4b45-4227-a790-00350002a909
md"""
## Using Julia with VSCode
VSCode gives two ways to interact with Julia: the REPL and the editor
### The Julia REPL
The read-eval-print-loop (REPL) is the command line interface of Julia, akin to the MATLAB Command Window or the Python Shell. It allows single lines of code to be written and evaluated, and is also the place where any programs you write will run. It can be opened by the key sequence `Alt` + `J` followed by `Alt` + `O`, or by opening VSCode's command palette via the **View** menu or with `Ctrl` + `Shift` + `P`, and searching for the command **Julia: Start REPL**. It is identical to the command line which appears when running Julia as an application, so there is no need to interact with Julia outside of VSCode.
To test it, try copying the code in the boxes below, and see if the outputs match:
"""
# ╔═╡ 07d24863-82aa-4b13-aa92-1b06a0b5276b
a = 3;
# ╔═╡ c8366549-83b0-4a1c-b526-a4c38c1b1151
b = 2
# ╔═╡ 1c46f6c4-fba1-460d-91ff-a7f5e4506041
a + b
# ╔═╡ d3da52ff-1f54-4b15-bdb7-1c213154cc89
md"""
Note that without the semicolon, an output is displayed, which may or may not be desirable. Multiple lines can be written at once by seperation by semicolons, e.g.:
"""
# ╔═╡ 21f97b7f-7dd1-4f12-9812-081f08ed3f1f
c = a + 4; b * c
# ╔═╡ 6d3d80e1-c4e9-43eb-8750-48ce16a55e63
md"""
One particularly useful feature of the REPL is help mode. By typing `?` into the REPL. the prompt changes from **`julia>`** to **`help?>`**, after which typing the name of any variable or function tells you about it. Help mode can be exited by `Backspace` on an empty line, or `Ctrl` + `C` at any time.
### The editor
The editor pane is the large central pane, which will allow the writing of programs in the form of scripts. To create a new file, select **File** and choose **New File** from the menu, or use `Ctrl` + `N`. Then, you need to tell VSCode that you will be writing Julia, which can be done through the **Select a language** prompt, or by saving the file with the file type *.jl*.
VSCode will prompt you to open a folder, which you will want to do. This not only allows for saving of your files to a chosen location, but also will determine the place from which any file paths that you use (such as in the section [Customising outputs](#3665a395-ae1c-40cf-8e98-00534f53ee4f)), allowing your program to work independent of its file location.
Once a script has been written and saved, it can be run through the REPL by clicking the triangle in the top right and selecting **Julia: Execute File in REPL**. This does not have a keyboard shortcut automatically, but this (and any other keyboard shortcuts) can be changed through the command palette (`Ctrl` + `Shift` + `P`) by searching for the relevant command and clicking the cog that appears when you mouse over it.
"""
# ╔═╡ ffcdf202-a14d-417a-9a82-bf3e5ba38972
md"""
## Learning Julia and its advantages
This manual doesn't focus on teaching Julia, purely because there are already plenty of freely available resources online to get you started
- A good place to start is the Julia Manual on Julia's official website, starting with <https://docs.julialang.org/en/v1/manual/getting-started/> and navigable through the left-hand menu
- Another great option is the Julia Express linked at <https://github.com/bkamins/The-Julia-Express/>
- For programmers already familiar with other languages, a useful page from the manual is <https://docs.julialang.org/en/v1/manual/noteworthy-differences/>, detailing how Julia differs from other languages, and <https://cheatsheets.quantecon.org/>, which gives explicit differences in syntax between Julia, Python, and MATLAB
- A short introductory guide is at <https://computationalthinking.mit.edu/Spring21/basic_syntax/>. This is an interactive Pluto notebook, with the ability to edit the snippets of code (see section on Pluto notebooks for further details). Further similar material is also available at <https://computationalthinking.mit.edu/>
- A quick cheatsheet for Julia syntax and its abilities is at <https://juliadocs.github.io/Julia-Cheat-Sheet/>
- A similar cheatsheet for the Plots package is at <https://github.com/sswatson/cheatsheets/blob/master/plotsjl-cheatsheet.pdf>
- YouTube (as usual) provides a plethora of videos of a wide range of qualities; simply searching for "Learn Julia", or the Julia language channel <https://www.youtube.com/c/TheJuliaLanguage>, would be a good start
However, there are some particular features worth highlighting:
"""
# ╔═╡ af2dd15c-cb27-483f-b047-69c70d9e7532
md"""
### Unicode support
Julia is unusual in supporting the use of characters usual Latin alphabet, Arabic numerals, and common English punctuation. For example:
- Instead of `pi` and `exp(1)`, we can use `π` and `ℯ`
- Where Greek letters are used by mathematical convention, they can be used in programs, such as `ρ` for density, or `λ` for the parameter of a Poisson distribution, improving comprehension
- Some symbols have syntactic meaning, such as using `≤` instead of `<=`, or `∈` instead of the keyword `in`
- Non-Latin writing systems are supported, allowing variable/function names as well as text within the program to be written in your (human) language of choice
- Emoji can be used similarly. Unfortunately, the author of this manual is too boring to appreciate this. `😒 = true`
These can be copied and pasted into your code, or directly typed if you have the capabilities, but the easiest way tends to be to use ``\LaTeX``-like shortcuts with tab-completion, such as `\alpha<tab>` for `α` or `\leq<tab>` for `≤`. The help mode of the REPL allows pasting of characters to see the relevant shortcuts available for typing them.
"""
# ╔═╡ 6cf95416-a4d0-442b-8dfa-5e59d07d4a15
md"""
### Mathematical notation uncommon in other languages
The usual syntax for defining functions is similar to many other programming languages
"""
# ╔═╡ 92715aab-25fe-4387-9fd9-45c7e632b50d
function f(x,y)
return √(x^2 + y^2)
end
# ╔═╡ a151d910-5408-40cc-a352-2eeef5434551
f(3,4)
# ╔═╡ 4f540782-83d9-4401-8865-78bb39af2aae
md"""
However, an alternative syntax is particularly useful for readability of short functions, by writing them in the compact form:
"""
# ╔═╡ dd43c006-3455-49db-97bc-e928408a0539
g(x,y) = √(x^2 + y^2)
# ╔═╡ 45a265d6-b489-49d1-b93d-4907f6ea66b2
g(3,4)
# ╔═╡ 26b5cc22-db0b-46f4-9d45-d3830223d336
md"""
This is convenient notation since it mirrors the mathematical equivalent. The expression after the equals must be a single line, but can also be extended to longer expressions by using a `begin`-`end` block, which allows multiple lines to be treated together as a single line:
"""
# ╔═╡ 59b2dc85-0ce7-4e99-aeae-170cc13cfe87
h(x) = begin
if 0 < x < 3
return x
else
return 0
end
end
# ╔═╡ 2bb4926a-2b5b-4e02-b1f9-d8225b909ae5
h(2)
# ╔═╡ e3884fe1-4451-4601-b483-6b4ee03d572e
h(-2)
# ╔═╡ 74e50b24-39d8-49b4-b651-e4cbe07d382f
md"""
The function `h` above also demonstrates another ability of Julia, which is to allow multiple comparisons in the same statement. In many languages, `0 < x < 3` would be invalid syntax, and would have to be written as `0 < x && x < 3`.
A third feature of Julia that appeals to standard mathematical notation is function composition:
"""
# ╔═╡ 0c4f78f4-055b-47fd-8c64-8a77259a13c1
hg = h ∘ g
# ╔═╡ a2cf252e-604c-40b7-9bff-d0970ebf9b80
md"""
Here, `hg` isn't technically a function, but a `ComposedFunction`, although it acts in much the same way.
"""
# ╔═╡ cef7f72e-73fe-4a6a-b807-46e14d794014
hg(1,1)
# ╔═╡ a5717ddf-2e3c-4138-8d7c-40f9d6fb1a12
hg(3,5)
# ╔═╡ be6f9061-dd1b-4c8a-bd13-82455e8a0207
md"""
A fourth such feature is the ability to use numeric coefficients to denote multiplication. Consider the following:
"""
# ╔═╡ a671de2a-de76-4728-bf7b-f751c284aace
d = 2; e = 2d
# ╔═╡ 1b859eb6-525f-4405-b4fc-181ff335532b
md"""
In most other programming languages, this would cause an error, as the implicit multiplication wouldn't be recognised. Julia, however, knows that prefixing a variable name with a number means multiplication, so treats it as such.
This only works with numeric literals, i.e. actual numbers, not variables which take a number as a value. For example, trying to multiply `d` and `e` like this results in:
"""
# ╔═╡ c33eeb03-07d8-4f16-8691-568bc830e468
de
# ╔═╡ 7e80b6cc-d4b6-45c7-a14f-76e0f67a6053
md"""
because Julia interprets this as the variable with name `de`, not `d` multiplied by `e`.
"""
# ╔═╡ 2fe58313-a17a-4af0-b68f-8bdefd0a035d
md"""
### Short-circuiting and the ternary operator
The `if`-statement is one of the most used in programming, since it allows the program to respond differently depending on different conditions. In cases where `if`-statements are short, the syntax can become bulky, so can be substituted for some concise alternatives.
Usually, the **AND** (`&&`) and **OR** (`||`) operators are used for combining logical statements. An optimisation which allows more efficient code is *short-circuiting*:
- Consider the statement `<statement-1> && <statement-2>`. If `<statement-1>` is `false`, then there is no need to evaluate `<statement-2>`, since the overall result will always be `false`, so Julia *short-circuits* by ignoring it and simply returning `false`
- Similarly, in the case `<statement-1> || <statement-2>` with `<statement-1>` true, the overall result will be `true` without checking `<statement-2>`
A useful way of using this is to have `<statement-2>` not be a logical statement at all, and instead be a piece of code. This will then only run if no short-circuit is made, abbreviating:
```julia
if <statement-1>
<statement-2> => <statement-1> && <statement-2>
end
```
and
```julia
if !<statement-1>
<statement-2> => <statement-1> || <statement-2>
end
```
For example:
"""
# ╔═╡ 0d6f6cf0-4706-4469-bd4b-ca65af27ffe6
3 > 4 && "Secret message"
# ╔═╡ 3ac36546-1ddd-479d-86c5-d02e07993069
3 < 4 && "Secret message"
# ╔═╡ 89b0c356-3962-4201-abb6-caeb334fce6a
md"""
Another common use for an `if`-statement is to assign a variable one of two values, depending on whether a condition is met or not. This can be done by the ternary operator, which provides the following abbreviation
```julia
if <condition>
x = <valueif>
else => x = (<condition> ? <valueif> : <valueelse>)
x = <valueelse>
end
```
which can be seen in the following example:
"""
# ╔═╡ c5193537-9e52-4391-b352-3aaf640192c4
3 > 4 ? "This is true" : "This is false"
# ╔═╡ db2c9293-1c23-4708-b1a9-5ed013d1f8f4
3 < 4 ? "This is true" : "This is false"
# ╔═╡ f3cfccf4-ff2d-44af-9735-242c2c1e80ac
md"""
Alternatively, consider how the function `h` in the previous section could be rewritten to make use of the ternary operator.
"""
# ╔═╡ 4325ba80-3823-4c76-992e-351969e67222
md"""
### Broadcasting and splat
If a function takes an array (such as a vector or a matrix) or similar collection (such as a tuple) as an input, it will treat it as such, and operations will act as such (such as matrix products). However, sometimes different behaviour is desired.
Julia has inbuilt functionality for elementwise operation without needing to define a separate function to do so, which is called broadcasting. Consider the function:
"""
# ╔═╡ 84588d2f-de11-44d3-8b1d-afcaa9b59282
p(x) = x^3 - 4x^2 + 2x
# ╔═╡ cb5f1991-5928-4bae-b853-935d08c1f61a
A = [ [1,2,3] [4,5,6] [7,8,9] ]
# ╔═╡ f7943ee4-6927-4aef-b97e-b70691e431f7
md"""
For matrix `A`, `p(A)` is defined by squaring and cubing `A`
"""
# ╔═╡ e526a902-224f-4f19-8bab-7c53be471671
p(A)
# ╔═╡ d9788710-0493-41b2-8510-08ff76614977
md"""
If instead we want `p` to act elementwise, Julia provides two simple options. The first is the `broadcast` function:
"""
# ╔═╡ cb4f8751-6678-4db3-98f1-2c380d272428
broadcast(p,A)
# ╔═╡ 6ee30cb8-70c9-46fb-bc03-c1917a342ffc
md"""
and the second is following the function name with a `.`:
"""
# ╔═╡ fffd5baf-9142-4c32-9a85-c0258e6366be
p.(A)
# ╔═╡ 30f3d516-6f44-4eb3-8792-aa4e5ef7a93f
md"""
Another thing that you may need to do with an array is pass each element individually as separate arguments to a function, which is done through the splat operator `...`. As an example, consider the difference between:
"""
# ╔═╡ 7c081773-bb95-489c-844a-16f9c5bd950f
+(A)
# ╔═╡ 8c58354d-8a1f-400b-a4a1-ff2ea9016e72
+(A...)
# ╔═╡ 91855dee-a9a6-4782-b4af-62a87c2705b3
md"""
The same syntax has another use, that is for defining your own functions with an arbitrary number of arguments without needing them to be contained in a tuple beforehand, such as:
"""
# ╔═╡ f1a45f51-6ad6-43c2-904f-4fff9e399535
function myminimum(x...)
m = Inf
for y ∈ x
if y < m
m = y
end
end
return m
end
# ╔═╡ 0b5793f1-8834-4258-846d-47b76bc9b6c8
md"""
Here, `x` captures all of the inputs to the function in a single tuple, meaning that any number of arguments can be specified without needing to write separate functions:
"""
# ╔═╡ 4df7cae4-4eeb-4681-a8d3-00c4f457ded2
myminimum(12,15,9,151,23)
# ╔═╡ d3d71501-0dc7-4d9e-a3b4-4cc62aa640ab
myminimum(π,ℯ,Inf)
# ╔═╡ ab8d0a33-c0cd-4ad3-9d94-b9b1afbcca4b
md"""
### Packages
As it is, Julia is already equipped with many useful tools, which for some projects may already be sufficient to complete them. However, there is far more that Julia can do through the package system.
Packages are (usually quite specialised) extensions to Julia's functionality which can be downloaded and used in your programs. Any of these can be downloaded for the first time by running `using Pkg; Pkg.add("<package-name>")` in the REPL. Alternatively, pressing `]` in the REPL puts it in package mode, allowing packages to be downloaded by simply typing `add("<package-name>")`. This only needs to be done the first time that a package is used, as it will download and remain accessible to you in the future. To access the functionality of the package, run `using <package-name>`.
Over 4000 packages exist for Julia, which can be searched through by any of the options linked at <https://julialang.org/packages/>. Some particularly useful packages are used in this manual, as well as in the accompanying case studies.
"""
# ╔═╡ 2691d33f-7ecc-4c5b-a249-ac3f16fbefdb
md"""
### Expressions and Macros
The `Expr` data type is used to store Julia code as a variable, which is not run until the user wishes to evaluate it. For example:
"""
# ╔═╡ 46c6cbb6-e2a7-4ad9-8869-8f31a15c9358
q = quote
if 3 < 4
return true
else
return false
end
end;
# ╔═╡ dbe458d4-f24a-4f8d-8c31-b18555afa57b
md"""
`q` stores the entire block of code as written inside the `quote`-`end` block as a variable, which can be run at a later stage with `eval`.
"""
# ╔═╡ 9e65f10e-3963-45c6-a517-cb78756d8d77
typeof(q)
# ╔═╡ 2e9e8b67-7173-4e77-b5d1-f7eaa8834b5e
eval(q)
# ╔═╡ 47a3f741-4064-477e-8eb9-fc0ccac5858c
md"""
These can be useful in many situations, such as to operate on pieces of code, or generate code automatically to simplify repetitivity. As an example, suppose we want to calculate the first ``50`` Fibonacci numbers and store them as `F₁`, `F₂`, ..., `F₅₀`. First, we can write a custom function to get the symbol `:Fₙ` (which is much like an `Expr` but represents a single variable/function name instead of a larger snippet of code) from an integer `n`:
"""
# ╔═╡ 18d2d68b-95cb-44a6-93d6-0f1dfd32b03a
function symbolFn(n)
subscriptn = String([Char(0x2080+d) for d in reverse(digits(n))])
return Symbol("F",subscriptn)
end
# ╔═╡ de04ed4a-84f4-4030-84dd-0092e7772ffa
symbolFn(123)
# ╔═╡ 1a0ee7c6-58ca-44e5-a94d-b5cf60940392
md"""
Then, the variables themselves can be created:
"""
# ╔═╡ c24f6d7e-0c78-41f4-9468-3d4a6210c47a
begin
F₁ = 1
F₂ = 1
for n ∈ 3:50
eval( :( $(symbolFn(n)) = $(symbolFn(n-1)) + $(symbolFn(n-2)) ) )
end
end
# ╔═╡ 61ba579c-981f-48df-a002-ab452897c311
md"""
and then displayed (note that the `with_terminal` function creates the terminal for the notebook, in the REPL only the `[println ...]` line is needed):
"""
# ╔═╡ ffd60a8d-0c0a-4f76-8948-d6314d344878
with_terminal() do
[println( String(s), " = $(eval(:($s)))" ) for s ∈ symbolFn.(1:15)]
end
# ╔═╡ a0e8c156-480d-40d5-a3dc-36e1757ae0f5
F₅₀
# ╔═╡ 85843b08-dcf9-4449-88f9-255773605970
md"""
One particular use for expressions is in macros. These are function-like in that they take an input, run some pre-determined code using the input, and produce an output. However, macros differ from functions as they take their inputs as `Expr`s, and also return `Expr`s which are then run as code. Macros are distinguished as such by an `@` preceding their name.
For example, the macro `@mutiple` runs the input code `n` times (note that `n` is specified as an integer by the double colon, so the macro will not run unless an integer is given). An example usage of this macro is also shown below:
"""
# ╔═╡ cdde6b43-39f2-4cda-bfea-eef48ee494eb
macro multiple(n::Integer, expr)
return quote
$([esc(expr) for i ∈ 1:n]...)
end
end
# ╔═╡ 3c21e020-7287-4a3c-9046-7e2ab1150065
begin
x = 1
@multiple 10 x += 1
end;
# ╔═╡ fb37b982-a6f2-4461-ab12-6cf6e3a2837f
x
# ╔═╡ 9193e3e6-f1aa-40f8-b009-b712248e72c3
md"""
The macro `@macroexpand` can be used to show the expression returned by another macro before it is evaluated, which helps to see how macros work and is invaluable for debugging custom macros.
Two related useful macros are `@elapsed`, which times how long its argument takes to run in seconds, returning the time while discarding the result, and `@time`, which prints the time and returns the result. An important note about these two is that the first time a function runs, Julia has to compile it first, so it will always be slower than subsequent runs. To counteract this, make sure to either run the function first, or use the macros from `BenchmarkTools` as described in the next section [Efficiency](#d508655f-663b-4baa-af93-d0cbd5935a97).
"""
# ╔═╡ a6023033-f699-4913-83b3-d4abb76e42e6
begin
inv([[1,0] [0,1]])
@elapsed inv(rand(1000,1000))
end
# ╔═╡ 8e0bb01c-93e6-4f7b-9d5f-6f815370bad6
md"""
Further uses for macros can be seen in later sections.
"""
# ╔═╡ d508655f-663b-4baa-af93-d0cbd5935a97
md"""
### Efficiency
One of the principal advantages that Julia has over other languages, especially Matlab, is the speed of execution, and the ease of achieving this. This is because Julia is able to efficiently translate its code into the same machine code as from the equivalent compilation of C code (arguably the gold-standard for efficiency), giving Julia readability and usability while not compromising on speed. A discussion of this is given (with an aesthetic that conceals its modernity) at <https://web.mit.edu/18.06/www/Spring17/Julia-intro.pdf>
Additionally, Julia has many inbuilt ways to exploit knowledge of properties of the variables in order to increase the speed of execution. To demonstrate one instance of this, we will use the `LinearAlgebra` package (which includes many opportunities for such shortcuts to be made) and the `BenchmarkTools` package (which provides better macros for estimating the average time that a function takes than `@time` or `@elapsed`).
"""
# ╔═╡ 086ee7e2-4159-4ddd-bd56-f7c5474696a4
md"""
One of the matrix types that `LinearAlgebra` presents is `Hermitian`, which is a useful property of a matrix to be able to exploit, since quicker algorithms exist for many tasks for Hermitian matrices compared to general matrices. To demonstrate this, we will time calculating the eigenvalues of random ``1000 \times 1000`` complex matrices versus random ``1000 \times 1000`` Hermitian matrices (which are in fact random complex matrices turned into Hermitian matrices by removing the imaginary parts from the diagonal entries and transposing the complex conjugate of the triangle above the diagonal onto the triangle below the diagonal):
"""
# ╔═╡ 9cca126f-d8c9-46de-9cc7-2a7a4d94beaa
@belapsed eigvals(rand(ComplexF64,1000,1000))
# ╔═╡ 285f7f1c-6f64-46c4-8956-69dd8988b131
@belapsed (eigvals ∘ Hermitian ∘ rand)(ComplexF64,1000,1000)
# ╔═╡ dd71ca55-2e7b-40e7-9811-97739b236eaa
md"""
The use of the composition has no impact on the speed, but helps with readability by avoiding large stacks of parentheses.
Julia can also recognise properties in matrices that it isn't told. For example, if the random Hermitian matrices are returned to the normal `Matrix` type, the `eigvals` algorithm is still just as fast:
"""
# ╔═╡ 106b6981-0691-46ad-a74c-637eaf97c91d
@belapsed eigvals(H) setup = (H = (Matrix ∘ Hermitian ∘ rand)(ComplexF64,1000,1000))
# ╔═╡ 7091f035-8f17-460f-a9e9-d7cd9198e106
md"""
In order for your own code to make best use of the potential efficiency that Julia boasts, refer to documentation, the case studies accompanying this manual, as well as <https://docs.julialang.org/en/v1/manual/performance-tips/>.
"""
# ╔═╡ 3665a395-ae1c-40cf-8e98-00534f53ee4f
md"""
## Customising outputs
*Note: Code referencing external files in this section is not interactive. To see the functionality in full, transfer the code yourself to your own scripts and test them out.*
As standard, most outputs that Julia will be able to give will be in terms of variables such as numbers, vectors, matrics, etc., or text printed to the REPL. For more complex outputs, we need to look further afield.
"""
# ╔═╡ 992d6562-950d-4071-b189-b39a13fb7f30
md"""
### Outputting to files
One option for a different output is to send the outputs to a separate file instead of the REPL. This is done in three steps:
- Open a file which can be written into (denoted by the `"w"` parameter) as a variable (so that it can be referenced later). The file name given represents a path relative to the current folder, which can be viewed by `pwd()`. If no file with this path exists, one will be created.
```julia
textfile = open("text.txt", "w")
```
- Write into the file, by using functions such as `show` and `println` in combination with the file as a parameter. For example, the vector `a = [1, 2, 3, 4]` can be written into the file by
```julia
println(textfile, a)
```
- Close the file to save it
```julia
close(textfile)
```
If all of the data needed to be written into the file is ready, these can be combined into a single statement, of the form
```julia
open("text.txt", "w") do textfile
println(textfile, a)
end
```
which will automatically close the file once the `do` statement has finished.
"""
# ╔═╡ a4c99e37-b3b8-446e-a655-d4ac1237d0c7
md"""
### Creating tables with PrettyTables
A table is often the best way to present data, so having a way of creating them automatically is very useful. Contrary to expectation, the package `Tables` is not the package to do this, since it is more useful for database-like manipulation of tables rather than displaying them as objects. For this purpose, one good option is `PrettyTables`.
"""
# ╔═╡ 4f886a63-42fa-4c4d-b7fe-c0caa2884ad3
md"""
In its most basic usage, `PrettyTables` allows the conversion of a matrix into a table
"""
# ╔═╡ 0a95770e-b495-4a4d-9e59-d1fbf662829d
M = rand(4,4)
# ╔═╡ 8f408461-f868-41d8-98c9-42babca608f7
with_terminal() do
pretty_table(M)
end
# ╔═╡ 24e06691-f641-4f41-a3f4-dfd57f600c68
md"""
One important note with `PrettyTables` is that the tables outputted are printed to the REPL (or another output of choice as we'll see later), and are not returned as variables. As a result, for display purposes within this notebook, the tables will have to be returned in mini-terminals as above, created by the function `with_terminal`. Unfortunately, the limitations of these terminals means that many features of `PrettyTables` cannot be viewed directly from this notebook. To use this code in the REPL, copy only the contents of the `do` block (in this case `pretty_table(M)`).
The power of `PrettyTables` comes in the multitude of customisations that can be made to the table. These, and the package as a whole, are comprehensively documented at <https://ronisbr.github.io/PrettyTables.jl/stable/>.
The most impactful keyword is `backend`, which allows the choice between three forms of output for the table (and most other keywords depend on which backend is chosen):
- `backend = :text` is the default, outputting the table in raw text
- `backend = :html` outputs HTML defining the table
- `backend = :latex` outputs $\LaTeX$ code defining the table as a `tabular` object
`PrettyTables` also allows creating preset configurations which can be used for multiple tables. This can be done either through the `set_table_conf` and `pretty_table_with_conf` functions:
"""
# ╔═╡ e9eca1cf-149e-4763-8024-1093841c5430
tableconfig = set_pt_conf(
tf = tf_markdown,
columns_width = 15,
alignment = :c,
formatters = ((value,i,j) -> round(value, digits = 3))
);
# ╔═╡ 359594ae-dc8b-4d59-babd-5d5163098cad
with_terminal() do
pretty_table_with_conf(tableconfig, M; header = ["Player $i" for i = 1:4])
end
# ╔═╡ 384289b4-2788-47ba-a061-7ce025db7a4b
md"""
or through the use of macros, with `@ptconf` setting the configuration, `@pt` using this configuration to display tables, and `@ptconfclean` clearing the configuration:
"""
# ╔═╡ 3a54dbdb-7fa8-4fcc-864a-f67a8c3df010
@ptconf(
backend = :latex,
highlighters = LatexHighlighter((data,i,j) -> data[i,j] > 0.5, "textbf"),
wrap_table = false,
alignment = :c
)
# ╔═╡ 87ee2688-4612-48f3-b75f-af68ce8061f7
with_terminal() do
@pt :header = ["Player $i" for i = 1:4] M
end
# ╔═╡ 736b81c0-f10f-4dcb-a1a3-0791bc169cee
md"""
In addition, the outputs can be written to files much like `show` or `println` can have their output written to files:
```julia
open("tablefile.txt", "w") do io
pretty_table(io, M; header = ["Player $i" for i = 1:4])
end
```
"""
# ╔═╡ 6916be23-b667-471a-bd5d-e45163e7f7df
md"""
### Creating and saving graphics with Plots
One of the most useful packages for Julia is the `Plots` package, allowing the creation of plots, graphs, and diagrams of many kinds.
"""
# ╔═╡ 0e81d3de-fc91-4327-a9a2-8b375d74f4fa
md"""
The most basic plots that `Plots` makes are line graphs between input points, or plots of functions:
"""
# ╔═╡ 6a6a1d26-c4b7-4acf-9cb9-028415d36655
begin
xdata = collect(1:10)
ydata = [10,6,5,4,8,9,3,4,2,1]
plot(xdata, ydata)
end
# ╔═╡ cd9b60e0-11d3-4a7d-bbfc-2576d64364ff
plot(sinc)
# ╔═╡ 79b48255-2f6d-4825-8139-9825a71100cb
md"""
Changing attributes allows the appearance of the plot to be customised. Some of the most useful are:
- `title` controls the title of the plot
- `label` controls the name given to the line in the legend, `legend` controls whether the legend appears or not
- `xlims` and `ylims` (and `zlims` for 3D plots) control the extent of the corresponding axis
- `grid` controls whether a grid appears in the background of the plot, while `minorgrid` does the same with a grid with smaller divisions
- `showaxis` controls which axes are shown, while `ticks` controls the numbering of the axes
A more complete list can be found in the documentation at <http://docs.juliaplots.org/latest/>
"""
# ╔═╡ 81de08b7-e78c-4238-9eb5-d032dbd55fd0
plot(
tan,
title = "The tangent function",
legend = false,
xlims = (-π,π),
ylims = (-10,10),
grid = false,
showaxis = :x,
framestyle = :origin
)
# ╔═╡ b475321e-824e-4a77-bb4c-07d7730591f1
md"""
Different plot types can be chosen using the attribute `seriestype`, or for some more common types, a specialised function. Additional series types can be found in other packages, such as `StatsPlots` for additional methods of statistical data visualisation, and `GraphRecipes` for plotting graphs of the graph theoretical kind. Also, plots can be saved as variables and then added to with `plot!` and related functions:
"""
# ╔═╡ f31c2367-d0de-485b-a06f-2b29cb1d319a
plot(
[(0,1),(4,3),(1,6),(5,2),(6,3),(2,4)],
label = "Blue",
seriestype = :scatter,
xlims = (-1,7),
ylims = (-1,7),
framestyle = :origin
)
# ╔═╡ 3fab1731-0854-4248-89e2-e04cc909c68c
barchart = bar(
[("A",4),("B",5),("C",1),("D",4),("E",6),("F",2)],
legend = false,
fillcolor = :green
)
# ╔═╡ 98f2a28d-5a83-4981-acb2-d68268bc03a7
bar!(
barchart,
[("A",2),("B",6),("C",3),("D",3),("E",6),("F",5)],
color = :red,
bar_width = 0.5
)
# ╔═╡ ebdfd3f2-7d95-4391-81e4-1d384b6aa52a
md"""
Different plots can be grouped together into a single image, with the layout customisable by using the `layout` attribute:
"""
# ╔═╡ 37da7e59-a1e3-454c-86ee-959c761fedeb
begin
function customfnplot(fnname)
return plot(
eval(Meta.parse(fnname)),
xlims = (0,4π),
legend = false,
title = fnname
)
end
sinplot = customfnplot("sin")
cosplot = customfnplot("cos")
end;
# ╔═╡ e44a80d2-dd32-443b-a918-2af821cad6ac
doubleplot = plot(sinplot, cosplot, layout = (2,1))
# ╔═╡ 6ceecd44-e185-4bcd-993c-94677f033714
md"""
Plots can be saved as files by the function `savefig`, or can be saved by file type specific functions such as `png`. The following code, if run, would save the `doubleplot` graph as *sinandcos.png*:
```julia
png(doubleplot,"sinandcos")
```
"""
# ╔═╡ a8973a1e-b1a7-4792-8c0a-4c8c8b4ecd60
md"""
## Example - The logistic map
The logistic map (<https://en.wikipedia.org/wiki/Logistic_map>) is given by the difference equation
```math
x_{n+1} = r x_n (1 - x_n)
```
where `` x_0 \in [0,1] `` and `` r \in [0,4] ``. It is notable for its chaotic behaviour as ``r`` approaches ``4``.
To begin with, we need a function to iterate the difference equation:
"""
# ╔═╡ d595f9fe-83bc-497a-8f2e-558bf893a3b7
logisticiteration(r,x) = r * x * (1-x)
# ╔═╡ 7c0491c6-8144-4b0c-83d3-7011c6e98b9a
md"""
We will run the difference equation for `N` iterations:
"""
# ╔═╡ 5df85afe-df4c-4812-be38-8b9e2ba8f261
N = 100
# ╔═╡ b8727283-b7f3-4d76-addf-3ca45ac7703b
md"""
Choose a value for x₀ (in general this doesn't massively impact the eventual result):
"""
# ╔═╡ ac8421b4-a2da-4274-9192-9c8c1f9790f9
x₀ = 0.5
# ╔═╡ 43546149-7226-4e06-a7ce-6c6f8c4cddae
md"""
Now choose a value for r:
"""
# ╔═╡ e2c61bfb-9abf-4dda-a8bc-0adeb163b6b4
r = 3.1
# ╔═╡ 2bfdf102-f4c5-4f17-8440-6722095a901e
md"""
We then iterate the function `logisticiteration`, and finally can plot the graph of ``x_n`` against ``n``
"""
# ╔═╡ ee0d8687-73f7-497f-9222-61578e2391e8
begin
sequence = zeros(N+1); sequence[1] = x₀;
for n ∈ 1:N
sequence[n+1] = logisticiteration(r,sequence[n])
end
end
# ╔═╡ 976d19d8-f319-43de-ad60-d5706db26df8
plot(0:N, sequence, legend=false)
# ╔═╡ 851db6f0-0194-4c88-83dd-c2570c0c8729
md"""
If ``r > 3``, the oscillatory behaviour characterising the beginning of the chaotic behaviour of the logistic map is evident. However, it is not the best graphical demonstration of the chaos which occurs for ``r ⪆ 3.57``; instead let's approximate the bifurcation diagram, a graph of the parameter `r` against the corresponding oscillatory values of the difference equation. For this, the algorithm that will be used is detailed at <http://www.math.le.ac.uk/people/rld8/ma1251/lab3.html>.
To start with, choose a range of values of `r`. The syntax `0:0.005:4` gives all of the numbers counting from `0` to `4` in intervals of `0.005`:
"""
# ╔═╡ adbdc425-5de6-42be-9399-36f24f2d8316
rvalues = 0:0.005:4
# ╔═╡ b3d635a1-6c3a-4d0b-800c-d08c5c7a7789
md"""
All iterations will start with `x₀ = 0.5`. We need to discard the earliest iterations before they have converged close to the eventual oscillatory values of the sequence
"""
# ╔═╡ a643373c-ca5f-4d7d-9e94-9d1dcc7446e7
discardediterations = 200
# ╔═╡ 6d2765c1-ed26-4c78-98d8-02c71e7c2af6
md"""
Then, the sequence will continue for some more iterations to ensure that many of the oscillatory values are plotted. We need to put a limit on this number of iterations otherwise the program won't terminate
"""
# ╔═╡ 96e2fbe3-8569-49a8-af5b-bae456024b66
maxoscillatoryvalues = 100
# ╔═╡ aad017bb-7fa3-4af6-85cc-6d9018c744d8
md"""
For each value of ``r``, we run the iteration, plotting each point, and terminating early for efficiency if any iteration results in a value sufficiently close (in this case within ``0.001``) to the last discarded value.
"""
# ╔═╡ 62c56061-9064-45a2-bf9b-c9f17fb2f8cd
md"""
If you are viewing this manual as an editable Pluto notebook, try some different ranges for `rvalues` (in particular, an interesting result occurs by choosing a range of values from ``-2`` to ``0``). It may be necessary to press the button below to refresh the graph.
"""
# ╔═╡ 3de49c33-a185-4a67-820c-e1b87a48b484
@bind bifurcationreset Button("Recalculate graph")
# ╔═╡ 547a3549-ba11-4dbc-b316-0c2dd7384b0d
begin
bifurcationreset
points = Vector{Tuple{Float64,Float64}}(undef, 0)
end;
# ╔═╡ ad032574-1ca6-483d-b642-58af6838f9d0
for rvalue ∈ rvalues
x = 0.5
for i ∈ 1:discardediterations
x = logisticiteration(rvalue,x)
end
maxdiscardedvalue = x
for i ∈ 1:maxoscillatoryvalues
x = logisticiteration(rvalue,x)
push!(points,(rvalue, x))
if abs(x - maxdiscardedvalue) < 0.001
break
end
end
end
# ╔═╡ 97d306da-666d-4ba9-bd6c-16b79212f926
bifurcationplot = scatter(points, markersize = 1, markercolor = :black, legend = false, xlims = (min(rvalues...),max(rvalues...)), ylims = (-0.5,1.5))
# ╔═╡ 3cb29926-3332-429b-b8b6-0b95113b388b
md"""
## Pluto notebooks
Pluto notebooks are created from the Pluto package for Julia. They act as an alternative, more interactive method of presentation of Julia code; indeed this manual itself is a Pluto notebook. Pluto notebooks are edited from the browser, with text written in Markdown, which is simple but reasonably powerful text-editing tool, with a good introductory guide at several pages of <https://www.markdownguide.org/>.
Other than being made specifically for Julia, Pluto's main draw is its reactivity, meaning that different cells work more like a live-updating network of code than a simple linear script. This can mean that not all Julia programs are suitable for Pluto notebook form, but does provide an excellent method of presentation for Julia code as well displaying the outputs beside the code which creates them, while also allowing efficient editing of code and recalculation of only the cells which are affected by these edits.
To use Pluto:
- Run `using Pkg; Pkg.add("Pluto");` in the Julia REPL and wait for the package to be downloaded and installed
- Now run `using Pluto; Pluto.run()`, which should open in your browser, or give an address of the form `localhost:1234/?secret=XXXXXXXX` for you to open
- You will be presented with a welcome page. A good place to start is the sample notebooks provided by the developers of Pluto, or this manual (see section [Using this manual](#b695a54f-da17-4acf-aa30-5fb2344df48e)) and the case studies that go along with it
A similar although not Julia-specific notebook tool is Jupyter, which can be installed for VSCode or run in the browser like Pluto. For more information on this, see <https://jupyter.org/>, and the Jupyter extension for VSCode found through the extension search.
### Excess Pluto code
The following code is run for the purposes of creating the contents table, and is not otherwise relevant to the discussion in this manual.
"""
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
[compat]
BenchmarkTools = "~1.1.1"
Plots = "~1.18.2"
PlutoUI = "~0.7.9"
PrettyTables = "~1.1.0"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
[[Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.1"
[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
[[Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[BenchmarkTools]]
deps = ["JSON", "Logging", "Printf", "Statistics", "UUIDs"]
git-tree-sha1 = "c31ebabde28d102b602bada60ce8922c266d205b"
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
version = "1.1.1"
[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.6+5"
[[Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "e2f47f6d8337369411569fd45ae5753ca10394c6"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.0+6"
[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random", "StaticArrays"]
git-tree-sha1 = "ed268efe58512df8c7e224d2e170afd76dd6a417"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.13.0"
[[ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.0"
[[Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.8"
[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "dc7dedc2c2aa9faf59a55c622760a25cbefbe941"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.31.0"
[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
[[Contour]]
deps = ["StaticArrays"]
git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.5.7"
[[Crayons]]
git-tree-sha1 = "3f71217b538d7aaee0b69ab47d9b7724ca8afa0d"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.0.4"
[[DataAPI]]
git-tree-sha1 = "ee400abb2298bd13bfc3df1c412ed228061a2385"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.7.0"
[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "4437b64df1e0adccc3e5d1adbc3ac741095e4677"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.9"
[[DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"
[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"