-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjcp_showcase.jl
2919 lines (2381 loc) · 112 KB
/
jcp_showcase.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.19.16
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 iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end
local el = $(esc(element))
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)
el
end
end
# ╔═╡ 03e3c34f-f43e-4bf8-bc39-c66dd9059c0e
begin
using DataFrames, Flux, PlutoUI, Test
using AtomsBase, AtomsIO
using AtomGraphs, AtomicGraphNets, ChemistryFeaturization
end
# ╔═╡ 10a99c8e-2ef5-43f7-a82f-0f7dc30e2d6d
# If the notebook is being run on JuliaHub, first retrieve the dataset assets required for running the notebook.
if haskey(ENV, "JULIAHUB_APP_URL")
begin
using JuliaHubData, DataSets
datasets = ["mp-195", "VInO4"]
for structure in datasets
open(IO, dataset("thazhemadam/$(structure)")) do io
write("$(structure).cif", io)
end
open(IO, dataset("thazhemadam/$(structure)-image")) do io
write("$(structure).png", io)
end
end
end
end
# ╔═╡ 7234d025-4a70-4d55-a199-1c26c45ddd23
using GeometricFlux
# ╔═╡ 95450e24-a1a3-11ed-2b36-c9ef4bfe3dd0
md"""
# Chemellia Showcase!
This notebook steps through some aspects of the Chemellia ecosystem for the case of graph-based representations and models. It also highlights integrations with other Julia ecosystems and interfaces that allow Chemellia itself to be quite lightweight.
"""
# ╔═╡ 76cb16d4-61eb-4db4-8574-247078043ba1
TableOfContents()
# ╔═╡ a55e9ca6-0396-4dc1-afd8-379dc03063fe
md"""
## Building Graphs
The first step is to go from a full 3D representation of a crystal structure to the graph-based one that we'll use as input to our model. We'll start out playing with HoPt$_3$, a structure I pulled from the [Materials Project](https://materialsproject.org/) database. A unit cell looks like this:
$(PlutoUI.LocalResource("./mp-195.png"))
Let's read in the CIF file as an `AtomGraph` and take a look around...
"""
# ╔═╡ a41c48b6-3a4c-4bf5-9cfd-81388424a20d
HoPt₃ = AtomGraph("mp-195.cif")
# ╔═╡ 5b68df70-f055-4069-a35b-c173870e984e
propertynames(HoPt₃)
# ╔═╡ 71e8634e-16d9-4422-81bf-8e33f29fd487
md"It stores both the `graph` that we built, and the representation of the full 3D `structure` that was generated along the way. That structure makes use of the [AtomsBase](https://github.com/JuliaMolSim/AtomsBase.jl) interface for easy interoperability of representations. The graph is a `SimpleWeightedGraph` from the [JuliaGraphs](https://juliagraphs.org/) ecosystem, and makes use of the Graphs.jl interface. Let's visualize it:"
# ╔═╡ 10d1afae-6bf4-4914-bfaa-43f02e956c4b
visualize(HoPt₃)
# ╔═╡ f7a6ae01-2e6d-4c16-aaf8-9af17a0e59e8
md"""
(The `visualize` function in AtomGraphs.jl makes use of the GraphPlot.jl package, another package within the JuliaGraphs ecosystem.)
The AtomGraphs.jl package is capable of reading from a variety of file formats for representing atoms and molecules, and can also directly take any object that is an AtomsBase `AbstractSystem` (such as a structure used in a [DFTK](https://docs.dftk.org/stable/) or [Molly](https://github.com/JuliaMolSim/Molly.jl) calculation).
We can also do some customization of how the graphs are built. To explore that, let's look at another crystal structure, this time with a somewhat larger unit cell...
$(PlutoUI.LocalResource("./VInO4.png"))
Let's play a bit with what the cutoff radius does to the connectivity of the graph and the "apparent" coordination numbers of the atoms/nodes...
"""
# ╔═╡ 578e1a80-9499-42db-8ed3-da1e0713e604
cutoff_slider = @bind cutoff Scrubbable(2.75:0.25:4, format=".2f")
# ╔═╡ c20fb391-1765-4b29-9eb8-7f2c8f4351bf
begin
VInO₄ = AtomGraph("VInO4.cif", cutoff_radius=cutoff)
visualize(VInO₄)
end
# ╔═╡ 7e3ad6e5-323b-4e29-ba58-6c243a416ea9
md"""
#### Other graph-building options
* You can tweak the behavior of the cutoff method to generate edges via some more keyword arguments. `max_num_nbr` is reasonably self-explanatory. There is also `dist_decay_func`, which sets how the edge weights decay with separation distance.
* Another way to build the graph, rather than the somewhat naïve cutoff method, is to construct Voronoi tessellations of the structure and build edges only between atoms that share a face. This option is turned on by passing the keyword argument `use_voronoi=true`
"""
# ╔═╡ 660b7b62-8edf-4bdc-9d6c-c1b46002590c
md"""
## Featurization
Once we've built our graph representation the way we like it, we can featurize it, "annotating" it with information that we would want to feed into an ML model. Let's step through some of the "vocabulary" around featurization within Chemellia. In particular, it's handled within the interface provided by the ChemistryFeaturization package.
### Feature Descriptors
The main building block of a featurization is a **feature descriptor** object, which should be a subtype of `AbstractFeatureDescriptor`. For now, we'll focus `ElementFeatureDescriptor`s, which only need elemental information in order to compute their values.
To start, suppose we're interested in featurizing our graphs with information about which block of the periodic table (s, p, d, f) an element is in. We can construct the feature descriptor like so:
"""
# ╔═╡ a55da127-e349-42fe-9280-e1c868c025f7
block = ElementFeature.ElementFeatureDescriptor("Block")
# ╔═╡ c731b3fc-8b39-40a6-9b75-5e021795a495
md"""
We can "call" the feature descriptor directly on an `AtomGraph` to get its value for each atom...
"""
# ╔═╡ 6f69dffb-57c2-410e-9e4a-9c2eb62124cf
block(HoPt₃)
# ╔═╡ 0e077478-0bf6-45ca-8969-22c4ad009b08
md"What other element features are built in?"
# ╔═╡ 9d589e76-c65c-4fc1-b013-265237677cb9
elementfeature_names
# ╔═╡ 03426b8e-4535-4e3d-bb81-73e067214100
md"The ones that just have names of orbitals are the energy levels of those orbitals in the given element. Those provide a nice opportunity to illustrate another important function..."
# ╔═╡ 0656f790-f295-48a5-8a18-f671dd1db925
begin
sixp = ElementFeature.ElementFeatureDescriptor("6p")
encodable_elements(sixp)
end
# ╔═╡ e18cdb44-4e20-4e93-a065-638bde8318b0
md"""
Those are the only elements for which this feature is defined! Nifty.
We can also easily define custom features. For an `ElementFeatureDescriptor`, all that's needed is a lookup table defining Check this out:
"""
# ╔═╡ b73dc3d3-ee38-46c7-a268-e47c0d692c9a
begin
lookup_table = DataFrame(["Ho" 73; "Pt" 92; "In" 126; "V" 40], [:Symbol, :Awesomeness])
awesomeness = ElementFeature.ElementFeatureDescriptor("Awesomeness", lookup_table)
end
# ╔═╡ 87aedd6e-9af1-442d-93bd-6b1a45838315
awesomeness(HoPt₃)
# ╔═╡ bef802d7-9715-4c11-bb73-de2949cbc0fc
@test_throws AssertionError awesomeness(VInO₄) # The awesomeness feature isn't defined for VInO₄!
# ╔═╡ fc4625bb-9b16-4fc8-8b8e-175758115aeb
md"""
...and there you see the usefulness of the `encodable_elements` functionality.
### Codecs
The next idea we'll introduce (and one that's key to the separation of concerns within the Chemellia framework) is that of Codecs, or "encoder-decoders." The key insight behind Codecs is that the actual value of a feature is entirely distinct from the way it is encoded in order to be passed into a model, and in fact, often one might want to encode the same feature in multiple distinct ways for different applications. In addition, often the encoded version of a feature is substantially less human-readable than its actual value.
#### OneHotOneCold
As one example, consider the `OneHotOneCold` Codec. As the name suggests, this Codec performs one-hot encoding of a given feature value and decodes using a one-cold decoding scheme.
##### Categorical Values
Remember the `block` feature descriptor from before? We can very easily encode and decode this categorical feature using one-hot encoding.
"""
# ╔═╡ 17ab9f6e-abde-453e-aace-b3694eb31f64
begin
ohoc_block = OneHotOneCold(true, ["s", "p", "d", "f"])
local encoded_block = encode(block(HoPt₃), ohoc_block)
local decoded_block = map(x -> decode(x, ohoc_block), encoded_block)
@info "Feature" block(HoPt₃)
@info "Encoded Feature" encoded_block
@info "Decoded Feature" decoded_block
end
# ╔═╡ 4bdcc41b-083a-4687-aee5-bbc23f94363b
md"""
##### Continuous Values
Alright, we've seen that this works for a categorical variable. But, does this also work for continuous values too? Yes, it does!
Let's consider a continuous feature, Atomic mass.
"""
# ╔═╡ 5603bf38-7e37-4372-a6c1-7c8f6dd472a0
amu = ElementFeatureDescriptor("Atomic mass")
# ╔═╡ 7b6424a4-fdec-470d-9483-75a2f323d16d
md"""
Let's also define the bins with heuristically selected values, given the range of values.
"""
# ╔═╡ 19812716-75a9-4e9d-9254-c4629842430e
begin
ohoc_amu = OneHotOneCold(false, [163, 165, 194, 197])
local encoded_amu = encode(amu(HoPt₃), ohoc_amu)
local decoded_amu = map(x -> decode(x, ohoc_amu), encoded_amu)
@info "Feature" amu(HoPt₃)
@info "Encoded Feature" encoded_amu
@info "Decoded Feature" decoded_amu
end
# ╔═╡ 658a48c2-8409-4128-88e5-b974fe30701b
md"""
As it is with continuous values, after binning, we cannot recover the original values while decoding, only the bins into which the values were decoded themselves.
"""
# ╔═╡ f995ee00-d7c0-477a-8237-f9ddc1209468
md"""
#### Simple Codec
Let's now check out how we can write a simple Codec that performs a simple scaling and shifting operation, using a `SimpleCodec`!
We will have to define the encoding and decoding mechanism, and we can simply use `encode` and `decode` as we would normally!
"""
# ╔═╡ dd814c0a-e9d5-4749-bf8a-cb25cd7b0cab
begin
local simple = SimpleCodec(x -> 2x, x -> x/2)
local encoded_amu = encode(amu(HoPt₃), simple)
local decoded_amu = map(x -> decode(x, simple), encoded_amu)
@info "Feature" amu(HoPt₃)
@info "Encoded Feature" encoded_amu
@info "Decoded Feature" decoded_amu
end
# ╔═╡ 4d7c27c9-6540-4165-8fea-fbae95cdcca4
md"""
#### Direct Codec
We can also perform the same scaling operation using a `DirectCodec`, which is meant to be used for the same.
"""
# ╔═╡ a7260180-244c-4600-bd89-72e7bc857544
begin
local direct = DirectCodec(2) # to scale by 2
local encoded_amu = encode(amu(HoPt₃), direct)
local decoded_amu = map(x -> decode(x, direct), encoded_amu)
@info "Feature" amu(HoPt₃)
@info "Encoded Feature" encoded_amu
@info "Decoded Feature" decoded_amu
end
# ╔═╡ 2b5b1ef3-a782-43d5-9f16-20bc8303d67a
md"""#### Custom Codecs
But what if we want to write a custom Codec? It's simple! There's only two rules that your custom Codec needs to comply with.
* Your custom Codec must be a sub-type of `AbstractCodec`.
* Your custom Codec must have an associated `encode` and `decode` mechanism defined.
"""
# ╔═╡ 78791a9e-9537-4d66-9609-d470c90c675e
md"""
### Featurization Objects
Featurization objects store a set of feature descriptors, their associated codecs, and a procedure for combining the encoded features into whatever format they need to be in to be consumed by the model of interest. In keeping with ChemistryFeaturization being primarily an interface, concrete implementations of `AbstractFeaturization` types typically live in the repository associated with the model that they're used with, since a featurization approach is often rather specific to a model architecture.
In this example, we'll focus on `GraphNodeFeaturization` provided by the AtomicGraphNets package.
The simplest way to construct a `GraphNodeFeaturization` is just to provide a list of feature names. If they are all features that are built in to ChemistryFeaturization (that is, no additional lookup table is provided), then this is all you need!
"""
# ╔═╡ d2d30fca-8914-4853-ba20-32f50244be01
fzn = GraphNodeFeaturization(["Block", "Atomic radius"])
# ╔═╡ 26ac75f4-bab7-47e7-aad9-c6fa9e031737
md"""But we can customize things a bit more, of course! For example, if actual features and codecs aren't provided, the features will be constructed with default binnings and the results of `default_codec` will be used to populate the `codecs` field (remember that the first flag is for whether the feature is categorical valued or not):"""
# ╔═╡ 1686acf1-8d78-46a2-b1c6-925d1a3bccef
fzn.codecs
# ╔═╡ 57bae6d8-d52f-4f2c-be6e-a1b1fc017dcb
md"""Let's try building another `GraphNodeFeaturization` that includes some of the custom features we defined above (again, if we don't explicitly include codecs, the default ones will be built automatically)..."""
# ╔═╡ c77230c7-4b80-4312-b91d-2569b4d9e947
fzn_custom = GraphNodeFeaturization([block, awesomeness])
# ╔═╡ 601dcef9-a5fe-4939-888f-6acc455b852e
md"""Note that `encodable_elements` works on a featurization exactly the way you would expect (namely, it returns the intersection of the results on each feature):"""
# ╔═╡ d47cc98c-464c-4661-bcde-8d6fb6c04a86
encodable_elements(fzn_custom) # just the ones that awesomeness can encode
# ╔═╡ cb3e6815-4e48-4dff-90c3-cba87886cdab
md"""Finally, for ultimate tweakability, we can also feed in our own codecs, too!"""
# ╔═╡ 8686925b-5e74-43dd-9f55-afbf9fbef1de
fzn_morecustom = GraphNodeFeaturization([block, amu], [ohoc_block, ohoc_amu])
# ╔═╡ 6ae1ed8e-603a-4828-a562-1361516ec9ab
md"""Now let's see how we actually encode a featurization. Internally, what this function will do is call the `encode` method for each feature and then combine the results in the way specified for that featurization type. In the case of `GraphNodeFeaturization`, that simply means a concatenation."""
# ╔═╡ 5399ebb4-fed3-43dc-affe-1612a9b12bf1
encode(HoPt₃, fzn)
# ╔═╡ f99a0111-1348-4c12-8bc7-ac02ea4cee21
md"""And of course, we can also decode!"""
# ╔═╡ ad0ea23c-512c-482b-ab02-59397fe3a9ab
decode(encode(HoPt₃, fzn), fzn)
# ╔═╡ 6bf7dd34-d1f3-4145-ac95-069886c8a850
md"""
## Model Building, Evaluation, and Training
In this section, we will demonstrate how to build inputs for and architecture of an AtomicGraphNets model similar to those from `cgcnn.py`.
"""
# ╔═╡ 3bedaca0-d74b-4fa2-b910-593c8603ee33
md"""
### FeaturizedAtoms objects
FeaturizedAtoms objects encapsulate an atomic structure, a featurization scheme and the resultant encoded features that are generated by applying the featurization scheme to the atomic structure.
This mechanism ensures that we have the sufficient data (and metadata) required to effectively pre-featurize and serialize the object at given point, all while retaining the provenance of the encoded features (within reasonable limits, depending on the encoding and decoding mechanisms).
"""
# ╔═╡ 15d31f64-8d36-4b85-bb11-cd80b111b781
fg = featurize(HoPt₃, fzn)
# ╔═╡ 9dbff198-c067-4252-9673-450844810116
md"""We can `decode` a `FeaturizedAtoms` object as a single argument, since it has everything that's needed..."""
# ╔═╡ 66af0ad2-aea8-4416-8627-c0f29384c7da
decode(fg)
# ╔═╡ c0241b9c-77d1-47a0-9eb5-d65e7036e69c
md"""
### AtomicGraphNets layers and models
AtomicGraphNets provides a number of standard layers for operations such as graph convolution and pooling, as well as interfacing seamlessly with layers provided by Flux.jl.
The "workhorse" layer is the graph convolutional layer, `AGNConv`. Its action is to perform a graph convolution (using the graph Laplacian stored in the `AtomGraph` object). This is multiplied by the `convweight` matrix, added to the original input features multiplied by the `selfweight` matrix, and finally, the `bias` is added element-wise to give the final output.
We can construct one as follows, feeding the desired input and output node feature vector lengths:
"""
# ╔═╡ 53f83fcb-50e7-4d32-ab69-56fe4e54f425
la = AGNConv(14=>14)
# ╔═╡ 2d0393dc-8083-4fe9-9d5f-e9cd76af243f
md"""This can be applied directly to our featurized graph from above. The output is a copy of the graph Laplacian as well as the output feature matrix (i.e. the node feature vectors concatenated together). The Laplacian is ``passed through'' so that the full `FeaturizedAtoms` object doesn't need to be."""
# ╔═╡ 8d619f4c-beea-4917-8f71-49dd7bc82501
la(fg)
# ╔═╡ c81bd54d-c1de-44a5-b586-601310463be4
md"""We can apply it multiple times, too..."""
# ╔═╡ 82bad2b4-4598-436e-811b-fb19c9a69c9f
la(la(fg))
# ╔═╡ def924ff-4e46-4670-9f4b-112a1bf243d1
md"""Or chain multiple of them together with different sized latent spaces... (`Chain` is just Flux.jl shorthand for function composition)"""
# ╔═╡ 4f860da9-22d2-4e4b-8fa5-e9f9797039cb
begin
c = Chain(la, AGNConv(14=>10), AGNConv(10=>6))
c(fg)
end
# ╔═╡ 5e374c75-5fc1-4407-a667-33b1e79553a3
md"""But to build a full model, we need some other layers, too -- note that the output size of a convolutional layer will depend on the size of the input graph. We can use a *pooling* layer to get to a predictable output size. The `AGNPool` layer will apply a specified pooling function (for example, maximum or average) across nodes and reduce along the features (dim, stride, and pad will be automatically computed from the desired input and output sizes and a target ratio between dim and feature length) to get to the desired output dimensions:"""
# ╔═╡ bc5bc9a7-3e5a-47bb-bd86-bac74208df39
c2 = Chain(c, AGNPool("mean", 6, 3, 0.4))
# ╔═╡ e7a82f17-ed97-48e2-a8f4-95cb6b65a7ce
c2(fg) # output now will always be 3 x 1
# ╔═╡ 92ef3b47-c050-4a1b-bebf-11d09be0665a
md"""We can of course build up entire models "from scratch" in this way. However, AtomicGraphNets also includes some convenience functions for standard types of architectures. One of these is `build_CGCNN`, which will build a model similar to those in cgcnn.py. It takes one required argument for `input_feature_length` and a number of optional keyword arguments to make tweaks to the architecture such as number of layers, activation functions, etc. We'll utilize a few of them here."""
# ╔═╡ e6bc074f-f68f-4285-b058-7e2f6c665663
model = build_CGCNN(14, pooled_feature_length=8, num_hidden_layers=2)
# ╔═╡ 2d5a6b65-7703-4d31-8eb7-e7ef90460f65
md"""We can evaluate the model on a single input to get a prediction:"""
# ╔═╡ 58656c12-d6b3-42bb-ba58-f1d283f98614
model(fg)
# ╔═╡ 04106bba-7302-4e93-aecd-73a14d0aea03
md"""...but that doesn't mean much when we don't even know what we're predicting, and haven't done any model training!
Training proceeds as with any Flux.jl model. Let's demonstrate how that works for a "toy" dataset..."""
# ╔═╡ 98bd6bea-9d2e-48c3-843b-e29e860fe5ae
train_set = [(fg, 1.0)]
# ╔═╡ 32df46cc-4131-40bc-a1e0-1b46a5dcb01c
begin
opt_state = Flux.setup(Adam(), model)
loss = Flux.Losses.mse
for epoch in 1:5
Flux.train!(model, train_set, opt_state) do m, x, y
loss(m(x), y)
end
end
end
# ╔═╡ 9e640d76-eaa5-45fe-b2d4-f6ee5483dfed
md"""If we now evaluate the model on that same piece of data again, we'll see the result has moved in the right direction..."""
# ╔═╡ 89a79d36-d201-43f1-8a56-c66457cc4848
model(fg)
# ╔═╡ c280f1b3-ad5e-4023-ab97-7e8f7f0b1c3b
md"""
### More Interoperability!
As another demonstration of the interoperability enabled by Chemellia and Julia in general, we will include here an example of how to use layers from the [GeometricFlux](https://github.com/FluxML/GeometricFlux.jl) package in AtomicGraphNets models through the power of multiple dispatch. As of April 2023, this functionality exists on [an unmerged branch](https://github.com/Chemellia/AtomicGraphNets.jl/blob/layer_sharing/examples/2_layer_sharing/layer_sharing.jl) of AtomicGraphNets. If and when it gets fleshed out, it will be merged in as an "officially supported" feature.
"""
# ╔═╡ b22e0107-c5fc-43e1-bcb3-cd39a66b0732
import GraphSignals: normalized_adjacency_matrix
# ╔═╡ 67b8e10f-04e2-43e6-8c34-b9168ed1b676
md"""Let's remind ourselves of the size of the encoded features in our FeaturedGraph from above, should be 4 (# atoms) x 14 (# features, 4 for block and 10 for radius)"""
# ╔═╡ f30a15f7-f330-44f4-ba1a-1299d63252c5
size(fg.encoded_features) # check size, should be 16 (# atoms) x 14 (# features, 4 for block and 10 for radius)
# ╔═╡ bc54ac22-2c74-43e7-96c4-4e65ffa849b4
md"""We know that we can apply the layer from above to this FeaturedGraph:"""
# ╔═╡ 88125ff9-0a50-4dd5-9510-37ae19d37349
la(fg) # first output is graph laplacian, second is new feature matrix
# ╔═╡ 112f5908-d8b9-4784-b3e2-64145b96702a
md"""Okay, so let's start with the `GCNConv` layer. It has a syntax that takes in a normalized adjacency matrix and a feature matrix, so we can dispatch to that
the only slight additional wrinkle is it expects the transpose in the argument, so to "translate" appropriately, we need to transpose the input and then output again. We can do this by defining the dispatch of a `GCNConv` layer on a `FeaturedGraph` object."""
# ╔═╡ a77703aa-ee96-4259-b2c7-c935c08aa661
(l::GCNConv)(fg::FeaturizedAtoms{AtomGraph{T}, GraphNodeFeaturization}) where T = Matrix(l(normalized_adjacency_matrix(fg.atoms.graph), fg.encoded_features')')
# ╔═╡ 9898c33f-e3f9-4273-9196-4cfcd7d6bc8a
md"""We'll also define the dispatch onto the tuples that AGNConv outputs so we can easily chain them together -- note that this is a slight hack and will change the behavior a bit because we'll be passing a graph Laplacian as an adjacency matrix...also we transpose it back to "default" to the AGN convention as well as passing through the Laplacian..."""
# ╔═╡ f4251f8d-4cd9-49f9-88f6-617305115417
(l::GCNConv)(t::Tuple{Matrix{R1},Matrix{R2}}) where {R1<:Real,R2<:Real} = (t[1], l(t[1], t[2]')')
# ╔═╡ 345d7e2d-a758-4282-8750-1b0ca70dcff9
md"""Let's make sure that this works for an individual layer..."""
# ╔═╡ 783d0cea-fa78-4e03-900d-63b2cfe72a9f
lg = GCNConv(14=>10)
# ╔═╡ 1744c053-a7be-47b3-a43e-ba892f47a11f
lg(fg)
# ╔═╡ 1d199188-384a-4836-a051-b93b7d75ca86
md"""...and also for a chained combination of layers..."""
# ╔═╡ 43609597-d5e3-454b-af6b-abb7f01a91ea
m = Chain(la, lg)
# ╔═╡ 0adcca11-bbca-42ca-ba38-3e7ffee703cc
m(fg)
# ╔═╡ 60394155-ba71-450e-93b2-8123b487561d
md"""But GCNConv is almost the same as AtomicGraphNets' `AGNConv` really, so it's not really all that interesting or exciting, let's try something a bit different...how about the `TopKPool` layer?
(note that this layer as constructed takes a fixed adjacency matrix, so it's likely not that useful for our purposes, this is more for demonstration...though I think it would be pretty easy to make a version that doesn't need to be that way)
As before, we first define the dispatch..."""
# ╔═╡ c5ca6518-a34b-4990-aca7-6e2ff157eb5a
(tk::TopKPool)(t::Tuple{Matrix{R1},AbstractMatrix{R2}}) where {R1<:Real,R2<:Real} = tk(t[2]')
# ╔═╡ d3d149fd-6652-444e-83fa-0701162f947a
md"""...and check that it works."""
# ╔═╡ 35603fa9-254d-4e74-a2d3-d1a72a593107
tk = TopKPool(fg.atoms.graph.weights, 4, 10) # adjacency matrix, k, input size
# ╔═╡ 68c64be8-2664-4c24-8e90-939e94102aea
m2 = Chain(la, lg, tk)
# ╔═╡ 52f90156-7640-4e74-86f0-6d352475818d
m2(fg)
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
AtomGraphs = "5a360db7-3336-4c46-b5a1-be4fe079ea55"
AtomicGraphNets = "ccdf130a-3c88-4595-a023-a7e7f78b9dd5"
AtomsBase = "a963bdd2-2df7-4f54-a1ee-49d51e6be12a"
AtomsIO = "1692102d-eeb4-4df9-807b-c9517f998d44"
ChemistryFeaturization = "6c925690-434a-421d-aea7-51398c5b007a"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DataSets = "c9661210-8a83-48f0-b833-72e62abce419"
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
GeometricFlux = "7e08b658-56d3-11e9-2997-919d5b31e4ea"
GraphSignals = "3ebe565e-a4b5-49c6-aed2-300248c3a9c1"
JuliaHubData = "e241c0f9-2941-4184-86f1-92558f4420e8"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[compat]
AtomGraphs = "~0.1.3"
AtomicGraphNets = "~0.2.4"
AtomsBase = "~0.2.5"
AtomsIO = "~0.1.2"
ChemistryFeaturization = "~0.7.1"
DataFrames = "~1.3.6"
DataSets = "~0.2.9"
Flux = "~0.13.13"
GeometricFlux = "~0.13.8"
GraphSignals = "~0.8.5"
JuliaHubData = "~0.3.6"
PlutoUI = "~0.7.50"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.8.5"
manifest_format = "2.0"
project_hash = "dd2c330517a73b5fc5552c0cfecf9cb08a45305a"
[[deps.ASEconvert]]
deps = ["AtomsBase", "CondaPkg", "PeriodicTable", "PythonCall", "Unitful", "UnitfulAtomic"]
git-tree-sha1 = "8f62517390341fa47bce341a598aed60d824b14a"
uuid = "3da9722f-58c2-4165-81be-b4d7253e8fd2"
version = "0.1.3"
[[deps.AWS]]
deps = ["Base64", "Compat", "Dates", "Downloads", "GitHub", "HTTP", "IniFile", "JSON", "MbedTLS", "Mocking", "OrderedCollections", "Random", "SHA", "Sockets", "URIs", "UUIDs", "XMLDict"]
git-tree-sha1 = "8ae44ce9415f2c2c8163258c003460530a6f095b"
uuid = "fbe9abb3-538b-5e4e-ba9e-bc94f4f92ebc"
version = "1.82.0"
[[deps.AbstractFFTs]]
deps = ["ChainRulesCore", "LinearAlgebra"]
git-tree-sha1 = "16b6dbc4cf7caee4e1e75c49485ec67b667098a0"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.3.1"
[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "8eaf9f1b4921132a4cff3f36a1d9ba923b14a481"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.1.4"
[[deps.AbstractTrees]]
git-tree-sha1 = "faa260e4cb5aba097a73fab382dd4b5819d8ec8c"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.4.4"
[[deps.Accessors]]
deps = ["Compat", "CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Requires", "StaticArrays", "Test"]
git-tree-sha1 = "c7dddee3f32ceac12abd9a21cd0c4cb489f230d2"
uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
version = "0.1.29"
[[deps.Adapt]]
deps = ["LinearAlgebra", "Requires"]
git-tree-sha1 = "cc37d689f599e8df4f464b2fa3870ff7db7492ef"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.6.1"
[[deps.ArgCheck]]
git-tree-sha1 = "a3a402a35a2f7e0b87828ccabbd5ebfbebe356b4"
uuid = "dce04be8-c92d-5529-be00-80e4d2c0e197"
version = "2.3.0"
[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.1"
[[deps.ArnoldiMethod]]
deps = ["LinearAlgebra", "Random", "StaticArrays"]
git-tree-sha1 = "62e51b39331de8911e4a7ff6f5aaf38a5f4cc0ae"
uuid = "ec485272-7323-5ecc-a04f-4719b315124d"
version = "0.2.0"
[[deps.ArrayInterface]]
deps = ["Adapt", "LinearAlgebra", "Requires", "SnoopPrecompile", "SparseArrays", "SuiteSparse"]
git-tree-sha1 = "38911c7737e123b28182d89027f4216cfc8a9da7"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "7.4.3"
[[deps.ArrayInterfaceCore]]
deps = ["LinearAlgebra", "SnoopPrecompile", "SparseArrays", "SuiteSparse"]
git-tree-sha1 = "e5f08b5689b1aad068e01751889f2f615c7db36d"
uuid = "30b0a656-2188-435a-8636-2ec0e6a096e2"
version = "0.1.29"
[[deps.ArrayInterfaceTracker]]
deps = ["ArrayInterfaceCore", "Tracker"]
git-tree-sha1 = "9600e1ef98f7067dc91c22c0759c60580a0320dd"
uuid = "a2b0951a-f94f-4742-8780-617792921f9b"
version = "0.1.1"
[[deps.ArrayLayouts]]
deps = ["FillArrays", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "4aff5fa660eb95c2e0deb6bcdabe4d9a96bc4667"
uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
version = "0.8.18"
[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
[[deps.AtomGraphs]]
deps = ["AtomsBase", "AtomsIO", "Cairo", "ChemistryFeaturization", "Colors", "CondaPkg", "Fontconfig", "GraphPlot", "Graphs", "LinearAlgebra", "MolecularGraph", "NearestNeighbors", "PythonCall", "Serialization", "SimpleWeightedGraphs", "StaticArrays", "Unitful", "Xtals"]
git-tree-sha1 = "f65574f86702121147a7723e2f68c04431a5d5fe"
uuid = "5a360db7-3336-4c46-b5a1-be4fe079ea55"
version = "0.1.3"
[[deps.AtomicGraphNets]]
deps = ["AtomGraphs", "CSV", "ChemistryFeaturization", "DataFrames", "DelimitedFiles", "DiffEqSensitivity", "DifferentialEquations", "Flux", "Graphs", "LinearAlgebra", "NNlib", "Pkg", "Random", "Serialization", "SimpleWeightedGraphs", "SparseArrays", "Statistics", "Zygote"]
git-tree-sha1 = "9ca8021d9ef775e1e1cbdb3a134fc58aff57ad72"
uuid = "ccdf130a-3c88-4595-a023-a7e7f78b9dd5"
version = "0.2.4"
[[deps.Atomix]]
deps = ["UnsafeAtomics"]
git-tree-sha1 = "c06a868224ecba914baa6942988e2f2aade419be"
uuid = "a9b6321e-bd34-4604-b9c9-b65b8de01458"
version = "0.1.0"
[[deps.AtomsBase]]
deps = ["PeriodicTable", "Printf", "StaticArrays", "Unitful", "UnitfulAtomic"]
git-tree-sha1 = "3b71708412baff568e5be0dd91d54aa807595b73"
uuid = "a963bdd2-2df7-4f54-a1ee-49d51e6be12a"
version = "0.2.5"
[[deps.AtomsIO]]
deps = ["ASEconvert", "AtomsBase", "Chemfiles", "ExtXYZ", "LinearAlgebra", "Logging", "PeriodicTable", "Reexport", "StaticArrays", "Unitful", "UnitfulAtomic"]
git-tree-sha1 = "3c896c6126cac9fbf0adac857a53049516cf45a7"
uuid = "1692102d-eeb4-4df9-807b-c9517f998d44"
version = "0.1.2"
[[deps.BFloat16s]]
deps = ["LinearAlgebra", "Printf", "Random", "Test"]
git-tree-sha1 = "a598ecb0d717092b5539dbbe890c98bac842b072"
uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b"
version = "0.2.0"
[[deps.BandedMatrices]]
deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra", "SnoopPrecompile", "SparseArrays"]
git-tree-sha1 = "6ef8fc1d77b60f41041d59ce61ef9eb41ed97a83"
uuid = "aae01518-5342-5314-be14-df237901396f"
version = "0.17.18"
[[deps.BangBang]]
deps = ["Compat", "ConstructionBase", "Future", "InitialValues", "LinearAlgebra", "Requires", "Setfield", "Tables", "ZygoteRules"]
git-tree-sha1 = "7fe6d92c4f281cf4ca6f2fba0ce7b299742da7ca"
uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66"
version = "0.3.37"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[deps.Baselet]]
git-tree-sha1 = "aebf55e6d7795e02ca500a689d326ac979aaf89e"
uuid = "9718e550-a3fa-408a-8086-8db961cd8217"
version = "0.1.1"
[[deps.Bio3DView]]
deps = ["Requires"]
git-tree-sha1 = "a1a4d498d8574e979ae3b66325fa03102b7980e8"
uuid = "99c8bb3a-9d13-5280-9740-b4880ed9c598"
version = "0.1.4"
[[deps.BitFlags]]
git-tree-sha1 = "43b1a4a8f797c1cddadf60499a8a077d4af2cd2d"
uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35"
version = "0.1.7"
[[deps.BitTwiddlingConvenienceFunctions]]
deps = ["Static"]
git-tree-sha1 = "0c5f81f47bbbcf4aea7b2959135713459170798b"
uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b"
version = "0.1.5"
[[deps.BlockArrays]]
deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"]
git-tree-sha1 = "3b15c61bcece7c426ea641d143c808ace3661973"
uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"
version = "0.16.25"
[[deps.BlockBandedMatrices]]
deps = ["ArrayLayouts", "BandedMatrices", "BlockArrays", "FillArrays", "LinearAlgebra", "MatrixFactorizations", "SparseArrays", "Statistics"]
git-tree-sha1 = "f389a2752664c4103f9c481b4766d7eed78ad85b"
uuid = "ffab5731-97b5-5995-9138-79e8c1846df0"
version = "0.11.10"
[[deps.BoundaryValueDiffEq]]
deps = ["BandedMatrices", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "NLsolve", "Reexport", "SciMLBase", "SparseArrays"]
git-tree-sha1 = "ed8e837bfb3d1e3157022c9636ec1c722b637318"
uuid = "764a87c0-6b3e-53db-9096-fe964310641d"
version = "2.11.0"
[[deps.BufferedStreams]]
git-tree-sha1 = "bb065b14d7f941b8617bc323063dbe79f55d16ea"
uuid = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d"
version = "1.1.0"
[[deps.Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.8+0"
[[deps.CEnum]]
git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.2"
[[deps.CPUSummary]]
deps = ["CpuId", "IfElse", "Static"]
git-tree-sha1 = "2c144ddb46b552f72d7eafe7cc2f50746e41ea21"
uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9"
version = "0.2.2"
[[deps.CSV]]
deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "SentinelArrays", "SnoopPrecompile", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"]
git-tree-sha1 = "c700cce799b51c9045473de751e9319bdd1c6e94"
uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
version = "0.10.9"
[[deps.CUDA]]
deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"]
git-tree-sha1 = "6717cb9a3425ebb7b31ca4f832823615d175f64a"
uuid = "052768ef-5323-5732-b1bb-66c8b64840ba"
version = "3.13.1"
[[deps.Cairo]]
deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"]
git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b"
uuid = "159f3aea-2a34-519c-b102-8c37f9878175"
version = "1.0.5"
[[deps.Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_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 = "4b859a208b2397a7a623a03449e4636bdb17bcf2"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+1"
[[deps.Calculus]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad"
uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9"
version = "0.5.1"
[[deps.Cassette]]
git-tree-sha1 = "a70f220ea09ec61401745ff338f8fb340420165c"
uuid = "7057c7e9-c182-5462-911a-8362d720325c"
version = "0.3.11"
[[deps.ChainRules]]
deps = ["Adapt", "ChainRulesCore", "Compat", "Distributed", "GPUArraysCore", "IrrationalConstants", "LinearAlgebra", "Random", "RealDot", "SparseArrays", "Statistics", "StructArrays"]
git-tree-sha1 = "8bae903893aeeb429cf732cf1888490b93ecf265"
uuid = "082447d4-558c-5d27-93f4-14fc19e9eca2"
version = "1.49.0"
[[deps.ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "c6d890a52d2c4d55d326439580c3b8d0875a77d9"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.15.7"
[[deps.ChangesOfVariables]]
deps = ["LinearAlgebra", "Test"]
git-tree-sha1 = "f84967c4497e0e1955f9a582c232b02847c5f589"
uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
version = "0.1.7"
[[deps.Chemfiles]]
deps = ["Chemfiles_jll", "DocStringExtensions"]
git-tree-sha1 = "9126d0271c337ca5ed02ba92f2dec087c4260d4a"
uuid = "46823bd8-5fb3-5f92-9aa0-96921f3dd015"
version = "0.10.31"
[[deps.Chemfiles_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d4e54b053fc584e7a0f37e9d3a5c4500927b343a"
uuid = "78a364fa-1a3c-552a-b4bb-8fa0f9c1fcca"
version = "0.10.3+0"
[[deps.ChemistryFeaturization]]
deps = ["AtomsBase", "CSV", "DataFrames", "Flux", "JSON3", "LinearAlgebra", "Serialization", "SparseArrays"]
git-tree-sha1 = "030c6e5be9f316b3870b033662ab908efa41d102"
uuid = "6c925690-434a-421d-aea7-51398c5b007a"
version = "0.7.1"
[[deps.CloseOpenIntervals]]
deps = ["Static", "StaticArrayInterface"]
git-tree-sha1 = "70232f82ffaab9dc52585e0dd043b5e0c6b714f1"
uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9"
version = "0.1.12"
[[deps.CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.1"
[[deps.ColorSchemes]]
deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"]
git-tree-sha1 = "be6ab11021cd29f0344d5c4357b163af05a48cba"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.21.0"
[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.4"
[[deps.ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
git-tree-sha1 = "600cc5508d66b78aae350f7accdb58763ac18589"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.9.10"
[[deps.Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.10"
[[deps.CommonSolve]]
git-tree-sha1 = "9441451ee712d1aec22edad62db1a9af3dc8d852"
uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2"
version = "0.2.3"
[[deps.CommonSubexpressions]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7"
uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
version = "0.3.0"
[[deps.Compat]]
deps = ["Dates", "LinearAlgebra", "UUIDs"]
git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "4.6.1"
[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.0.1+0"
[[deps.Compose]]
deps = ["Base64", "Colors", "DataStructures", "Dates", "IterTools", "JSON", "LinearAlgebra", "Measures", "Printf", "Random", "Requires", "Statistics", "UUIDs"]
git-tree-sha1 = "bf6570a34c850f99407b494757f5d7ad233a7257"
uuid = "a81c6b42-2e10-5240-aca2-a61377ecd94b"
version = "0.9.5"
[[deps.CompositeTypes]]
git-tree-sha1 = "02d2316b7ffceff992f3096ae48c7829a8aa0638"
uuid = "b152e2b5-7a66-4b01-a709-34e65c35f657"
version = "0.1.3"
[[deps.CompositionsBase]]
git-tree-sha1 = "455419f7e328a1a2493cabc6428d79e951349769"
uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b"
version = "0.1.1"
[[deps.ConcurrentUtilities]]
deps = ["Serialization", "Sockets"]
git-tree-sha1 = "b306df2650947e9eb100ec125ff8c65ca2053d30"
uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
version = "2.1.1"
[[deps.CondaPkg]]
deps = ["JSON3", "Markdown", "MicroMamba", "Pidfile", "Pkg", "TOML"]
git-tree-sha1 = "741146cf2ced5859faae76a84b541aa9af1a78bb"
uuid = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
version = "0.2.18"
[[deps.ConstructionBase]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "89a9db8d28102b094992472d333674bd1a83ce2a"
uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
version = "1.5.1"
[[deps.ContextVariablesX]]
deps = ["Compat", "Logging", "UUIDs"]
git-tree-sha1 = "25cc3803f1030ab855e383129dcd3dc294e322cc"
uuid = "6add18c4-b38d-439d-96f6-d6bc489c04c5"
version = "0.1.3"
[[deps.CpuId]]
deps = ["Markdown"]
git-tree-sha1 = "fcbb72b032692610bfbdb15018ac16a36cf2e406"
uuid = "adafc99b-e345-5852-983c-f28acb93d879"
version = "0.3.1"
[[deps.Crayons]]
git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.1.1"
[[deps.DataAPI]]
git-tree-sha1 = "e8119c1a33d267e16108be441a287a6981ba1630"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.14.0"
[[deps.DataDeps]]
deps = ["HTTP", "Libdl", "Reexport", "SHA", "p7zip_jll"]
git-tree-sha1 = "bc0a264d3e7b3eeb0b6fc9f6481f970697f29805"
uuid = "124859b0-ceae-595e-8997-d05f6a7a8dfe"
version = "0.7.10"
[[deps.DataFrames]]
deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Reexport", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
git-tree-sha1 = "db2a9cb664fcea7836da4b414c3278d71dd602d2"
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
version = "1.3.6"
[[deps.DataSets]]
deps = ["AbstractTrees", "Base64", "Markdown", "REPL", "ReplMaker", "ResourceContexts", "SHA", "TOML", "UUIDs"]
git-tree-sha1 = "bbb3ea880461e62709e8cb181856de19c383e99c"
uuid = "c9661210-8a83-48f0-b833-72e62abce419"
version = "0.2.9"
[[deps.DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.13"
[[deps.DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"
[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[deps.DefineSingletons]]
git-tree-sha1 = "0fba8b706d0178b4dc7fd44a96a92382c9065c2c"
uuid = "244e2a9f-e319-4986-a169-4d1fe445cd52"
version = "0.1.2"
[[deps.DelayDiffEq]]
deps = ["ArrayInterface", "DataStructures", "DiffEqBase", "LinearAlgebra", "Logging", "OrdinaryDiffEq", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SimpleUnPack"]
git-tree-sha1 = "89f3fbfe78f9d116d1ed0721d65b0b2cf9b36169"
uuid = "bcd4f6db-9728-5f36-b5f7-82caef46ccdb"
version = "5.42.0"
[[deps.DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
[[deps.DensityInterface]]
deps = ["InverseFunctions", "Test"]
git-tree-sha1 = "80c3e8639e3353e5d2912fb3a1916b8455e2494b"
uuid = "b429d917-457f-4dbc-8f4c-0cc954292b1d"
version = "0.4.0"
[[deps.DiffEqBase]]
deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "Distributions", "DocStringExtensions", "EnumX", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"]
git-tree-sha1 = "988bbd7283aaee5c34cd3cc09e78e7c45a931c5b"
uuid = "2b5f629d-d688-5b77-993f-72d75c75574e"
version = "6.123.0"
[[deps.DiffEqCallbacks]]
deps = ["DataStructures", "DiffEqBase", "ForwardDiff", "LinearAlgebra", "Markdown", "NLsolve", "Parameters", "RecipesBase", "RecursiveArrayTools", "SciMLBase", "StaticArraysCore"]
git-tree-sha1 = "63b6be7b396ad395825f3cc48c56b53bfaf7e69d"
uuid = "459566f4-90b8-5000-8ac3-15dfb0a30def"
version = "2.26.1"
[[deps.DiffEqNoiseProcess]]
deps = ["DiffEqBase", "Distributions", "GPUArraysCore", "LinearAlgebra", "Markdown", "Optim", "PoissonRandom", "QuadGK", "Random", "Random123", "RandomNumbers", "RecipesBase", "RecursiveArrayTools", "Requires", "ResettableStacks", "SciMLBase", "StaticArrays", "Statistics"]
git-tree-sha1 = "2c4ed3eedb87579bfe9f20ecc2440de06b9f3b89"
uuid = "77a26b50-5914-5dd7-bc55-306e6241c503"
version = "5.16.0"
[[deps.DiffEqOperators]]
deps = ["BandedMatrices", "BlockBandedMatrices", "DiffEqBase", "DomainSets", "ForwardDiff", "LazyArrays", "LazyBandedMatrices", "LinearAlgebra", "LoopVectorization", "NNlib", "Requires", "RuntimeGeneratedFunctions", "SciMLBase", "SparseArrays", "SparseDiffTools", "StaticArrays", "SuiteSparse"]
git-tree-sha1 = "ef0f24d69406bf85092c843691ab2ae3b2e34b77"
uuid = "9fdde737-9c7f-55bf-ade8-46b3f136cc48"
version = "4.45.0"
[[deps.DiffEqSensitivity]]
deps = ["Adapt", "ArrayInterfaceCore", "ArrayInterfaceTracker", "Cassette", "ChainRulesCore", "DiffEqBase", "DiffEqCallbacks", "DiffEqNoiseProcess", "DiffEqOperators", "DiffRules", "Distributions", "EllipsisNotation", "Enzyme", "FiniteDiff", "ForwardDiff", "GPUArrays", "LinearAlgebra", "LinearSolve", "Markdown", "OrdinaryDiffEq", "Parameters", "QuadGK", "Random", "RandomNumbers", "RecursiveArrayTools", "Reexport", "ReverseDiff", "SciMLBase", "Statistics", "StochasticDiffEq", "Tracker", "Zygote", "ZygoteRules"]
git-tree-sha1 = "87fd2c08bd8749906cdf253a240b21a5c92b7214"
uuid = "41bf760c-e81c-5289-8e54-58b1f1f8abe2"
version = "6.79.0"
[[deps.DiffResults]]
deps = ["StaticArraysCore"]
git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621"
uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
version = "1.1.0"
[[deps.DiffRules]]
deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"]
git-tree-sha1 = "a4ad7ef19d2cdc2eff57abbbe68032b1cd0bd8f8"