From 598ef8c4409a21546cd4b7b6137f6fd1fd3b06e9 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 10 Feb 2021 15:15:42 -0500 Subject: [PATCH 001/239] starts perturbation fn for individual structs --- src/autocat/perturbations.py | 95 ++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/autocat/perturbations.py diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py new file mode 100644 index 00000000..c90efbbe --- /dev/null +++ b/src/autocat/perturbations.py @@ -0,0 +1,95 @@ +from ase.io import read, write +from ase import Atoms +from typing import List +from typing import Union +from typing import Dict +import numpy as np + + +def generate_perturbed_dataset( + base_structures: List[Atoms], + atom_indices_to_perturb_dictionary: Dict[str, List[int]], +): + """ + + Generates a dataset consisting of perturbed structures from + a base list of structures and keeps track of displacement + vectors + + Parameters + ---------- + + base_structures: + List of Atoms objects or name of file containing structure + as a strings specifying the base structures to be + perturbed + + atom_indices_to_perturb_dictionary: + Dictionary List of atomic indices for the atoms that should + be perturbed. Keys are each of the provided base structures + + Returns + ------- + + """ + return None + + +def perturb_structure( + base_structure: Union[str, Atoms], + atom_indices_to_perturb: List[int], + minimum_perturbation_distance: float = 0.1, + maximum_perturbation_distance: float = 1.0, +): + """ + + Perturbs specific atoms in a given structure and keeps + track of the displacement vectors of each displaced atom + + Parameters + ---------- + + base_structure: + Atoms object or name of file containing structure + as a string specifying the base structure to be + perturbed + + atom_indices_to_perturb: + List of atomic indices for the atoms that should + be perturbed + + minimum_perturbation_distance: + Float of minimum acceptable perturbation distance + + maximum_perturbation_distance: + Float of maximum acceptable perturbation distance + + Returns + ------- + + perturb_dictionary: + Dictionary with perturbed structure and displacement vectors + + """ + if isinstance(base_structure, Atoms): + ase_obj = base_structure.copy() + elif isinstance(base_structure, str): + ase_obj = read(base_structure) + else: + raise TypeError("base_structure needs to be either a str or ase.Atoms object") + + pert_matrix = np.zeros(ase_obj.positions.shape) + + for idx in atom_indices_to_perturb: + # randomize +/- direction of each perturbation + signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=2, size=(1, 3)) + # generate perturbation matrix + pert_matrix[idx, :] = signs * np.random.uniform( + low=minimum_perturbation_distance, + high=maximum_perturbation_distance, + size=(1, 3), + ) + + ase_obj.positions += pert_matrix + + return {"structure": ase_obj, "perturbation_matrix": pert_matrix} From b600add287dcbada76d82c47cf95f38b9ddda551 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 10 Feb 2021 15:34:07 -0500 Subject: [PATCH 002/239] adds direction constraints --- src/autocat/perturbations.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index c90efbbe..ced5c86d 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -40,6 +40,7 @@ def perturb_structure( atom_indices_to_perturb: List[int], minimum_perturbation_distance: float = 0.1, maximum_perturbation_distance: float = 1.0, + directions: List[bool] = None, ): """ @@ -64,6 +65,10 @@ def perturb_structure( maximum_perturbation_distance: Float of maximum acceptable perturbation distance + directions: + List of bools indicating which cartesian directions + the atoms are allowed to be perturbed in + Returns ------- @@ -78,16 +83,25 @@ def perturb_structure( else: raise TypeError("base_structure needs to be either a str or ase.Atoms object") + if directions is None: + constr = [True, True, True] + else: + constr = directions + pert_matrix = np.zeros(ase_obj.positions.shape) for idx in atom_indices_to_perturb: # randomize +/- direction of each perturbation signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=2, size=(1, 3)) # generate perturbation matrix - pert_matrix[idx, :] = signs * np.random.uniform( - low=minimum_perturbation_distance, - high=maximum_perturbation_distance, - size=(1, 3), + pert_matrix[idx, :] = ( + constr + * signs + * np.random.uniform( + low=minimum_perturbation_distance, + high=maximum_perturbation_distance, + size=(1, 3), + ) ) ase_obj.positions += pert_matrix From b907cedf742904d19160db4c317b1f8f2ddf3fca Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 10 Feb 2021 17:58:16 -0500 Subject: [PATCH 003/239] adds bulk generation of perturbed structures; fixes sign error in perturbation matrix --- src/autocat/perturbations.py | 40 ++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index ced5c86d..6a235270 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -9,6 +9,10 @@ def generate_perturbed_dataset( base_structures: List[Atoms], atom_indices_to_perturb_dictionary: Dict[str, List[int]], + minimum_perturbation_distance: float = 0.1, + maximum_perturbation_distance: float = 1.0, + directions: List[bool] = None, + num_of_perturbations: int = 10, ): """ @@ -28,11 +32,43 @@ def generate_perturbed_dataset( Dictionary List of atomic indices for the atoms that should be perturbed. Keys are each of the provided base structures + minimum_perturbation_distance: + Float of minimum acceptable perturbation distance + + maximum_perturbation_distance: + Float of maximum acceptable perturbation distance + + directions: + List of bools indicating which cartesian directions + the atoms are allowed to be perturbed in + Returns ------- + perturbed_dict: + Dictionary containing all generated perturbed structures + with their corresponding perturbation matrices + """ - return None + + perturbed_dict = {} + + for structure in base_structures: + if isinstance(structure, Atoms): + name = structure.get_chemical_formula() + else: + name = structure + perturbed_dict[name] = {} + for i in range(num_of_perturbations): + perturbed_dict[name][str(i)] = perturb_structure( + structure, + atom_indices_to_perturb=atom_indices_to_perturb_dictionary[name], + minimum_perturbation_distance=minimum_perturbation_distance, + maximum_perturbation_distance=maximum_perturbation_distance, + directions=directions, + ) + + return perturbed_dict def perturb_structure( @@ -92,7 +128,7 @@ def perturb_structure( for idx in atom_indices_to_perturb: # randomize +/- direction of each perturbation - signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=2, size=(1, 3)) + signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=11, size=(1, 3)) # generate perturbation matrix pert_matrix[idx, :] = ( constr From 1b348fbf91877c3e0354e24c7b04a31cd1845025 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 11 Feb 2021 15:43:01 -0500 Subject: [PATCH 004/239] allows writing perturbed structs to disk --- src/autocat/perturbations.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 6a235270..cbc1df94 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -4,6 +4,7 @@ from typing import Union from typing import Dict import numpy as np +import os def generate_perturbed_dataset( @@ -13,6 +14,8 @@ def generate_perturbed_dataset( maximum_perturbation_distance: float = 1.0, directions: List[bool] = None, num_of_perturbations: int = 10, + write_to_disk: bool = False, + write_location: str = ".", ): """ @@ -42,6 +45,15 @@ def generate_perturbed_dataset( List of bools indicating which cartesian directions the atoms are allowed to be perturbed in + write_to_disk: + Boolean specifying whether the perturbed structures generated should be + written to disk. + Defaults to False. + + write_location: + String with the location where the perturbed structure + files written to disk. + Returns ------- @@ -58,6 +70,7 @@ def generate_perturbed_dataset( name = structure.get_chemical_formula() else: name = structure + perturbed_dict[name] = {} for i in range(num_of_perturbations): perturbed_dict[name][str(i)] = perturb_structure( @@ -67,6 +80,12 @@ def generate_perturbed_dataset( maximum_perturbation_distance=maximum_perturbation_distance, directions=directions, ) + traj_file_path = None + if write_to_disk: + traj_file_path = os.path.join(write_location, f"{name}_{str(i)}.traj") + perturbed_dict[name][str(i)]["structure"].write(traj_file_path) + print(f"{name}_{str(i)}.traj written to {traj_file_path}") + perturbed_dict[name][str(i)].update({"traj_file_path": traj_file_path}) return perturbed_dict From 370eb5cc3fc70a47574ad3400ffc45d6824b677a Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 11 Feb 2021 17:26:09 -0500 Subject: [PATCH 005/239] write_location bugfix; rm suffix from name when struct read from file --- src/autocat/perturbations.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index cbc1df94..ef367f5b 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -16,6 +16,7 @@ def generate_perturbed_dataset( num_of_perturbations: int = 10, write_to_disk: bool = False, write_location: str = ".", + dirs_exist_ok: bool = False, ): """ @@ -45,6 +46,10 @@ def generate_perturbed_dataset( List of bools indicating which cartesian directions the atoms are allowed to be perturbed in + num_of_perturbations: + Int specifying number of perturbations to generate. + Default 10 + write_to_disk: Boolean specifying whether the perturbed structures generated should be written to disk. @@ -54,6 +59,13 @@ def generate_perturbed_dataset( String with the location where the perturbed structure files written to disk. + dirs_exist_ok: + Boolean specifying whether existing directories/files should be + overwritten or not. This is passed on to the `os.makedirs` builtin. + Defaults to False (raises an error if directories corresponding the + species and crystal structure already exist). + + Returns ------- @@ -65,11 +77,16 @@ def generate_perturbed_dataset( perturbed_dict = {} + if write_to_disk: + os.makedirs(write_location, exist_ok=dirs_exist_ok) + for structure in base_structures: if isinstance(structure, Atoms): name = structure.get_chemical_formula() + elif isinstance(structure, str): + name = ".".join(structure.split(".")[:-1]) else: - name = structure + raise TypeError(f"Structure needs to be either a str or ase.Atoms object") perturbed_dict[name] = {} for i in range(num_of_perturbations): From a2ba7c1702e86f2c40065a3efd9090078a68dd5d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 12 Feb 2021 18:19:19 -0500 Subject: [PATCH 006/239] incorporate nested organization --- src/autocat/perturbations.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index ef367f5b..58793a89 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -77,9 +77,6 @@ def generate_perturbed_dataset( perturbed_dict = {} - if write_to_disk: - os.makedirs(write_location, exist_ok=dirs_exist_ok) - for structure in base_structures: if isinstance(structure, Atoms): name = structure.get_chemical_formula() @@ -99,7 +96,9 @@ def generate_perturbed_dataset( ) traj_file_path = None if write_to_disk: - traj_file_path = os.path.join(write_location, f"{name}_{str(i)}.traj") + dir_path = os.path.join(write_location, f"{name}/{str(i)}") + os.makedirs(dir_path, exist_ok=dirs_exist_ok) + traj_file_path = os.path.join(dir_path, f"{name}{str(i)}.traj") perturbed_dict[name][str(i)]["structure"].write(traj_file_path) print(f"{name}_{str(i)}.traj written to {traj_file_path}") perturbed_dict[name][str(i)].update({"traj_file_path": traj_file_path}) From cc6b6f46befda8b0809b2f674bae9817d1701907 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 15 Feb 2021 12:28:38 -0500 Subject: [PATCH 007/239] writes perturbation matrices as json files to disk --- src/autocat/perturbations.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 58793a89..8a04607e 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -5,6 +5,7 @@ from typing import Dict import numpy as np import os +import json def generate_perturbed_dataset( @@ -95,13 +96,32 @@ def generate_perturbed_dataset( directions=directions, ) traj_file_path = None + pert_mat_file_path = None if write_to_disk: dir_path = os.path.join(write_location, f"{name}/{str(i)}") os.makedirs(dir_path, exist_ok=dirs_exist_ok) - traj_file_path = os.path.join(dir_path, f"{name}{str(i)}.traj") + traj_file_path = os.path.join(dir_path, f"perturbed_structure.traj") + # write perturbed structure perturbed_dict[name][str(i)]["structure"].write(traj_file_path) - print(f"{name}_{str(i)}.traj written to {traj_file_path}") + print( + f"{name} perturbed structure {str(i)} written to {traj_file_path}" + ) + pert_mat_file_path = os.path.join(dir_path, "perturbation_matrix.json") + with open(pert_mat_file_path, "w") as f: + # convert to np.array to list for json + pert_mat_list = perturbed_dict[name][str(i)][ + "perturbation_matrix" + ].tolist() + # write perturbation matrix to json + json.dump(pert_mat_list, f) + print( + f"{name} perturbed structure and matrix {str(i)} written to {traj_file_path}" + ) + # update output dictionary with write paths perturbed_dict[name][str(i)].update({"traj_file_path": traj_file_path}) + perturbed_dict[name][str(i)].update( + {"pert_mat_file_path": pert_mat_file_path} + ) return perturbed_dict From 82b08a7701fe158713fb026c8d9709a3db432343 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 15 Feb 2021 12:31:32 -0500 Subject: [PATCH 008/239] fix typo in pert_matrix print statement --- src/autocat/perturbations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 8a04607e..13a0cc30 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -115,7 +115,7 @@ def generate_perturbed_dataset( # write perturbation matrix to json json.dump(pert_mat_list, f) print( - f"{name} perturbed structure and matrix {str(i)} written to {traj_file_path}" + f"{name} perturbed matrix {str(i)} written to {pert_mat_file_path}" ) # update output dictionary with write paths perturbed_dict[name][str(i)].update({"traj_file_path": traj_file_path}) From 33b05944ae5ff4ba0cb8831675eca5fa196b0878 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 15 Feb 2021 15:00:50 -0500 Subject: [PATCH 009/239] allows directions to be given on per-struct basis --- src/autocat/perturbations.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 13a0cc30..6776d3b1 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -10,10 +10,10 @@ def generate_perturbed_dataset( base_structures: List[Atoms], - atom_indices_to_perturb_dictionary: Dict[str, List[int]], + atom_indices_to_perturb_dictionary: Dict[Union[str, Atoms], List[int]], minimum_perturbation_distance: float = 0.1, maximum_perturbation_distance: float = 1.0, - directions: List[bool] = None, + directions_dictionary: Dict[Union[str, Atoms], List[bool]] = None, num_of_perturbations: int = 10, write_to_disk: bool = False, write_location: str = ".", @@ -34,18 +34,21 @@ def generate_perturbed_dataset( perturbed atom_indices_to_perturb_dictionary: - Dictionary List of atomic indices for the atoms that should + Dictionary of list of atomic indices for the atoms that should be perturbed. Keys are each of the provided base structures minimum_perturbation_distance: Float of minimum acceptable perturbation distance + Default: 0.1 Angstrom maximum_perturbation_distance: Float of maximum acceptable perturbation distance + Default: 1.0 Angstrom - directions: + directions_dictionary: List of bools indicating which cartesian directions the atoms are allowed to be perturbed in + Default: free to perturb in all cartesian directions num_of_perturbations: Int specifying number of perturbations to generate. @@ -78,6 +81,9 @@ def generate_perturbed_dataset( perturbed_dict = {} + if directions_dictionary is None: + directions_dictionary = {} + for structure in base_structures: if isinstance(structure, Atoms): name = structure.get_chemical_formula() @@ -86,6 +92,9 @@ def generate_perturbed_dataset( else: raise TypeError(f"Structure needs to be either a str or ase.Atoms object") + if name not in directions_dictionary: + directions_dictionary[name] = None + perturbed_dict[name] = {} for i in range(num_of_perturbations): perturbed_dict[name][str(i)] = perturb_structure( @@ -93,7 +102,7 @@ def generate_perturbed_dataset( atom_indices_to_perturb=atom_indices_to_perturb_dictionary[name], minimum_perturbation_distance=minimum_perturbation_distance, maximum_perturbation_distance=maximum_perturbation_distance, - directions=directions, + directions=directions_dictionary[name], ) traj_file_path = None pert_mat_file_path = None @@ -152,13 +161,16 @@ def perturb_structure( minimum_perturbation_distance: Float of minimum acceptable perturbation distance + Default: 0.1 Angstrom maximum_perturbation_distance: Float of maximum acceptable perturbation distance + Default: 1.0 Angstrom directions: List of bools indicating which cartesian directions the atoms are allowed to be perturbed in + Default: free to perturb in all cartesian directions Returns ------- From f60a31a9a8d02e1f34d75508e2622b53e31af681 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 15 Feb 2021 16:20:22 -0500 Subject: [PATCH 010/239] adds perturbation tests --- tests/test_perturbations.py | 124 ++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 tests/test_perturbations.py diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py new file mode 100644 index 00000000..54d1393c --- /dev/null +++ b/tests/test_perturbations.py @@ -0,0 +1,124 @@ +"""Unit tests for the `autocat.perturbations` module""" + +import os +import pytest +import numpy as np + +import tempfile + +from autocat.surface import generate_surface_structures +from autocat.adsorption import place_adsorbate +from autocat.perturbations import perturb_structure +from autocat.perturbations import generate_perturbed_dataset + + +def test_perturb_structure_directions(): + # Tests fixing directions of perturbations + sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "H")["custom"]["structure"] + # fixed in all directions + p_struct = perturb_structure( + base_struct, atom_indices_to_perturb=[-1], directions=[False, False, False] + ) + assert (p_struct["structure"].positions[-1] == base_struct.positions[-1]).all() + # free in all directions + p_struct = perturb_structure( + base_struct, atom_indices_to_perturb=[-1], directions=[True, True, True] + ) + assert (p_struct["structure"].positions[-1] != base_struct.positions[-1]).all() + # free in 1D + p_struct = perturb_structure( + base_struct, atom_indices_to_perturb=[-1], directions=[False, False, True] + ) + assert p_struct["structure"].positions[-1][0] == base_struct.positions[-1][0] + assert p_struct["structure"].positions[-1][1] == base_struct.positions[-1][1] + assert p_struct["structure"].positions[-1][-1] != base_struct.positions[-1][-1] + + +def test_perturb_structure_matrix(): + # Tests matrix matches perturbed structure + sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + p_struct = perturb_structure(base_struct, atom_indices_to_perturb=[-1, -2]) + o_pert = base_struct.positions[-2] + p_struct["perturbation_matrix"][-2] + assert np.allclose(p_struct["structure"].positions[-2], o_pert) + h_pert = base_struct.positions[-1] + p_struct["perturbation_matrix"][-1] + assert np.allclose(p_struct["structure"].positions[-1], h_pert) + + +def test_generate_perturbed_dataset_num_of_perturbations(): + # Tests number of perturbations generated + sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct], + atom_indices_to_perturb_dictionary={ + base_struct.get_chemical_formula(): [-1, -2] + }, + num_of_perturbations=15, + ) + assert len(p_set["HOPt36"].keys()) == 15 + + +def test_generate_perturbed_dataset_multiple_base_structures(): + # Tests giving multiple base_structures + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct1, base_struct2], + atom_indices_to_perturb_dictionary={ + base_struct1.get_chemical_formula(): [-1], + base_struct2.get_chemical_formula(): [-2], + }, + directions_dictionary={ + base_struct1.get_chemical_formula(): [False, False, True] + }, + ) + # Check all base structures perturbed + assert len(p_set.keys()) == 2 + assert "HCu36N" in p_set + assert "HOPt36" in p_set + # Check correct atom indices perturbed for each base_structure + assert (p_set["HCu36N"]["2"]["perturbation_matrix"][-1] == np.zeros(3)).all() + assert (p_set["HCu36N"]["3"]["perturbation_matrix"][-2] != np.zeros(3)).all() + assert (p_set["HOPt36"]["6"]["perturbation_matrix"][-1] != np.zeros(3)).any() + # Check correct direction constraints applied to each base_structure + assert np.isclose(p_set["HOPt36"]["6"]["perturbation_matrix"][-1][0], 0.0) + assert np.isclose(p_set["HOPt36"]["6"]["perturbation_matrix"][-1][1], 0.0) + assert not np.isclose(p_set["HOPt36"]["6"]["perturbation_matrix"][-1][-1], 0.0) + assert not np.isclose(p_set["HCu36N"]["1"]["perturbation_matrix"][-2][0], 0.0) + + +def test_generate_perturbed_dataset_write_location(): + # Tests write location + _tmp_dir = tempfile.TemporaryDirectory().name + sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct], + atom_indices_to_perturb_dictionary={base_struct.get_chemical_formula(): [-1]}, + write_to_disk=True, + write_location=_tmp_dir, + ) + assert os.path.samefile( + p_set["HOPt36"]["0"]["traj_file_path"], + os.path.join(_tmp_dir, "HOPt36/0/perturbed_structure.traj"), + ) + assert os.path.samefile( + p_set["HOPt36"]["0"]["pert_mat_file_path"], + os.path.join(_tmp_dir, "HOPt36/0/perturbation_matrix.json"), + ) From 81c1b925289fe93f5b9a7d30b2fae86c18c75799 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 15 Feb 2021 19:26:53 -0500 Subject: [PATCH 011/239] add qml wrapper for coulomb matrix --- src/autocat/learning/__init__.py | 0 src/autocat/learning/featurizers.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/autocat/learning/__init__.py create mode 100644 src/autocat/learning/featurizers.py diff --git a/src/autocat/learning/__init__.py b/src/autocat/learning/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py new file mode 100644 index 00000000..a7b1c558 --- /dev/null +++ b/src/autocat/learning/featurizers.py @@ -0,0 +1,29 @@ +import qml + +import tempfile +import os + +from ase import Atoms + + +def get_coloumb_matrix(structure: Atoms, **kwargs): + """ + Wrapper for `qml.mol.generate_coulomb_matrix` + + Parameters + ---------- + + structure: + Atoms object of structure for which to generate a coulomb matrix + + Returns + ------- + + coulomb_matrix: + Numpy array of coulomb matrix + """ + with tempfile.TemporaryDirectory() as _tmp_dir: + structure.write(os.path.join(_tmp_dir, "tmp.xyz")) + mol = qml.Compound(xyz=os.path.join(_tmp_dir, "tmp.xyz")) + mol.generate_coulomb_matrix(size=mol.natoms, **kwargs) + return mol.representation From b106bc03974762531e416188942c544a732c7fd8 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 16 Feb 2021 16:34:11 -0500 Subject: [PATCH 012/239] starts adding qml wrappers --- src/autocat/learning/featurizers.py | 53 +++++++++++++++++++++++------ src/autocat/learning/predictors.py | 37 ++++++++++++++++++++ 2 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 src/autocat/learning/predictors.py diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index a7b1c558..c9b9cb99 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -2,28 +2,61 @@ import tempfile import os +import numpy as np + +from typing import List from ase import Atoms -def get_coloumb_matrix(structure: Atoms, **kwargs): +def get_X(structures: List[Atoms], size: int = None, **kwargs): + """ + Generate representation matrix X from list of ase.Atoms objects + + Parameters + ---------- + + structures: + List of ase.Atoms objects to be used to construct X matrix + + size: + Size of the largest structure to be supported by the representation. + Default: number of atoms in largest structure within `structures` + + Returns + ------- + + X: + np.ndarray of X representation + """ + if size is None: + size = max([len(s) for s in structures]) + + qml_mols = [ase_atoms_to_qml_compound(m) for m in structures] + + for mol in qml_mols: + mol.generate_coulomb_matrix(size=size, **kwargs) + + return np.array([mol.representation for mol in qml_mols]) + + +def ase_atoms_to_qml_compound(ase_atoms: Atoms): """ - Wrapper for `qml.mol.generate_coulomb_matrix` + Converter from `ase.Atoms` to `qml.Compound` Parameters ---------- - structure: - Atoms object of structure for which to generate a coulomb matrix + ase_atoms: + Atoms object to be converted to a qml.Compound Returns ------- - coulomb_matrix: - Numpy array of coulomb matrix + qml_compound: + qml.Compound object corresponding to give Atoms object """ with tempfile.TemporaryDirectory() as _tmp_dir: - structure.write(os.path.join(_tmp_dir, "tmp.xyz")) - mol = qml.Compound(xyz=os.path.join(_tmp_dir, "tmp.xyz")) - mol.generate_coulomb_matrix(size=mol.natoms, **kwargs) - return mol.representation + ase_atoms.write(os.path.join(_tmp_dir, "tmp.xyz")) + qml_compound = qml.Compound(xyz=os.path.join(_tmp_dir, "tmp.xyz")) + return qml_compound diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py new file mode 100644 index 00000000..5fa5c2e5 --- /dev/null +++ b/src/autocat/learning/predictors.py @@ -0,0 +1,37 @@ +import qml.kernels as qml_kernels +import numpy as np + + +def get_kernel(X1: np.ndarray, X2: np.ndarray, kernel_type: str = "gaussian", **kwargs): + """ + Wrapper for `qml` kernels + + Parameters + ---------- + + X1,X2: + Numpy arrays to be used to calculate the kernel + + kernel_type: + String giving type of kernel to be generated. + Options: + - gaussian (default) + - laplacian + - linear + - matern + - sargan + + Returns + ------- + + kernel_matrix: + Numpy array of generated kernel matrix + """ + kernel_funcs = { + "laplacian": qml_kernels.laplacian_kernel, + "gaussian": qml_kernels.gaussian_kernel, + "linear": qml_kernels.linear_kernel, + "matern": qml_kernels.matern_kernel, + "sargan": qml_kernels.sargan_kernel, + } + return kernel_funcs[kernel_type](X1, X2, **kwargs) From 6b749c9e48e0954038e5fa60b9c501938d7e6f1c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 16 Feb 2021 17:08:51 -0500 Subject: [PATCH 013/239] add wrapper for qml cholesky decomp --- src/autocat/learning/predictors.py | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 5fa5c2e5..89aa9d59 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -1,7 +1,47 @@ import qml.kernels as qml_kernels +import qml.math as qml_math import numpy as np +def get_alphas( + kernel: np.ndarray, + perturbation_matrices: np.ndarray, + regularization_strength: float = 1e-8, +): + """ + Wrapper for `qml.math.cho_solve` to + solve for the regression coefficients using cholesky + decomposition (ie. training a KRR) + + alphas = (K + lambda*I)^(-1)y + + Parameters + ---------- + + kernel: + Numpy array of kernel matrix (e.g. generated via `get_kernel`) + + perturbation_matrices: + Numpy array containing perturbation matrices corresponding to the + input structures which form the labels when training + + regularization_strength: + Float specifying regularization strength (lambda in eqtn above) + + perturbation + + Returns + ------- + + alphas: + Numpy array of regression coefficients + + """ + K = kernel + regularization_strength * np.identity(kernel.shape) + + return qml_math.cho_solve(K, perturbation_matrices) + + def get_kernel(X1: np.ndarray, X2: np.ndarray, kernel_type: str = "gaussian", **kwargs): """ Wrapper for `qml` kernels From f0a1a99abbfc827dde1d23396f91f79df018413b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 18 Feb 2021 16:51:57 -0500 Subject: [PATCH 014/239] collects pert matrices for easier use as labels --- src/autocat/perturbations.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 6776d3b1..c4a889c7 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -84,6 +84,8 @@ def generate_perturbed_dataset( if directions_dictionary is None: directions_dictionary = {} + collected_matrices = [] + for structure in base_structures: if isinstance(structure, Atoms): name = structure.get_chemical_formula() @@ -104,6 +106,9 @@ def generate_perturbed_dataset( maximum_perturbation_distance=maximum_perturbation_distance, directions=directions_dictionary[name], ) + collected_matrices.append( + perturbed_dict[name][str(i)]["perturbation_matrix"].flatten() + ) traj_file_path = None pert_mat_file_path = None if write_to_disk: @@ -131,6 +136,7 @@ def generate_perturbed_dataset( perturbed_dict[name][str(i)].update( {"pert_mat_file_path": pert_mat_file_path} ) + perturbed_dict["collected_matrices"] = np.array(collected_matrices) return perturbed_dict From 85310dbb4861d69d70e622d5587d11ef6c9809cd Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 18 Feb 2021 16:52:18 -0500 Subject: [PATCH 015/239] adds tests for matrix collection --- tests/test_perturbations.py | 50 ++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 54d1393c..23f8245d 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -87,7 +87,7 @@ def test_generate_perturbed_dataset_multiple_base_structures(): }, ) # Check all base structures perturbed - assert len(p_set.keys()) == 2 + assert len(p_set.keys()) - 1 == 2 assert "HCu36N" in p_set assert "HOPt36" in p_set # Check correct atom indices perturbed for each base_structure @@ -122,3 +122,51 @@ def test_generate_perturbed_dataset_write_location(): p_set["HOPt36"]["0"]["pert_mat_file_path"], os.path.join(_tmp_dir, "HOPt36/0/perturbation_matrix.json"), ) + + +def test_generate_perturbed_dataset_collected_matrices(): + # Check that matrices properly collected for 1 base_structure + sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct], + atom_indices_to_perturb_dictionary={ + base_struct.get_chemical_formula(): [-1, -2] + }, + num_of_perturbations=5, + ) + manual_collect = [] + for struct in p_set["HOPt36"]: + manual_collect.append(p_set["HOPt36"][struct]["perturbation_matrix"].flatten()) + manual_collect = np.array(manual_collect) + assert np.allclose(p_set["collected_matrices"], manual_collect) + assert p_set["collected_matrices"].shape == (5, 114) + + +def test_generate_perturbed_dataset_collected_matrices_multiple(): + # check that matrices properly collected for 2 base structures + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "OH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct1, base_struct2], + atom_indices_to_perturb_dictionary={ + base_struct1.get_chemical_formula(): [-1], + base_struct2.get_chemical_formula(): [-2], + }, + num_of_perturbations=5, + ) + manual_collect = [] + for base in ["HOPt36", "HCu36O"]: + for struct in p_set[base]: + manual_collect.append(p_set[base][struct]["perturbation_matrix"].flatten()) + manual_collect = np.array(manual_collect) + assert np.allclose(p_set["collected_matrices"], manual_collect) + assert p_set["collected_matrices"].shape == (10, 114) From 02a993fdb92eca4f666b6d7e64f680dee6b4a038 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 18 Feb 2021 17:16:42 -0500 Subject: [PATCH 016/239] adds opt to write collected mat to disk --- src/autocat/perturbations.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index c4a889c7..0bce4312 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -136,7 +136,17 @@ def generate_perturbed_dataset( perturbed_dict[name][str(i)].update( {"pert_mat_file_path": pert_mat_file_path} ) - perturbed_dict["collected_matrices"] = np.array(collected_matrices) + perturbed_dict["collected_matrices"] = np.array(collected_matrices) + collected_matrices_path = None + if write_to_disk: + collected_matrices_path = os.path.join( + write_location, "collected_matrices.json" + ) + coll = perturbed_dict["collected_matrices"].tolist() + with open(collected_matrices_path, "w") as f: + json.dump(coll, f) + print(f"Collected matrices written to {collected_matrices_path}") + perturbed_dict.update({"collected_matrices_path": collected_matrices_path}) return perturbed_dict From 79ea72067fc00ab193a201d68e0d6186efff1477 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 18 Feb 2021 17:17:00 -0500 Subject: [PATCH 017/239] test for writing collected mat to disk --- tests/test_perturbations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 23f8245d..27374368 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -87,7 +87,6 @@ def test_generate_perturbed_dataset_multiple_base_structures(): }, ) # Check all base structures perturbed - assert len(p_set.keys()) - 1 == 2 assert "HCu36N" in p_set assert "HOPt36" in p_set # Check correct atom indices perturbed for each base_structure @@ -122,6 +121,10 @@ def test_generate_perturbed_dataset_write_location(): p_set["HOPt36"]["0"]["pert_mat_file_path"], os.path.join(_tmp_dir, "HOPt36/0/perturbation_matrix.json"), ) + assert os.path.samefile( + p_set["collected_matrices_path"], + os.path.join(_tmp_dir, "collected_matrices.json"), + ) def test_generate_perturbed_dataset_collected_matrices(): From 137d3a16b96b06b7742ece2d0debb5e31e517ede Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 19 Feb 2021 12:55:44 -0500 Subject: [PATCH 018/239] adds option to write X to disk --- src/autocat/learning/featurizers.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index c9b9cb99..91a1e73d 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -3,13 +3,20 @@ import tempfile import os import numpy as np +import json from typing import List from ase import Atoms -def get_X(structures: List[Atoms], size: int = None, **kwargs): +def get_X( + structures: List[Atoms], + size: int = None, + write_to_disk: bool = False, + write_location: str = ".", + **kwargs, +): """ Generate representation matrix X from list of ase.Atoms objects @@ -23,6 +30,13 @@ def get_X(structures: List[Atoms], size: int = None, **kwargs): Size of the largest structure to be supported by the representation. Default: number of atoms in largest structure within `structures` + write_to_disk: + Boolean specifying whether X should be written to disk as a json. + Defaults to False. + + write_location: + String with the location where X should be written to disk. + Returns ------- @@ -37,7 +51,16 @@ def get_X(structures: List[Atoms], size: int = None, **kwargs): for mol in qml_mols: mol.generate_coulomb_matrix(size=size, **kwargs) - return np.array([mol.representation for mol in qml_mols]) + X = np.array([mol.representation for mol in qml_mols]) + + if write_to_disk: + write_path = os.path.join(write_location, "X.json") + X_list = X.tolist() + with open(write_path, "w") as f: + json.dump(X_list, f) + print(f"X written to {write_path}") + + return X def ase_atoms_to_qml_compound(ase_atoms: Atoms): From 615652f051846ed31349fd65df227868c4d29e25 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 23 Feb 2021 16:36:53 -0500 Subject: [PATCH 019/239] add 0 padding to labels for structs of diff size --- src/autocat/perturbations.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 0bce4312..c8b41778 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -136,17 +136,25 @@ def generate_perturbed_dataset( perturbed_dict[name][str(i)].update( {"pert_mat_file_path": pert_mat_file_path} ) - perturbed_dict["collected_matrices"] = np.array(collected_matrices) - collected_matrices_path = None - if write_to_disk: - collected_matrices_path = os.path.join( - write_location, "collected_matrices.json" - ) - coll = perturbed_dict["collected_matrices"].tolist() - with open(collected_matrices_path, "w") as f: - json.dump(coll, f) - print(f"Collected matrices written to {collected_matrices_path}") - perturbed_dict.update({"collected_matrices_path": collected_matrices_path}) + # find length of largest structure + largest_size = max([len(i) for i in collected_matrices]) + # ensures correct sized padding + collected_matrices_array = np.zeros((len(collected_matrices), largest_size)) + # substitute in collected matrices + for idx, row in enumerate(collected_matrices): + collected_matrices_array[idx, : len(row)] = row + + perturbed_dict["collected_matrices"] = collected_matrices_array + collected_matrices_path = None + if write_to_disk: + collected_matrices_path = os.path.join( + write_location, "collected_matrices.json" + ) + coll = perturbed_dict["collected_matrices"].tolist() + with open(collected_matrices_path, "w") as f: + json.dump(coll, f) + print(f"Collected matrices written to {collected_matrices_path}") + perturbed_dict.update({"collected_matrices_path": collected_matrices_path}) return perturbed_dict From 414b0916e2fdf6b91b5c053e2e28a4f1066b2475 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 23 Feb 2021 16:37:18 -0500 Subject: [PATCH 020/239] test for label padding --- tests/test_perturbations.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 27374368..a57dbae7 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -149,7 +149,7 @@ def test_generate_perturbed_dataset_collected_matrices(): def test_generate_perturbed_dataset_collected_matrices_multiple(): - # check that matrices properly collected for 2 base structures + # check that matrices properly collected for 2 base structures of == size sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] @@ -173,3 +173,30 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): manual_collect = np.array(manual_collect) assert np.allclose(p_set["collected_matrices"], manual_collect) assert p_set["collected_matrices"].shape == (10, 114) + + # check that matrices properly collected for 2 base structures of != size + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "H")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct1, base_struct2], + atom_indices_to_perturb_dictionary={ + base_struct1.get_chemical_formula(): [-1], + base_struct2.get_chemical_formula(): [-2], + }, + num_of_perturbations=5, + ) + manual_collect = [] + for base in ["HOPt36", "HCu36"]: + for struct in p_set[base]: + manual_collect.append(p_set[base][struct]["perturbation_matrix"].flatten()) + manual_collect_array = np.zeros((10, 114)) + for idx, m in enumerate(manual_collect): + manual_collect_array[idx, : len(m)] = m + assert np.allclose(p_set["collected_matrices"], manual_collect_array) + assert p_set["collected_matrices"].shape == (10, 114) From 910856d838713de8295d596728555ef72f027270 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 23 Feb 2021 17:29:36 -0500 Subject: [PATCH 021/239] allow specifying max struct size --- src/autocat/perturbations.py | 15 +++++++++++++-- tests/test_perturbations.py | 10 ++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index c8b41778..18687ed8 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -14,6 +14,7 @@ def generate_perturbed_dataset( minimum_perturbation_distance: float = 0.1, maximum_perturbation_distance: float = 1.0, directions_dictionary: Dict[Union[str, Atoms], List[bool]] = None, + maximum_structure_size: int = None, num_of_perturbations: int = 10, write_to_disk: bool = False, write_location: str = ".", @@ -50,6 +51,12 @@ def generate_perturbed_dataset( the atoms are allowed to be perturbed in Default: free to perturb in all cartesian directions + maximum_structure_size: + Integer giving the largest number of atoms in a structure + that should be able to be considered. Used to obtain shape + of collected matrices + Default: number of atoms in largest base structure + num_of_perturbations: Int specifying number of perturbations to generate. Default 10 @@ -136,8 +143,12 @@ def generate_perturbed_dataset( perturbed_dict[name][str(i)].update( {"pert_mat_file_path": pert_mat_file_path} ) - # find length of largest structure - largest_size = max([len(i) for i in collected_matrices]) + if maximum_structure_size is None: + # find flattened length of largest structure + largest_size = max([len(i) for i in collected_matrices]) + else: + # factor of 3 from flattening (ie. x,y,z) + largest_size = 3 * maximum_structure_size # ensures correct sized padding collected_matrices_array = np.zeros((len(collected_matrices), largest_size)) # substitute in collected matrices diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index a57dbae7..4866da77 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -146,6 +146,16 @@ def test_generate_perturbed_dataset_collected_matrices(): manual_collect = np.array(manual_collect) assert np.allclose(p_set["collected_matrices"], manual_collect) assert p_set["collected_matrices"].shape == (5, 114) + # Check when given specific maximum_structure_size + p_set = generate_perturbed_dataset( + [base_struct], + atom_indices_to_perturb_dictionary={ + base_struct.get_chemical_formula(): [-1, -2] + }, + num_of_perturbations=5, + maximum_structure_size=45, + ) + assert p_set["collected_matrices"].shape == (5, 135) def test_generate_perturbed_dataset_collected_matrices_multiple(): From ebf34662611ef5bd49b93fba780eed9622e4f0b3 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 24 Feb 2021 12:41:51 -0500 Subject: [PATCH 022/239] start reorganizing into structure and site feat --- src/autocat/learning/featurizers.py | 107 ++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 91a1e73d..a976134b 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -1,4 +1,6 @@ import qml +from matminer.featurizers.site import GaussianSymmFunc +from matminer.featurizers.site import SOAP import tempfile import os @@ -8,6 +10,7 @@ from typing import List from ase import Atoms +from pymatgen.io.ase import AseAtomsAdaptor def get_X( @@ -63,6 +66,110 @@ def get_X( return X +def adsorbate_featurization( + structure: Atoms, + adsorbate_indices: List[int], + featurizer: str = "behler-parinello", + **kwargs, +): + """ + Featurizes adsorbate on a support via `matminer.featurizers.site` + functions. + + Parameters + ---------- + + structure: + Atoms object of full structure (adsorbate + slab) to be featurized + + adsorbate_indices: + List of atomic indices specifying the adsorbate to be + featurized + + featurizer: + String indicating featurizer to be used. + + Options: + behler-parinello (default): gaussian symmetry functions + soap: smooth overlap of atomic positions + + Returns + ------- + + representation: + Np.ndarray of adsorbate representation + + """ + conv = AseAtomsAdaptor() + pymat_struct = conv.get_structure(structure) + + representation = np.zeros(len(adsorbate_indices)) + + if featurizer == "behler-parinello": + feat = GaussianSymmFunc(**kwargs) + + elif featurizer == "soap": + feat = SOAP(**kwargs) + + else: + raise NotImplementedError("selected featurizer not implemented") + + for i, idx in enumerate(adsorbate_indices): + representation[i] = feat.featurize(pymat_struct, idx) + + return representation + + +def full_structure_featurization( + structure: Atoms, size: int = None, featurizer: str = "coulomb_matrix", **kwargs, +): + """ + Featurizes the entire structure (including the adsorbate) using + the representations available in `qml.representations` + + Parameters + ---------- + + structure: + Atoms object of structure to be featurized + + size: + Size of the largest structure to be supported by the representation. + Default: number of atoms in largest structure within `structures` + + featurizer: + String indicating featurizer to be used. + + Options: + coulomb_matrix (default): flattened coulomb matrix + coulomb_matrix_eigenvalues: eigenvalues of coulomb matrix + bob: bag of bonds + + Returns + ------- + + representation: + Np.ndarray of structure representation + """ + if size is None: + size = len(structure) + + qml_struct = ase_atoms_to_qml_compound(structure) + + if featurizer == "coulomb_matrix": + qml_struct.generate_coulomb_matrix(size=size, **kwargs) + + elif featurizer == "coulomb_matrix_eigenvalues": + qml_struct.generate_eigenvalue_coulomb_matrix(size=size, **kwargs) + + elif featurizer == "bob": + qml_struct.generate_bob(size=size, **kwargs) + else: + raise NotImplementedError("selected featurizer not implemented") + + return qml_struct.representation + + def ase_atoms_to_qml_compound(ase_atoms: Atoms): """ Converter from `ase.Atoms` to `qml.Compound` From 690f0fcab98339992c941e4fd8f783aaf3807a0f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 26 Feb 2021 15:26:47 -0500 Subject: [PATCH 023/239] move qml converter to io --- src/autocat/io/__init__.py | 0 src/autocat/io/qml.py | 27 +++++++++++++++++++++++++++ src/autocat/learning/featurizers.py | 24 ++---------------------- 3 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 src/autocat/io/__init__.py create mode 100644 src/autocat/io/qml.py diff --git a/src/autocat/io/__init__.py b/src/autocat/io/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/autocat/io/qml.py b/src/autocat/io/qml.py new file mode 100644 index 00000000..eb5ea0d6 --- /dev/null +++ b/src/autocat/io/qml.py @@ -0,0 +1,27 @@ +import qml +import os +from ase import Atoms + +import tempfile + + +def ase_atoms_to_qml_compound(ase_atoms: Atoms): + """ + Converter from `ase.Atoms` to `qml.Compound` + + Parameters + ---------- + + ase_atoms: + Atoms object to be converted to a qml.Compound + + Returns + ------- + + qml_compound: + qml.Compound object corresponding to give Atoms object + """ + with tempfile.TemporaryDirectory() as _tmp_dir: + ase_atoms.write(os.path.join(_tmp_dir, "tmp.xyz")) + qml_compound = qml.Compound(xyz=os.path.join(_tmp_dir, "tmp.xyz")) + return qml_compound diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index a976134b..b5c35638 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -12,6 +12,8 @@ from ase import Atoms from pymatgen.io.ase import AseAtomsAdaptor +from autocat.io.qml import ase_atoms_to_qml_compound + def get_X( structures: List[Atoms], @@ -168,25 +170,3 @@ def full_structure_featurization( raise NotImplementedError("selected featurizer not implemented") return qml_struct.representation - - -def ase_atoms_to_qml_compound(ase_atoms: Atoms): - """ - Converter from `ase.Atoms` to `qml.Compound` - - Parameters - ---------- - - ase_atoms: - Atoms object to be converted to a qml.Compound - - Returns - ------- - - qml_compound: - qml.Compound object corresponding to give Atoms object - """ - with tempfile.TemporaryDirectory() as _tmp_dir: - ase_atoms.write(os.path.join(_tmp_dir, "tmp.xyz")) - qml_compound = qml.Compound(xyz=os.path.join(_tmp_dir, "tmp.xyz")) - return qml_compound From 8ce729a45c4a0dfdb0d363a0c24e60294157b703 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 1 Mar 2021 17:50:26 -0500 Subject: [PATCH 024/239] switch to dscribe for full_struct matrices --- src/autocat/learning/featurizers.py | 48 +++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index b5c35638..80a00ee5 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -2,6 +2,10 @@ from matminer.featurizers.site import GaussianSymmFunc from matminer.featurizers.site import SOAP +from dscribe.descriptors import SineMatrix +from dscribe.descriptors import EwaldSumMatrix +from dscribe.descriptors import CoulombMatrix + import tempfile import os import numpy as np @@ -123,11 +127,16 @@ def adsorbate_featurization( def full_structure_featurization( - structure: Atoms, size: int = None, featurizer: str = "coulomb_matrix", **kwargs, + structure: Atoms, + size: int = None, + featurizer: str = "sine_matrix", + permutation: str = "none", + n_jobs: int = 1, + **kwargs, ): """ Featurizes the entire structure (including the adsorbate) using - the representations available in `qml.representations` + representations available in `dscribe` and `qml` Parameters ---------- @@ -137,15 +146,25 @@ def full_structure_featurization( size: Size of the largest structure to be supported by the representation. - Default: number of atoms in largest structure within `structures` + Default: number of atoms in `structures` featurizer: String indicating featurizer to be used. Options: - coulomb_matrix (default): flattened coulomb matrix - coulomb_matrix_eigenvalues: eigenvalues of coulomb matrix - bob: bag of bonds + - sine_matrix (default) + - coulomb_matrix (N.B.: does not support periodicity) + - bag_of_bonds + + permutation: + String specifying how ordering is handled. This is fed into + `dscribe` featurizers (ie. sine_matrix, ewald_sum_matrix, coulomb_matrix) + Default: "none", maintains same ordering as input Atoms structure + (N.B. this differs from the `dscribe` default) + + n_jobs: + Int specifiying number of parallel jobs to run which is fed into `dscribe` + featurizers (ie. sine_matrix, coulomb_matrix) Returns ------- @@ -156,17 +175,20 @@ def full_structure_featurization( if size is None: size = len(structure) - qml_struct = ase_atoms_to_qml_compound(structure) + if featurizer == "sine_matrix": + sm = SineMatrix(n_atoms_max=size, permutation=permutation, **kwargs) + rep = sm.create(structure, n_jobs=n_jobs).reshape(-1,) - if featurizer == "coulomb_matrix": - qml_struct.generate_coulomb_matrix(size=size, **kwargs) - - elif featurizer == "coulomb_matrix_eigenvalues": - qml_struct.generate_eigenvalue_coulomb_matrix(size=size, **kwargs) + elif featurizer == "coulomb_matrix": + cm = CoulombMatrix(n_atoms_max=size, permutation=permutation, **kwargs) + rep = cm.create(structure, n_jobs=n_jobs).reshape(-1,) elif featurizer == "bob": + qml_struct = ase_atoms_to_qml_compound(structure) qml_struct.generate_bob(size=size, **kwargs) + return qml_struct.representation + else: raise NotImplementedError("selected featurizer not implemented") - return qml_struct.representation + return rep From 572974dff85f2d30c85efe94a49b052e7e1c45ab Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 1 Mar 2021 17:51:23 -0500 Subject: [PATCH 025/239] adds full_struct tests --- tests/learning/__init__.py | 0 tests/learning/test_featurizers.py | 47 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 tests/learning/__init__.py create mode 100644 tests/learning/test_featurizers.py diff --git a/tests/learning/__init__.py b/tests/learning/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py new file mode 100644 index 00000000..e4421cd3 --- /dev/null +++ b/tests/learning/test_featurizers.py @@ -0,0 +1,47 @@ +"""Unit tests for the `autocat.learning.featurizersi` module.""" + +import os +import numpy as np + +import pytest + +from dscribe.descriptors import SineMatrix +from dscribe.descriptors import CoulombMatrix + +import qml + +from autocat.io.qml import ase_atoms_to_qml_compound + +from autocat.surface import generate_surface_structures +from autocat.learning.featurizers import full_structure_featurization + + +def test_full_structure_featurization_sine(): + # Tests Sine Matrix Generation + surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] + sine_matrix = full_structure_featurization(surf) + sm = SineMatrix(n_atoms_max=len(surf), permutation="none") + assert np.allclose(sine_matrix, sm.create(surf)) + # Check padding + sine_matrix = full_structure_featurization(surf, size=40) + assert sine_matrix.shape == (1600,) + + +def test_full_structure_featurization_coulomb(): + # Tests Coulomb Matrix Generation + surf = generate_surface_structures(["Pt"])["Pt"]["fcc100"]["structure"] + coulomb_matrix = full_structure_featurization(surf, featurizer="coulomb_matrix") + cm = CoulombMatrix(n_atoms_max=len(surf), permutation="none") + assert np.allclose(coulomb_matrix, cm.create(surf)) + # Check padding + coulomb_matrix = full_structure_featurization(surf, size=45) + assert coulomb_matrix.shape == (2025,) + + +def test_full_structure_featurization_bob(): + # Tests Bag of Bonds generation + surf = generate_surface_structures(["Ru"])["Ru"]["hcp0001"]["structure"] + bob = full_structure_featurization(surf, featurizer="bob") + qml_struct = ase_atoms_to_qml_compound(surf) + qml_struct.generate_bob() + assert np.allclose(bob, qml_struct.representation) From 725792580cf71539351262ee65855dc45988fe13 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 1 Mar 2021 22:27:13 -0500 Subject: [PATCH 026/239] adds acsf,soap adsorbate feat from dscribe --- src/autocat/learning/featurizers.py | 41 ++++++++++++++++++----------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 80a00ee5..f5724a12 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -1,10 +1,10 @@ import qml -from matminer.featurizers.site import GaussianSymmFunc -from matminer.featurizers.site import SOAP from dscribe.descriptors import SineMatrix from dscribe.descriptors import EwaldSumMatrix from dscribe.descriptors import CoulombMatrix +from dscribe.descriptors import ACSF +from dscribe.descriptors import SOAP import tempfile import os @@ -75,7 +75,9 @@ def get_X( def adsorbate_featurization( structure: Atoms, adsorbate_indices: List[int], - featurizer: str = "behler-parinello", + species_list: List[str] = None, + featurizer: str = "acsf", + rcut: float = 6.0, **kwargs, ): """ @@ -92,13 +94,24 @@ def adsorbate_featurization( List of atomic indices specifying the adsorbate to be featurized + species_list: + List of chemical species that should be covered by representation + which is fed into `dscribe.descriptors.{ACSF,SOAP}` + (ie. any species expected to be encountered) + Default: species present in `structure` + featurizer: String indicating featurizer to be used. Options: - behler-parinello (default): gaussian symmetry functions + acsf (default): atom centered symmetry functions soap: smooth overlap of atomic positions + rcut: + Float giving cutoff radius to be used when generating + representation. + Default: 6 angstroms + Returns ------- @@ -106,23 +119,21 @@ def adsorbate_featurization( Np.ndarray of adsorbate representation """ - conv = AseAtomsAdaptor() - pymat_struct = conv.get_structure(structure) + if species_list is None: + species_array = np.unique(structure.get_chemical_symbols()) + species_list = species_array.tolist() - representation = np.zeros(len(adsorbate_indices)) - - if featurizer == "behler-parinello": - feat = GaussianSymmFunc(**kwargs) + if featurizer == "acsf": + acsf = ACSF(rcut=rcut, species=species_list, **kwargs) + representation = acsf.create(structure, positions=adsorbate_indices) elif featurizer == "soap": - feat = SOAP(**kwargs) + soap = SOAP(rcut=rcut, species=species_list, **kwargs) + representation = soap.create(structure, positions=adsorbate_indices) else: raise NotImplementedError("selected featurizer not implemented") - for i, idx in enumerate(adsorbate_indices): - representation[i] = feat.featurize(pymat_struct, idx) - return representation @@ -154,7 +165,7 @@ def full_structure_featurization( Options: - sine_matrix (default) - coulomb_matrix (N.B.: does not support periodicity) - - bag_of_bonds + - bob (bag of bonds) permutation: String specifying how ordering is handled. This is fed into From 0431d7b86cddb2189f3cf02c89f55295ca750cab Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 1 Mar 2021 22:28:04 -0500 Subject: [PATCH 027/239] add adsorbate feat tests --- tests/learning/test_featurizers.py | 31 +++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index e4421cd3..43dd9641 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -7,13 +7,16 @@ from dscribe.descriptors import SineMatrix from dscribe.descriptors import CoulombMatrix +from dscribe.descriptors import ACSF +from dscribe.descriptors import SOAP import qml from autocat.io.qml import ase_atoms_to_qml_compound - +from autocat.adsorption import generate_rxn_structures from autocat.surface import generate_surface_structures from autocat.learning.featurizers import full_structure_featurization +from autocat.learning.featurizers import adsorbate_featurization def test_full_structure_featurization_sine(): @@ -45,3 +48,29 @@ def test_full_structure_featurization_bob(): qml_struct = ase_atoms_to_qml_compound(surf) qml_struct.generate_bob() assert np.allclose(bob, qml_struct.representation) + + +def test_adsorbate_featurization_acsf(): + # Tests Atom Centered Symmetry Function generation + surf = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] + ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ + "structure" + ] + acsf_feat = adsorbate_featurization(ads_struct, [36]) + species = np.unique(ads_struct.get_chemical_symbols()).tolist() + acsf = ACSF(rcut=6.0, species=species) + assert np.allclose(acsf_feat, acsf.create(ads_struct, [36])) + + +def test_adsorbate_featurization_soap(): + # Tests Smooth Overlap of Atomic Positions + surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] + ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ + "structure" + ] + soap_feat = adsorbate_featurization( + ads_struct, [36], featurizer="soap", nmax=8, lmax=6 + ) + species = np.unique(ads_struct.get_chemical_symbols()).tolist() + soap = SOAP(rcut=6.0, species=species, nmax=8, lmax=6) + assert np.allclose(soap_feat, soap.create(ads_struct, [36])) From 365ed23ba4b4be9238894a0bde590b60c32fcce2 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 2 Mar 2021 15:00:28 -0500 Subject: [PATCH 028/239] adds combined catalyst featurization --- src/autocat/learning/featurizers.py | 71 +++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index f5724a12..957948d5 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -11,7 +11,7 @@ import numpy as np import json -from typing import List +from typing import List, Dict from ase import Atoms from pymatgen.io.ase import AseAtomsAdaptor @@ -72,6 +72,67 @@ def get_X( return X +def catalyst_featurization( + structure: Atoms, + adsorbate_indices: List[int], + structure_featurizer: str = "sine_matrix", + adsorbate_featurizer: str = "acsf", + structure_featurization_kwargs: Dict[str, float] = None, + adsorbate_featurization_kwargs: Dict[str, float] = None, +): + """ + Featurizes a system containing an adsorbate + substrate + in terms of both a full structure and adsorbate + featurization via concatenation. + + In other words, + catalyst_featurization = full_structure_featurization + adsorbate_featurization + + Parameters + ---------- + + structure: + Atoms object of full structure (adsorbate + slab) to be featurized + + adsorbate_indices: + List of atomic indices specifying the adsorbate to be + featurized + + structure_featurizer: + String giving featurizer to be used for full structure which will be + fed into `full_structure_featurization` + + adsorbate_featurizer: + String giving featurizer to be used for full structure which will be + fed into `adsorbate_structure_featurization` + + structure_featurization_kwargs: + kwargs to be fed into `full_structure_featurization` + + adsorbate_featurization_kwargs: + kwargs to be fed into `adsorbate_featurization` + + Returns + ------- + + cat_feat: + Np.ndarray of featurized structure + + """ + + struct_feat = full_structure_featurization( + structure, **structure_featurization_kwargs, featurizer=structure_featurizer + ) + ads_feat = adsorbate_featurization( + structure, + featurizer=adsorbate_featurizer, + adsorbate_indices=adsorbate_indices, + **adsorbate_featurization_kwargs, + ) + cat_feat = np.concatenate((struct_feat, ads_feat)) + return cat_feat + + def adsorbate_featurization( structure: Atoms, adsorbate_indices: List[int], @@ -125,11 +186,15 @@ def adsorbate_featurization( if featurizer == "acsf": acsf = ACSF(rcut=rcut, species=species_list, **kwargs) - representation = acsf.create(structure, positions=adsorbate_indices) + representation = acsf.create(structure, positions=adsorbate_indices).reshape( + -1, + ) elif featurizer == "soap": soap = SOAP(rcut=rcut, species=species_list, **kwargs) - representation = soap.create(structure, positions=adsorbate_indices) + representation = soap.create(structure, positions=adsorbate_indices).reshape( + -1, + ) else: raise NotImplementedError("selected featurizer not implemented") From dc5aa8e26333f88b08f63b55a379adfd43c1cb20 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 2 Mar 2021 15:01:05 -0500 Subject: [PATCH 029/239] test for catalyst featurization --- tests/learning/test_featurizers.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 43dd9641..52f98f31 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -17,6 +17,7 @@ from autocat.surface import generate_surface_structures from autocat.learning.featurizers import full_structure_featurization from autocat.learning.featurizers import adsorbate_featurization +from autocat.learning.featurizers import catalyst_featurization def test_full_structure_featurization_sine(): @@ -74,3 +75,25 @@ def test_adsorbate_featurization_soap(): species = np.unique(ads_struct.get_chemical_symbols()).tolist() soap = SOAP(rcut=6.0, species=species, nmax=8, lmax=6) assert np.allclose(soap_feat, soap.create(ads_struct, [36])) + + +def test_catalyst_featurization_concatentation(): + # Tests that the representations are properly concatenated + # with kwargs input appropriately + surf = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] + ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ + "structure" + ] + cat = catalyst_featurization( + ads_struct, + [36], + adsorbate_featurization_kwargs={"rcut": 5.0}, + structure_featurization_kwargs={"size": 40}, + ) + sm = SineMatrix(n_atoms_max=40, permutation="none") + struct = sm.create(ads_struct).reshape(-1,) + species = np.unique(ads_struct.get_chemical_symbols()).tolist() + acsf = ACSF(rcut=5.0, species=species) + ads = acsf.create(ads_struct, [36]).reshape(-1,) + cat_ref = np.concatenate((struct, ads)) + assert np.allclose(cat, cat_ref) From 626639fdbd809fd47da009311fe50407d2e9a0fb Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 8 Mar 2021 15:45:55 -0500 Subject: [PATCH 030/239] adds get_X fn + other tweaks - size -> maximum_structure_size - acsf default -> soap for adsorbate - adds helper fn for number of features --- src/autocat/learning/featurizers.py | 205 +++++++++++++++++++++++----- tests/learning/test_featurizers.py | 92 +++++++++++-- 2 files changed, 255 insertions(+), 42 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 957948d5..e44d6cb4 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -12,16 +12,25 @@ import json from typing import List, Dict +from typing import Union from ase import Atoms +from ase.io import read from pymatgen.io.ase import AseAtomsAdaptor from autocat.io.qml import ase_atoms_to_qml_compound def get_X( - structures: List[Atoms], - size: int = None, + structures: List[Union[Atoms, str]], + adsorbate_indices_dictionary: Dict[str, int], + maximum_structure_size: int = None, + structure_featurizer: str = "sine_matrix", + maximum_adsorbate_size: int = None, + adsorbate_featurizer: str = "soap", + species_list: List[str] = None, + structure_featurization_kwargs: Dict[str, float] = None, + adsorbate_featurization_kwargs: Dict[str, float] = None, write_to_disk: bool = False, write_location: str = ".", **kwargs, @@ -33,12 +42,42 @@ def get_X( ---------- structures: - List of ase.Atoms objects to be used to construct X matrix + List of ase.Atoms objects or structure filename strings to be used to construct X matrix - size: + adsorbate_indices_dictionary: + Dictionary mapping structures to desired adsorbate_indices + (N.B. if structure is given as an ase.Atoms object, + the key for this dictionary should be ase.Atoms.get_chemical_formula()) + + maximum_structure_size: Size of the largest structure to be supported by the representation. Default: number of atoms in largest structure within `structures` + structure_featurizer: + String giving featurizer to be used for full structure which will be + fed into `full_structure_featurization` + + maximum_adsorbate_size: + Integer giving the maximum adsorbate size to be encountered + (ie. this determines if zero-padding should be applied and how much). + If the provided value is less than the adsorbate size given by + `adsorbate_indices`, representation will remain size of the adsorbate. + Default: size of adsorbate provided + + adsorbate_featurizer: + String giving featurizer to be used for full structure which will be + fed into `adsorbate_structure_featurization` + + species_list: + List of species that could be encountered for featurization. + Default: Parses over all `structures` and collects all encountered species + + structure_featurization_kwargs: + kwargs to be fed into `full_structure_featurization` + + adsorbate_featurization_kwargs: + kwargs to be fed into `adsorbate_featurization` + write_to_disk: Boolean specifying whether X should be written to disk as a json. Defaults to False. @@ -49,24 +88,70 @@ def get_X( Returns ------- - X: + X_array: np.ndarray of X representation + Shape is (# of structures, maximum_structure_size + maximum_adsorbate_size * # of adsorbates) """ - if size is None: - size = max([len(s) for s in structures]) + if maximum_structure_size is None: + maximum_structure_size = max([len(s) for s in structures]) + + if maximum_adsorbate_size is None: + maximum_adsorbate_size = max( + [len(adsorbate_indices_dictionary[a]) for a in adsorbate_indices_dictionary] + ) - qml_mols = [ase_atoms_to_qml_compound(m) for m in structures] + if adsorbate_featurization_kwargs is None: + adsorbate_featurization_kwargs = {} - for mol in qml_mols: - mol.generate_coulomb_matrix(size=size, **kwargs) + if structure_featurization_kwargs is None: + structure_featurization_kwargs = {} - X = np.array([mol.representation for mol in qml_mols]) + if species_list is None: + species_list = [] + for s in structures: + found_species = np.unique(s.get_chemical_symbols()).tolist() + new_species = [spec for spec in found_species if spec not in species_list] + species_list.extend(new_species) + + num_of_adsorbate_features = _get_number_of_features( + featurizer=adsorbate_featurizer, + species=species_list, + **adsorbate_featurization_kwargs, + ) + + X = np.zeros( + ( + len(structures), + maximum_structure_size ** 2 + + maximum_adsorbate_size * num_of_adsorbate_features, + ) + ) + for idx, structure in enumerate(structures): + if isinstance(structure, Atoms): + name = structure.get_chemical_formula() + ase_struct = structure.copy() + elif isinstance(structure, str): + name = structure + ase_struct = read(structure) + else: + raise TypeError(f"Each structure needs to be either a str or ase.Atoms") + cat_feat = catalyst_featurization( + ase_struct, + adsorbate_indices=adsorbate_indices_dictionary[name], + structure_featurizer=structure_featurizer, + adsorbate_featurizer=adsorbate_featurizer, + maximum_structure_size=maximum_structure_size, + maximum_adsorbate_size=maximum_adsorbate_size, + species_list=species_list, + structure_featurization_kwargs=structure_featurization_kwargs, + adsorbate_featurization_kwargs=adsorbate_featurization_kwargs, + ) + X[idx] = cat_feat if write_to_disk: write_path = os.path.join(write_location, "X.json") - X_list = X.tolist() with open(write_path, "w") as f: - json.dump(X_list, f) + json.dump(X, f) print(f"X written to {write_path}") return X @@ -76,7 +161,10 @@ def catalyst_featurization( structure: Atoms, adsorbate_indices: List[int], structure_featurizer: str = "sine_matrix", - adsorbate_featurizer: str = "acsf", + adsorbate_featurizer: str = "soap", + maximum_structure_size: int = None, + maximum_adsorbate_size: int = None, + species_list: List[str] = None, structure_featurization_kwargs: Dict[str, float] = None, adsorbate_featurization_kwargs: Dict[str, float] = None, ): @@ -119,14 +207,24 @@ def catalyst_featurization( Np.ndarray of featurized structure """ + if structure_featurization_kwargs is None: + structure_featurization_kwargs = {} + + if adsorbate_featurization_kwargs is None: + adsorbate_featurization_kwargs = {} struct_feat = full_structure_featurization( - structure, **structure_featurization_kwargs, featurizer=structure_featurizer + structure, + featurizer=structure_featurizer, + maximum_structure_size=maximum_structure_size, + **structure_featurization_kwargs, ) ads_feat = adsorbate_featurization( structure, featurizer=adsorbate_featurizer, adsorbate_indices=adsorbate_indices, + species_list=species_list, + maximum_adsorbate_size=maximum_adsorbate_size, **adsorbate_featurization_kwargs, ) cat_feat = np.concatenate((struct_feat, ads_feat)) @@ -137,8 +235,9 @@ def adsorbate_featurization( structure: Atoms, adsorbate_indices: List[int], species_list: List[str] = None, - featurizer: str = "acsf", + featurizer: str = "soap", rcut: float = 6.0, + maximum_adsorbate_size: int = None, **kwargs, ): """ @@ -173,6 +272,13 @@ def adsorbate_featurization( representation. Default: 6 angstroms + maximum_adsorbate_size: + Integer giving the maximum adsorbate size to be encountered + (ie. this determines if zero-padding should be applied and how much). + If the provided value is less than the adsorbate size given by + `adsorbate_indices`, representation will remain size of the adsorbate. + Default: size of adsorbate provided + Returns ------- @@ -180,31 +286,43 @@ def adsorbate_featurization( Np.ndarray of adsorbate representation """ + # Checks if species list given if species_list is None: - species_array = np.unique(structure.get_chemical_symbols()) - species_list = species_array.tolist() + species_list = np.unique(structure.get_chemical_symbols()).tolist() - if featurizer == "acsf": - acsf = ACSF(rcut=rcut, species=species_list, **kwargs) - representation = acsf.create(structure, positions=adsorbate_indices).reshape( - -1, - ) + # Checks if max adsorbate size specified + if maximum_adsorbate_size is None: + maximum_adsorbate_size = len(adsorbate_indices) - elif featurizer == "soap": + # Selects appropriate featurizer + if featurizer == "soap": soap = SOAP(rcut=rcut, species=species_list, **kwargs) representation = soap.create(structure, positions=adsorbate_indices).reshape( -1, ) + num_of_features = soap.get_number_of_features() + + elif featurizer == "acsf": + acsf = ACSF(rcut=rcut, species=species_list, **kwargs) + representation = acsf.create(structure, positions=adsorbate_indices).reshape( + -1, + ) + num_of_features = acsf.get_number_of_features() else: raise NotImplementedError("selected featurizer not implemented") + # Checks if padding needs to be applied + if len(representation) < maximum_adsorbate_size * num_of_features: + diff = maximum_adsorbate_size * num_of_features - len(representation) + representation = np.pad(representation, (0, diff)) + return representation def full_structure_featurization( structure: Atoms, - size: int = None, + maximum_structure_size: int = None, featurizer: str = "sine_matrix", permutation: str = "none", n_jobs: int = 1, @@ -220,7 +338,7 @@ def full_structure_featurization( structure: Atoms object of structure to be featurized - size: + maximum_structure_size: Size of the largest structure to be supported by the representation. Default: number of atoms in `structures` @@ -248,23 +366,48 @@ def full_structure_featurization( representation: Np.ndarray of structure representation """ - if size is None: - size = len(structure) + if maximum_structure_size is None: + maximum_structure_size = len(structure) if featurizer == "sine_matrix": - sm = SineMatrix(n_atoms_max=size, permutation=permutation, **kwargs) + sm = SineMatrix( + n_atoms_max=maximum_structure_size, permutation=permutation, **kwargs + ) rep = sm.create(structure, n_jobs=n_jobs).reshape(-1,) elif featurizer == "coulomb_matrix": - cm = CoulombMatrix(n_atoms_max=size, permutation=permutation, **kwargs) + cm = CoulombMatrix( + n_atoms_max=maximum_structure_size, permutation=permutation, **kwargs + ) rep = cm.create(structure, n_jobs=n_jobs).reshape(-1,) elif featurizer == "bob": qml_struct = ase_atoms_to_qml_compound(structure) - qml_struct.generate_bob(size=size, **kwargs) + qml_struct.generate_bob(size=maximum_structure_size, **kwargs) return qml_struct.representation else: raise NotImplementedError("selected featurizer not implemented") return rep + + +def _get_number_of_features(featurizer, **kwargs): + """ + Wrapper of `get_number_of_features` method for `dscribe` + featurizers + """ + supported_featurizers = { + "sine_matrix": SineMatrix, + "coulomb_matrix": CoulombMatrix, + "soap": SOAP, + "acsf": ACSF, + } + + if featurizer not in supported_featurizers: + raise NotImplementedError( + "selected featurizer does not currently support this feature" + ) + + feat = supported_featurizers[featurizer](**kwargs) + return feat.get_number_of_features() diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 52f98f31..d66812de 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -18,6 +18,8 @@ from autocat.learning.featurizers import full_structure_featurization from autocat.learning.featurizers import adsorbate_featurization from autocat.learning.featurizers import catalyst_featurization +from autocat.learning.featurizers import _get_number_of_features +from autocat.learning.featurizers import get_X def test_full_structure_featurization_sine(): @@ -27,7 +29,7 @@ def test_full_structure_featurization_sine(): sm = SineMatrix(n_atoms_max=len(surf), permutation="none") assert np.allclose(sine_matrix, sm.create(surf)) # Check padding - sine_matrix = full_structure_featurization(surf, size=40) + sine_matrix = full_structure_featurization(surf, maximum_structure_size=40) assert sine_matrix.shape == (1600,) @@ -38,7 +40,7 @@ def test_full_structure_featurization_coulomb(): cm = CoulombMatrix(n_atoms_max=len(surf), permutation="none") assert np.allclose(coulomb_matrix, cm.create(surf)) # Check padding - coulomb_matrix = full_structure_featurization(surf, size=45) + coulomb_matrix = full_structure_featurization(surf, maximum_structure_size=45) assert coulomb_matrix.shape == (2025,) @@ -57,7 +59,7 @@ def test_adsorbate_featurization_acsf(): ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ "structure" ] - acsf_feat = adsorbate_featurization(ads_struct, [36]) + acsf_feat = adsorbate_featurization(ads_struct, [36], featurizer="acsf") species = np.unique(ads_struct.get_chemical_symbols()).tolist() acsf = ACSF(rcut=6.0, species=species) assert np.allclose(acsf_feat, acsf.create(ads_struct, [36])) @@ -70,30 +72,98 @@ def test_adsorbate_featurization_soap(): "structure" ] soap_feat = adsorbate_featurization( - ads_struct, [36], featurizer="soap", nmax=8, lmax=6 + ads_struct, [-1], featurizer="soap", nmax=8, lmax=6 ) species = np.unique(ads_struct.get_chemical_symbols()).tolist() soap = SOAP(rcut=6.0, species=species, nmax=8, lmax=6) - assert np.allclose(soap_feat, soap.create(ads_struct, [36])) + assert np.allclose(soap_feat, soap.create(ads_struct, [-1])) + assert soap_feat.shape == (soap.get_number_of_features(),) + + +def test_adsorbate_featurization_padding(): + # Tests that padding is properly applied + surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] + ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ + "structure" + ] + soap_feat = adsorbate_featurization( + ads_struct, [36], featurizer="soap", nmax=8, lmax=6, maximum_adsorbate_size=10 + ) + + species = np.unique(ads_struct.get_chemical_symbols()).tolist() + num_of_features = _get_number_of_features( + featurizer="soap", species=species, rcut=6.0, nmax=8, lmax=6 + ) + + assert (soap_feat[-num_of_features * 9 :] == np.zeros(num_of_features * 9)).all() + soap_feat = adsorbate_featurization( + ads_struct, + [35, 36], + featurizer="soap", + nmax=8, + lmax=6, + maximum_adsorbate_size=10, + ) + assert (soap_feat[-num_of_features * 8 :] == np.zeros(num_of_features * 8)).all() def test_catalyst_featurization_concatentation(): # Tests that the representations are properly concatenated # with kwargs input appropriately surf = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ + ads_struct = generate_rxn_structures(surf, ads=["OH"])["OH"]["ontop"]["0.0_0.0"][ "structure" ] cat = catalyst_featurization( ads_struct, - [36], - adsorbate_featurization_kwargs={"rcut": 5.0}, - structure_featurization_kwargs={"size": 40}, + [-1, -2], + maximum_structure_size=40, + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) sm = SineMatrix(n_atoms_max=40, permutation="none") struct = sm.create(ads_struct).reshape(-1,) species = np.unique(ads_struct.get_chemical_symbols()).tolist() - acsf = ACSF(rcut=5.0, species=species) - ads = acsf.create(ads_struct, [36]).reshape(-1,) + soap = SOAP(rcut=5.0, nmax=8, lmax=6, species=species) + ads = soap.create(ads_struct, [-1, -2]).reshape(-1,) cat_ref = np.concatenate((struct, ads)) assert np.allclose(cat, cat_ref) + num_of_adsorbate_features = soap.get_number_of_features() + assert len(cat) == 40 ** 2 + num_of_adsorbate_features * 2 + + +def test_get_X_concatenation(): + # Tests that the resulting X is concatenated and ordered properly + structs = [] + surf1 = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] + ads1 = generate_rxn_structures( + surf1, + ads=["NH3", "CO"], + all_sym_sites=False, + sites={"origin": [(0.0, 0.0)]}, + height={"CO": 1.5}, + rots={"NH3": [[180.0, "x"], [90.0, "z"]], "CO": [[180.0, "y"]]}, + ) + structs.append(ads1["NH3"]["origin"]["0.0_0.0"]["structure"]) + structs.append(ads1["CO"]["origin"]["0.0_0.0"]["structure"]) + surf2 = generate_surface_structures(["Ru"])["Ru"]["hcp0001"]["structure"] + ads2 = generate_rxn_structures( + surf2, ads=["N"], all_sym_sites=False, sites={"origin": [(0.0, 0.0)]}, + ) + structs.append(ads2["N"]["origin"]["0.0_0.0"]["structure"]) + + X = get_X( + structs, + adsorbate_indices_dictionary={ + structs[0].get_chemical_formula(): [-4, -3, -2, -1], + structs[1].get_chemical_formula(): [-2, -1], + structs[2].get_chemical_formula(): [-1], + }, + maximum_structure_size=50, + maximum_adsorbate_size=5, + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + species_list = ["Pt", "Ru", "N", "C", "O", "H"] + num_of_adsorbate_features = _get_number_of_features( + featurizer="soap", rcut=5.0, nmax=8, lmax=6, species=species_list + ) + assert X.shape == (len(structs), 50 ** 2 + 5 * num_of_adsorbate_features) From 4a7d6e94bbc4036d04b0aaa9bcbc22012130fd5c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 8 Mar 2021 16:46:37 -0500 Subject: [PATCH 031/239] fix writing X, rm bob for now --- src/autocat/learning/featurizers.py | 12 ++------ tests/learning/test_featurizers.py | 43 +++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index e44d6cb4..4e4bb0bc 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -1,5 +1,3 @@ -import qml - from dscribe.descriptors import SineMatrix from dscribe.descriptors import EwaldSumMatrix from dscribe.descriptors import CoulombMatrix @@ -149,9 +147,11 @@ def get_X( X[idx] = cat_feat if write_to_disk: + if not os.path.isdir(write_location): + os.makedirs(write_location) write_path = os.path.join(write_location, "X.json") with open(write_path, "w") as f: - json.dump(X, f) + json.dump(X.tolist(), f) print(f"X written to {write_path}") return X @@ -348,7 +348,6 @@ def full_structure_featurization( Options: - sine_matrix (default) - coulomb_matrix (N.B.: does not support periodicity) - - bob (bag of bonds) permutation: String specifying how ordering is handled. This is fed into @@ -381,11 +380,6 @@ def full_structure_featurization( ) rep = cm.create(structure, n_jobs=n_jobs).reshape(-1,) - elif featurizer == "bob": - qml_struct = ase_atoms_to_qml_compound(structure) - qml_struct.generate_bob(size=maximum_structure_size, **kwargs) - return qml_struct.representation - else: raise NotImplementedError("selected featurizer not implemented") diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index d66812de..c10ac946 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -2,9 +2,12 @@ import os import numpy as np +import json import pytest +import tempfile + from dscribe.descriptors import SineMatrix from dscribe.descriptors import CoulombMatrix from dscribe.descriptors import ACSF @@ -44,15 +47,6 @@ def test_full_structure_featurization_coulomb(): assert coulomb_matrix.shape == (2025,) -def test_full_structure_featurization_bob(): - # Tests Bag of Bonds generation - surf = generate_surface_structures(["Ru"])["Ru"]["hcp0001"]["structure"] - bob = full_structure_featurization(surf, featurizer="bob") - qml_struct = ase_atoms_to_qml_compound(surf) - qml_struct.generate_bob() - assert np.allclose(bob, qml_struct.representation) - - def test_adsorbate_featurization_acsf(): # Tests Atom Centered Symmetry Function generation surf = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] @@ -167,3 +161,34 @@ def test_get_X_concatenation(): featurizer="soap", rcut=5.0, nmax=8, lmax=6, species=species_list ) assert X.shape == (len(structs), 50 ** 2 + 5 * num_of_adsorbate_features) + + +def test_get_X_write_location(): + # Tests user-specified write location for X + structs = [] + surf1 = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] + ads1 = generate_rxn_structures( + surf1, + ads=["NH3", "CO"], + all_sym_sites=False, + sites={"origin": [(0.0, 0.0)]}, + height={"CO": 1.5}, + rots={"NH3": [[180.0, "x"], [90.0, "z"]], "CO": [[180.0, "y"]]}, + ) + structs.append(ads1["NH3"]["origin"]["0.0_0.0"]["structure"]) + structs.append(ads1["CO"]["origin"]["0.0_0.0"]["structure"]) + + _tmp_dir = tempfile.TemporaryDirectory().name + X = get_X( + structs, + adsorbate_indices_dictionary={ + structs[0].get_chemical_formula(): [-4, -3, -2, -1], + structs[1].get_chemical_formula(): [-2, -1], + }, + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + write_to_disk=True, + write_location=_tmp_dir, + ) + with open(os.path.join(_tmp_dir, "X.json"), "r") as f: + X_written = json.load(f) + assert np.allclose(X, X_written) From f3f500414b16ebe3348022396390794ac819ab9d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 8 Mar 2021 17:27:01 -0500 Subject: [PATCH 032/239] collect structure paths --- src/autocat/perturbations.py | 3 +++ tests/test_perturbations.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 18687ed8..5cf5ffde 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -92,6 +92,7 @@ def generate_perturbed_dataset( directions_dictionary = {} collected_matrices = [] + collected_structure_paths = [] for structure in base_structures: if isinstance(structure, Atoms): @@ -143,6 +144,7 @@ def generate_perturbed_dataset( perturbed_dict[name][str(i)].update( {"pert_mat_file_path": pert_mat_file_path} ) + collected_structure_paths.append(traj_file_path) if maximum_structure_size is None: # find flattened length of largest structure largest_size = max([len(i) for i in collected_matrices]) @@ -166,6 +168,7 @@ def generate_perturbed_dataset( json.dump(coll, f) print(f"Collected matrices written to {collected_matrices_path}") perturbed_dict.update({"collected_matrices_path": collected_matrices_path}) + perturbed_dict.update({"collected_structure_paths": collected_structure_paths}) return perturbed_dict diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 4866da77..69f2dad3 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -210,3 +210,26 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): manual_collect_array[idx, : len(m)] = m assert np.allclose(p_set["collected_matrices"], manual_collect_array) assert p_set["collected_matrices"].shape == (10, 114) + + +def test_generate_perturbed_dataset_collected_structure_paths(): + # Tests that collected structure paths in correct order + _tmp_dir = tempfile.TemporaryDirectory().name + sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct], + atom_indices_to_perturb_dictionary={base_struct.get_chemical_formula(): [-1]}, + write_to_disk=True, + write_location=_tmp_dir, + ) + assert os.path.samefile( + p_set["collected_structure_paths"][0], + os.path.join(_tmp_dir, "HOPt36/0/perturbed_structure.traj"), + ) + assert os.path.samefile( + p_set["collected_structure_paths"][7], + os.path.join(_tmp_dir, "HOPt36/7/perturbed_structure.traj"), + ) From 3fd18f5832eeba26a23cc76a8727bd2601a47027 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 9 Mar 2021 13:12:18 -0500 Subject: [PATCH 033/239] names now include base struct idx, collect structs --- src/autocat/perturbations.py | 52 ++++++++++++++++---- tests/test_perturbations.py | 94 ++++++++++++++++++++++++------------ 2 files changed, 105 insertions(+), 41 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 5cf5ffde..6229bc7a 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -8,6 +8,10 @@ import json +class AutocatPerturbationError(Exception): + pass + + def generate_perturbed_dataset( base_structures: List[Atoms], atom_indices_to_perturb_dictionary: Dict[Union[str, Atoms], List[int]], @@ -36,7 +40,10 @@ def generate_perturbed_dataset( atom_indices_to_perturb_dictionary: Dictionary of list of atomic indices for the atoms that should - be perturbed. Keys are each of the provided base structures + be perturbed. Keys are each of the provided base structures. + If an ase.Atoms object is given in `base_structures`, its corresponding + key here should be: + f"{base_structure.get_chemical_formula()}_{index_in_`base_structures`}" minimum_perturbation_distance: Float of minimum acceptable perturbation distance @@ -47,8 +54,11 @@ def generate_perturbed_dataset( Default: 1.0 Angstrom directions_dictionary: - List of bools indicating which cartesian directions - the atoms are allowed to be perturbed in + Dictionary of List of bools indicating which cartesian directions + the atoms are allowed to be perturbed in for each `base_structure`. + If an ase.Atoms object is given in `base_structures`, its corresponding + key here should be: + f"{base_structure.get_chemical_formula()}_{index_in_`base_structures`}" Default: free to perturb in all cartesian directions maximum_structure_size: @@ -93,18 +103,28 @@ def generate_perturbed_dataset( collected_matrices = [] collected_structure_paths = [] + collected_structures = [] - for structure in base_structures: + # loop over each base structure + for structure_index, structure in enumerate(base_structures): + # get name of each base structure if isinstance(structure, Atoms): - name = structure.get_chemical_formula() + name = structure.get_chemical_formula() + "_" + str(structure_index) elif isinstance(structure, str): name = ".".join(structure.split(".")[:-1]) else: raise TypeError(f"Structure needs to be either a str or ase.Atoms object") + # make sure no base_structures with the same name + if name in perturbed_dict: + msg = f"Multiple input base structures named {name}" + raise AutocatPerturbationError(msg) + + # assigns default of free to perturb in all directions if needed if name not in directions_dictionary: directions_dictionary[name] = None + # apply perturbations perturbed_dict[name] = {} for i in range(num_of_perturbations): perturbed_dict[name][str(i)] = perturb_structure( @@ -114,16 +134,18 @@ def generate_perturbed_dataset( maximum_perturbation_distance=maximum_perturbation_distance, directions=directions_dictionary[name], ) + # keeps flattened atomic coordinates difference vector collected_matrices.append( perturbed_dict[name][str(i)]["perturbation_matrix"].flatten() ) + traj_file_path = None pert_mat_file_path = None if write_to_disk: dir_path = os.path.join(write_location, f"{name}/{str(i)}") os.makedirs(dir_path, exist_ok=dirs_exist_ok) traj_file_path = os.path.join(dir_path, f"perturbed_structure.traj") - # write perturbed structure + # write perturbed structure to disk perturbed_dict[name][str(i)]["structure"].write(traj_file_path) print( f"{name} perturbed structure {str(i)} written to {traj_file_path}" @@ -144,7 +166,11 @@ def generate_perturbed_dataset( perturbed_dict[name][str(i)].update( {"pert_mat_file_path": pert_mat_file_path} ) + # Collects all of the structures into a single list in the same + # order as the collected matrix rows + collected_structures.append(perturbed_dict[name][str(i)]["structure"]) collected_structure_paths.append(traj_file_path) + if maximum_structure_size is None: # find flattened length of largest structure largest_size = max([len(i) for i in collected_matrices]) @@ -153,12 +179,14 @@ def generate_perturbed_dataset( largest_size = 3 * maximum_structure_size # ensures correct sized padding collected_matrices_array = np.zeros((len(collected_matrices), largest_size)) - # substitute in collected matrices + # substitute in collected matrices for each row for idx, row in enumerate(collected_matrices): collected_matrices_array[idx, : len(row)] = row + # adds collected matrices to dict that will be returned perturbed_dict["collected_matrices"] = collected_matrices_array collected_matrices_path = None + # write matrix to disk as json if desired if write_to_disk: collected_matrices_path = os.path.join( write_location, "collected_matrices.json" @@ -167,8 +195,14 @@ def generate_perturbed_dataset( with open(collected_matrices_path, "w") as f: json.dump(coll, f) print(f"Collected matrices written to {collected_matrices_path}") - perturbed_dict.update({"collected_matrices_path": collected_matrices_path}) - perturbed_dict.update({"collected_structure_paths": collected_structure_paths}) + # update output dict with collected structures and paths + perturbed_dict.update( + { + "collected_matrices_path": collected_matrices_path, + "collected_structures": collected_structures, + "collected_structure_paths": collected_structure_paths, + } + ) return perturbed_dict diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 69f2dad3..41a28b1f 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -6,6 +6,8 @@ import tempfile +from ase import Atoms + from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate from autocat.perturbations import perturb_structure @@ -59,11 +61,11 @@ def test_generate_perturbed_dataset_num_of_perturbations(): p_set = generate_perturbed_dataset( [base_struct], atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula(): [-1, -2] + base_struct.get_chemical_formula() + "_0": [-1, -2] }, num_of_perturbations=15, ) - assert len(p_set["HOPt36"].keys()) == 15 + assert len(p_set["HOPt36_0"].keys()) == 15 def test_generate_perturbed_dataset_multiple_base_structures(): @@ -79,25 +81,25 @@ def test_generate_perturbed_dataset_multiple_base_structures(): p_set = generate_perturbed_dataset( [base_struct1, base_struct2], atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula(): [-1], - base_struct2.get_chemical_formula(): [-2], + base_struct1.get_chemical_formula() + "_0": [-1], + base_struct2.get_chemical_formula() + "_1": [-2], }, directions_dictionary={ - base_struct1.get_chemical_formula(): [False, False, True] + base_struct1.get_chemical_formula() + "_0": [False, False, True] }, ) # Check all base structures perturbed - assert "HCu36N" in p_set - assert "HOPt36" in p_set + assert "HCu36N_1" in p_set + assert "HOPt36_0" in p_set # Check correct atom indices perturbed for each base_structure - assert (p_set["HCu36N"]["2"]["perturbation_matrix"][-1] == np.zeros(3)).all() - assert (p_set["HCu36N"]["3"]["perturbation_matrix"][-2] != np.zeros(3)).all() - assert (p_set["HOPt36"]["6"]["perturbation_matrix"][-1] != np.zeros(3)).any() + assert (p_set["HCu36N_1"]["2"]["perturbation_matrix"][-1] == np.zeros(3)).all() + assert (p_set["HCu36N_1"]["3"]["perturbation_matrix"][-2] != np.zeros(3)).all() + assert (p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1] != np.zeros(3)).any() # Check correct direction constraints applied to each base_structure - assert np.isclose(p_set["HOPt36"]["6"]["perturbation_matrix"][-1][0], 0.0) - assert np.isclose(p_set["HOPt36"]["6"]["perturbation_matrix"][-1][1], 0.0) - assert not np.isclose(p_set["HOPt36"]["6"]["perturbation_matrix"][-1][-1], 0.0) - assert not np.isclose(p_set["HCu36N"]["1"]["perturbation_matrix"][-2][0], 0.0) + assert np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][0], 0.0) + assert np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][1], 0.0) + assert not np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][-1], 0.0) + assert not np.isclose(p_set["HCu36N_1"]["1"]["perturbation_matrix"][-2][0], 0.0) def test_generate_perturbed_dataset_write_location(): @@ -109,17 +111,19 @@ def test_generate_perturbed_dataset_write_location(): base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] p_set = generate_perturbed_dataset( [base_struct], - atom_indices_to_perturb_dictionary={base_struct.get_chemical_formula(): [-1]}, + atom_indices_to_perturb_dictionary={ + base_struct.get_chemical_formula() + "_0": [-1] + }, write_to_disk=True, write_location=_tmp_dir, ) assert os.path.samefile( - p_set["HOPt36"]["0"]["traj_file_path"], - os.path.join(_tmp_dir, "HOPt36/0/perturbed_structure.traj"), + p_set["HOPt36_0"]["0"]["traj_file_path"], + os.path.join(_tmp_dir, "HOPt36_0/0/perturbed_structure.traj"), ) assert os.path.samefile( - p_set["HOPt36"]["0"]["pert_mat_file_path"], - os.path.join(_tmp_dir, "HOPt36/0/perturbation_matrix.json"), + p_set["HOPt36_0"]["0"]["pert_mat_file_path"], + os.path.join(_tmp_dir, "HOPt36_0/0/perturbation_matrix.json"), ) assert os.path.samefile( p_set["collected_matrices_path"], @@ -136,13 +140,15 @@ def test_generate_perturbed_dataset_collected_matrices(): p_set = generate_perturbed_dataset( [base_struct], atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula(): [-1, -2] + base_struct.get_chemical_formula() + "_0": [-1, -2] }, num_of_perturbations=5, ) manual_collect = [] - for struct in p_set["HOPt36"]: - manual_collect.append(p_set["HOPt36"][struct]["perturbation_matrix"].flatten()) + for struct in p_set["HOPt36_0"]: + manual_collect.append( + p_set["HOPt36_0"][struct]["perturbation_matrix"].flatten() + ) manual_collect = np.array(manual_collect) assert np.allclose(p_set["collected_matrices"], manual_collect) assert p_set["collected_matrices"].shape == (5, 114) @@ -150,7 +156,7 @@ def test_generate_perturbed_dataset_collected_matrices(): p_set = generate_perturbed_dataset( [base_struct], atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula(): [-1, -2] + base_struct.get_chemical_formula() + "_0": [-1, -2] }, num_of_perturbations=5, maximum_structure_size=45, @@ -171,13 +177,13 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): p_set = generate_perturbed_dataset( [base_struct1, base_struct2], atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula(): [-1], - base_struct2.get_chemical_formula(): [-2], + base_struct1.get_chemical_formula() + "_0": [-1], + base_struct2.get_chemical_formula() + "_1": [-2], }, num_of_perturbations=5, ) manual_collect = [] - for base in ["HOPt36", "HCu36O"]: + for base in ["HOPt36_0", "HCu36O_1"]: for struct in p_set[base]: manual_collect.append(p_set[base][struct]["perturbation_matrix"].flatten()) manual_collect = np.array(manual_collect) @@ -196,13 +202,13 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): p_set = generate_perturbed_dataset( [base_struct1, base_struct2], atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula(): [-1], - base_struct2.get_chemical_formula(): [-2], + base_struct1.get_chemical_formula() + "_0": [-1], + base_struct2.get_chemical_formula() + "_1": [-2], }, num_of_perturbations=5, ) manual_collect = [] - for base in ["HOPt36", "HCu36"]: + for base in ["HOPt36_0", "HCu36_1"]: for struct in p_set[base]: manual_collect.append(p_set[base][struct]["perturbation_matrix"].flatten()) manual_collect_array = np.zeros((10, 114)) @@ -221,15 +227,39 @@ def test_generate_perturbed_dataset_collected_structure_paths(): base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] p_set = generate_perturbed_dataset( [base_struct], - atom_indices_to_perturb_dictionary={base_struct.get_chemical_formula(): [-1]}, + atom_indices_to_perturb_dictionary={ + base_struct.get_chemical_formula() + "_0": [-1] + }, write_to_disk=True, write_location=_tmp_dir, ) assert os.path.samefile( p_set["collected_structure_paths"][0], - os.path.join(_tmp_dir, "HOPt36/0/perturbed_structure.traj"), + os.path.join(_tmp_dir, "HOPt36_0/0/perturbed_structure.traj"), ) assert os.path.samefile( p_set["collected_structure_paths"][7], - os.path.join(_tmp_dir, "HOPt36/7/perturbed_structure.traj"), + os.path.join(_tmp_dir, "HOPt36_0/7/perturbed_structure.traj"), + ) + + +def test_generate_perturbed_dataset_collected_structures(): + # Test that all of the structures are collected + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "OH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct1, base_struct2], + atom_indices_to_perturb_dictionary={ + base_struct1.get_chemical_formula() + "_0": [-1], + base_struct2.get_chemical_formula() + "_1": [-2], + }, + num_of_perturbations=5, ) + assert len(p_set["collected_structures"]) == 10 + assert isinstance(p_set["collected_structures"][3], Atoms) From edcab7f841a7a385bbb3a399ca564442f04766f3 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 9 Mar 2021 13:32:30 -0500 Subject: [PATCH 034/239] propagate new naming scheme from perturbations --- src/autocat/learning/featurizers.py | 2 +- tests/learning/test_featurizers.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 4e4bb0bc..99dc1c77 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -126,7 +126,7 @@ def get_X( ) for idx, structure in enumerate(structures): if isinstance(structure, Atoms): - name = structure.get_chemical_formula() + name = structure.get_chemical_formula() + "_" + str(idx) ase_struct = structure.copy() elif isinstance(structure, str): name = structure diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index c10ac946..11bc7e48 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -148,9 +148,9 @@ def test_get_X_concatenation(): X = get_X( structs, adsorbate_indices_dictionary={ - structs[0].get_chemical_formula(): [-4, -3, -2, -1], - structs[1].get_chemical_formula(): [-2, -1], - structs[2].get_chemical_formula(): [-1], + structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], + structs[1].get_chemical_formula() + "_1": [-2, -1], + structs[2].get_chemical_formula() + "_2": [-1], }, maximum_structure_size=50, maximum_adsorbate_size=5, @@ -182,8 +182,8 @@ def test_get_X_write_location(): X = get_X( structs, adsorbate_indices_dictionary={ - structs[0].get_chemical_formula(): [-4, -3, -2, -1], - structs[1].get_chemical_formula(): [-2, -1], + structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], + structs[1].get_chemical_formula() + "_1": [-2, -1], }, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, write_to_disk=True, From bac50f11cb7a29ba8e47da3e890adab3640d82c7 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 9 Mar 2021 15:53:35 -0500 Subject: [PATCH 035/239] shift from qml.math -> sklearn --- src/autocat/learning/predictors.py | 123 ++++++++++++++++------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 89aa9d59..5d5f18d7 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -1,77 +1,92 @@ -import qml.kernels as qml_kernels -import qml.math as qml_math import numpy as np +from typing import List +from typing import Dict +from typing import Union -def get_alphas( - kernel: np.ndarray, - perturbation_matrices: np.ndarray, - regularization_strength: float = 1e-8, +from ase import Atoms + +from sklearn.model_selection import KFold +from sklearn.linear_model import BayesianRidge +from sklearn.kernel_ridge import KernelRidge + +from autocat.learning.featurizers import get_X + + +def get_trained_model_on_perturbed_systems( + perturbed_structures: List[Union[Atoms, str]], + adsorbate_indices_dictionary: Dict[str, int], + collected_matrices: np.ndarray, + model_name: str = "bayes", + structure_featurizer: str = "sine_matrix", + adsorbate_featurizer: str = "soap", + featurization_kwargs: Dict = None, + model_kwargs: Dict = None, ): """ - Wrapper for `qml.math.cho_solve` to - solve for the regression coefficients using cholesky - decomposition (ie. training a KRR) - - alphas = (K + lambda*I)^(-1)y + Given a list of base_structures, will generate perturbed structures + and train a regression model on them Parameters ---------- - kernel: - Numpy array of kernel matrix (e.g. generated via `get_kernel`) + perturbed_structures: + List of perturbed structures to be trained upon - perturbation_matrices: - Numpy array containing perturbation matrices corresponding to the - input structures which form the labels when training + adsorbate_indices_dictionary: + Dictionary mapping structures to desired adsorbate_indices + (N.B. if structure is given as an ase.Atoms object, + the key for this dictionary should be + f"{structure.get_chemical_formula()}_{index_in_`perturbed_structures`}") - regularization_strength: - Float specifying regularization strength (lambda in eqtn above) + collected_matrices: + Numpy array of collected matrices of perturbations corresponding to + each of the perturbed structures. + This can be generated via `autocat.perturbations.generate_perturbed_dataset`. + Shape should be (# of structures, 3 * # of atoms in the largest structure) - perturbation - - Returns - ------- + model_name: + String giving the name of the `sklearn` regression model to use. + Options: + - bayes: Bayesian Ridge Regression + - krr: Kernel Ridge Regression - alphas: - Numpy array of regression coefficients + structure_featurizer: + String giving featurizer to be used for full structure which will be + fed into `autocat.learning.featurizers.full_structure_featurization` - """ - K = kernel + regularization_strength * np.identity(kernel.shape) - - return qml_math.cho_solve(K, perturbation_matrices) + adsorbate_featurizer: + String giving featurizer to be used for full structure which will be + fed into `autocat.learning.featurizers.adsorbate_structure_featurization` + Returns + ------- -def get_kernel(X1: np.ndarray, X2: np.ndarray, kernel_type: str = "gaussian", **kwargs): + trained_model: + Trained `sklearn` model object """ - Wrapper for `qml` kernels + X = get_X( + perturbed_structures, + adsorbate_indices_dictionary=adsorbate_indices_dictionary, + structure_featurizer=structure_featurizer, + adsorbate_featurizer=adsorbate_featurizer, + **featurization_kwargs + ) - Parameters - ---------- + regressor = _get_regressor(model_name, **model_kwargs) - X1,X2: - Numpy arrays to be used to calculate the kernel + regressor.fit(X, collected_matrices) - kernel_type: - String giving type of kernel to be generated. - Options: - - gaussian (default) - - laplacian - - linear - - matern - - sargan + return regressor - Returns - ------- - kernel_matrix: - Numpy array of generated kernel matrix +def _get_regressor(model_name: str, **kwargs): + """ + Gets `sklearn` regressor object """ - kernel_funcs = { - "laplacian": qml_kernels.laplacian_kernel, - "gaussian": qml_kernels.gaussian_kernel, - "linear": qml_kernels.linear_kernel, - "matern": qml_kernels.matern_kernel, - "sargan": qml_kernels.sargan_kernel, - } - return kernel_funcs[kernel_type](X1, X2, **kwargs) + if model_name == "bayes": + return BayesianRidge(**kwargs) + elif model_name == "krr": + return KernelRidge(**kwargs) + else: + raise NotImplementedError("model selected not implemented") From 5d6f2ac0b6f16815a82e6bfecc56efbe47da31be Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 9 Mar 2021 16:37:21 -0500 Subject: [PATCH 036/239] add fn for predicting corrected struct given model --- src/autocat/learning/predictors.py | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 5d5f18d7..98c458a6 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -11,6 +11,74 @@ from sklearn.kernel_ridge import KernelRidge from autocat.learning.featurizers import get_X +from autocat.learning.featurizers import catalyst_featurization + +Regressor = Union[BayesianRidge, KernelRidge] + + +def predict_initial_configuration( + inital_structure_guess: Atoms, + adsorbate_indices: List[int], + trained_regressor_model: Regressor, + featurization_kwargs: Dict[str, float] = None, +): + """ + From a trained model, will predict corrected structure + of a given initial structure guess + + Parameters + ---------- + + initial_structure_guess: + Atoms object of an initial guess for an adsorbate + on a surface to be optimized + + adsorbate_indices: + List of ints giving the atomic indices of the adsorbate + atoms that can be perturbed + + trained_regressor_model: + Fit sklearn regression model to be used for prediction + + Returns + ------- + + predicted_correction_matrix: + Matrix of predicted corrections that were applied + + uncertainty_estimate: + Standard deviation of prediction from regressor + (only supported for `bayes` at present) + + corrected_structure: + Atoms object with corrections applied + + """ + featurized_input = catalyst_featurization( + inital_structure_guess, **featurization_kwargs + ) + + if isinstance(trained_regressor_model, BayesianRidge): + ( + flat_predicted_correction_matrix, + uncertainty_estimate, + ) = trained_regressor_model.predict(featurized_input, return_std=True) + predicted_correction_matrix = flat_predicted_correction_matrix.reshape(-1, 3) + + elif isinstance(trained_regressor_model, KernelRidge): + uncertainty_estimate = 0.0 + flat_predicted_correction_matrix = trained_regressor_model.predict( + featurized_input + ) + predicted_correction_matrix = flat_predicted_correction_matrix.reshape(-1, 3) + + else: + raise TypeError("Trained Regressor Model is not a supported type") + + corrected_structure = inital_structure_guess.copy() + corrected_structure.positions += predicted_correction_matrix + + return predicted_correction_matrix, uncertainty_estimate, corrected_structure def get_trained_model_on_perturbed_systems( From 831abe58213142d05c518516775bf2c87de1c216 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 11 Mar 2021 16:41:06 -0500 Subject: [PATCH 037/239] various fixes - rm bayes (no multi-target) - explicitly define structure and adsorbate kwarg dicts - start writing predictor tests - rm unused kwargs from get_X --- src/autocat/learning/featurizers.py | 1 - src/autocat/learning/predictors.py | 48 +++++++-------- tests/learning/test_predictors.py | 90 +++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 27 deletions(-) create mode 100644 tests/learning/test_predictors.py diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 99dc1c77..7808306e 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -31,7 +31,6 @@ def get_X( adsorbate_featurization_kwargs: Dict[str, float] = None, write_to_disk: bool = False, write_location: str = ".", - **kwargs, ): """ Generate representation matrix X from list of ase.Atoms objects diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 98c458a6..675ef85d 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -7,20 +7,18 @@ from ase import Atoms from sklearn.model_selection import KFold -from sklearn.linear_model import BayesianRidge from sklearn.kernel_ridge import KernelRidge from autocat.learning.featurizers import get_X from autocat.learning.featurizers import catalyst_featurization -Regressor = Union[BayesianRidge, KernelRidge] - def predict_initial_configuration( inital_structure_guess: Atoms, adsorbate_indices: List[int], - trained_regressor_model: Regressor, - featurization_kwargs: Dict[str, float] = None, + trained_regressor_model: KernelRidge, + structure_featurization_kwargs: Dict[str, float] = None, + adsorbate_featurization_kwargs: Dict[str, float] = None, ): """ From a trained model, will predict corrected structure @@ -55,18 +53,13 @@ def predict_initial_configuration( """ featurized_input = catalyst_featurization( - inital_structure_guess, **featurization_kwargs - ) + inital_structure_guess, + adsorbate_indices=adsorbate_indices, + structure_featurization_kwargs=structure_featurization_kwargs, + adsorbate_featurization_kwargs=adsorbate_featurization_kwargs, + ).reshape(1, -1) - if isinstance(trained_regressor_model, BayesianRidge): - ( - flat_predicted_correction_matrix, - uncertainty_estimate, - ) = trained_regressor_model.predict(featurized_input, return_std=True) - predicted_correction_matrix = flat_predicted_correction_matrix.reshape(-1, 3) - - elif isinstance(trained_regressor_model, KernelRidge): - uncertainty_estimate = 0.0 + if isinstance(trained_regressor_model, KernelRidge): flat_predicted_correction_matrix = trained_regressor_model.predict( featurized_input ) @@ -78,22 +71,23 @@ def predict_initial_configuration( corrected_structure = inital_structure_guess.copy() corrected_structure.positions += predicted_correction_matrix - return predicted_correction_matrix, uncertainty_estimate, corrected_structure + return predicted_correction_matrix, corrected_structure def get_trained_model_on_perturbed_systems( perturbed_structures: List[Union[Atoms, str]], adsorbate_indices_dictionary: Dict[str, int], collected_matrices: np.ndarray, - model_name: str = "bayes", + model_name: str = "krr", structure_featurizer: str = "sine_matrix", adsorbate_featurizer: str = "soap", - featurization_kwargs: Dict = None, + structure_featurization_kwargs: Dict = None, + adsorbate_featurization_kwargs: Dict = None, model_kwargs: Dict = None, ): """ - Given a list of base_structures, will generate perturbed structures - and train a regression model on them + Given a list of perturbed structures + will featurize and train a regression model on them Parameters ---------- @@ -116,7 +110,6 @@ def get_trained_model_on_perturbed_systems( model_name: String giving the name of the `sklearn` regression model to use. Options: - - bayes: Bayesian Ridge Regression - krr: Kernel Ridge Regression structure_featurizer: @@ -138,9 +131,13 @@ def get_trained_model_on_perturbed_systems( adsorbate_indices_dictionary=adsorbate_indices_dictionary, structure_featurizer=structure_featurizer, adsorbate_featurizer=adsorbate_featurizer, - **featurization_kwargs + structure_featurization_kwargs=structure_featurization_kwargs, + adsorbate_featurization_kwargs=adsorbate_featurization_kwargs, ) + if model_kwargs is None: + model_kwargs = {} + regressor = _get_regressor(model_name, **model_kwargs) regressor.fit(X, collected_matrices) @@ -152,9 +149,8 @@ def _get_regressor(model_name: str, **kwargs): """ Gets `sklearn` regressor object """ - if model_name == "bayes": - return BayesianRidge(**kwargs) - elif model_name == "krr": + if model_name == "krr": return KernelRidge(**kwargs) + # other regressors to be implemented else: raise NotImplementedError("model selected not implemented") diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py new file mode 100644 index 00000000..b23810b6 --- /dev/null +++ b/tests/learning/test_predictors.py @@ -0,0 +1,90 @@ +"""Unit tests for the `autocat.learning.predictors` module""" + +import os +import pytest +import numpy as np + +from sklearn.kernel_ridge import KernelRidge + +from ase import Atoms + +from autocat.adsorption import place_adsorbate +from autocat.surface import generate_surface_structures +from autocat.perturbations import generate_perturbed_dataset +from autocat.learning.featurizers import get_X +from autocat.learning.featurizers import catalyst_featurization +from autocat.learning.predictors import get_trained_model_on_perturbed_systems +from autocat.learning.predictors import predict_initial_configuration + + +def test_get_trained_model_on_perturbed_systems(): + # Test returns a fit model + sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct], + atom_indices_to_perturb_dictionary={ + base_struct.get_chemical_formula() + "_0": [-1, -2] + }, + num_of_perturbations=15, + ) + p_structures = p_set["collected_structures"] + collected_matrices = p_set["collected_matrices"] + trained_model = get_trained_model_on_perturbed_systems( + p_structures, + adsorbate_indices_dictionary={ + base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] + for i in range(15) + }, + collected_matrices=collected_matrices, + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + # check correct regressor is used + assert isinstance(trained_model, KernelRidge) + + # check if fit + t_feat = catalyst_featurization( + p_structures[0], + [-1, -2], + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + # will raise a NotFittedError if not fit + trained_model.predict(t_feat.reshape(1, -1)) + + +def test_predict_initial_configuration_formats(): + # Test outputs are returned as expected + sub = generate_surface_structures(["Fe"], facets={"Fe": ["100"]})["Fe"]["bcc100"][ + "structure" + ] + base_struct = place_adsorbate(sub, "CO")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct], + atom_indices_to_perturb_dictionary={ + base_struct.get_chemical_formula() + "_0": [-1, -2] + }, + num_of_perturbations=20, + ) + p_structures = p_set["collected_structures"] + collected_matrices = p_set["collected_matrices"] + trained_model = get_trained_model_on_perturbed_systems( + p_structures, + adsorbate_indices_dictionary={ + base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] + for i in range(20) + }, + collected_matrices=collected_matrices, + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + predicted_correction_matrix, corrected_structure = predict_initial_configuration( + p_structures[0], + [-1, -2], + trained_model, + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + assert isinstance(corrected_structure, Atoms) + assert isinstance(predicted_correction_matrix, np.ndarray) + # check correction matrix returned in shape of coordinates matrix + assert predicted_correction_matrix.shape == (len(corrected_structure), 3) From a9a7a95f998072968dafb769831cf6146c419bac Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 19 Mar 2021 11:39:34 -0400 Subject: [PATCH 038/239] predictor functions -> predictor class --- src/autocat/learning/predictors.py | 392 +++++++++++++++++++---------- tests/learning/test_predictors.py | 40 ++- 2 files changed, 272 insertions(+), 160 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 675ef85d..44d56c6e 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -13,144 +13,260 @@ from autocat.learning.featurizers import catalyst_featurization -def predict_initial_configuration( - inital_structure_guess: Atoms, - adsorbate_indices: List[int], - trained_regressor_model: KernelRidge, - structure_featurization_kwargs: Dict[str, float] = None, - adsorbate_featurization_kwargs: Dict[str, float] = None, -): - """ - From a trained model, will predict corrected structure - of a given initial structure guess - - Parameters - ---------- - - initial_structure_guess: - Atoms object of an initial guess for an adsorbate - on a surface to be optimized - - adsorbate_indices: - List of ints giving the atomic indices of the adsorbate - atoms that can be perturbed - - trained_regressor_model: - Fit sklearn regression model to be used for prediction - - Returns - ------- - - predicted_correction_matrix: - Matrix of predicted corrections that were applied - - uncertainty_estimate: - Standard deviation of prediction from regressor - (only supported for `bayes` at present) - - corrected_structure: - Atoms object with corrections applied - - """ - featurized_input = catalyst_featurization( - inital_structure_guess, - adsorbate_indices=adsorbate_indices, - structure_featurization_kwargs=structure_featurization_kwargs, - adsorbate_featurization_kwargs=adsorbate_featurization_kwargs, - ).reshape(1, -1) - - if isinstance(trained_regressor_model, KernelRidge): - flat_predicted_correction_matrix = trained_regressor_model.predict( - featurized_input +class AutoCatStructureCorrector(KernelRidge): + def __init__( + self, + structure_featurizer: str = None, + adsorbate_featurizer: str = None, + maximum_structure_size: int = None, + maximum_adsorbate_size: int = None, + species_list: List[str] = None, + structure_featurization_kwargs: Dict = None, + adsorbate_featurization_kwargs: Dict = None, + **model_kwargs, + ): + """ + Constructor. + + Parameters + ---------- + + structure_featurizer: + String giving featurizer to be used for full structure which will be + fed into `autocat.learning.featurizers.full_structure_featurization` + + adsorbate_featurizer: + String giving featurizer to be used for full structure which will be + fed into `autocat.learning.featurizers.adsorbate_structure_featurization + + maximum_structure_size: + Size of the largest structure to be supported by the representation. + Default: number of atoms in largest structure within `structures` + + maximum_adsorbate_size: + Integer giving the maximum adsorbate size to be encountered + (ie. this determines if zero-padding should be applied and how much). + If the provided value is less than the adsorbate size given by + `adsorbate_indices`, representation will remain size of the adsorbate. + Default: size of adsorbate provided + + species_list: + List of species that could be encountered for featurization. + Default: Parses over all `structures` and collects all encountered species + + """ + super(AutoCatStructureCorrector, self).__init__(**model_kwargs) + + self.is_fit = False + + self._structure_featurizer = "sine_matrix" + self.structure_featurizer = structure_featurizer + + self._adsorbate_featurizer = "soap" + self.adsorbate_featurizer = adsorbate_featurizer + + self._structure_featurization_kwargs = None + self.structure_featurization_kwargs = structure_featurization_kwargs + + self._adsorbate_featurization_kwargs = {"rcut": 6.0, "nmax": 8, "lmax": 6} + self.adsorbate_featurization_kwargs = adsorbate_featurization_kwargs + + self._maximum_structure_size = None + self.maximum_structure_size = maximum_structure_size + + self._maximum_adsorbate_size = None + self.maximum_adsorbate_size = maximum_adsorbate_size + + self._species_list = None + self.species_list = species_list + + @property + def structure_featurizer(self): + return self._structure_featurizer + + @structure_featurizer.setter + def structure_featurizer(self, structure_featurizer): + if structure_featurizer is not None: + self._structure_featurizer = structure_featurizer + if self.is_fit: + self.is_fit = False + + @property + def adsorbate_featurizer(self): + return self._adsorbate_featurizer + + @adsorbate_featurizer.setter + def adsorbate_featurizer(self, adsorbate_featurizer): + if adsorbate_featurizer is not None: + self._adsorbate_featurizer = adsorbate_featurizer + if self.is_fit: + self.is_fit = False + + @property + def structure_featurization_kwargs(self): + return self._structure_featurization_kwargs + + @structure_featurization_kwargs.setter + def structure_featurization_kwargs(self, structure_featurization_kwargs): + if structure_featurization_kwargs is not None: + assert isinstance(structure_featurization_kwargs, dict) + if self._structure_featurization_kwargs is not None: + self._structure_featurization_kwargs.update( + structure_featurization_kwargs + ) + else: + self._structure_featurization_kwargs = structure_featurization_kwargs + if self.is_fit: + self.is_fit = False + + @property + def adsorbate_featurization_kwargs(self): + return self._adsorbate_featurization_kwargs + + @adsorbate_featurization_kwargs.setter + def adsorbate_featurization_kwargs(self, adsorbate_featurization_kwargs): + if adsorbate_featurization_kwargs is not None: + assert isinstance(adsorbate_featurization_kwargs, dict) + self._adsorbate_featurization_kwargs.update(adsorbate_featurization_kwargs) + if self.is_fit: + self.is_fit = False + + @property + def maximum_structure_size(self): + return self._maximum_structure_size + + @maximum_structure_size.setter + def maximum_structure_size(self, maximum_structure_size): + if maximum_structure_size is not None: + self._maximum_structure_size = maximum_structure_size + if self.is_fit: + self.is_fit = False + + @property + def maximum_adsorbate_size(self): + return self._maximum_adsorbate_size + + @maximum_adsorbate_size.setter + def maximum_adsorbate_size(self, maximum_adsorbate_size): + if maximum_adsorbate_size is not None: + self._maximum_adsorbate_size = maximum_adsorbate_size + if self.is_fit: + self.is_fit = False + + @property + def species_list(self): + return self._species_list + + @species_list.setter + def species_list(self, species_list): + if species_list is not None: + self._species_list = species_list + if self.is_fit: + self.is_fit = False + + def fit( + self, + perturbed_structures: List[Union[Atoms, str]], + adsorbate_indices_dictionary: Dict[str, int], + collected_matrices: np.ndarray, + ): + """ + Given a list of perturbed structures + will featurize and train a regression model on them + + Parameters + ---------- + + perturbed_structures: + List of perturbed structures to be trained upon + + adsorbate_indices_dictionary: + Dictionary mapping structures to desired adsorbate_indices + (N.B. if structure is given as an ase.Atoms object, + the key for this dictionary should be + f"{structure.get_chemical_formula()}_{index_in_`perturbed_structures`}") + + collected_matrices: + Numpy array of collected matrices of perturbations corresponding to + each of the perturbed structures. + This can be generated via `autocat.perturbations.generate_perturbed_dataset`. + Shape should be (# of structures, 3 * # of atoms in the largest structure) + + Returns + ------- + + trained_model: + Trained `sklearn` model object + """ + X = get_X( + perturbed_structures, + adsorbate_indices_dictionary=adsorbate_indices_dictionary, + maximum_structure_size=self.maximum_structure_size, + structure_featurizer=self.structure_featurizer, + maximum_adsorbate_size=self.maximum_adsorbate_size, + adsorbate_featurizer=self.adsorbate_featurizer, + species_list=self.species_list, + structure_featurization_kwargs=self.structure_featurization_kwargs, + adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) + + super(AutoCatStructureCorrector, self).fit(X, collected_matrices) + self.is_fit = True + + def predict( + self, inital_structure_guess: Atoms, adsorbate_indices: List[int], + ): + """ + From a trained model, will predict corrected structure + of a given initial structure guess + + Parameters + ---------- + + initial_structure_guess: + Atoms object of an initial guess for an adsorbate + on a surface to be optimized + + adsorbate_indices: + List of ints giving the atomic indices of the adsorbate + atoms that can be perturbed + + trained_regressor_model: + Fit sklearn regression model to be used for prediction + + Returns + ------- + + predicted_correction_matrix: + Matrix of predicted corrections that were applied + + uncertainty_estimate: + Standard deviation of prediction from regressor + (only supported for `bayes` at present) + + corrected_structure: + Atoms object with corrections applied + + """ + assert self.is_fit + featurized_input = catalyst_featurization( + inital_structure_guess, + adsorbate_indices=adsorbate_indices, + structure_featurizer=self.structure_featurizer, + adsorbate_featurizer=self.adsorbate_featurizer, + maximum_structure_size=self.maximum_structure_size, + maximum_adsorbate_size=self.maximum_adsorbate_size, + species_list=self.species_list, + structure_featurization_kwargs=self.structure_featurization_kwargs, + adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, + ).reshape(1, -1) + + flat_predicted_correction_matrix = super( + AutoCatStructureCorrector, self + ).predict(featurized_input) predicted_correction_matrix = flat_predicted_correction_matrix.reshape(-1, 3) - else: - raise TypeError("Trained Regressor Model is not a supported type") - - corrected_structure = inital_structure_guess.copy() - corrected_structure.positions += predicted_correction_matrix - - return predicted_correction_matrix, corrected_structure - - -def get_trained_model_on_perturbed_systems( - perturbed_structures: List[Union[Atoms, str]], - adsorbate_indices_dictionary: Dict[str, int], - collected_matrices: np.ndarray, - model_name: str = "krr", - structure_featurizer: str = "sine_matrix", - adsorbate_featurizer: str = "soap", - structure_featurization_kwargs: Dict = None, - adsorbate_featurization_kwargs: Dict = None, - model_kwargs: Dict = None, -): - """ - Given a list of perturbed structures - will featurize and train a regression model on them - - Parameters - ---------- - - perturbed_structures: - List of perturbed structures to be trained upon - - adsorbate_indices_dictionary: - Dictionary mapping structures to desired adsorbate_indices - (N.B. if structure is given as an ase.Atoms object, - the key for this dictionary should be - f"{structure.get_chemical_formula()}_{index_in_`perturbed_structures`}") - - collected_matrices: - Numpy array of collected matrices of perturbations corresponding to - each of the perturbed structures. - This can be generated via `autocat.perturbations.generate_perturbed_dataset`. - Shape should be (# of structures, 3 * # of atoms in the largest structure) - - model_name: - String giving the name of the `sklearn` regression model to use. - Options: - - krr: Kernel Ridge Regression - - structure_featurizer: - String giving featurizer to be used for full structure which will be - fed into `autocat.learning.featurizers.full_structure_featurization` - - adsorbate_featurizer: - String giving featurizer to be used for full structure which will be - fed into `autocat.learning.featurizers.adsorbate_structure_featurization` - - Returns - ------- - - trained_model: - Trained `sklearn` model object - """ - X = get_X( - perturbed_structures, - adsorbate_indices_dictionary=adsorbate_indices_dictionary, - structure_featurizer=structure_featurizer, - adsorbate_featurizer=adsorbate_featurizer, - structure_featurization_kwargs=structure_featurization_kwargs, - adsorbate_featurization_kwargs=adsorbate_featurization_kwargs, - ) - - if model_kwargs is None: - model_kwargs = {} - - regressor = _get_regressor(model_name, **model_kwargs) - - regressor.fit(X, collected_matrices) - - return regressor - - -def _get_regressor(model_name: str, **kwargs): - """ - Gets `sklearn` regressor object - """ - if model_name == "krr": - return KernelRidge(**kwargs) - # other regressors to be implemented - else: - raise NotImplementedError("model selected not implemented") + corrected_structure = inital_structure_guess.copy() + corrected_structure.positions += predicted_correction_matrix + + return predicted_correction_matrix, corrected_structure diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index b23810b6..5218adbe 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -13,11 +13,10 @@ from autocat.perturbations import generate_perturbed_dataset from autocat.learning.featurizers import get_X from autocat.learning.featurizers import catalyst_featurization -from autocat.learning.predictors import get_trained_model_on_perturbed_systems -from autocat.learning.predictors import predict_initial_configuration +from autocat.learning.predictors import AutoCatStructureCorrector -def test_get_trained_model_on_perturbed_systems(): +def test_fit_model_on_perturbed_systems(): # Test returns a fit model sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" @@ -32,26 +31,24 @@ def test_get_trained_model_on_perturbed_systems(): ) p_structures = p_set["collected_structures"] collected_matrices = p_set["collected_matrices"] - trained_model = get_trained_model_on_perturbed_systems( + acsc = AutoCatStructureCorrector( + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6} + ) + acsc.fit( p_structures, adsorbate_indices_dictionary={ base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] for i in range(15) }, collected_matrices=collected_matrices, - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) - # check correct regressor is used - assert isinstance(trained_model, KernelRidge) + assert acsc.adsorbate_featurizer == "soap" + assert acsc.is_fit - # check if fit - t_feat = catalyst_featurization( - p_structures[0], - [-1, -2], - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, - ) - # will raise a NotFittedError if not fit - trained_model.predict(t_feat.reshape(1, -1)) + # check no longer fit after changing setting + acsc.adsorbate_featurizer = "acsf" + assert acsc.adsorbate_featurizer == "acsf" + assert not acsc.is_fit def test_predict_initial_configuration_formats(): @@ -69,20 +66,19 @@ def test_predict_initial_configuration_formats(): ) p_structures = p_set["collected_structures"] collected_matrices = p_set["collected_matrices"] - trained_model = get_trained_model_on_perturbed_systems( + acsc = AutoCatStructureCorrector( + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6} + ) + acsc.fit( p_structures, adsorbate_indices_dictionary={ base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] for i in range(20) }, collected_matrices=collected_matrices, - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) - predicted_correction_matrix, corrected_structure = predict_initial_configuration( - p_structures[0], - [-1, -2], - trained_model, - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + predicted_correction_matrix, corrected_structure = acsc.predict( + p_structures[0], [-1, -2], ) assert isinstance(corrected_structure, Atoms) assert isinstance(predicted_correction_matrix, np.ndarray) From 76a7e8d61ac8894f9175e923389e4199eec603ea Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 19 Mar 2021 12:12:14 -0400 Subject: [PATCH 039/239] allows prediction on multi structs at once --- src/autocat/learning/predictors.py | 34 ++++++++++++++++++------------ tests/learning/test_predictors.py | 20 +++++++++++------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 44d56c6e..0b7099a3 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -10,7 +10,6 @@ from sklearn.kernel_ridge import KernelRidge from autocat.learning.featurizers import get_X -from autocat.learning.featurizers import catalyst_featurization class AutoCatStructureCorrector(KernelRidge): @@ -214,7 +213,9 @@ def fit( self.is_fit = True def predict( - self, inital_structure_guess: Atoms, adsorbate_indices: List[int], + self, + initial_structure_guesses: List[Atoms], + adsorbate_indices_dictionary: Dict[str, int], ): """ From a trained model, will predict corrected structure @@ -249,9 +250,9 @@ def predict( """ assert self.is_fit - featurized_input = catalyst_featurization( - inital_structure_guess, - adsorbate_indices=adsorbate_indices, + featurized_input = get_X( + structures=initial_structure_guesses, + adsorbate_indices_dictionary=adsorbate_indices_dictionary, structure_featurizer=self.structure_featurizer, adsorbate_featurizer=self.adsorbate_featurizer, maximum_structure_size=self.maximum_structure_size, @@ -259,14 +260,21 @@ def predict( species_list=self.species_list, structure_featurization_kwargs=self.structure_featurization_kwargs, adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, - ).reshape(1, -1) + ) + + predicted_correction_matrix = super(AutoCatStructureCorrector, self).predict( + featurized_input + ) - flat_predicted_correction_matrix = super( - AutoCatStructureCorrector, self - ).predict(featurized_input) - predicted_correction_matrix = flat_predicted_correction_matrix.reshape(-1, 3) + corrected_structures = [ + init_struct.copy() for init_struct in initial_structure_guesses + ] - corrected_structure = inital_structure_guess.copy() - corrected_structure.positions += predicted_correction_matrix + corrected_structures = [] + for idx, struct in enumerate(initial_structure_guesses): + cs = struct.copy() + corr = predicted_correction_matrix[idx, :].reshape(-1, 3) + cs.positions += corr + corrected_structures.append(cs) - return predicted_correction_matrix, corrected_structure + return predicted_correction_matrix, corrected_structures diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 5218adbe..5ee26570 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -70,17 +70,21 @@ def test_predict_initial_configuration_formats(): adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6} ) acsc.fit( - p_structures, + p_structures[:15], adsorbate_indices_dictionary={ base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] - for i in range(20) + for i in range(15) }, - collected_matrices=collected_matrices, + collected_matrices=collected_matrices[:15, :], ) - predicted_correction_matrix, corrected_structure = acsc.predict( - p_structures[0], [-1, -2], + predicted_correction_matrix, corrected_structures = acsc.predict( + p_structures[15:], + adsorbate_indices_dictionary={ + base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] + for i in range(5) + }, ) - assert isinstance(corrected_structure, Atoms) + assert isinstance(corrected_structures[0], Atoms) + assert len(corrected_structures) == 5 assert isinstance(predicted_correction_matrix, np.ndarray) - # check correction matrix returned in shape of coordinates matrix - assert predicted_correction_matrix.shape == (len(corrected_structure), 3) + assert predicted_correction_matrix.shape[0] == 5 From 73ecce74656945ccdbc8d5e78cb7d9e5d9f64a49 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 19 Mar 2021 12:17:11 -0400 Subject: [PATCH 040/239] update docstrings --- src/autocat/learning/featurizers.py | 3 ++- src/autocat/learning/predictors.py | 21 ++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 7808306e..395ab2e1 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -44,7 +44,8 @@ def get_X( adsorbate_indices_dictionary: Dictionary mapping structures to desired adsorbate_indices (N.B. if structure is given as an ase.Atoms object, - the key for this dictionary should be ase.Atoms.get_chemical_formula()) + the key for this dictionary should be + ase.Atoms.get_chemical_formula() + "_" + str(index in list)) maximum_structure_size: Size of the largest structure to be supported by the representation. diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 0b7099a3..920937a7 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -224,16 +224,15 @@ def predict( Parameters ---------- - initial_structure_guess: - Atoms object of an initial guess for an adsorbate - on a surface to be optimized + initial_structure_guesses: + List of Atoms objects of initial guesses for adsorbate + placement to be optimized - adsorbate_indices: - List of ints giving the atomic indices of the adsorbate - atoms that can be perturbed - - trained_regressor_model: - Fit sklearn regression model to be used for prediction + adsorbate_indices_dictionary: + Dictionary mapping structures to desired adsorbate_indices + (N.B. if structures is given as ase.Atoms objects, + the key for this dictionary should be + ase.Atoms.get_chemical_formula()+ "_" + str(index in list) Returns ------- @@ -241,10 +240,6 @@ def predict( predicted_correction_matrix: Matrix of predicted corrections that were applied - uncertainty_estimate: - Standard deviation of prediction from regressor - (only supported for `bayes` at present) - corrected_structure: Atoms object with corrections applied From 16d3cba5c3539844498b2d85b229621693477d97 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 22 Mar 2021 16:51:36 -0400 Subject: [PATCH 041/239] keep max size,spec data, rm qml imports --- src/autocat/learning/featurizers.py | 2 +- src/autocat/learning/predictors.py | 23 ++++++++++++++++++++++- tests/learning/test_featurizers.py | 4 +--- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 395ab2e1..ae1b4228 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -16,7 +16,7 @@ from ase.io import read from pymatgen.io.ase import AseAtomsAdaptor -from autocat.io.qml import ase_atoms_to_qml_compound +# from autocat.io.qml import ase_atoms_to_qml_compound def get_X( diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 920937a7..52b191d0 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -210,6 +210,27 @@ def fit( ) super(AutoCatStructureCorrector, self).fit(X, collected_matrices) + + if self.maximum_structure_size is None: + self.maximum_structure_size = max([len(s) for s in perturbed_structures]) + + if self.maximum_adsorbate_size is None: + self.maximum_adsorbate_size = max( + [ + len(adsorbate_indices_dictionary[a]) + for a in adsorbate_indices_dictionary + ] + ) + + if self.species_list is None: + species_list = [] + for s in perturbed_structures: + found_species = np.unique(s.get_chemical_symbols()).tolist() + new_species = [ + spec for spec in found_species if spec not in species_list + ] + species_list.extend(new_species) + self.species_list = species_list self.is_fit = True def predict( @@ -268,7 +289,7 @@ def predict( corrected_structures = [] for idx, struct in enumerate(initial_structure_guesses): cs = struct.copy() - corr = predicted_correction_matrix[idx, :].reshape(-1, 3) + corr = predicted_correction_matrix[idx, : 3 * len(cs)].reshape(len(cs), 3) cs.positions += corr corrected_structures.append(cs) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 11bc7e48..f90494ae 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -13,9 +13,7 @@ from dscribe.descriptors import ACSF from dscribe.descriptors import SOAP -import qml - -from autocat.io.qml import ase_atoms_to_qml_compound +# from autocat.io.qml import ase_atoms_to_qml_compound from autocat.adsorption import generate_rxn_structures from autocat.surface import generate_surface_structures from autocat.learning.featurizers import full_structure_featurization From 4efe3dc76e6e87540d4b535b5eed785319c9c8f7 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 24 Mar 2021 15:17:51 -0400 Subject: [PATCH 042/239] reduce mat labels to only be for pert atoms --- src/autocat/perturbations.py | 24 +++++++++++++++------ tests/test_perturbations.py | 42 ++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 6229bc7a..a9c86b37 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -18,7 +18,7 @@ def generate_perturbed_dataset( minimum_perturbation_distance: float = 0.1, maximum_perturbation_distance: float = 1.0, directions_dictionary: Dict[Union[str, Atoms], List[bool]] = None, - maximum_structure_size: int = None, + maximum_adsorbate_size: int = None, num_of_perturbations: int = 10, write_to_disk: bool = False, write_location: str = ".", @@ -61,8 +61,8 @@ def generate_perturbed_dataset( f"{base_structure.get_chemical_formula()}_{index_in_`base_structures`}" Default: free to perturb in all cartesian directions - maximum_structure_size: - Integer giving the largest number of atoms in a structure + maximum_adsorbate_size: + Integer giving the largest number of atoms in an adsorbate that should be able to be considered. Used to obtain shape of collected matrices Default: number of atoms in largest base structure @@ -171,12 +171,17 @@ def generate_perturbed_dataset( collected_structures.append(perturbed_dict[name][str(i)]["structure"]) collected_structure_paths.append(traj_file_path) - if maximum_structure_size is None: + if maximum_adsorbate_size is None: # find flattened length of largest structure - largest_size = max([len(i) for i in collected_matrices]) + largest_size = 3 * max( + [ + len(atom_indices_to_perturb_dictionary[i]) + for i in atom_indices_to_perturb_dictionary + ] + ) else: # factor of 3 from flattening (ie. x,y,z) - largest_size = 3 * maximum_structure_size + largest_size = 3 * maximum_adsorbate_size # ensures correct sized padding collected_matrices_array = np.zeros((len(collected_matrices), largest_size)) # substitute in collected matrices for each row @@ -265,6 +270,8 @@ def perturb_structure( pert_matrix = np.zeros(ase_obj.positions.shape) + atom_indices_to_perturb.sort() + for idx in atom_indices_to_perturb: # randomize +/- direction of each perturbation signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=11, size=(1, 3)) @@ -281,4 +288,7 @@ def perturb_structure( ase_obj.positions += pert_matrix - return {"structure": ase_obj, "perturbation_matrix": pert_matrix} + return { + "structure": ase_obj, + "perturbation_matrix": pert_matrix[atom_indices_to_perturb], + } diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 41a28b1f..72d600a2 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -46,7 +46,11 @@ def test_perturb_structure_matrix(): ] base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] p_struct = perturb_structure(base_struct, atom_indices_to_perturb=[-1, -2]) + # print(p_struct["structure"].positions[-2]) + # print(base_struct.positions[-2]) + # print(p_struct["perturbation_matrix"][-2]) o_pert = base_struct.positions[-2] + p_struct["perturbation_matrix"][-2] + # print(o_pert) assert np.allclose(p_struct["structure"].positions[-2], o_pert) h_pert = base_struct.positions[-1] + p_struct["perturbation_matrix"][-1] assert np.allclose(p_struct["structure"].positions[-1], h_pert) @@ -91,15 +95,11 @@ def test_generate_perturbed_dataset_multiple_base_structures(): # Check all base structures perturbed assert "HCu36N_1" in p_set assert "HOPt36_0" in p_set - # Check correct atom indices perturbed for each base_structure - assert (p_set["HCu36N_1"]["2"]["perturbation_matrix"][-1] == np.zeros(3)).all() - assert (p_set["HCu36N_1"]["3"]["perturbation_matrix"][-2] != np.zeros(3)).all() - assert (p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1] != np.zeros(3)).any() # Check correct direction constraints applied to each base_structure assert np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][0], 0.0) assert np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][1], 0.0) assert not np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][-1], 0.0) - assert not np.isclose(p_set["HCu36N_1"]["1"]["perturbation_matrix"][-2][0], 0.0) + assert not np.isclose(p_set["HCu36N_1"]["1"]["perturbation_matrix"][-1][0], 0.0) def test_generate_perturbed_dataset_write_location(): @@ -144,14 +144,12 @@ def test_generate_perturbed_dataset_collected_matrices(): }, num_of_perturbations=5, ) - manual_collect = [] - for struct in p_set["HOPt36_0"]: - manual_collect.append( - p_set["HOPt36_0"][struct]["perturbation_matrix"].flatten() - ) - manual_collect = np.array(manual_collect) + manual_collect = np.zeros((5, 6)) + for idx, struct in enumerate(p_set["HOPt36_0"]): + flat = p_set["HOPt36_0"][struct]["perturbation_matrix"].flatten() + manual_collect[idx, : len(flat)] = flat assert np.allclose(p_set["collected_matrices"], manual_collect) - assert p_set["collected_matrices"].shape == (5, 114) + assert p_set["collected_matrices"].shape == (5, 6) # Check when given specific maximum_structure_size p_set = generate_perturbed_dataset( [base_struct], @@ -159,9 +157,9 @@ def test_generate_perturbed_dataset_collected_matrices(): base_struct.get_chemical_formula() + "_0": [-1, -2] }, num_of_perturbations=5, - maximum_structure_size=45, + maximum_adsorbate_size=15, ) - assert p_set["collected_matrices"].shape == (5, 135) + assert p_set["collected_matrices"].shape == (5, 45) def test_generate_perturbed_dataset_collected_matrices_multiple(): @@ -177,18 +175,20 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): p_set = generate_perturbed_dataset( [base_struct1, base_struct2], atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula() + "_0": [-1], + base_struct1.get_chemical_formula() + "_0": [-2, -1], base_struct2.get_chemical_formula() + "_1": [-2], }, num_of_perturbations=5, ) - manual_collect = [] + manual_collect = np.zeros((10, 6)) + counter = 0 for base in ["HOPt36_0", "HCu36O_1"]: for struct in p_set[base]: - manual_collect.append(p_set[base][struct]["perturbation_matrix"].flatten()) - manual_collect = np.array(manual_collect) + flat = p_set[base][struct]["perturbation_matrix"].flatten() + manual_collect[counter, : len(flat)] = flat + counter += 1 assert np.allclose(p_set["collected_matrices"], manual_collect) - assert p_set["collected_matrices"].shape == (10, 114) + assert p_set["collected_matrices"].shape == (10, 6) # check that matrices properly collected for 2 base structures of != size sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ @@ -211,11 +211,11 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): for base in ["HOPt36_0", "HCu36_1"]: for struct in p_set[base]: manual_collect.append(p_set[base][struct]["perturbation_matrix"].flatten()) - manual_collect_array = np.zeros((10, 114)) + manual_collect_array = np.zeros((10, 3)) for idx, m in enumerate(manual_collect): manual_collect_array[idx, : len(m)] = m assert np.allclose(p_set["collected_matrices"], manual_collect_array) - assert p_set["collected_matrices"].shape == (10, 114) + assert p_set["collected_matrices"].shape == (10, 3) def test_generate_perturbed_dataset_collected_structure_paths(): From 312be086e85912b85b73a6129332759594285c23 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 24 Mar 2021 15:43:11 -0400 Subject: [PATCH 043/239] predictor updates - updates predict to use new label fmt when correcting the struct - new test to ensure corrections are applied correctly to structs --- src/autocat/learning/predictors.py | 10 ++++++++-- tests/learning/test_predictors.py | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 52b191d0..f36f291a 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -289,8 +289,14 @@ def predict( corrected_structures = [] for idx, struct in enumerate(initial_structure_guesses): cs = struct.copy() - corr = predicted_correction_matrix[idx, : 3 * len(cs)].reshape(len(cs), 3) - cs.positions += corr + name = cs.get_chemical_formula() + "_" + str(idx) + list_of_adsorbate_indices = adsorbate_indices_dictionary[name] + list_of_adsorbate_indices.sort() + num_of_adsorbates = len(list_of_adsorbate_indices) + corr = predicted_correction_matrix[idx, : 3 * num_of_adsorbates].reshape( + num_of_adsorbates, 3 + ) + cs.positions[list_of_adsorbate_indices] += corr corrected_structures.append(cs) return predicted_correction_matrix, corrected_structures diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 5ee26570..6d2def62 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -88,3 +88,9 @@ def test_predict_initial_configuration_formats(): assert len(corrected_structures) == 5 assert isinstance(predicted_correction_matrix, np.ndarray) assert predicted_correction_matrix.shape[0] == 5 + # check that predicted correction matrix is applied correctly + manual = p_structures[15].copy() + manual_corr_mat = predicted_correction_matrix[0].reshape(2, 3) + manual.positions[-1] += manual_corr_mat[-1] + manual.positions[-2] += manual_corr_mat[-2] + assert np.allclose(manual.positions, corrected_structures[0].positions) From 517ec7fb4e3b56293a585f8a2f7793b677c50fdc Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 30 Mar 2021 18:07:12 -0400 Subject: [PATCH 044/239] adds method for returning num of features --- src/autocat/learning/predictors.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index f36f291a..eae122b7 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -10,6 +10,7 @@ from sklearn.kernel_ridge import KernelRidge from autocat.learning.featurizers import get_X +from autocat.learning.featurizers import _get_number_of_features class AutoCatStructureCorrector(KernelRidge): @@ -163,6 +164,23 @@ def species_list(self, species_list): if self.is_fit: self.is_fit = False + def get_total_number_of_features(self): + str_kwargs = self.structure_featurization_kwargs + ads_kwargs = self.adsorbate_featurization_kwargs + if str_kwargs is None: + str_kwargs = {} + if self.structure_featurizer in ["sine_matrix", "coulomb_matrix"]: + str_kwargs.update({"n_atoms_max": self.maximum_structure_size}) + if ads_kwargs is None: + ads_kwargs = {} + if self.adsorbate_featurizer == "soap": + ads_kwargs.update({"species": self.species_list}) + num_struct_feat = _get_number_of_features( + self.structure_featurizer, **str_kwargs + ) + num_ads_feat = _get_number_of_features(self.adsorbate_featurizer, **ads_kwargs) + return num_struct_feat, num_ads_feat + def fit( self, perturbed_structures: List[Union[Atoms, str]], From 84398a83f7ddd9646d192bc133a2620931dc2ada Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 1 Apr 2021 15:44:39 -0400 Subject: [PATCH 045/239] multiple improvements - allows either struct feat or ads feat (no longer requires both) - update SOAP defaults - extend get number of features method for if only 1 type used - KRR -> GPR --- src/autocat/learning/featurizers.py | 72 ++++++++++++++++++----------- src/autocat/learning/predictors.py | 39 ++++++++++------ tests/learning/test_predictors.py | 8 +++- 3 files changed, 78 insertions(+), 41 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index ae1b4228..a3cb8f00 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -111,19 +111,32 @@ def get_X( new_species = [spec for spec in found_species if spec not in species_list] species_list.extend(new_species) - num_of_adsorbate_features = _get_number_of_features( - featurizer=adsorbate_featurizer, - species=species_list, - **adsorbate_featurization_kwargs, - ) - - X = np.zeros( - ( - len(structures), - maximum_structure_size ** 2 - + maximum_adsorbate_size * num_of_adsorbate_features, + if adsorbate_featurizer is not None: + num_of_adsorbate_features = _get_number_of_features( + featurizer=adsorbate_featurizer, + species=species_list, + **adsorbate_featurization_kwargs, ) - ) + + if (structure_featurizer is not None) and (adsorbate_featurizer is not None): + X = np.zeros( + ( + len(structures), + maximum_structure_size ** 2 + + maximum_adsorbate_size * num_of_adsorbate_features, + ) + ) + elif (structure_featurizer is None) and (adsorbate_featurizer is not None): + X = np.zeros( + (len(structures), maximum_adsorbate_size * num_of_adsorbate_features) + ) + + elif (structure_featurizer is not None) and (adsorbate_featurizer is None): + X = np.zeros((len(structures), maximum_structure_size ** 2)) + else: + msg = "Need to specify either a structure or adsorbate featurizer" + raise ValueError(msg) + for idx, structure in enumerate(structures): if isinstance(structure, Atoms): name = structure.get_chemical_formula() + "_" + str(idx) @@ -213,20 +226,27 @@ def catalyst_featurization( if adsorbate_featurization_kwargs is None: adsorbate_featurization_kwargs = {} - struct_feat = full_structure_featurization( - structure, - featurizer=structure_featurizer, - maximum_structure_size=maximum_structure_size, - **structure_featurization_kwargs, - ) - ads_feat = adsorbate_featurization( - structure, - featurizer=adsorbate_featurizer, - adsorbate_indices=adsorbate_indices, - species_list=species_list, - maximum_adsorbate_size=maximum_adsorbate_size, - **adsorbate_featurization_kwargs, - ) + if structure_featurizer is not None: + struct_feat = full_structure_featurization( + structure, + featurizer=structure_featurizer, + maximum_structure_size=maximum_structure_size, + **structure_featurization_kwargs, + ) + else: + struct_feat = np.array([]) + + if adsorbate_featurizer is not None: + ads_feat = adsorbate_featurization( + structure, + featurizer=adsorbate_featurizer, + adsorbate_indices=adsorbate_indices, + species_list=species_list, + maximum_adsorbate_size=maximum_adsorbate_size, + **adsorbate_featurization_kwargs, + ) + else: + ads_feat = np.array([]) cat_feat = np.concatenate((struct_feat, ads_feat)) return cat_feat diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index eae122b7..e0136131 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -7,13 +7,13 @@ from ase import Atoms from sklearn.model_selection import KFold -from sklearn.kernel_ridge import KernelRidge +from sklearn.gaussian_process import GaussianProcessRegressor from autocat.learning.featurizers import get_X from autocat.learning.featurizers import _get_number_of_features -class AutoCatStructureCorrector(KernelRidge): +class AutoCatStructureCorrector(GaussianProcessRegressor): def __init__( self, structure_featurizer: str = None, @@ -59,16 +59,16 @@ def __init__( self.is_fit = False - self._structure_featurizer = "sine_matrix" + self._structure_featurizer = None self.structure_featurizer = structure_featurizer - self._adsorbate_featurizer = "soap" + self._adsorbate_featurizer = None self.adsorbate_featurizer = adsorbate_featurizer self._structure_featurization_kwargs = None self.structure_featurization_kwargs = structure_featurization_kwargs - self._adsorbate_featurization_kwargs = {"rcut": 6.0, "nmax": 8, "lmax": 6} + self._adsorbate_featurization_kwargs = {"rcut": 3.0, "nmax": 4, "lmax": 4} self.adsorbate_featurization_kwargs = adsorbate_featurization_kwargs self._maximum_structure_size = None @@ -165,20 +165,33 @@ def species_list(self, species_list): self.is_fit = False def get_total_number_of_features(self): + # get specified kwargs for featurizers str_kwargs = self.structure_featurization_kwargs ads_kwargs = self.adsorbate_featurization_kwargs if str_kwargs is None: str_kwargs = {} - if self.structure_featurizer in ["sine_matrix", "coulomb_matrix"]: - str_kwargs.update({"n_atoms_max": self.maximum_structure_size}) if ads_kwargs is None: ads_kwargs = {} - if self.adsorbate_featurizer == "soap": - ads_kwargs.update({"species": self.species_list}) - num_struct_feat = _get_number_of_features( - self.structure_featurizer, **str_kwargs - ) - num_ads_feat = _get_number_of_features(self.adsorbate_featurizer, **ads_kwargs) + if self.structure_featurizer is not None: + # check if one of dscribe structure featurizers + if self.structure_featurizer in ["sine_matrix", "coulomb_matrix"]: + str_kwargs.update({"n_atoms_max": self.maximum_structure_size}) + num_struct_feat = _get_number_of_features( + self.structure_featurizer, **str_kwargs + ) + else: + # no structure featurizer present + num_struct_feat = 0 + if self.adsorbate_featurizer is not None: + # check if one of dscribe structure featurizers + if self.adsorbate_featurizer == "soap": + ads_kwargs.update({"species": self.species_list}) + num_ads_feat = _get_number_of_features( + self.adsorbate_featurizer, **ads_kwargs + ) + else: + # no adsorbate featurizer present + num_ads_feat = 0 return num_struct_feat, num_ads_feat def fit( diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 6d2def62..9b30b381 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -32,7 +32,9 @@ def test_fit_model_on_perturbed_systems(): p_structures = p_set["collected_structures"] collected_matrices = p_set["collected_matrices"] acsc = AutoCatStructureCorrector( - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6} + structure_featurizer="sine_matrix", + adsorbate_featurizer="soap", + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( p_structures, @@ -67,7 +69,9 @@ def test_predict_initial_configuration_formats(): p_structures = p_set["collected_structures"] collected_matrices = p_set["collected_matrices"] acsc = AutoCatStructureCorrector( - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6} + structure_featurizer="sine_matrix", + adsorbate_featurizer="soap", + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( p_structures[:15], From caa844f170ec2a7baf9f84c0518915225e6a65cd Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 1 Apr 2021 17:25:55 -0400 Subject: [PATCH 046/239] adds elemental prop feat from matminer --- src/autocat/learning/featurizers.py | 51 +++++++++++++++++++++++++---- tests/learning/test_featurizers.py | 23 +++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index a3cb8f00..86a12b4c 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -4,6 +4,8 @@ from dscribe.descriptors import ACSF from dscribe.descriptors import SOAP +from matminer.featurizers.composition import ElementProperty + import tempfile import os import numpy as np @@ -345,6 +347,7 @@ def full_structure_featurization( maximum_structure_size: int = None, featurizer: str = "sine_matrix", permutation: str = "none", + elementalproperty_preset: str = "magpie", n_jobs: int = 1, **kwargs, ): @@ -364,10 +367,10 @@ def full_structure_featurization( featurizer: String indicating featurizer to be used. - Options: - sine_matrix (default) - coulomb_matrix (N.B.: does not support periodicity) + - element property permutation: String specifying how ordering is handled. This is fed into @@ -375,6 +378,16 @@ def full_structure_featurization( Default: "none", maintains same ordering as input Atoms structure (N.B. this differs from the `dscribe` default) + elementalproperty_preset: + String giving the preset to be pulled from. + Options: + - magpie (default) + - pymatgen + - deml + - matscholar_el + - megnet_el + See `matminer` documentation for more details + n_jobs: Int specifiying number of parallel jobs to run which is fed into `dscribe` featurizers (ie. sine_matrix, coulomb_matrix) @@ -400,28 +413,52 @@ def full_structure_featurization( ) rep = cm.create(structure, n_jobs=n_jobs).reshape(-1,) + elif featurizer == "elemental_property": + ep = ElementProperty.from_preset(elementalproperty_preset) + conv = AseAtomsAdaptor() + pymat = conv.get_structure(structure) + rep = np.array(ep.featurize(pymat.composition)) + else: raise NotImplementedError("selected featurizer not implemented") return rep -def _get_number_of_features(featurizer, **kwargs): +def _get_number_of_features( + featurizer, elementalproperty_preset: str = "magpie", **kwargs +): """ + Helper function to get number of features. + Wrapper of `get_number_of_features` method for `dscribe` featurizers + + If `matminer`'s elemental property, calculated based off of + number of features X number of stats """ - supported_featurizers = { + supported_dscribe_featurizers = { "sine_matrix": SineMatrix, "coulomb_matrix": CoulombMatrix, "soap": SOAP, "acsf": ACSF, } - if featurizer not in supported_featurizers: + supported_matminer_featurizers = { + "elemental_property": ElementProperty.from_preset(elementalproperty_preset) + } + + if featurizer in supported_dscribe_featurizers: + feat = supported_dscribe_featurizers[featurizer](**kwargs) + return feat.get_number_of_features() + + elif featurizer in supported_matminer_featurizers: + # included for if other matminer featurizers implemented + if featurizer == "elemental_property": + ep = supported_matminer_featurizers[featurizer] + return len(ep.features) * len(ep.stats) + + else: raise NotImplementedError( "selected featurizer does not currently support this feature" ) - - feat = supported_featurizers[featurizer](**kwargs) - return feat.get_number_of_features() diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index f90494ae..d31f9c49 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -13,6 +13,8 @@ from dscribe.descriptors import ACSF from dscribe.descriptors import SOAP +from matminer.featurizers.composition import ElementProperty + # from autocat.io.qml import ase_atoms_to_qml_compound from autocat.adsorption import generate_rxn_structures from autocat.surface import generate_surface_structures @@ -22,6 +24,8 @@ from autocat.learning.featurizers import _get_number_of_features from autocat.learning.featurizers import get_X +from pymatgen.io.ase import AseAtomsAdaptor + def test_full_structure_featurization_sine(): # Tests Sine Matrix Generation @@ -45,6 +49,25 @@ def test_full_structure_featurization_coulomb(): assert coulomb_matrix.shape == (2025,) +def test_full_structure_featurization_elemental_property(): + # Tests the Elemental Property featurization + surf = generate_surface_structures(["Cu"])["Cu"]["fcc111"]["structure"] + elem_prop = full_structure_featurization(surf, featurizer="elemental_property") + ep = ElementProperty.from_preset("magpie") + conv = AseAtomsAdaptor() + pymat = conv.get_structure(surf) + manual_elem_prop = ep.featurize(pymat.composition) + assert np.allclose(elem_prop, manual_elem_prop) + assert elem_prop.shape == (132,) + elem_prop = full_structure_featurization( + surf, featurizer="elemental_property", elementalproperty_preset="deml" + ) + ep = ElementProperty.from_preset("deml") + manual_elem_prop = ep.featurize(pymat.composition) + assert np.allclose(elem_prop, manual_elem_prop) + assert len(elem_prop) == _get_number_of_features("elemental_property", "deml") + + def test_adsorbate_featurization_acsf(): # Tests Atom Centered Symmetry Function generation surf = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] From c6fdabe7eef6f4d474c0e9933d8fe124b2575361 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 1 Apr 2021 19:09:05 -0400 Subject: [PATCH 047/239] model class can be given, new kwarg set behavior --- src/autocat/learning/predictors.py | 59 +++++++++++++++++++++++------- tests/learning/test_predictors.py | 9 +++++ 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index e0136131..d2818886 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -13,9 +13,10 @@ from autocat.learning.featurizers import _get_number_of_features -class AutoCatStructureCorrector(GaussianProcessRegressor): +class AutoCatStructureCorrector: def __init__( self, + model_class=None, structure_featurizer: str = None, adsorbate_featurizer: str = None, maximum_structure_size: int = None, @@ -23,7 +24,7 @@ def __init__( species_list: List[str] = None, structure_featurization_kwargs: Dict = None, adsorbate_featurization_kwargs: Dict = None, - **model_kwargs, + model_kwargs: Dict = None, ): """ Constructor. @@ -31,6 +32,12 @@ def __init__( Parameters ---------- + model_class: + Class of regression model to be used for training and prediction. + If this is changed after initialization, all previously set + model_kwargs will be removed. + N.B. must have fit and predict methods + structure_featurizer: String giving featurizer to be used for full structure which will be fed into `autocat.learning.featurizers.full_structure_featurization` @@ -55,10 +62,16 @@ def __init__( Default: Parses over all `structures` and collects all encountered species """ - super(AutoCatStructureCorrector, self).__init__(**model_kwargs) - self.is_fit = False + self._model_class = GaussianProcessRegressor + self.model_class = model_class + + self._model_kwargs = None + self.model_kwargs = model_kwargs + + self._regressor = self.model_class(self.model_kwargs) + self._structure_featurizer = None self.structure_featurizer = structure_featurizer @@ -80,6 +93,32 @@ def __init__( self._species_list = None self.species_list = species_list + @property + def model_class(self): + return self._model_class + + @model_class.setter + def model_class(self, model_class): + if model_class is not None: + self._model_class = model_class + # removes any model kwargs from previous model + self._model_kwargs = None + if self.is_fit: + self.is_fit = False + + @property + def model_kwargs(self): + return self._model_kwargs + + @model_kwargs.setter + def model_kwargs(self, model_kwargs): + if model_kwargs is not None: + assert isinstance(model_kwargs, dict) + if self._model_kwargs is not None: + self._model_kwargs = model_kwargs + if self.is_fit: + self.is_fit = False + @property def structure_featurizer(self): return self._structure_featurizer @@ -111,10 +150,6 @@ def structure_featurization_kwargs(self, structure_featurization_kwargs): if structure_featurization_kwargs is not None: assert isinstance(structure_featurization_kwargs, dict) if self._structure_featurization_kwargs is not None: - self._structure_featurization_kwargs.update( - structure_featurization_kwargs - ) - else: self._structure_featurization_kwargs = structure_featurization_kwargs if self.is_fit: self.is_fit = False @@ -127,7 +162,7 @@ def adsorbate_featurization_kwargs(self): def adsorbate_featurization_kwargs(self, adsorbate_featurization_kwargs): if adsorbate_featurization_kwargs is not None: assert isinstance(adsorbate_featurization_kwargs, dict) - self._adsorbate_featurization_kwargs.update(adsorbate_featurization_kwargs) + self._adsorbate_featurization_kwargs = adsorbate_featurization_kwargs if self.is_fit: self.is_fit = False @@ -240,7 +275,7 @@ def fit( adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - super(AutoCatStructureCorrector, self).fit(X, collected_matrices) + self._regressor.fit(X, collected_matrices) if self.maximum_structure_size is None: self.maximum_structure_size = max([len(s) for s in perturbed_structures]) @@ -309,9 +344,7 @@ def predict( adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - predicted_correction_matrix = super(AutoCatStructureCorrector, self).predict( - featurized_input - ) + predicted_correction_matrix = self._regressor.predict(featurized_input) corrected_structures = [ init_struct.copy() for init_struct in initial_structure_guesses diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 9b30b381..ce492a45 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -5,6 +5,7 @@ import numpy as np from sklearn.kernel_ridge import KernelRidge +from sklearn.gaussian_process import GaussianProcessRegressor from ase import Atoms @@ -98,3 +99,11 @@ def test_predict_initial_configuration_formats(): manual.positions[-1] += manual_corr_mat[-1] manual.positions[-2] += manual_corr_mat[-2] assert np.allclose(manual.positions, corrected_structures[0].positions) + + +def test_model_class(): + # Tests providing regression model class + acsc = AutoCatStructureCorrector(KernelRidge, model_kwargs={"gamma": 0.5}) + assert isinstance(acsc._regressor, KernelRidge) + acsc.model_class = GaussianProcessRegressor + assert acsc.model_kwargs is None From 22348194509e5c935a4d683f5f9359d44083b523 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 1 Apr 2021 20:00:05 -0400 Subject: [PATCH 048/239] makes ads list optional, generalizes X shape --- src/autocat/learning/featurizers.py | 48 ++++++++++++++++++++++++----- src/autocat/learning/predictors.py | 4 +-- tests/learning/test_featurizers.py | 27 ++++++++++++++++ 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 86a12b4c..42bd25ba 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -21,11 +21,16 @@ # from autocat.io.qml import ase_atoms_to_qml_compound +class AutoCatFeaturizationError(Exception): + pass + + def get_X( structures: List[Union[Atoms, str]], - adsorbate_indices_dictionary: Dict[str, int], + adsorbate_indices_dictionary: Dict[str, int] = None, maximum_structure_size: int = None, structure_featurizer: str = "sine_matrix", + elementalproperty_preset: str = "magpie", maximum_adsorbate_size: int = None, adsorbate_featurizer: str = "soap", species_list: List[str] = None, @@ -44,7 +49,8 @@ def get_X( List of ase.Atoms objects or structure filename strings to be used to construct X matrix adsorbate_indices_dictionary: - Dictionary mapping structures to desired adsorbate_indices + Dictionary mapping structures to desired adsorbate_indices. + Only necessary if adsorbate featurization is specified (N.B. if structure is given as an ase.Atoms object, the key for this dictionary should be ase.Atoms.get_chemical_formula() + "_" + str(index in list)) @@ -114,17 +120,33 @@ def get_X( species_list.extend(new_species) if adsorbate_featurizer is not None: + # check that adsorbate indices specified + if adsorbate_indices_dictionary is None: + msg = "For adsorbate featurization, adsorbate indices must be specified" + raise AutoCatFeaturizationError(msg) + # find number of adsorbate features num_of_adsorbate_features = _get_number_of_features( featurizer=adsorbate_featurizer, species=species_list, **adsorbate_featurization_kwargs, ) + if structure_featurizer is not None: + if structure_featurizer in ["sine_matrix", "coulomb_matrix"]: + num_structure_features = maximum_structure_size ** 2 + else: + # elemental property featurizer + num_structure_features = _get_number_of_features( + structure_featurizer, + elementalproperty_preset=elementalproperty_preset, + **structure_featurization_kwargs, + ) + if (structure_featurizer is not None) and (adsorbate_featurizer is not None): X = np.zeros( ( len(structures), - maximum_structure_size ** 2 + num_structure_features + maximum_adsorbate_size * num_of_adsorbate_features, ) ) @@ -134,10 +156,10 @@ def get_X( ) elif (structure_featurizer is not None) and (adsorbate_featurizer is None): - X = np.zeros((len(structures), maximum_structure_size ** 2)) + X = np.zeros((len(structures), num_structure_features)) else: msg = "Need to specify either a structure or adsorbate featurizer" - raise ValueError(msg) + raise AutoCatFeaturizationError(msg) for idx, structure in enumerate(structures): if isinstance(structure, Atoms): @@ -147,7 +169,11 @@ def get_X( name = structure ase_struct = read(structure) else: - raise TypeError(f"Each structure needs to be either a str or ase.Atoms") + msg = f"Each structure needs to be either a str or ase.Atoms. Got {type(structure)}" + raise AutoCatFeaturizationError(msg) + # if no adsorbate featurizer, makes placeholder dict for each loop + if adsorbate_featurizer is None: + adsorbate_indices_dictionary = {name: None} cat_feat = catalyst_featurization( ase_struct, adsorbate_indices=adsorbate_indices_dictionary[name], @@ -174,8 +200,9 @@ def get_X( def catalyst_featurization( structure: Atoms, - adsorbate_indices: List[int], + adsorbate_indices: List[int] = None, structure_featurizer: str = "sine_matrix", + elementalproperty_preset: str = "magpie", adsorbate_featurizer: str = "soap", maximum_structure_size: int = None, maximum_adsorbate_size: int = None, @@ -199,7 +226,8 @@ def catalyst_featurization( adsorbate_indices: List of atomic indices specifying the adsorbate to be - featurized + featurized. + Only necessary if adsorbate featurization is specified structure_featurizer: String giving featurizer to be used for full structure which will be @@ -233,12 +261,16 @@ def catalyst_featurization( structure, featurizer=structure_featurizer, maximum_structure_size=maximum_structure_size, + elementalproperty_preset=elementalproperty_preset, **structure_featurization_kwargs, ) else: struct_feat = np.array([]) if adsorbate_featurizer is not None: + if adsorbate_indices is None: + msg = "For adsorbate featurization, adsorbate indices must be specified" + raise AutoCatFeaturizationError(msg) ads_feat = adsorbate_featurization( structure, featurizer=adsorbate_featurizer, diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index d2818886..d480808a 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -232,8 +232,8 @@ def get_total_number_of_features(self): def fit( self, perturbed_structures: List[Union[Atoms, str]], - adsorbate_indices_dictionary: Dict[str, int], collected_matrices: np.ndarray, + adsorbate_indices_dictionary: Dict[str, int] = None, ): """ Given a list of perturbed structures @@ -302,7 +302,7 @@ def fit( def predict( self, initial_structure_guesses: List[Atoms], - adsorbate_indices_dictionary: Dict[str, int], + adsorbate_indices_dictionary: Dict[str, int] = None, ): """ From a trained model, will predict corrected structure diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index d31f9c49..7fd94614 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -182,6 +182,33 @@ def test_get_X_concatenation(): featurizer="soap", rcut=5.0, nmax=8, lmax=6, species=species_list ) assert X.shape == (len(structs), 50 ** 2 + 5 * num_of_adsorbate_features) + # Check for full structure featurization only + X = get_X( + structs, + structure_featurizer="elemental_property", + adsorbate_featurizer=None, + maximum_structure_size=50, + maximum_adsorbate_size=5, + ) + assert X.shape == (len(structs), 132) + # Check for adsorbate featurization only + X = get_X( + structs, + structure_featurizer=None, + adsorbate_indices_dictionary={ + structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], + structs[1].get_chemical_formula() + "_1": [-2, -1], + structs[2].get_chemical_formula() + "_2": [-1], + }, + maximum_structure_size=50, + maximum_adsorbate_size=5, + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + species_list = ["Pt", "Ru", "N", "C", "O", "H"] + num_of_adsorbate_features = _get_number_of_features( + featurizer="soap", rcut=5.0, nmax=8, lmax=6, species=species_list + ) + assert X.shape == (len(structs), 5 * num_of_adsorbate_features) def test_get_X_write_location(): From 3550cd4fc352fd3086c2d294395976cea1aba38b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 4 Apr 2021 16:16:18 -0400 Subject: [PATCH 049/239] adds ChemicalSRO feat, sorts ads idx --- src/autocat/learning/featurizers.py | 75 ++++++++++++++++++++++++----- tests/learning/test_featurizers.py | 58 +++++++++++++++++++++- 2 files changed, 119 insertions(+), 14 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 42bd25ba..fe0ff51a 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -5,6 +5,7 @@ from dscribe.descriptors import SOAP from matminer.featurizers.composition import ElementProperty +from matminer.featurizers.site import ChemicalSRO import tempfile import os @@ -17,6 +18,7 @@ from ase import Atoms from ase.io import read from pymatgen.io.ase import AseAtomsAdaptor +from pymatgen.analysis.local_env import VoronoiNN # from autocat.io.qml import ase_atoms_to_qml_compound @@ -125,11 +127,19 @@ def get_X( msg = "For adsorbate featurization, adsorbate indices must be specified" raise AutoCatFeaturizationError(msg) # find number of adsorbate features - num_of_adsorbate_features = _get_number_of_features( - featurizer=adsorbate_featurizer, - species=species_list, - **adsorbate_featurization_kwargs, - ) + if adsorbate_featurizer in ["soap", "acsf"]: + num_of_adsorbate_features = _get_number_of_features( + featurizer=adsorbate_featurizer, + species=species_list, + **adsorbate_featurization_kwargs, + ) + else: + # chemical_sro + num_of_adsorbate_features = _get_number_of_features( + featurizer=adsorbate_featurizer, + species=species_list, + **adsorbate_featurization_kwargs, + ) if structure_featurizer is not None: if structure_featurizer in ["sine_matrix", "coulomb_matrix"]: @@ -310,7 +320,8 @@ def adsorbate_featurization( species_list: List of chemical species that should be covered by representation - which is fed into `dscribe.descriptors.{ACSF,SOAP}` + which is fed into `dscribe.descriptors.{ACSF,SOAP}` or + `ChemicalSRO.includes` (ie. any species expected to be encountered) Default: species present in `structure` @@ -318,12 +329,14 @@ def adsorbate_featurization( String indicating featurizer to be used. Options: - acsf (default): atom centered symmetry functions - soap: smooth overlap of atomic positions + acsf: atom centered symmetry functions + soap (default): smooth overlap of atomic positions + chemical_sro: chemical short range ordering rcut: Float giving cutoff radius to be used when generating - representation. + representation. For chemical_sro, this is the cutoff used + for determining the nearest neighbors Default: 6 angstroms maximum_adsorbate_size: @@ -340,6 +353,9 @@ def adsorbate_featurization( Np.ndarray of adsorbate representation """ + # Ensures that adsorbate indices specified are sorted + adsorbate_indices.sort() + # Checks if species list given if species_list is None: species_list = np.unique(structure.get_chemical_symbols()).tolist() @@ -363,6 +379,34 @@ def adsorbate_featurization( ) num_of_features = acsf.get_number_of_features() + elif featurizer == "chemical_sro": + # generate nn calculator + vnn = VoronoiNN(cutoff=rcut, allow_pathological=True) + csro = ChemicalSRO(vnn, includes=species_list) + # convert ase structure to pymatgen + conv = AseAtomsAdaptor() + pym_struct = conv.get_structure(structure) + # format list for csro fitting (to get species) + formatted_list = [[pym_struct, idx] for idx in adsorbate_indices] + csro.fit(formatted_list) + # concatenate representation for each adsorbate atom + representation = np.array([]) + for idx in adsorbate_indices: + raw_feat = csro.featurize(pym_struct, idx) + # csro only generates for species observed in fit + # as well as includes, so to be generalizable + # we use full species list and place values + # in the appropriate species location + labels = csro.feature_labels() + feat = np.zeros(len(species_list)) + for i, label in enumerate(labels): + # finds where corresponding species is in full species list + lbl_idx = np.where(np.array(species_list) == label.split("_")[1]) + feat[lbl_idx] = raw_feat[i] + representation = np.concatenate((representation, feat)) + # number of features is number of species specified + num_of_features = len(species_list) + else: raise NotImplementedError("selected featurizer not implemented") @@ -458,7 +502,10 @@ def full_structure_featurization( def _get_number_of_features( - featurizer, elementalproperty_preset: str = "magpie", **kwargs + featurizer, + elementalproperty_preset: str = "magpie", + species: List[str] = None, + **kwargs, ): """ Helper function to get number of features. @@ -477,18 +524,20 @@ def _get_number_of_features( } supported_matminer_featurizers = { - "elemental_property": ElementProperty.from_preset(elementalproperty_preset) + "elemental_property": ElementProperty.from_preset(elementalproperty_preset), + "chemical_sro": None, } if featurizer in supported_dscribe_featurizers: - feat = supported_dscribe_featurizers[featurizer](**kwargs) + feat = supported_dscribe_featurizers[featurizer](species=species, **kwargs) return feat.get_number_of_features() elif featurizer in supported_matminer_featurizers: - # included for if other matminer featurizers implemented if featurizer == "elemental_property": ep = supported_matminer_featurizers[featurizer] return len(ep.features) * len(ep.stats) + elif featurizer == "chemical_sro": + return len(species) else: raise NotImplementedError( diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 7fd94614..26d69b9a 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -14,6 +14,7 @@ from dscribe.descriptors import SOAP from matminer.featurizers.composition import ElementProperty +from matminer.featurizers.site import ChemicalSRO # from autocat.io.qml import ase_atoms_to_qml_compound from autocat.adsorption import generate_rxn_structures @@ -25,6 +26,7 @@ from autocat.learning.featurizers import get_X from pymatgen.io.ase import AseAtomsAdaptor +from pymatgen.analysis.local_env import VoronoiNN def test_full_structure_featurization_sine(): @@ -95,6 +97,32 @@ def test_adsorbate_featurization_soap(): assert soap_feat.shape == (soap.get_number_of_features(),) +def test_adsorbate_featurization_chemical_sro(): + # Tests Chemical Short Range Ordering Featurization + surf = generate_surface_structures(["Li"])["Li"]["bcc100"]["structure"] + ads_struct = generate_rxn_structures(surf, ads=["OH"])["OH"]["ontop"]["0.0_0.0"][ + "structure" + ] + csro_feat = adsorbate_featurization( + ads_struct, + [-2, -1], + featurizer="chemical_sro", + rcut=10.0, + species_list=["Li", "O", "H"], + ) + assert csro_feat.shape == (6,) + species = ["Li", "O", "H"] + vnn = VoronoiNN(cutoff=10.0, allow_pathological=True) + csro = ChemicalSRO(vnn, includes=species) + conv = AseAtomsAdaptor() + pym_struct = conv.get_structure(ads_struct) + csro.fit([[pym_struct, -2], [pym_struct, -1]]) + manual_feat = csro.featurize(pym_struct, -2) + print(csro.feature_labels()) + manual_feat = np.concatenate((manual_feat, csro.featurize(pym_struct, -1))) + assert np.allclose(csro_feat, manual_feat) + + def test_adsorbate_featurization_padding(): # Tests that padding is properly applied surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] @@ -120,6 +148,18 @@ def test_adsorbate_featurization_padding(): maximum_adsorbate_size=10, ) assert (soap_feat[-num_of_features * 8 :] == np.zeros(num_of_features * 8)).all() + csro_feat = adsorbate_featurization( + ads_struct, + [35, 36], + featurizer="chemical_sro", + rcut=10.0, + maximum_adsorbate_size=4, + species_list=["Fe", "H", "Li"], + ) + assert csro_feat.shape == (12,) + assert (csro_feat[6:] == np.zeros(6)).all() + assert csro_feat[5] == 0.0 + assert csro_feat[2] == 0.0 def test_catalyst_featurization_concatentation(): @@ -139,7 +179,7 @@ def test_catalyst_featurization_concatentation(): struct = sm.create(ads_struct).reshape(-1,) species = np.unique(ads_struct.get_chemical_symbols()).tolist() soap = SOAP(rcut=5.0, nmax=8, lmax=6, species=species) - ads = soap.create(ads_struct, [-1, -2]).reshape(-1,) + ads = soap.create(ads_struct, [-2, -1]).reshape(-1,) cat_ref = np.concatenate((struct, ads)) assert np.allclose(cat, cat_ref) num_of_adsorbate_features = soap.get_number_of_features() @@ -209,6 +249,22 @@ def test_get_X_concatenation(): featurizer="soap", rcut=5.0, nmax=8, lmax=6, species=species_list ) assert X.shape == (len(structs), 5 * num_of_adsorbate_features) + X = get_X( + structs, + structure_featurizer=None, + adsorbate_featurizer="chemical_sro", + adsorbate_indices_dictionary={ + structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], + structs[1].get_chemical_formula() + "_1": [-2, -1], + structs[2].get_chemical_formula() + "_2": [-1], + }, + maximum_adsorbate_size=5, + species_list=species_list, + ) + num_of_adsorbate_features = _get_number_of_features( + featurizer="chemical_sro", rcut=5.0, species=species_list + ) + assert X.shape == (len(structs), 5 * num_of_adsorbate_features) def test_get_X_write_location(): From d0f364f4d54e7652177b031eeef1c95dfb5826b2 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 5 Apr 2021 21:16:44 -0400 Subject: [PATCH 050/239] add op site fingerprint, make reg public, add unc --- src/autocat/learning/featurizers.py | 16 +++++++++++++ src/autocat/learning/predictors.py | 10 ++++---- tests/learning/test_featurizers.py | 36 ++++++++++++++++++++++++++++- tests/learning/test_predictors.py | 4 ++-- 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index fe0ff51a..1b6c6266 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -6,6 +6,7 @@ from matminer.featurizers.composition import ElementProperty from matminer.featurizers.site import ChemicalSRO +from matminer.featurizers.site import OPSiteFingerprint import tempfile import os @@ -332,6 +333,7 @@ def adsorbate_featurization( acsf: atom centered symmetry functions soap (default): smooth overlap of atomic positions chemical_sro: chemical short range ordering + op_sitefingerprint: order parameter site fingerprint rcut: Float giving cutoff radius to be used when generating @@ -407,6 +409,16 @@ def adsorbate_featurization( # number of features is number of species specified num_of_features = len(species_list) + elif featurizer == "op_sitefingerprint": + opsf = OPSiteFingerprint(**kwargs) + conv = AseAtomsAdaptor() + pym_struct = conv.get_structure(structure) + representation = np.array([]) + for idx in adsorbate_indices: + feat = opsf.featurize(pym_struct, idx) + representation = np.concatenate((representation, feat)) + num_of_features = len(opsf.feature_labels()) + else: raise NotImplementedError("selected featurizer not implemented") @@ -526,6 +538,7 @@ def _get_number_of_features( supported_matminer_featurizers = { "elemental_property": ElementProperty.from_preset(elementalproperty_preset), "chemical_sro": None, + "op_sitefingerprint": OPSiteFingerprint, } if featurizer in supported_dscribe_featurizers: @@ -538,6 +551,9 @@ def _get_number_of_features( return len(ep.features) * len(ep.stats) elif featurizer == "chemical_sro": return len(species) + elif featurizer == "op_sitefingerprint": + opsf = supported_matminer_featurizers[featurizer](**kwargs) + return len(opsf.feature_labels()) else: raise NotImplementedError( diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index d480808a..1871c349 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -70,7 +70,7 @@ def __init__( self._model_kwargs = None self.model_kwargs = model_kwargs - self._regressor = self.model_class(self.model_kwargs) + self.regressor = self.model_class(self.model_kwargs) self._structure_featurizer = None self.structure_featurizer = structure_featurizer @@ -275,7 +275,7 @@ def fit( adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - self._regressor.fit(X, collected_matrices) + self.regressor.fit(X, collected_matrices) if self.maximum_structure_size is None: self.maximum_structure_size = max([len(s) for s in perturbed_structures]) @@ -344,7 +344,9 @@ def predict( adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - predicted_correction_matrix = self._regressor.predict(featurized_input) + predicted_correction_matrix, unc = self.regressor.predict( + featurized_input, return_std=True + ) corrected_structures = [ init_struct.copy() for init_struct in initial_structure_guesses @@ -363,4 +365,4 @@ def predict( cs.positions[list_of_adsorbate_indices] += corr corrected_structures.append(cs) - return predicted_correction_matrix, corrected_structures + return predicted_correction_matrix, corrected_structures, unc diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 26d69b9a..2898a8ee 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -15,6 +15,7 @@ from matminer.featurizers.composition import ElementProperty from matminer.featurizers.site import ChemicalSRO +from matminer.featurizers.site import OPSiteFingerprint # from autocat.io.qml import ase_atoms_to_qml_compound from autocat.adsorption import generate_rxn_structures @@ -118,11 +119,30 @@ def test_adsorbate_featurization_chemical_sro(): pym_struct = conv.get_structure(ads_struct) csro.fit([[pym_struct, -2], [pym_struct, -1]]) manual_feat = csro.featurize(pym_struct, -2) - print(csro.feature_labels()) manual_feat = np.concatenate((manual_feat, csro.featurize(pym_struct, -1))) assert np.allclose(csro_feat, manual_feat) +def test_adsorbate_featurization_op_sitefingerprint(): + # Test Order Parameter Site Fingerprints + surf = generate_surface_structures(["Au"])["Au"]["fcc100"]["structure"] + ads_struct = generate_rxn_structures(surf, ads=["NH"])["NH"]["ontop"]["0.0_0.0"][ + "structure" + ] + opsf_feat = adsorbate_featurization( + ads_struct, [-1, -2], featurizer="op_sitefingerprint", maximum_adsorbate_size=4 + ) + opsf = OPSiteFingerprint() + conv = AseAtomsAdaptor() + pym_struct = conv.get_structure(ads_struct) + manual_feat = opsf.featurize(pym_struct, -2) + manual_feat = np.concatenate((manual_feat, opsf.featurize(pym_struct, -1))) + manual_feat = np.concatenate( + (manual_feat, np.zeros(2 * len(opsf.feature_labels()))) + ) + assert np.allclose(opsf_feat, manual_feat) + + def test_adsorbate_featurization_padding(): # Tests that padding is properly applied surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] @@ -265,6 +285,20 @@ def test_get_X_concatenation(): featurizer="chemical_sro", rcut=5.0, species=species_list ) assert X.shape == (len(structs), 5 * num_of_adsorbate_features) + X = get_X( + structs, + structure_featurizer=None, + adsorbate_featurizer="op_sitefingerprint", + adsorbate_indices_dictionary={ + structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], + structs[1].get_chemical_formula() + "_1": [-2, -1], + structs[2].get_chemical_formula() + "_2": [-1], + }, + maximum_adsorbate_size=5, + species_list=species_list, + ) + num_of_adsorbate_features = _get_number_of_features(featurizer="op_sitefingerprint") + assert X.shape == (len(structs), 5 * num_of_adsorbate_features) def test_get_X_write_location(): diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index ce492a45..df7724ae 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -82,7 +82,7 @@ def test_predict_initial_configuration_formats(): }, collected_matrices=collected_matrices[:15, :], ) - predicted_correction_matrix, corrected_structures = acsc.predict( + predicted_correction_matrix, corrected_structures, _ = acsc.predict( p_structures[15:], adsorbate_indices_dictionary={ base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] @@ -104,6 +104,6 @@ def test_predict_initial_configuration_formats(): def test_model_class(): # Tests providing regression model class acsc = AutoCatStructureCorrector(KernelRidge, model_kwargs={"gamma": 0.5}) - assert isinstance(acsc._regressor, KernelRidge) + assert isinstance(acsc.regressor, KernelRidge) acsc.model_class = GaussianProcessRegressor assert acsc.model_kwargs is None From d3dfd5581861728e47f2ab2dea085610a78c0f81 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 5 Apr 2021 21:23:26 -0400 Subject: [PATCH 051/239] rm default ads feat kwargs --- src/autocat/learning/predictors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 1871c349..cfef209e 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -81,7 +81,7 @@ def __init__( self._structure_featurization_kwargs = None self.structure_featurization_kwargs = structure_featurization_kwargs - self._adsorbate_featurization_kwargs = {"rcut": 3.0, "nmax": 4, "lmax": 4} + self._adsorbate_featurization_kwargs = None self.adsorbate_featurization_kwargs = adsorbate_featurization_kwargs self._maximum_structure_size = None From 205019567ac7964edd6b3d2bf86bf762240e53fb Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 6 Apr 2021 16:20:12 -0400 Subject: [PATCH 052/239] model kwarg bugfixes - passes model kwargs to regressor - updates regressor whenever model kwargs are changed --- src/autocat/learning/predictors.py | 10 ++++++---- tests/learning/test_predictors.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index cfef209e..4e30af66 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -70,7 +70,7 @@ def __init__( self._model_kwargs = None self.model_kwargs = model_kwargs - self.regressor = self.model_class(self.model_kwargs) + self.regressor = self.model_class(**self.model_kwargs or {}) self._structure_featurizer = None self.structure_featurizer = structure_featurizer @@ -102,9 +102,12 @@ def model_class(self, model_class): if model_class is not None: self._model_class = model_class # removes any model kwargs from previous model + # if changed self._model_kwargs = None if self.is_fit: self.is_fit = False + # generates new regressor with default settings + self.regressor = self._model_class() @property def model_kwargs(self): @@ -113,11 +116,10 @@ def model_kwargs(self): @model_kwargs.setter def model_kwargs(self, model_kwargs): if model_kwargs is not None: - assert isinstance(model_kwargs, dict) - if self._model_kwargs is not None: - self._model_kwargs = model_kwargs + self._model_kwargs = model_kwargs if self.is_fit: self.is_fit = False + self.regressor = self.model_class(**model_kwargs) @property def structure_featurizer(self): diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index df7724ae..8ffb030f 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -101,9 +101,17 @@ def test_predict_initial_configuration_formats(): assert np.allclose(manual.positions, corrected_structures[0].positions) -def test_model_class(): - # Tests providing regression model class +def test_model_class_and_kwargs(): + # Tests providing regression model class and kwargs acsc = AutoCatStructureCorrector(KernelRidge, model_kwargs={"gamma": 0.5}) assert isinstance(acsc.regressor, KernelRidge) + # check that regressor created with correct kwarg + assert acsc.regressor.gamma == 0.5 + assert acsc.model_kwargs == {"gamma": 0.5} acsc.model_class = GaussianProcessRegressor + # check that kwargs are removed when class is changed assert acsc.model_kwargs is None + acsc = AutoCatStructureCorrector() + acsc.model_kwargs = {"alpha": 2.5} + assert acsc.model_kwargs == {"alpha": 2.5} + assert acsc.regressor.alpha == 2.5 From ee17861dd244b9eb5a1e6b25eefb3bada7817a8e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 9 Apr 2021 16:25:09 -0400 Subject: [PATCH 053/239] adds struct refining (ie. only feat ads+surf) --- src/autocat/learning/featurizers.py | 53 ++++++++++++++++++++++++++++- src/autocat/learning/predictors.py | 32 ++++++++++++++++- tests/learning/test_featurizers.py | 35 ++++++++++++++++--- tests/learning/test_predictors.py | 7 +++- 4 files changed, 120 insertions(+), 7 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 1b6c6266..0eddf320 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -37,6 +37,7 @@ def get_X( maximum_adsorbate_size: int = None, adsorbate_featurizer: str = "soap", species_list: List[str] = None, + refine_structures: bool = True, structure_featurization_kwargs: Dict[str, float] = None, adsorbate_featurization_kwargs: Dict[str, float] = None, write_to_disk: bool = False, @@ -81,6 +82,11 @@ def get_X( List of species that could be encountered for featurization. Default: Parses over all `structures` and collects all encountered species + refine_structures: + Bool indicating whether the structures should be refined to include + only the adsorbate and surface layer. Requires tags for all structures + to have adsorbate atoms and surface atoms as 0 and 1, respectively + structure_featurization_kwargs: kwargs to be fed into `full_structure_featurization` @@ -102,7 +108,14 @@ def get_X( Shape is (# of structures, maximum_structure_size + maximum_adsorbate_size * # of adsorbates) """ if maximum_structure_size is None: - maximum_structure_size = max([len(s) for s in structures]) + if refine_structures: + ref_structures = [ + structure[np.where(structure.get_tags() < 2)[0].tolist()] + for structure in structures + ] + maximum_structure_size = max([len(ref) for ref in ref_structures]) + else: + maximum_structure_size = max([len(s) for s in structures]) if maximum_adsorbate_size is None: maximum_adsorbate_size = max( @@ -193,6 +206,7 @@ def get_X( maximum_structure_size=maximum_structure_size, maximum_adsorbate_size=maximum_adsorbate_size, species_list=species_list, + refine_structure=refine_structures, structure_featurization_kwargs=structure_featurization_kwargs, adsorbate_featurization_kwargs=adsorbate_featurization_kwargs, ) @@ -220,6 +234,7 @@ def catalyst_featurization( species_list: List[str] = None, structure_featurization_kwargs: Dict[str, float] = None, adsorbate_featurization_kwargs: Dict[str, float] = None, + refine_structure: bool = True, ): """ Featurizes a system containing an adsorbate + substrate @@ -254,6 +269,11 @@ def catalyst_featurization( adsorbate_featurization_kwargs: kwargs to be fed into `adsorbate_featurization` + refine_structure: + Bool indicating whether the structure should be refined to include + only the adsorbate and surface layer. Requires tags for the structure + to have adsorbate atoms and surface atoms as 0 and 1, respectivel + Returns ------- @@ -273,6 +293,7 @@ def catalyst_featurization( featurizer=structure_featurizer, maximum_structure_size=maximum_structure_size, elementalproperty_preset=elementalproperty_preset, + refine_structure=refine_structure, **structure_featurization_kwargs, ) else: @@ -288,6 +309,7 @@ def catalyst_featurization( adsorbate_indices=adsorbate_indices, species_list=species_list, maximum_adsorbate_size=maximum_adsorbate_size, + refine_structure=refine_structure, **adsorbate_featurization_kwargs, ) else: @@ -303,6 +325,7 @@ def adsorbate_featurization( featurizer: str = "soap", rcut: float = 6.0, maximum_adsorbate_size: int = None, + refine_structure: bool = True, **kwargs, ): """ @@ -348,6 +371,11 @@ def adsorbate_featurization( `adsorbate_indices`, representation will remain size of the adsorbate. Default: size of adsorbate provided + refine_structure: + Bool indicating whether the structure should be refined to include + only the adsorbate and surface layer. Requires tags for the structure + to have adsorbate atoms and surface atoms as 0 and 1, respectivel + Returns ------- @@ -358,6 +386,19 @@ def adsorbate_featurization( # Ensures that adsorbate indices specified are sorted adsorbate_indices.sort() + if refine_structure: + new_indices = np.where(structure.get_tags() < 2)[0].tolist() + # update the adsorbate indices for refined structure + new_adsorbate_indices = [] + for ads_idx in adsorbate_indices: + # wraps index + if ads_idx < 0: + ads_idx = len(structure) + ads_idx + new_adsorbate_indices.append(new_indices.index(ads_idx)) + adsorbate_indices = new_adsorbate_indices + # refine the structure + structure = structure[new_indices] + # Checks if species list given if species_list is None: species_list = np.unique(structure.get_chemical_symbols()).tolist() @@ -437,6 +478,7 @@ def full_structure_featurization( permutation: str = "none", elementalproperty_preset: str = "magpie", n_jobs: int = 1, + refine_structure: bool = True, **kwargs, ): """ @@ -480,12 +522,21 @@ def full_structure_featurization( Int specifiying number of parallel jobs to run which is fed into `dscribe` featurizers (ie. sine_matrix, coulomb_matrix) + refine_structure: + Bool indicating whether the structure should be refined to include + only the adsorbate and surface layer. Requires tags for the structure + to have adsorbate atoms and surface atoms as 0 and 1, respectively + Returns ------- representation: Np.ndarray of structure representation """ + + if refine_structure: + structure = structure[np.where(structure.get_tags() < 2)[0].tolist()] + if maximum_structure_size is None: maximum_structure_size = len(structure) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 4e30af66..4dc2b1f1 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -22,6 +22,7 @@ def __init__( maximum_structure_size: int = None, maximum_adsorbate_size: int = None, species_list: List[str] = None, + refine_structures: bool = None, structure_featurization_kwargs: Dict = None, adsorbate_featurization_kwargs: Dict = None, model_kwargs: Dict = None, @@ -61,6 +62,11 @@ def __init__( List of species that could be encountered for featurization. Default: Parses over all `structures` and collects all encountered species + refine_structures: + Bool indicating whether the structures should be refined to include + only the adsorbate and surface layer. Requires tags for all structures + to have adsorbate atoms and surface atoms as 0 and 1, respectively + """ self.is_fit = False @@ -72,6 +78,9 @@ def __init__( self.regressor = self.model_class(**self.model_kwargs or {}) + self._refine_structures = True + self.refine_structures = refine_structures + self._structure_featurizer = None self.structure_featurizer = structure_featurizer @@ -121,6 +130,17 @@ def model_kwargs(self, model_kwargs): self.is_fit = False self.regressor = self.model_class(**model_kwargs) + @property + def refine_structures(self): + return self._refine_structures + + @refine_structures.setter + def refine_structures(self, refine_structures): + if refine_structures is not None: + self._refine_structures = refine_structures + if self.is_fit: + self.is_fit = False + @property def structure_featurizer(self): return self._structure_featurizer @@ -273,6 +293,7 @@ def fit( maximum_adsorbate_size=self.maximum_adsorbate_size, adsorbate_featurizer=self.adsorbate_featurizer, species_list=self.species_list, + refine_structures=self.refine_structures, structure_featurization_kwargs=self.structure_featurization_kwargs, adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) @@ -280,7 +301,16 @@ def fit( self.regressor.fit(X, collected_matrices) if self.maximum_structure_size is None: - self.maximum_structure_size = max([len(s) for s in perturbed_structures]) + if self.refine_structures: + ref_structures = [ + structure[np.where(structure.get_tags() < 2)[0].tolist()] + for structure in perturbed_structures + ] + self.maximum_structure_size = max([len(ref) for ref in ref_structures]) + else: + self.maximum_structure_size = max( + [len(s) for s in perturbed_structures] + ) if self.maximum_adsorbate_size is None: self.maximum_adsorbate_size = max( diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 2898a8ee..3bd03e83 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -33,18 +33,26 @@ def test_full_structure_featurization_sine(): # Tests Sine Matrix Generation surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] - sine_matrix = full_structure_featurization(surf) + sine_matrix = full_structure_featurization(surf, refine_structure=False) sm = SineMatrix(n_atoms_max=len(surf), permutation="none") assert np.allclose(sine_matrix, sm.create(surf)) # Check padding sine_matrix = full_structure_featurization(surf, maximum_structure_size=40) assert sine_matrix.shape == (1600,) + # Check refined structure + sine_matrix = full_structure_featurization(surf, refine_structure=True) + surf = surf[np.where(surf.get_tags() < 2)[0].tolist()] + sm = SineMatrix(n_atoms_max=len(surf), permutation="none") + assert np.allclose(sine_matrix, sm.create(surf)) + assert sine_matrix.shape == (len(surf) ** 2,) def test_full_structure_featurization_coulomb(): # Tests Coulomb Matrix Generation surf = generate_surface_structures(["Pt"])["Pt"]["fcc100"]["structure"] - coulomb_matrix = full_structure_featurization(surf, featurizer="coulomb_matrix") + coulomb_matrix = full_structure_featurization( + surf, featurizer="coulomb_matrix", refine_structure=False + ) cm = CoulombMatrix(n_atoms_max=len(surf), permutation="none") assert np.allclose(coulomb_matrix, cm.create(surf)) # Check padding @@ -77,7 +85,9 @@ def test_adsorbate_featurization_acsf(): ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ "structure" ] - acsf_feat = adsorbate_featurization(ads_struct, [36], featurizer="acsf") + acsf_feat = adsorbate_featurization( + ads_struct, [36], featurizer="acsf", refine_structure=False + ) species = np.unique(ads_struct.get_chemical_symbols()).tolist() acsf = ACSF(rcut=6.0, species=species) assert np.allclose(acsf_feat, acsf.create(ads_struct, [36])) @@ -92,6 +102,7 @@ def test_adsorbate_featurization_soap(): soap_feat = adsorbate_featurization( ads_struct, [-1], featurizer="soap", nmax=8, lmax=6 ) + ads_struct = ads_struct[np.where(ads_struct.get_tags() < 2)[0].tolist()] species = np.unique(ads_struct.get_chemical_symbols()).tolist() soap = SOAP(rcut=6.0, species=species, nmax=8, lmax=6) assert np.allclose(soap_feat, soap.create(ads_struct, [-1])) @@ -110,6 +121,7 @@ def test_adsorbate_featurization_chemical_sro(): featurizer="chemical_sro", rcut=10.0, species_list=["Li", "O", "H"], + refine_structure=False, ) assert csro_feat.shape == (6,) species = ["Li", "O", "H"] @@ -130,7 +142,11 @@ def test_adsorbate_featurization_op_sitefingerprint(): "structure" ] opsf_feat = adsorbate_featurization( - ads_struct, [-1, -2], featurizer="op_sitefingerprint", maximum_adsorbate_size=4 + ads_struct, + [-1, -2], + featurizer="op_sitefingerprint", + maximum_adsorbate_size=4, + refine_structure=False, ) opsf = OPSiteFingerprint() conv = AseAtomsAdaptor() @@ -194,6 +210,7 @@ def test_catalyst_featurization_concatentation(): [-1, -2], maximum_structure_size=40, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + refine_structure=False, ) sm = SineMatrix(n_atoms_max=40, permutation="none") struct = sm.create(ads_struct).reshape(-1,) @@ -204,6 +221,16 @@ def test_catalyst_featurization_concatentation(): assert np.allclose(cat, cat_ref) num_of_adsorbate_features = soap.get_number_of_features() assert len(cat) == 40 ** 2 + num_of_adsorbate_features * 2 + # Check with structure refining + cat = catalyst_featurization( + ads_struct, + [-1, -2], + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + ref_ads_struct = ads_struct[np.where(ads_struct.get_tags() < 2)[0].tolist()] + sm = SineMatrix(n_atoms_max=len(ref_ads_struct), permutation="none") + num_of_adsorbate_features = soap.get_number_of_features() + assert len(cat) == len(ref_ads_struct) ** 2 + num_of_adsorbate_features * 2 def test_get_X_concatenation(): diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 8ffb030f..6e65c2b3 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -82,7 +82,7 @@ def test_predict_initial_configuration_formats(): }, collected_matrices=collected_matrices[:15, :], ) - predicted_correction_matrix, corrected_structures, _ = acsc.predict( + predicted_correction_matrix, corrected_structures, uncs = acsc.predict( p_structures[15:], adsorbate_indices_dictionary={ base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] @@ -91,6 +91,9 @@ def test_predict_initial_configuration_formats(): ) assert isinstance(corrected_structures[0], Atoms) assert len(corrected_structures) == 5 + # check that even with refining, corrected structure is + # returned to full size + assert len(corrected_structures[2]) == len(p_structures[17]) assert isinstance(predicted_correction_matrix, np.ndarray) assert predicted_correction_matrix.shape[0] == 5 # check that predicted correction matrix is applied correctly @@ -99,6 +102,8 @@ def test_predict_initial_configuration_formats(): manual.positions[-1] += manual_corr_mat[-1] manual.positions[-2] += manual_corr_mat[-2] assert np.allclose(manual.positions, corrected_structures[0].positions) + # check dimension of uncertainty estimates + assert len(uncs) == 5 def test_model_class_and_kwargs(): From 402cf56dd639e84c7709f449424fcd032d6095b0 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 12 Apr 2021 16:03:24 -0400 Subject: [PATCH 054/239] atoms to be perturbed now specified by tags --- src/autocat/perturbations.py | 82 ++++++++++---------------- tests/learning/test_predictors.py | 16 +---- tests/test_perturbations.py | 98 +++++++------------------------ 3 files changed, 55 insertions(+), 141 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index a9c86b37..7f421c96 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -14,10 +14,8 @@ class AutocatPerturbationError(Exception): def generate_perturbed_dataset( base_structures: List[Atoms], - atom_indices_to_perturb_dictionary: Dict[Union[str, Atoms], List[int]], minimum_perturbation_distance: float = 0.1, maximum_perturbation_distance: float = 1.0, - directions_dictionary: Dict[Union[str, Atoms], List[bool]] = None, maximum_adsorbate_size: int = None, num_of_perturbations: int = 10, write_to_disk: bool = False, @@ -30,6 +28,15 @@ def generate_perturbed_dataset( a base list of structures and keeps track of displacement vectors + Atoms to be perturbed are specified by their `tag`. + The options for constraints are also set using + `tag`s with the options as follows: + 0: free in all directions + -1: free in z only + -2: free in xy only + -3: free in x only + -4: free in y only + Parameters ---------- @@ -38,13 +45,6 @@ def generate_perturbed_dataset( as a strings specifying the base structures to be perturbed - atom_indices_to_perturb_dictionary: - Dictionary of list of atomic indices for the atoms that should - be perturbed. Keys are each of the provided base structures. - If an ase.Atoms object is given in `base_structures`, its corresponding - key here should be: - f"{base_structure.get_chemical_formula()}_{index_in_`base_structures`}" - minimum_perturbation_distance: Float of minimum acceptable perturbation distance Default: 0.1 Angstrom @@ -53,14 +53,6 @@ def generate_perturbed_dataset( Float of maximum acceptable perturbation distance Default: 1.0 Angstrom - directions_dictionary: - Dictionary of List of bools indicating which cartesian directions - the atoms are allowed to be perturbed in for each `base_structure`. - If an ase.Atoms object is given in `base_structures`, its corresponding - key here should be: - f"{base_structure.get_chemical_formula()}_{index_in_`base_structures`}" - Default: free to perturb in all cartesian directions - maximum_adsorbate_size: Integer giving the largest number of atoms in an adsorbate that should be able to be considered. Used to obtain shape @@ -98,9 +90,6 @@ def generate_perturbed_dataset( perturbed_dict = {} - if directions_dictionary is None: - directions_dictionary = {} - collected_matrices = [] collected_structure_paths = [] collected_structures = [] @@ -120,19 +109,13 @@ def generate_perturbed_dataset( msg = f"Multiple input base structures named {name}" raise AutocatPerturbationError(msg) - # assigns default of free to perturb in all directions if needed - if name not in directions_dictionary: - directions_dictionary[name] = None - # apply perturbations perturbed_dict[name] = {} for i in range(num_of_perturbations): perturbed_dict[name][str(i)] = perturb_structure( structure, - atom_indices_to_perturb=atom_indices_to_perturb_dictionary[name], minimum_perturbation_distance=minimum_perturbation_distance, maximum_perturbation_distance=maximum_perturbation_distance, - directions=directions_dictionary[name], ) # keeps flattened atomic coordinates difference vector collected_matrices.append( @@ -173,12 +156,10 @@ def generate_perturbed_dataset( if maximum_adsorbate_size is None: # find flattened length of largest structure - largest_size = 3 * max( - [ - len(atom_indices_to_perturb_dictionary[i]) - for i in atom_indices_to_perturb_dictionary - ] - ) + adsorbate_sizes = [] + for struct in base_structures: + adsorbate_sizes.append(len(np.where(struct.get_tags() <= 0)[0])) + largest_size = 3 * max(adsorbate_sizes) else: # factor of 3 from flattening (ie. x,y,z) largest_size = 3 * maximum_adsorbate_size @@ -214,16 +195,23 @@ def generate_perturbed_dataset( def perturb_structure( base_structure: Union[str, Atoms], - atom_indices_to_perturb: List[int], minimum_perturbation_distance: float = 0.1, maximum_perturbation_distance: float = 1.0, - directions: List[bool] = None, ): """ Perturbs specific atoms in a given structure and keeps track of the displacement vectors of each displaced atom + Atoms to be perturbed are specified by their `tag`. + The options for constraints are also set using + `tag`s with the options as follows: + 0: free in all directions + -1: free in z only + -2: free in xy only + -3: free in x only + -4: free in y only + Parameters ---------- @@ -232,10 +220,6 @@ def perturb_structure( as a string specifying the base structure to be perturbed - atom_indices_to_perturb: - List of atomic indices for the atoms that should - be perturbed - minimum_perturbation_distance: Float of minimum acceptable perturbation distance Default: 0.1 Angstrom @@ -244,11 +228,6 @@ def perturb_structure( Float of maximum acceptable perturbation distance Default: 1.0 Angstrom - directions: - List of bools indicating which cartesian directions - the atoms are allowed to be perturbed in - Default: free to perturb in all cartesian directions - Returns ------- @@ -263,21 +242,24 @@ def perturb_structure( else: raise TypeError("base_structure needs to be either a str or ase.Atoms object") - if directions is None: - constr = [True, True, True] - else: - constr = directions - pert_matrix = np.zeros(ase_obj.positions.shape) - atom_indices_to_perturb.sort() + atom_indices_to_perturb = np.where(ase_obj.get_tags() <= 0)[0].tolist() + + constr = [ + [True, True, True], # free + [False, True, False], # y only + [True, False, False], # x only + [True, True, False], # xy only + [False, False, True], # z only + ] for idx in atom_indices_to_perturb: # randomize +/- direction of each perturbation signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=11, size=(1, 3)) # generate perturbation matrix pert_matrix[idx, :] = ( - constr + constr[ase_obj[idx].tag] * signs * np.random.uniform( low=minimum_perturbation_distance, diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 6e65c2b3..f628d1c4 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -23,13 +23,7 @@ def test_fit_model_on_perturbed_systems(): "structure" ] base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] - p_set = generate_perturbed_dataset( - [base_struct], - atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula() + "_0": [-1, -2] - }, - num_of_perturbations=15, - ) + p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) p_structures = p_set["collected_structures"] collected_matrices = p_set["collected_matrices"] acsc = AutoCatStructureCorrector( @@ -60,13 +54,7 @@ def test_predict_initial_configuration_formats(): "structure" ] base_struct = place_adsorbate(sub, "CO")["custom"]["structure"] - p_set = generate_perturbed_dataset( - [base_struct], - atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula() + "_0": [-1, -2] - }, - num_of_perturbations=20, - ) + p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] collected_matrices = p_set["collected_matrices"] acsc = AutoCatStructureCorrector( diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 72d600a2..07b8a670 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -20,20 +20,12 @@ def test_perturb_structure_directions(): "structure" ] base_struct = place_adsorbate(sub, "H")["custom"]["structure"] - # fixed in all directions - p_struct = perturb_structure( - base_struct, atom_indices_to_perturb=[-1], directions=[False, False, False] - ) - assert (p_struct["structure"].positions[-1] == base_struct.positions[-1]).all() # free in all directions - p_struct = perturb_structure( - base_struct, atom_indices_to_perturb=[-1], directions=[True, True, True] - ) + p_struct = perturb_structure(base_struct,) assert (p_struct["structure"].positions[-1] != base_struct.positions[-1]).all() - # free in 1D - p_struct = perturb_structure( - base_struct, atom_indices_to_perturb=[-1], directions=[False, False, True] - ) + # free in z + base_struct[-1].tag = -1 + p_struct = perturb_structure(base_struct,) assert p_struct["structure"].positions[-1][0] == base_struct.positions[-1][0] assert p_struct["structure"].positions[-1][1] == base_struct.positions[-1][1] assert p_struct["structure"].positions[-1][-1] != base_struct.positions[-1][-1] @@ -45,12 +37,8 @@ def test_perturb_structure_matrix(): "structure" ] base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] - p_struct = perturb_structure(base_struct, atom_indices_to_perturb=[-1, -2]) - # print(p_struct["structure"].positions[-2]) - # print(base_struct.positions[-2]) - # print(p_struct["perturbation_matrix"][-2]) + p_struct = perturb_structure(base_struct) o_pert = base_struct.positions[-2] + p_struct["perturbation_matrix"][-2] - # print(o_pert) assert np.allclose(p_struct["structure"].positions[-2], o_pert) h_pert = base_struct.positions[-1] + p_struct["perturbation_matrix"][-1] assert np.allclose(p_struct["structure"].positions[-1], h_pert) @@ -62,13 +50,7 @@ def test_generate_perturbed_dataset_num_of_perturbations(): "structure" ] base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] - p_set = generate_perturbed_dataset( - [base_struct], - atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula() + "_0": [-1, -2] - }, - num_of_perturbations=15, - ) + p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) assert len(p_set["HOPt36_0"].keys()) == 15 @@ -82,16 +64,10 @@ def test_generate_perturbed_dataset_multiple_base_structures(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - p_set = generate_perturbed_dataset( - [base_struct1, base_struct2], - atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula() + "_0": [-1], - base_struct2.get_chemical_formula() + "_1": [-2], - }, - directions_dictionary={ - base_struct1.get_chemical_formula() + "_0": [False, False, True] - }, - ) + base_struct1[-2].tag = 1 + base_struct1[-1].tag = -1 + base_struct2[-1].tag = 1 + p_set = generate_perturbed_dataset([base_struct1, base_struct2],) # Check all base structures perturbed assert "HCu36N_1" in p_set assert "HOPt36_0" in p_set @@ -110,12 +86,7 @@ def test_generate_perturbed_dataset_write_location(): ] base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] p_set = generate_perturbed_dataset( - [base_struct], - atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula() + "_0": [-1] - }, - write_to_disk=True, - write_location=_tmp_dir, + [base_struct], write_to_disk=True, write_location=_tmp_dir, ) assert os.path.samefile( p_set["HOPt36_0"]["0"]["traj_file_path"], @@ -137,13 +108,7 @@ def test_generate_perturbed_dataset_collected_matrices(): "structure" ] base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] - p_set = generate_perturbed_dataset( - [base_struct], - atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula() + "_0": [-1, -2] - }, - num_of_perturbations=5, - ) + p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=5,) manual_collect = np.zeros((5, 6)) for idx, struct in enumerate(p_set["HOPt36_0"]): flat = p_set["HOPt36_0"][struct]["perturbation_matrix"].flatten() @@ -152,12 +117,7 @@ def test_generate_perturbed_dataset_collected_matrices(): assert p_set["collected_matrices"].shape == (5, 6) # Check when given specific maximum_structure_size p_set = generate_perturbed_dataset( - [base_struct], - atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula() + "_0": [-1, -2] - }, - num_of_perturbations=5, - maximum_adsorbate_size=15, + [base_struct], num_of_perturbations=5, maximum_adsorbate_size=15, ) assert p_set["collected_matrices"].shape == (5, 45) @@ -172,13 +132,9 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "OH")["custom"]["structure"] + base_struct2[-1].tag = 1 p_set = generate_perturbed_dataset( - [base_struct1, base_struct2], - atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula() + "_0": [-2, -1], - base_struct2.get_chemical_formula() + "_1": [-2], - }, - num_of_perturbations=5, + [base_struct1, base_struct2], num_of_perturbations=5, ) manual_collect = np.zeros((10, 6)) counter = 0 @@ -199,13 +155,9 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "H")["custom"]["structure"] + base_struct1[-2].tag = 1 p_set = generate_perturbed_dataset( - [base_struct1, base_struct2], - atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula() + "_0": [-1], - base_struct2.get_chemical_formula() + "_1": [-2], - }, - num_of_perturbations=5, + [base_struct1, base_struct2], num_of_perturbations=5, ) manual_collect = [] for base in ["HOPt36_0", "HCu36_1"]: @@ -226,12 +178,7 @@ def test_generate_perturbed_dataset_collected_structure_paths(): ] base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] p_set = generate_perturbed_dataset( - [base_struct], - atom_indices_to_perturb_dictionary={ - base_struct.get_chemical_formula() + "_0": [-1] - }, - write_to_disk=True, - write_location=_tmp_dir, + [base_struct], write_to_disk=True, write_location=_tmp_dir, ) assert os.path.samefile( p_set["collected_structure_paths"][0], @@ -253,13 +200,10 @@ def test_generate_perturbed_dataset_collected_structures(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "OH")["custom"]["structure"] + base_struct1[-2].tag = 1 + base_struct2[-1].tag = 1 p_set = generate_perturbed_dataset( - [base_struct1, base_struct2], - atom_indices_to_perturb_dictionary={ - base_struct1.get_chemical_formula() + "_0": [-1], - base_struct2.get_chemical_formula() + "_1": [-2], - }, - num_of_perturbations=5, + [base_struct1, base_struct2], num_of_perturbations=5, ) assert len(p_set["collected_structures"]) == 10 assert isinstance(p_set["collected_structures"][3], Atoms) From dd92d34a03324cff95e62be271b1aa0693f46129 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 12 Apr 2021 16:53:49 -0400 Subject: [PATCH 055/239] propagate new atoms_to_perturb paradigm --- src/autocat/learning/featurizers.py | 47 ++++----------------------- src/autocat/learning/predictors.py | 34 +++++--------------- tests/learning/test_featurizers.py | 50 ++++------------------------- tests/learning/test_predictors.py | 4 --- 4 files changed, 22 insertions(+), 113 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 0eddf320..704512d1 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -30,7 +30,6 @@ class AutoCatFeaturizationError(Exception): def get_X( structures: List[Union[Atoms, str]], - adsorbate_indices_dictionary: Dict[str, int] = None, maximum_structure_size: int = None, structure_featurizer: str = "sine_matrix", elementalproperty_preset: str = "magpie", @@ -52,13 +51,6 @@ def get_X( structures: List of ase.Atoms objects or structure filename strings to be used to construct X matrix - adsorbate_indices_dictionary: - Dictionary mapping structures to desired adsorbate_indices. - Only necessary if adsorbate featurization is specified - (N.B. if structure is given as an ase.Atoms object, - the key for this dictionary should be - ase.Atoms.get_chemical_formula() + "_" + str(index in list)) - maximum_structure_size: Size of the largest structure to be supported by the representation. Default: number of atoms in largest structure within `structures` @@ -118,9 +110,10 @@ def get_X( maximum_structure_size = max([len(s) for s in structures]) if maximum_adsorbate_size is None: - maximum_adsorbate_size = max( - [len(adsorbate_indices_dictionary[a]) for a in adsorbate_indices_dictionary] - ) + adsorbate_sizes = [] + for struct in structures: + adsorbate_sizes.append(len(np.where(struct.get_tags() <= 0)[0].tolist())) + maximum_adsorbate_size = max(adsorbate_sizes) if adsorbate_featurization_kwargs is None: adsorbate_featurization_kwargs = {} @@ -136,10 +129,6 @@ def get_X( species_list.extend(new_species) if adsorbate_featurizer is not None: - # check that adsorbate indices specified - if adsorbate_indices_dictionary is None: - msg = "For adsorbate featurization, adsorbate indices must be specified" - raise AutoCatFeaturizationError(msg) # find number of adsorbate features if adsorbate_featurizer in ["soap", "acsf"]: num_of_adsorbate_features = _get_number_of_features( @@ -187,20 +176,14 @@ def get_X( for idx, structure in enumerate(structures): if isinstance(structure, Atoms): - name = structure.get_chemical_formula() + "_" + str(idx) ase_struct = structure.copy() elif isinstance(structure, str): - name = structure ase_struct = read(structure) else: msg = f"Each structure needs to be either a str or ase.Atoms. Got {type(structure)}" raise AutoCatFeaturizationError(msg) - # if no adsorbate featurizer, makes placeholder dict for each loop - if adsorbate_featurizer is None: - adsorbate_indices_dictionary = {name: None} cat_feat = catalyst_featurization( ase_struct, - adsorbate_indices=adsorbate_indices_dictionary[name], structure_featurizer=structure_featurizer, adsorbate_featurizer=adsorbate_featurizer, maximum_structure_size=maximum_structure_size, @@ -225,7 +208,6 @@ def get_X( def catalyst_featurization( structure: Atoms, - adsorbate_indices: List[int] = None, structure_featurizer: str = "sine_matrix", elementalproperty_preset: str = "magpie", adsorbate_featurizer: str = "soap", @@ -250,11 +232,6 @@ def catalyst_featurization( structure: Atoms object of full structure (adsorbate + slab) to be featurized - adsorbate_indices: - List of atomic indices specifying the adsorbate to be - featurized. - Only necessary if adsorbate featurization is specified - structure_featurizer: String giving featurizer to be used for full structure which will be fed into `full_structure_featurization` @@ -300,13 +277,9 @@ def catalyst_featurization( struct_feat = np.array([]) if adsorbate_featurizer is not None: - if adsorbate_indices is None: - msg = "For adsorbate featurization, adsorbate indices must be specified" - raise AutoCatFeaturizationError(msg) ads_feat = adsorbate_featurization( structure, featurizer=adsorbate_featurizer, - adsorbate_indices=adsorbate_indices, species_list=species_list, maximum_adsorbate_size=maximum_adsorbate_size, refine_structure=refine_structure, @@ -320,7 +293,6 @@ def catalyst_featurization( def adsorbate_featurization( structure: Atoms, - adsorbate_indices: List[int], species_list: List[str] = None, featurizer: str = "soap", rcut: float = 6.0, @@ -329,8 +301,8 @@ def adsorbate_featurization( **kwargs, ): """ - Featurizes adsorbate on a support via `matminer.featurizers.site` - functions. + Featurizes adsorbate on a support via `dscribe` + and `matminer.featurizers.site` functions. Parameters ---------- @@ -338,10 +310,6 @@ def adsorbate_featurization( structure: Atoms object of full structure (adsorbate + slab) to be featurized - adsorbate_indices: - List of atomic indices specifying the adsorbate to be - featurized - species_list: List of chemical species that should be covered by representation which is fed into `dscribe.descriptors.{ACSF,SOAP}` or @@ -383,8 +351,7 @@ def adsorbate_featurization( Np.ndarray of adsorbate representation """ - # Ensures that adsorbate indices specified are sorted - adsorbate_indices.sort() + adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() if refine_structure: new_indices = np.where(structure.get_tags() < 2)[0].tolist() diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 4dc2b1f1..707980d6 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -255,7 +255,6 @@ def fit( self, perturbed_structures: List[Union[Atoms, str]], collected_matrices: np.ndarray, - adsorbate_indices_dictionary: Dict[str, int] = None, ): """ Given a list of perturbed structures @@ -267,12 +266,6 @@ def fit( perturbed_structures: List of perturbed structures to be trained upon - adsorbate_indices_dictionary: - Dictionary mapping structures to desired adsorbate_indices - (N.B. if structure is given as an ase.Atoms object, - the key for this dictionary should be - f"{structure.get_chemical_formula()}_{index_in_`perturbed_structures`}") - collected_matrices: Numpy array of collected matrices of perturbations corresponding to each of the perturbed structures. @@ -287,7 +280,6 @@ def fit( """ X = get_X( perturbed_structures, - adsorbate_indices_dictionary=adsorbate_indices_dictionary, maximum_structure_size=self.maximum_structure_size, structure_featurizer=self.structure_featurizer, maximum_adsorbate_size=self.maximum_adsorbate_size, @@ -313,12 +305,12 @@ def fit( ) if self.maximum_adsorbate_size is None: - self.maximum_adsorbate_size = max( - [ - len(adsorbate_indices_dictionary[a]) - for a in adsorbate_indices_dictionary - ] - ) + adsorbate_sizes = [] + for struct in perturbed_structures: + adsorbate_sizes.append( + len(np.where(struct.get_tags() <= 0)[0].tolist()) + ) + self.maximum_adsorbate_size = max(adsorbate_sizes) if self.species_list is None: species_list = [] @@ -332,9 +324,7 @@ def fit( self.is_fit = True def predict( - self, - initial_structure_guesses: List[Atoms], - adsorbate_indices_dictionary: Dict[str, int] = None, + self, initial_structure_guesses: List[Atoms], ): """ From a trained model, will predict corrected structure @@ -347,12 +337,6 @@ def predict( List of Atoms objects of initial guesses for adsorbate placement to be optimized - adsorbate_indices_dictionary: - Dictionary mapping structures to desired adsorbate_indices - (N.B. if structures is given as ase.Atoms objects, - the key for this dictionary should be - ase.Atoms.get_chemical_formula()+ "_" + str(index in list) - Returns ------- @@ -366,7 +350,6 @@ def predict( assert self.is_fit featurized_input = get_X( structures=initial_structure_guesses, - adsorbate_indices_dictionary=adsorbate_indices_dictionary, structure_featurizer=self.structure_featurizer, adsorbate_featurizer=self.adsorbate_featurizer, maximum_structure_size=self.maximum_structure_size, @@ -387,8 +370,7 @@ def predict( corrected_structures = [] for idx, struct in enumerate(initial_structure_guesses): cs = struct.copy() - name = cs.get_chemical_formula() + "_" + str(idx) - list_of_adsorbate_indices = adsorbate_indices_dictionary[name] + list_of_adsorbate_indices = np.where(struct.get_tags() <= 0)[0].tolist() list_of_adsorbate_indices.sort() num_of_adsorbates = len(list_of_adsorbate_indices) corr = predicted_correction_matrix[idx, : 3 * num_of_adsorbates].reshape( diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 3bd03e83..12b85e0f 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -86,7 +86,7 @@ def test_adsorbate_featurization_acsf(): "structure" ] acsf_feat = adsorbate_featurization( - ads_struct, [36], featurizer="acsf", refine_structure=False + ads_struct, featurizer="acsf", refine_structure=False ) species = np.unique(ads_struct.get_chemical_symbols()).tolist() acsf = ACSF(rcut=6.0, species=species) @@ -99,13 +99,11 @@ def test_adsorbate_featurization_soap(): ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ "structure" ] - soap_feat = adsorbate_featurization( - ads_struct, [-1], featurizer="soap", nmax=8, lmax=6 - ) + soap_feat = adsorbate_featurization(ads_struct, featurizer="soap", nmax=8, lmax=6) ads_struct = ads_struct[np.where(ads_struct.get_tags() < 2)[0].tolist()] species = np.unique(ads_struct.get_chemical_symbols()).tolist() soap = SOAP(rcut=6.0, species=species, nmax=8, lmax=6) - assert np.allclose(soap_feat, soap.create(ads_struct, [-1])) + assert np.allclose(soap_feat, soap.create(ads_struct, positions=[-1])) assert soap_feat.shape == (soap.get_number_of_features(),) @@ -117,7 +115,6 @@ def test_adsorbate_featurization_chemical_sro(): ] csro_feat = adsorbate_featurization( ads_struct, - [-2, -1], featurizer="chemical_sro", rcut=10.0, species_list=["Li", "O", "H"], @@ -143,7 +140,6 @@ def test_adsorbate_featurization_op_sitefingerprint(): ] opsf_feat = adsorbate_featurization( ads_struct, - [-1, -2], featurizer="op_sitefingerprint", maximum_adsorbate_size=4, refine_structure=False, @@ -166,7 +162,7 @@ def test_adsorbate_featurization_padding(): "structure" ] soap_feat = adsorbate_featurization( - ads_struct, [36], featurizer="soap", nmax=8, lmax=6, maximum_adsorbate_size=10 + ads_struct, featurizer="soap", nmax=8, lmax=6, maximum_adsorbate_size=10 ) species = np.unique(ads_struct.get_chemical_symbols()).tolist() @@ -175,18 +171,13 @@ def test_adsorbate_featurization_padding(): ) assert (soap_feat[-num_of_features * 9 :] == np.zeros(num_of_features * 9)).all() + ads_struct[35].tag = 0 soap_feat = adsorbate_featurization( - ads_struct, - [35, 36], - featurizer="soap", - nmax=8, - lmax=6, - maximum_adsorbate_size=10, + ads_struct, featurizer="soap", nmax=8, lmax=6, maximum_adsorbate_size=10, ) assert (soap_feat[-num_of_features * 8 :] == np.zeros(num_of_features * 8)).all() csro_feat = adsorbate_featurization( ads_struct, - [35, 36], featurizer="chemical_sro", rcut=10.0, maximum_adsorbate_size=4, @@ -207,7 +198,6 @@ def test_catalyst_featurization_concatentation(): ] cat = catalyst_featurization( ads_struct, - [-1, -2], maximum_structure_size=40, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, refine_structure=False, @@ -223,9 +213,7 @@ def test_catalyst_featurization_concatentation(): assert len(cat) == 40 ** 2 + num_of_adsorbate_features * 2 # Check with structure refining cat = catalyst_featurization( - ads_struct, - [-1, -2], - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ads_struct, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) ref_ads_struct = ads_struct[np.where(ads_struct.get_tags() < 2)[0].tolist()] sm = SineMatrix(n_atoms_max=len(ref_ads_struct), permutation="none") @@ -255,11 +243,6 @@ def test_get_X_concatenation(): X = get_X( structs, - adsorbate_indices_dictionary={ - structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], - structs[1].get_chemical_formula() + "_1": [-2, -1], - structs[2].get_chemical_formula() + "_2": [-1], - }, maximum_structure_size=50, maximum_adsorbate_size=5, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, @@ -282,11 +265,6 @@ def test_get_X_concatenation(): X = get_X( structs, structure_featurizer=None, - adsorbate_indices_dictionary={ - structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], - structs[1].get_chemical_formula() + "_1": [-2, -1], - structs[2].get_chemical_formula() + "_2": [-1], - }, maximum_structure_size=50, maximum_adsorbate_size=5, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, @@ -300,11 +278,6 @@ def test_get_X_concatenation(): structs, structure_featurizer=None, adsorbate_featurizer="chemical_sro", - adsorbate_indices_dictionary={ - structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], - structs[1].get_chemical_formula() + "_1": [-2, -1], - structs[2].get_chemical_formula() + "_2": [-1], - }, maximum_adsorbate_size=5, species_list=species_list, ) @@ -316,11 +289,6 @@ def test_get_X_concatenation(): structs, structure_featurizer=None, adsorbate_featurizer="op_sitefingerprint", - adsorbate_indices_dictionary={ - structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], - structs[1].get_chemical_formula() + "_1": [-2, -1], - structs[2].get_chemical_formula() + "_2": [-1], - }, maximum_adsorbate_size=5, species_list=species_list, ) @@ -346,10 +314,6 @@ def test_get_X_write_location(): _tmp_dir = tempfile.TemporaryDirectory().name X = get_X( structs, - adsorbate_indices_dictionary={ - structs[0].get_chemical_formula() + "_0": [-4, -3, -2, -1], - structs[1].get_chemical_formula() + "_1": [-2, -1], - }, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, write_to_disk=True, write_location=_tmp_dir, diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index f628d1c4..fe5a2b93 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -72,10 +72,6 @@ def test_predict_initial_configuration_formats(): ) predicted_correction_matrix, corrected_structures, uncs = acsc.predict( p_structures[15:], - adsorbate_indices_dictionary={ - base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] - for i in range(5) - }, ) assert isinstance(corrected_structures[0], Atoms) assert len(corrected_structures) == 5 From 57dec49e72b93ca1ab9548e1f4b0f2fe22e5ba10 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 13 Apr 2021 11:07:51 -0400 Subject: [PATCH 056/239] start sequential learning, fix predictors tests --- src/autocat/learning/sequential.py | 116 +++++++++++++++++++++++++++++ tests/learning/test_predictors.py | 14 +--- tests/learning/test_sequential.py | 41 ++++++++++ 3 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 src/autocat/learning/sequential.py create mode 100644 tests/learning/test_sequential.py diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py new file mode 100644 index 00000000..acdf868d --- /dev/null +++ b/src/autocat/learning/sequential.py @@ -0,0 +1,116 @@ +import numpy as np + +from typing import List +from typing import Dict +from typing import Union + +from ase import Atoms + +from autocat.perturbations import generate_perturbed_dataset +from autocat.learning.predictors import AutoCatStructureCorrector + +Array = List[float] + + +def simulated_sequential_learning( + structure_corrector: AutoCatStructureCorrector, + base_structures: List[Atoms], + minimum_perturbation_distance: float = 0.1, + maximum_perturbation_distance: float = 1.0, + initial_num_of_perturbations_per_base_structure: int = None, + batch_num_of_perturbations_per_base_structure: int = 2, + number_of_sl_loops: int = 100, +): + """ + Conducts a simulated sequential learning loop given + a set of base structures. For each loop, new perturbations + are generated. Maximum Uncertainty is used as the acquisition function + + Parameters + ---------- + + structure_corrector: + AutoCatStructureCorrector object to be used for fitting and prediction + + base_structures: + List of Atoms objects for all base structures to be perturbed for training + and testing + + minimum_perturbation_distance: + Float of minimum acceptable perturbation distance + Default: 0.1 Angstrom + + maximum_perturbation_distance: + Float of maximum acceptable perturbation distance + Default: 1.0 Angstrom + + initial_num_of_perturbations_per_base_structure: + Integer giving the number of perturbations to generate initially + for each of the given base structures for initial training purposes. + Default: uses same value as `batch_num_of_perturbations_per_base_structure` + + batch_num_of_perturbations_per_base_structure: + Integer giving the number of perturbations to generate + on each loop when finding the next candidate + + number_of_sl_loops: + Integer specifying the number of sequential learning loops to be conducted + + Returns + ------- + """ + if initial_num_of_perturbations_per_base_structure is None: + initial_num_of_perturbations_per_base_structure = ( + batch_num_of_perturbations_per_base_structure + ) + + initial_pert_dataset = generate_perturbed_dataset( + base_structures, + num_of_perturbations=initial_num_of_perturbations_per_base_structure, + maximum_perturbation_distance=maximum_perturbation_distance, + minimum_perturbation_distance=minimum_perturbation_distance, + ) + + train_pert_structures = initial_pert_dataset["collected_structures"] + train_pert_coll_matr = initial_pert_dataset["collected_matrices"] + + structure_corrector.fit(train_pert_structures, train_pert_coll_matr) + + full_unc_history = [] + max_unc_history = [] + pred_corr_matr_history = [] + while len(max_unc_history) < number_of_sl_loops: + # generate new perturbations to predict on + new_perturbations = generate_perturbed_dataset( + base_structures, + num_of_perturbations=batch_num_of_perturbations_per_base_structure, + maximum_perturbation_distance=maximum_perturbation_distance, + minimum_perturbation_distance=minimum_perturbation_distance, + ) + new_pert_structs = new_perturbations["collected_structures"] + new_pert_coll_matr = new_perturbations["collected_matrices"] + + # make predictions + _pred_corr_matr, _pred_corr_structs, _uncs = structure_corrector.predict( + new_pert_structs + ) + full_unc_history.append(_uncs) + pred_corr_matr_history.append(_pred_corr_matr) + + # find candidate with highest uncertainty + high_unc_idx = np.argmax(_uncs) + max_unc_history.append(_uncs[high_unc_idx]) + + next_candidate_struct = new_pert_structs[high_unc_idx] + # add new perturbed struct to training set + train_pert_structures.append(next_candidate_struct) + train_pert_coll_matr = np.concatenate( + (train_pert_coll_matr, new_pert_coll_matr[high_unc_idx].reshape(1, -1)), + axis=0, + ) + structure_corrector.fit(train_pert_structures, train_pert_coll_matr) + return { + "max_unc_history": max_unc_history, + "full_unc_history": full_unc_history, + "pred_corr_matr_history": pred_corr_matr_history, + } diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index fe5a2b93..23db1e56 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -32,12 +32,7 @@ def test_fit_model_on_perturbed_systems(): adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures, - adsorbate_indices_dictionary={ - base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] - for i in range(15) - }, - collected_matrices=collected_matrices, + p_structures, collected_matrices=collected_matrices, ) assert acsc.adsorbate_featurizer == "soap" assert acsc.is_fit @@ -63,12 +58,7 @@ def test_predict_initial_configuration_formats(): adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures[:15], - adsorbate_indices_dictionary={ - base_struct.get_chemical_formula() + "_" + str(i): [-1, -2] - for i in range(15) - }, - collected_matrices=collected_matrices[:15, :], + p_structures[:15], collected_matrices=collected_matrices[:15, :], ) predicted_correction_matrix, corrected_structures, uncs = acsc.predict( p_structures[15:], diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py new file mode 100644 index 00000000..6d040c64 --- /dev/null +++ b/tests/learning/test_sequential.py @@ -0,0 +1,41 @@ +"""Unit tests for the `autocat.learning.sequential` module""" + +import os +import pytest +import numpy as np + +from autocat.learning.predictors import AutoCatStructureCorrector +from autocat.learning.sequential import simulated_sequential_learning +from autocat.surface import generate_surface_structures +from autocat.adsorption import place_adsorbate + + +def test_simulated_sequential_outputs(): + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2], + initial_num_of_perturbations_per_base_structure=4, + batch_num_of_perturbations_per_base_structure=3, + number_of_sl_loops=4, + ) + # Test that number of max uncertainties equals number of sl loops + assert len(sl_dict["max_unc_history"]) == 4 + # Test the number of total uncertainties collected + assert len(sl_dict["full_unc_history"][-1]) == 6 + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2], + batch_num_of_perturbations_per_base_structure=1, + number_of_sl_loops=4, + ) + # check default for initial number of training perturbations + assert len(sl_dict["full_unc_history"][0]) == 2 From f505f013013c12862a161edcda421b9f7aeb27b5 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 14 Apr 2021 19:10:16 -0400 Subject: [PATCH 057/239] predictor now returns corrections directly --- src/autocat/learning/predictors.py | 16 +++++++++------- tests/learning/test_predictors.py | 10 ++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 707980d6..80854b85 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -340,8 +340,8 @@ def predict( Returns ------- - predicted_correction_matrix: - Matrix of predicted corrections that were applied + predicted_corrections: + List of corrections to be applied to each input structure corrected_structure: Atoms object with corrections applied @@ -359,7 +359,7 @@ def predict( adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - predicted_correction_matrix, unc = self.regressor.predict( + predicted_correction_matrix_full, unc = self.regressor.predict( featurized_input, return_std=True ) @@ -368,15 +368,17 @@ def predict( ] corrected_structures = [] + predicted_corrections = [] for idx, struct in enumerate(initial_structure_guesses): cs = struct.copy() list_of_adsorbate_indices = np.where(struct.get_tags() <= 0)[0].tolist() list_of_adsorbate_indices.sort() num_of_adsorbates = len(list_of_adsorbate_indices) - corr = predicted_correction_matrix[idx, : 3 * num_of_adsorbates].reshape( - num_of_adsorbates, 3 - ) + corr = predicted_correction_matrix_full[ + idx, : 3 * num_of_adsorbates + ].reshape(num_of_adsorbates, 3) + predicted_corrections.append(corr) cs.positions[list_of_adsorbate_indices] += corr corrected_structures.append(cs) - return predicted_correction_matrix, corrected_structures, unc + return predicted_corrections, corrected_structures, unc diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 23db1e56..940191a6 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -60,19 +60,17 @@ def test_predict_initial_configuration_formats(): acsc.fit( p_structures[:15], collected_matrices=collected_matrices[:15, :], ) - predicted_correction_matrix, corrected_structures, uncs = acsc.predict( - p_structures[15:], - ) + predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) assert isinstance(corrected_structures[0], Atoms) assert len(corrected_structures) == 5 # check that even with refining, corrected structure is # returned to full size assert len(corrected_structures[2]) == len(p_structures[17]) - assert isinstance(predicted_correction_matrix, np.ndarray) - assert predicted_correction_matrix.shape[0] == 5 + assert len(predicted_corrections) == 5 # check that predicted correction matrix is applied correctly manual = p_structures[15].copy() - manual_corr_mat = predicted_correction_matrix[0].reshape(2, 3) + manual_corr_mat = predicted_corrections[0] + assert predicted_corrections[0].shape == (2, 3) manual.positions[-1] += manual_corr_mat[-1] manual.positions[-2] += manual_corr_mat[-2] assert np.allclose(manual.positions, corrected_structures[0].positions) From 39e7017cf0f6b6b11056d8acdc24624594a683de Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Apr 2021 14:56:28 -0400 Subject: [PATCH 058/239] return corr list, rename matr to corrected_matrix --- src/autocat/perturbations.py | 27 +++++++++++++-------------- tests/test_perturbations.py | 26 +++++++++++++++----------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 7f421c96..b99fd907 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -90,7 +90,7 @@ def generate_perturbed_dataset( perturbed_dict = {} - collected_matrices = [] + corrections_list = [] collected_structure_paths = [] collected_structures = [] @@ -118,7 +118,7 @@ def generate_perturbed_dataset( maximum_perturbation_distance=maximum_perturbation_distance, ) # keeps flattened atomic coordinates difference vector - collected_matrices.append( + corrections_list.append( perturbed_dict[name][str(i)]["perturbation_matrix"].flatten() ) @@ -164,27 +164,26 @@ def generate_perturbed_dataset( # factor of 3 from flattening (ie. x,y,z) largest_size = 3 * maximum_adsorbate_size # ensures correct sized padding - collected_matrices_array = np.zeros((len(collected_matrices), largest_size)) + corrections_matrix = np.zeros((len(corrections_list), largest_size)) # substitute in collected matrices for each row - for idx, row in enumerate(collected_matrices): - collected_matrices_array[idx, : len(row)] = row + for idx, row in enumerate(corrections_list): + corrections_matrix[idx, : len(row)] = row # adds collected matrices to dict that will be returned - perturbed_dict["collected_matrices"] = collected_matrices_array - collected_matrices_path = None + perturbed_dict["correction_matrix"] = corrections_matrix + correction_matrix_path = None # write matrix to disk as json if desired if write_to_disk: - collected_matrices_path = os.path.join( - write_location, "collected_matrices.json" - ) - coll = perturbed_dict["collected_matrices"].tolist() - with open(collected_matrices_path, "w") as f: + correction_matrix_path = os.path.join(write_location, "correction_matrix.json") + coll = perturbed_dict["correction_matrix"].tolist() + with open(correction_matrix_path, "w") as f: json.dump(coll, f) - print(f"Collected matrices written to {collected_matrices_path}") + print(f"Correction matrix written to {correction_matrix_path}") # update output dict with collected structures and paths perturbed_dict.update( { - "collected_matrices_path": collected_matrices_path, + "correction_matrix_path": correction_matrix_path, + "corrections_list": corrections_list, "collected_structures": collected_structures, "collected_structure_paths": collected_structure_paths, } diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 07b8a670..9393315c 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -97,12 +97,12 @@ def test_generate_perturbed_dataset_write_location(): os.path.join(_tmp_dir, "HOPt36_0/0/perturbation_matrix.json"), ) assert os.path.samefile( - p_set["collected_matrices_path"], - os.path.join(_tmp_dir, "collected_matrices.json"), + p_set["correction_matrix_path"], + os.path.join(_tmp_dir, "correction_matrix.json"), ) -def test_generate_perturbed_dataset_collected_matrices(): +def test_generate_perturbed_dataset_correction_matrix(): # Check that matrices properly collected for 1 base_structure sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" @@ -113,16 +113,16 @@ def test_generate_perturbed_dataset_collected_matrices(): for idx, struct in enumerate(p_set["HOPt36_0"]): flat = p_set["HOPt36_0"][struct]["perturbation_matrix"].flatten() manual_collect[idx, : len(flat)] = flat - assert np.allclose(p_set["collected_matrices"], manual_collect) - assert p_set["collected_matrices"].shape == (5, 6) + assert np.allclose(p_set["correction_matrix"], manual_collect) + assert p_set["correction_matrix"].shape == (5, 6) # Check when given specific maximum_structure_size p_set = generate_perturbed_dataset( [base_struct], num_of_perturbations=5, maximum_adsorbate_size=15, ) - assert p_set["collected_matrices"].shape == (5, 45) + assert p_set["correction_matrix"].shape == (5, 45) -def test_generate_perturbed_dataset_collected_matrices_multiple(): +def test_generate_perturbed_dataset_correction_matrix_multiple(): # check that matrices properly collected for 2 base structures of == size sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" @@ -143,8 +143,8 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): flat = p_set[base][struct]["perturbation_matrix"].flatten() manual_collect[counter, : len(flat)] = flat counter += 1 - assert np.allclose(p_set["collected_matrices"], manual_collect) - assert p_set["collected_matrices"].shape == (10, 6) + assert np.allclose(p_set["correction_matrix"], manual_collect) + assert p_set["correction_matrix"].shape == (10, 6) # check that matrices properly collected for 2 base structures of != size sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ @@ -166,8 +166,12 @@ def test_generate_perturbed_dataset_collected_matrices_multiple(): manual_collect_array = np.zeros((10, 3)) for idx, m in enumerate(manual_collect): manual_collect_array[idx, : len(m)] = m - assert np.allclose(p_set["collected_matrices"], manual_collect_array) - assert p_set["collected_matrices"].shape == (10, 3) + assert np.allclose(p_set["correction_matrix"], manual_collect_array) + assert p_set["correction_matrix"].shape == (10, 3) + # check corrections list + corr_list = p_set["corrections_list"] + assert len(corr_list) == p_set["correction_matrix"].shape[0] + assert (corr_list[3] == p_set["correction_matrix"][3, : len(corr_list[3])]).all() def test_generate_perturbed_dataset_collected_structure_paths(): From 109ab5a4f072316e8f7f8b8d0b984f38f25d5a04 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Apr 2021 15:26:55 -0400 Subject: [PATCH 059/239] allow either corrections matr or list for fitting --- src/autocat/learning/predictors.py | 26 ++++++++++++++++++++++---- tests/learning/test_predictors.py | 20 ++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 80854b85..793d5f0b 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -13,6 +13,10 @@ from autocat.learning.featurizers import _get_number_of_features +class AutocatStructureCorrectorError(Exception): + pass + + class AutoCatStructureCorrector: def __init__( self, @@ -254,7 +258,8 @@ def get_total_number_of_features(self): def fit( self, perturbed_structures: List[Union[Atoms, str]], - collected_matrices: np.ndarray, + corrections_list: List[np.ndarray] = None, + correction_matrix: np.ndarray = None, ): """ Given a list of perturbed structures @@ -266,7 +271,7 @@ def fit( perturbed_structures: List of perturbed structures to be trained upon - collected_matrices: + correction_matrix: Numpy array of collected matrices of perturbations corresponding to each of the perturbed structures. This can be generated via `autocat.perturbations.generate_perturbed_dataset`. @@ -290,8 +295,6 @@ def fit( adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - self.regressor.fit(X, collected_matrices) - if self.maximum_structure_size is None: if self.refine_structures: ref_structures = [ @@ -321,6 +324,21 @@ def fit( ] species_list.extend(new_species) self.species_list = species_list + + if corrections_list is not None: + correction_matrix = np.zeros( + (len(corrections_list), self.maximum_adsorbate_size) + ) + for idx, row in enumerate(corrections_list): + correction_matrix[idx, : len(row)] = row + elif correction_matrix is not None: + pass + else: + msg = "Must specify either corrections list or matrix" + raise AutocatStructureCorrectorError(msg) + + self.regressor.fit(X, correction_matrix) + self.is_fit = True def predict( diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 940191a6..b51b2f7e 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -15,6 +15,7 @@ from autocat.learning.featurizers import get_X from autocat.learning.featurizers import catalyst_featurization from autocat.learning.predictors import AutoCatStructureCorrector +from autocat.learning.predictors import AutocatStructureCorrectorError def test_fit_model_on_perturbed_systems(): @@ -25,14 +26,14 @@ def test_fit_model_on_perturbed_systems(): base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) p_structures = p_set["collected_structures"] - collected_matrices = p_set["collected_matrices"] + correction_matrix = p_set["correction_matrix"] acsc = AutoCatStructureCorrector( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures, collected_matrices=collected_matrices, + p_structures, correction_matrix=correction_matrix, ) assert acsc.adsorbate_featurizer == "soap" assert acsc.is_fit @@ -42,6 +43,17 @@ def test_fit_model_on_perturbed_systems(): assert acsc.adsorbate_featurizer == "acsf" assert not acsc.is_fit + # check can be fit on corrections list + p_set["corrections_list"] + acsc.adsorbate_featurizer = "soap" + acsc.adsorbate_featurization_kwargs = {"rcut": 3.0, "nmax": 6, "lmax": 6} + acsc.fit( + p_structures, correction_matrix=correction_matrix, + ) + assert acsc.is_fit + with pytest.raises(AutocatStructureCorrectorError): + acsc.fit(p_structures) + def test_predict_initial_configuration_formats(): # Test outputs are returned as expected @@ -51,14 +63,14 @@ def test_predict_initial_configuration_formats(): base_struct = place_adsorbate(sub, "CO")["custom"]["structure"] p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] - collected_matrices = p_set["collected_matrices"] + correction_matrix = p_set["correction_matrix"] acsc = AutoCatStructureCorrector( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures[:15], collected_matrices=collected_matrices[:15, :], + p_structures[:15], correction_matrix=correction_matrix[:15, :], ) predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) assert isinstance(corrected_structures[0], Atoms) From c90bbefbf3dfeb3b3ccb252b656fd938d434437e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Apr 2021 15:53:06 -0400 Subject: [PATCH 060/239] keep real vals, fix corr matr from corr list --- src/autocat/learning/predictors.py | 2 +- src/autocat/learning/sequential.py | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 793d5f0b..f6f6e966 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -327,7 +327,7 @@ def fit( if corrections_list is not None: correction_matrix = np.zeros( - (len(corrections_list), self.maximum_adsorbate_size) + (len(corrections_list), 3 * self.maximum_adsorbate_size) ) for idx, row in enumerate(corrections_list): correction_matrix[idx, : len(row)] = row diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index acdf868d..54c3d083 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -72,13 +72,16 @@ def simulated_sequential_learning( ) train_pert_structures = initial_pert_dataset["collected_structures"] - train_pert_coll_matr = initial_pert_dataset["collected_matrices"] + train_pert_corr_list = initial_pert_dataset["corrections_list"] - structure_corrector.fit(train_pert_structures, train_pert_coll_matr) + structure_corrector.fit( + train_pert_structures, corrections_list=train_pert_corr_list + ) full_unc_history = [] max_unc_history = [] - pred_corr_matr_history = [] + pred_corrs_history = [] + real_corrs_history = [] while len(max_unc_history) < number_of_sl_loops: # generate new perturbations to predict on new_perturbations = generate_perturbed_dataset( @@ -88,14 +91,14 @@ def simulated_sequential_learning( minimum_perturbation_distance=minimum_perturbation_distance, ) new_pert_structs = new_perturbations["collected_structures"] - new_pert_coll_matr = new_perturbations["collected_matrices"] + new_pert_corr_list = new_perturbations["corrections_list"] # make predictions - _pred_corr_matr, _pred_corr_structs, _uncs = structure_corrector.predict( + _pred_corrs, _pred_corr_structs, _uncs = structure_corrector.predict( new_pert_structs ) full_unc_history.append(_uncs) - pred_corr_matr_history.append(_pred_corr_matr) + pred_corrs_history.append(_pred_corrs) # find candidate with highest uncertainty high_unc_idx = np.argmax(_uncs) @@ -104,13 +107,14 @@ def simulated_sequential_learning( next_candidate_struct = new_pert_structs[high_unc_idx] # add new perturbed struct to training set train_pert_structures.append(next_candidate_struct) - train_pert_coll_matr = np.concatenate( - (train_pert_coll_matr, new_pert_coll_matr[high_unc_idx].reshape(1, -1)), - axis=0, + train_pert_corr_list.append(new_pert_corr_list[high_unc_idx],) + real_corrs_history.append(new_pert_corr_list[high_unc_idx]) + structure_corrector.fit( + train_pert_structures, corrections_list=train_pert_corr_list ) - structure_corrector.fit(train_pert_structures, train_pert_coll_matr) return { "max_unc_history": max_unc_history, "full_unc_history": full_unc_history, - "pred_corr_matr_history": pred_corr_matr_history, + "pred_corrs_history": pred_corrs_history, + "real_corrs_history": real_corrs_history, } From f209d39578256f137d6de2df94f28777cc31d939 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Apr 2021 17:15:02 -0400 Subject: [PATCH 061/239] each corr list item now shape (num_ads_atoms, 3) --- src/autocat/learning/predictors.py | 9 ++++++--- src/autocat/perturbations.py | 6 ++---- tests/test_perturbations.py | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index f6f6e966..86f83768 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -330,7 +330,7 @@ def fit( (len(corrections_list), 3 * self.maximum_adsorbate_size) ) for idx, row in enumerate(corrections_list): - correction_matrix[idx, : len(row)] = row + correction_matrix[idx, : 3 * len(row)] = row.flatten() elif correction_matrix is not None: pass else: @@ -361,8 +361,11 @@ def predict( predicted_corrections: List of corrections to be applied to each input structure - corrected_structure: - Atoms object with corrections applied + corrected_structures: + List of Atoms object with corrections applied + + unc: + List of uncertainties for each prediction """ assert self.is_fit diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index b99fd907..0101b983 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -118,9 +118,7 @@ def generate_perturbed_dataset( maximum_perturbation_distance=maximum_perturbation_distance, ) # keeps flattened atomic coordinates difference vector - corrections_list.append( - perturbed_dict[name][str(i)]["perturbation_matrix"].flatten() - ) + corrections_list.append(perturbed_dict[name][str(i)]["perturbation_matrix"]) traj_file_path = None pert_mat_file_path = None @@ -167,7 +165,7 @@ def generate_perturbed_dataset( corrections_matrix = np.zeros((len(corrections_list), largest_size)) # substitute in collected matrices for each row for idx, row in enumerate(corrections_list): - corrections_matrix[idx, : len(row)] = row + corrections_matrix[idx, : 3 * len(row)] = row.flatten() # adds collected matrices to dict that will be returned perturbed_dict["correction_matrix"] = corrections_matrix diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 9393315c..3e054e83 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -171,7 +171,7 @@ def test_generate_perturbed_dataset_correction_matrix_multiple(): # check corrections list corr_list = p_set["corrections_list"] assert len(corr_list) == p_set["correction_matrix"].shape[0] - assert (corr_list[3] == p_set["correction_matrix"][3, : len(corr_list[3])]).all() + assert (corr_list[3] == p_set["correction_matrix"][3, : len(corr_list[3][0])]).all() def test_generate_perturbed_dataset_collected_structure_paths(): From 2e775e0a8fba5422505ff5bc6e83a91c8f660a94 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Apr 2021 17:55:57 -0400 Subject: [PATCH 062/239] add score method to predictor --- src/autocat/learning/predictors.py | 60 ++++++++++++++++++++++++++++++ tests/learning/test_predictors.py | 26 +++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 86f83768..dc191d64 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -403,3 +403,63 @@ def predict( corrected_structures.append(cs) return predicted_corrections, corrected_structures, unc + + def score( + self, + test_structure_guesses: List[Atoms], + corrections_list: List[np.ndarray], + metric="mae", + ): + """ + Returns a prediction score given the actual corrections. + + Parameters + ---------- + + test_structure_guesses: + List of Atoms objects of structures to be tested o + + corrections_list: + List of actual corrections as `np.arrays` + + metric: + How the performance metric should be calculated + Options: + - mae: average of the average norm displacement vector difference + - rmse: square root of the average of the average norm displacement + vector difference squared + + Returns + ------- + + score: + Float of calculated test score on the given data + """ + assert self.is_fit + + pred_corr, _, _ = self.predict(test_structure_guesses) + + if metric == "mae": + all_abs_vec_diff = [] + for i in range(len(pred_corr)): + assert pred_corr[i].shape == corrections_list[i].shape + print(np.linalg.norm(corrections_list[i] - pred_corr[i])) + N_i = len(pred_corr[i]) + abs_vec_diff = np.sum( + np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) + ) + all_abs_vec_diff.append(abs_vec_diff / N_i) + return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff) + elif metric == "rmse": + all_sq_vec_diff = [] + for i in range(len(pred_corr)): + assert pred_corr[i].shape == corrections_list[i].shape + N_i = len(pred_corr[i]) + sq_vec_diff = np.sum( + np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) ** 2 + ) + all_sq_vec_diff.append(sq_vec_diff / N_i) + return np.sum(all_sq_vec_diff) / len(all_sq_vec_diff) + else: + msg = f"Metric: {metric} is not supported" + raise AutocatStructureCorrectorError(msg) diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index b51b2f7e..8ca64ed6 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -90,6 +90,32 @@ def test_predict_initial_configuration_formats(): assert len(uncs) == 5 +def test_score_on_perturbed_systems(): + # Tests that the score metric yields floats + sub = generate_surface_structures(["Fe"], facets={"Fe": ["100"]})["Fe"]["bcc100"][ + "structure" + ] + base_struct = place_adsorbate(sub, "CO")["custom"]["structure"] + p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) + p_structures = p_set["collected_structures"] + correction_matrix = p_set["correction_matrix"] + corrections_list = p_set["corrections_list"] + acsc = AutoCatStructureCorrector( + structure_featurizer="sine_matrix", + adsorbate_featurizer="soap", + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + ) + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) + mae = acsc.score(p_structures[15:], corrections_list) + assert isinstance(mae, float) + rmse = acsc.score(p_structures[15:], corrections_list, metric="rmse") + assert mae != rmse + with pytest.raises(AutocatStructureCorrectorError): + acsc.score(p_structures[15:], corrections_list, metric="msd") + + def test_model_class_and_kwargs(): # Tests providing regression model class and kwargs acsc = AutoCatStructureCorrector(KernelRidge, model_kwargs={"gamma": 0.5}) From 3929349a4061a526a7c06908900572c59cf6a8d1 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Apr 2021 18:13:24 -0400 Subject: [PATCH 063/239] collect sl scores, update pert mag defaults --- src/autocat/learning/sequential.py | 23 +++++++++++++++++++---- src/autocat/perturbations.py | 8 ++++---- tests/learning/test_sequential.py | 4 ++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 54c3d083..60db02a4 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -15,8 +15,8 @@ def simulated_sequential_learning( structure_corrector: AutoCatStructureCorrector, base_structures: List[Atoms], - minimum_perturbation_distance: float = 0.1, - maximum_perturbation_distance: float = 1.0, + minimum_perturbation_distance: float = 0.01, + maximum_perturbation_distance: float = 0.75, initial_num_of_perturbations_per_base_structure: int = None, batch_num_of_perturbations_per_base_structure: int = 2, number_of_sl_loops: int = 100, @@ -38,11 +38,11 @@ def simulated_sequential_learning( minimum_perturbation_distance: Float of minimum acceptable perturbation distance - Default: 0.1 Angstrom + Default: 0.01 Angstrom maximum_perturbation_distance: Float of maximum acceptable perturbation distance - Default: 1.0 Angstrom + Default: 0.75 Angstrom initial_num_of_perturbations_per_base_structure: Integer giving the number of perturbations to generate initially @@ -82,6 +82,8 @@ def simulated_sequential_learning( max_unc_history = [] pred_corrs_history = [] real_corrs_history = [] + mae_history = [] + rmse_history = [] while len(max_unc_history) < number_of_sl_loops: # generate new perturbations to predict on new_perturbations = generate_perturbed_dataset( @@ -97,6 +99,17 @@ def simulated_sequential_learning( _pred_corrs, _pred_corr_structs, _uncs = structure_corrector.predict( new_pert_structs ) + + # get scores on new perturbations + mae_history.append( + structure_corrector.score(new_pert_structs, new_pert_corr_list) + ) + rmse_history.append( + structure_corrector.score( + new_pert_structs, new_pert_corr_list, metric="rmse" + ) + ) + full_unc_history.append(_uncs) pred_corrs_history.append(_pred_corrs) @@ -117,4 +130,6 @@ def simulated_sequential_learning( "full_unc_history": full_unc_history, "pred_corrs_history": pred_corrs_history, "real_corrs_history": real_corrs_history, + "mae_history": mae_history, + "rmse_history": rmse_history, } diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 0101b983..45f93cac 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -14,8 +14,8 @@ class AutocatPerturbationError(Exception): def generate_perturbed_dataset( base_structures: List[Atoms], - minimum_perturbation_distance: float = 0.1, - maximum_perturbation_distance: float = 1.0, + minimum_perturbation_distance: float = 0.01, + maximum_perturbation_distance: float = 0.75, maximum_adsorbate_size: int = None, num_of_perturbations: int = 10, write_to_disk: bool = False, @@ -47,11 +47,11 @@ def generate_perturbed_dataset( minimum_perturbation_distance: Float of minimum acceptable perturbation distance - Default: 0.1 Angstrom + Default: 0.01 Angstrom maximum_perturbation_distance: Float of maximum acceptable perturbation distance - Default: 1.0 Angstrom + Default: 0.75 Angstrom maximum_adsorbate_size: Integer giving the largest number of atoms in an adsorbate diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 6d040c64..8bd8a1f9 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -39,3 +39,7 @@ def test_simulated_sequential_outputs(): ) # check default for initial number of training perturbations assert len(sl_dict["full_unc_history"][0]) == 2 + + # check all mae and rmse scores collected + assert len(sl_dict["mae_history"]) == 4 + assert len(sl_dict["rmse_history"]) == 4 From 6596bdb8a37993cbd02d62508716e89be262241d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Apr 2021 18:36:22 -0400 Subject: [PATCH 064/239] expand docstrings, rm print statement from debug --- src/autocat/learning/predictors.py | 5 ++++- src/autocat/learning/sequential.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index dc191d64..68cc201b 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -277,6 +277,10 @@ def fit( This can be generated via `autocat.perturbations.generate_perturbed_dataset`. Shape should be (# of structures, 3 * # of atoms in the largest structure) + correction_list: + List of np.arrays of correction vectors + where each item is of shape (# of adsorbate atoms, 3) + Returns ------- @@ -443,7 +447,6 @@ def score( all_abs_vec_diff = [] for i in range(len(pred_corr)): assert pred_corr[i].shape == corrections_list[i].shape - print(np.linalg.norm(corrections_list[i] - pred_corr[i])) N_i = len(pred_corr[i]) abs_vec_diff = np.sum( np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 60db02a4..83889d65 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -58,6 +58,16 @@ def simulated_sequential_learning( Returns ------- + + sl_dict: + Dictionary containing histories of different quantities throughout + the calculation: + - maximum uncertainty history + - full uncertainty history + - predicted corrections history + - real corrections history + - mae history + - rmse history """ if initial_num_of_perturbations_per_base_structure is None: initial_num_of_perturbations_per_base_structure = ( From afc8ac0b5c4a16020c6715156f5476f0ccf3169b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 16 Apr 2021 12:41:50 -0400 Subject: [PATCH 065/239] adds writing sl run data to disk --- src/autocat/learning/sequential.py | 32 +++++++++++++++++++++++++----- tests/learning/test_sequential.py | 28 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 83889d65..bc0ba6f0 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -1,4 +1,6 @@ import numpy as np +import os +import json from typing import List from typing import Dict @@ -20,6 +22,8 @@ def simulated_sequential_learning( initial_num_of_perturbations_per_base_structure: int = None, batch_num_of_perturbations_per_base_structure: int = 2, number_of_sl_loops: int = 100, + write_to_disk: bool = False, + write_location: str = ".", ): """ Conducts a simulated sequential learning loop given @@ -56,6 +60,13 @@ def simulated_sequential_learning( number_of_sl_loops: Integer specifying the number of sequential learning loops to be conducted + write_to_disk: + Boolean specifying whether X should be written to disk as a json. + Defaults to False. + + write_location: + String with the location where X should be written to disk. + Returns ------- @@ -121,7 +132,8 @@ def simulated_sequential_learning( ) full_unc_history.append(_uncs) - pred_corrs_history.append(_pred_corrs) + # keeps as lists to make writing to disk easier + pred_corrs_history.append([p.tolist() for p in _pred_corrs]) # find candidate with highest uncertainty high_unc_idx = np.argmax(_uncs) @@ -135,11 +147,21 @@ def simulated_sequential_learning( structure_corrector.fit( train_pert_structures, corrections_list=train_pert_corr_list ) - return { - "max_unc_history": max_unc_history, - "full_unc_history": full_unc_history, + sl_dict = { + "max_unc_history": [mu.tolist() for mu in max_unc_history], + "full_unc_history": [unc.tolist() for unc in full_unc_history], "pred_corrs_history": pred_corrs_history, - "real_corrs_history": real_corrs_history, + "real_corrs_history": [r.tolist() for r in real_corrs_history], "mae_history": mae_history, "rmse_history": rmse_history, } + + if write_to_disk: + if not os.path.isdir(write_location): + os.makedirs(write_location) + write_path = os.path.join(write_location, "sl_dict.json") + with open(write_path, "w") as f: + json.dump(sl_dict, f) + print(f"SL dictionary written to {write_path}") + + return sl_dict diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 8bd8a1f9..6583eedd 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -3,6 +3,9 @@ import os import pytest import numpy as np +import json + +import tempfile from autocat.learning.predictors import AutoCatStructureCorrector from autocat.learning.sequential import simulated_sequential_learning @@ -43,3 +46,28 @@ def test_simulated_sequential_outputs(): # check all mae and rmse scores collected assert len(sl_dict["mae_history"]) == 4 assert len(sl_dict["rmse_history"]) == 4 + + +def test_simulated_sequential_write_to_disk(): + # Test writing out sl dict + _tmp_dir = tempfile.TemporaryDirectory().name + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2], + batch_num_of_perturbations_per_base_structure=1, + number_of_sl_loops=1, + write_to_disk=True, + write_location=_tmp_dir, + ) + with open(os.path.join(_tmp_dir, "sl_dict.json"), "r") as f: + sl_written = json.load(f) + assert sl_dict == sl_written From 94d51ce1ed6aa35f782e0387f01e60e469082f29 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 16 Apr 2021 13:31:55 -0400 Subject: [PATCH 066/239] allows giving testing base structures --- src/autocat/learning/sequential.py | 55 +++++++++++++++++++++++------- tests/learning/test_sequential.py | 35 +++++++++++++++++-- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index bc0ba6f0..fd517213 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -16,7 +16,8 @@ def simulated_sequential_learning( structure_corrector: AutoCatStructureCorrector, - base_structures: List[Atoms], + training_base_structures: List[Atoms], + testing_base_structures: List[Atoms] = None, minimum_perturbation_distance: float = 0.01, maximum_perturbation_distance: float = 0.75, initial_num_of_perturbations_per_base_structure: int = None, @@ -36,9 +37,12 @@ def simulated_sequential_learning( structure_corrector: AutoCatStructureCorrector object to be used for fitting and prediction - base_structures: + training_base_structures: List of Atoms objects for all base structures to be perturbed for training - and testing + and candidate selection upon each loop + + testing_base_structures: + List of Atoms objects for base structures that are minimum_perturbation_distance: Float of minimum acceptable perturbation distance @@ -86,7 +90,7 @@ def simulated_sequential_learning( ) initial_pert_dataset = generate_perturbed_dataset( - base_structures, + training_base_structures, num_of_perturbations=initial_num_of_perturbations_per_base_structure, maximum_perturbation_distance=maximum_perturbation_distance, minimum_perturbation_distance=minimum_perturbation_distance, @@ -99,16 +103,28 @@ def simulated_sequential_learning( train_pert_structures, corrections_list=train_pert_corr_list ) + if testing_base_structures is not None: + test_pert_dataset = generate_perturbed_dataset( + training_base_structures, + num_of_perturbations=initial_num_of_perturbations_per_base_structure, + maximum_perturbation_distance=maximum_perturbation_distance, + minimum_perturbation_distance=minimum_perturbation_distance, + ) + test_pert_structures = test_pert_dataset["collected_structures"] + test_pert_corr_list = test_pert_dataset["corrections_list"] + full_unc_history = [] max_unc_history = [] pred_corrs_history = [] real_corrs_history = [] - mae_history = [] - rmse_history = [] + mae_train_history = [] + rmse_train_history = [] + mae_test_history = [] + rmse_test_history = [] while len(max_unc_history) < number_of_sl_loops: # generate new perturbations to predict on new_perturbations = generate_perturbed_dataset( - base_structures, + training_base_structures, num_of_perturbations=batch_num_of_perturbations_per_base_structure, maximum_perturbation_distance=maximum_perturbation_distance, minimum_perturbation_distance=minimum_perturbation_distance, @@ -121,16 +137,27 @@ def simulated_sequential_learning( new_pert_structs ) - # get scores on new perturbations - mae_history.append( + # get scores on new perturbations (training) + mae_train_history.append( structure_corrector.score(new_pert_structs, new_pert_corr_list) ) - rmse_history.append( + rmse_train_history.append( structure_corrector.score( new_pert_structs, new_pert_corr_list, metric="rmse" ) ) + # get scores on test perturbations + if testing_base_structures is not None: + mae_test_history.append( + structure_corrector.score(test_pert_structures, test_pert_corr_list) + ) + rmse_test_history.append( + structure_corrector.score( + test_pert_structures, test_pert_corr_list, metric="rmse" + ) + ) + full_unc_history.append(_uncs) # keeps as lists to make writing to disk easier pred_corrs_history.append([p.tolist() for p in _pred_corrs]) @@ -152,10 +179,14 @@ def simulated_sequential_learning( "full_unc_history": [unc.tolist() for unc in full_unc_history], "pred_corrs_history": pred_corrs_history, "real_corrs_history": [r.tolist() for r in real_corrs_history], - "mae_history": mae_history, - "rmse_history": rmse_history, + "mae_train_history": mae_train_history, + "rmse_train_history": rmse_train_history, } + if testing_base_structures is not None: + sl_dict["mae_test_history"] = mae_test_history + sl_dict["rmse_test_history"] = rmse_test_history + if write_to_disk: if not os.path.isdir(write_location): os.makedirs(write_location) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 6583eedd..e88549d2 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -14,6 +14,7 @@ def test_simulated_sequential_outputs(): + # Test outputs without any testing structures sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] @@ -43,9 +44,35 @@ def test_simulated_sequential_outputs(): # check default for initial number of training perturbations assert len(sl_dict["full_unc_history"][0]) == 2 - # check all mae and rmse scores collected - assert len(sl_dict["mae_history"]) == 4 - assert len(sl_dict["rmse_history"]) == 4 + # check all mae and rmse training scores collected + assert len(sl_dict["mae_train_history"]) == 4 + assert len(sl_dict["rmse_train_history"]) == 4 + + +def test_simulated_sequential_outputs_testing(): + # Test with testing structures given + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] + acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2], + testing_base_structures=[base_struct3], + batch_num_of_perturbations_per_base_structure=3, + number_of_sl_loops=2, + ) + # Check lenght of testing scores + assert len(sl_dict["mae_test_history"]) == 2 + assert len(sl_dict["rmse_train_history"]) == 2 + assert len(sl_dict["mae_train_history"]) == 2 + assert sl_dict["mae_test_history"] != sl_dict["mae_train_history"] def test_simulated_sequential_write_to_disk(): @@ -59,10 +86,12 @@ def test_simulated_sequential_write_to_disk(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + base_struct3 = place_adsorbate(sub2, "N")["custom"]["structure"] acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") sl_dict = simulated_sequential_learning( acsc, [base_struct1, base_struct2], + testing_base_structures=[base_struct3], batch_num_of_perturbations_per_base_structure=1, number_of_sl_loops=1, write_to_disk=True, From 53b5752976cb66ff5976e9ffdc3276193fc5174b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 17 Apr 2021 17:56:00 -0400 Subject: [PATCH 067/239] bugfix: keeps only pred hist on max unc candidate --- src/autocat/learning/sequential.py | 7 +++++-- tests/learning/test_sequential.py | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index fd517213..03d67b4e 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -121,7 +121,10 @@ def simulated_sequential_learning( rmse_train_history = [] mae_test_history = [] rmse_test_history = [] + ctr = 0 while len(max_unc_history) < number_of_sl_loops: + ctr += 1 + print(f"Sequential Learning Iteration #{ctr}") # generate new perturbations to predict on new_perturbations = generate_perturbed_dataset( training_base_structures, @@ -159,8 +162,6 @@ def simulated_sequential_learning( ) full_unc_history.append(_uncs) - # keeps as lists to make writing to disk easier - pred_corrs_history.append([p.tolist() for p in _pred_corrs]) # find candidate with highest uncertainty high_unc_idx = np.argmax(_uncs) @@ -170,6 +171,8 @@ def simulated_sequential_learning( # add new perturbed struct to training set train_pert_structures.append(next_candidate_struct) train_pert_corr_list.append(new_pert_corr_list[high_unc_idx],) + # keeps as lists to make writing to disk easier + pred_corrs_history.append([p.tolist() for p in _pred_corrs[high_unc_idx]]) real_corrs_history.append(new_pert_corr_list[high_unc_idx]) structure_corrector.fit( train_pert_structures, corrections_list=train_pert_corr_list diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index e88549d2..e949d83f 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -48,6 +48,13 @@ def test_simulated_sequential_outputs(): assert len(sl_dict["mae_train_history"]) == 4 assert len(sl_dict["rmse_train_history"]) == 4 + # check prediction and correction history + assert len(sl_dict["pred_corrs_history"]) == len(sl_dict["real_corrs_history"]) + assert len(sl_dict["pred_corrs_history"][-1]) == len( + sl_dict["real_corrs_history"][-1] + ) + assert len(sl_dict["pred_corrs_history"][0]) == 2 + def test_simulated_sequential_outputs_testing(): # Test with testing structures given From 50f73f117c59064fdf0a11e4f57118966777c06e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 17 Apr 2021 21:44:30 -0400 Subject: [PATCH 068/239] allows picking N candidates with highest unc --- src/autocat/learning/sequential.py | 50 +++++++++++++++++++++++------- tests/learning/test_sequential.py | 40 +++++++++++++++++++++++- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 03d67b4e..2396cae8 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -14,6 +14,10 @@ Array = List[float] +class AutoCatSequentialLearningError(Exception): + pass + + def simulated_sequential_learning( structure_corrector: AutoCatStructureCorrector, training_base_structures: List[Atoms], @@ -22,6 +26,7 @@ def simulated_sequential_learning( maximum_perturbation_distance: float = 0.75, initial_num_of_perturbations_per_base_structure: int = None, batch_num_of_perturbations_per_base_structure: int = 2, + batch_size_to_add: int = 1, number_of_sl_loops: int = 100, write_to_disk: bool = False, write_location: str = ".", @@ -61,6 +66,12 @@ def simulated_sequential_learning( Integer giving the number of perturbations to generate on each loop when finding the next candidate + batch_size_to_add: + Integer giving the number of candidates to be added + to the training set on each loop. (ie. N candidates with + the highest uncertainties added for each iteration) + Default: 1 (ie. adds candidate with max unc on each loop) + number_of_sl_loops: Integer specifying the number of sequential learning loops to be conducted @@ -79,11 +90,24 @@ def simulated_sequential_learning( the calculation: - maximum uncertainty history - full uncertainty history - - predicted corrections history - - real corrections history - - mae history - - rmse history + - predicted corrections training history + - real corrections training history + - mae training history + - rmse training history + - mae testing history (if test structures given) + - rmse testing history (if test structures given) + For the corrections histories, the dimensions are as follows: + num of loops -> num of candidates added -> corrections applied """ + if ( + batch_num_of_perturbations_per_base_structure * len(training_base_structures) + < batch_size_to_add + ): + msg = ( + "Batch size to add must be greater than the number of candidates generated" + ) + raise AutoCatSequentialLearningError(msg) + if initial_num_of_perturbations_per_base_structure is None: initial_num_of_perturbations_per_base_structure = ( batch_num_of_perturbations_per_base_structure @@ -164,16 +188,20 @@ def simulated_sequential_learning( full_unc_history.append(_uncs) # find candidate with highest uncertainty - high_unc_idx = np.argmax(_uncs) + high_unc_idx = np.argsort(_uncs)[-batch_size_to_add:] max_unc_history.append(_uncs[high_unc_idx]) - next_candidate_struct = new_pert_structs[high_unc_idx] + next_candidate_struct = [new_pert_structs[idx] for idx in high_unc_idx] # add new perturbed struct to training set - train_pert_structures.append(next_candidate_struct) - train_pert_corr_list.append(new_pert_corr_list[high_unc_idx],) + train_pert_structures.extend(next_candidate_struct) + train_pert_corr_list.extend([new_pert_corr_list[idx] for idx in high_unc_idx],) # keeps as lists to make writing to disk easier - pred_corrs_history.append([p.tolist() for p in _pred_corrs[high_unc_idx]]) - real_corrs_history.append(new_pert_corr_list[high_unc_idx]) + pred_corrs_history.append( + [p.tolist() for p in [_pred_corrs[idx] for idx in high_unc_idx]] + ) + real_corrs_history.append( + [new_pert_corr_list[idx].tolist() for idx in high_unc_idx] + ) structure_corrector.fit( train_pert_structures, corrections_list=train_pert_corr_list ) @@ -181,7 +209,7 @@ def simulated_sequential_learning( "max_unc_history": [mu.tolist() for mu in max_unc_history], "full_unc_history": [unc.tolist() for unc in full_unc_history], "pred_corrs_history": pred_corrs_history, - "real_corrs_history": [r.tolist() for r in real_corrs_history], + "real_corrs_history": [r for r in real_corrs_history], "mae_train_history": mae_train_history, "rmse_train_history": rmse_train_history, } diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index e949d83f..54ed6fd0 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -53,7 +53,45 @@ def test_simulated_sequential_outputs(): assert len(sl_dict["pred_corrs_history"][-1]) == len( sl_dict["real_corrs_history"][-1] ) - assert len(sl_dict["pred_corrs_history"][0]) == 2 + assert len(sl_dict["pred_corrs_history"]) == 4 + assert len(sl_dict["real_corrs_history"]) == 4 + + +def test_simulated_sequential_batch_added(): + # Tests adding N candidates on each loop + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + bsta = 2 + num_loops = 3 + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2], + batch_num_of_perturbations_per_base_structure=4, + batch_size_to_add=bsta, + number_of_sl_loops=num_loops, + ) + # each max unc history should be equal to number of candidates added + assert len(sl_dict["max_unc_history"][0]) == bsta + # first dimension is number of loops + assert len(sl_dict["pred_corrs_history"]) == num_loops + # next dimension is size of candidates added on each loop + assert len(sl_dict["pred_corrs_history"][0]) == bsta + # next dimension is size of adsorbate + assert len(sl_dict["pred_corrs_history"][0][0]) == 2 + # next dimension is vector correction to atom in adsorbate + assert len(sl_dict["pred_corrs_history"][0][0][0]) == 3 + # check same holds for real history + assert len(sl_dict["real_corrs_history"]) == num_loops + assert len(sl_dict["real_corrs_history"][0]) == bsta + assert len(sl_dict["real_corrs_history"][0][0]) == 2 + assert len(sl_dict["real_corrs_history"][0][0][0]) == 3 def test_simulated_sequential_outputs_testing(): From 3123c9fc7a20e6e70ff7b0dcbcb7ef63519cfe8a Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 18 Apr 2021 17:03:15 -0400 Subject: [PATCH 069/239] fix testset to use test structs,add validation set --- src/autocat/learning/sequential.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 2396cae8..b44aa289 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -129,7 +129,7 @@ def simulated_sequential_learning( if testing_base_structures is not None: test_pert_dataset = generate_perturbed_dataset( - training_base_structures, + testing_base_structures, num_of_perturbations=initial_num_of_perturbations_per_base_structure, maximum_perturbation_distance=maximum_perturbation_distance, minimum_perturbation_distance=minimum_perturbation_distance, @@ -137,6 +137,15 @@ def simulated_sequential_learning( test_pert_structures = test_pert_dataset["collected_structures"] test_pert_corr_list = test_pert_dataset["corrections_list"] + validation_pert_dataset = generate_perturbed_dataset( + training_base_structures, + num_of_perturbations=initial_num_of_perturbations_per_base_structure, + maximum_perturbation_distance=maximum_perturbation_distance, + minimum_perturbation_distance=minimum_perturbation_distance, + ) + validation_pert_structures = validation_pert_dataset["collected_structures"] + validation_pert_corr_list = validation_pert_dataset["corrections_list"] + full_unc_history = [] max_unc_history = [] pred_corrs_history = [] @@ -166,11 +175,13 @@ def simulated_sequential_learning( # get scores on new perturbations (training) mae_train_history.append( - structure_corrector.score(new_pert_structs, new_pert_corr_list) + structure_corrector.score( + validation_pert_structures, validation_pert_corr_list + ) ) rmse_train_history.append( structure_corrector.score( - new_pert_structs, new_pert_corr_list, metric="rmse" + validation_pert_structures, validation_pert_corr_list, metric="rmse" ) ) From a921c4339d3ebb9a4d34bbbafcb35638b0915058 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 19 Apr 2021 17:14:45 -0400 Subject: [PATCH 070/239] adds multi-sl --- src/autocat/learning/sequential.py | 79 +++++++++++++++++++++++++++++- tests/learning/test_sequential.py | 62 +++++++++++++++++++++++ 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index b44aa289..707ea1bf 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -1,6 +1,7 @@ import numpy as np import os import json +from joblib import Parallel, delayed from typing import List from typing import Dict @@ -18,6 +19,80 @@ class AutoCatSequentialLearningError(Exception): pass +def multiple_sequential_learning_runs( + structure_corrector: AutoCatStructureCorrector, + training_base_structures: List[Atoms], + number_of_runs: int = 5, + number_parallel_jobs: int = None, + write_to_disk: bool = False, + write_location: str = ".", + **sl_kwargs, +): + """ + Conducts multiple sequential learning runs + + Parameters + ---------- + + structure_corrector: + AutoCatStructureCorrector object to be used for fitting and prediction + + training_base_structures: + List of Atoms objects for all base structures to be perturbed for training + and candidate selection upon each loop + + number_of_runs: + Integer of number of runs to be done + + number_parallel_jobs: + Integer giving the number of cores to be paralellized across + using `joblib` + + write_to_disk: + Boolean specifying whether runs history should be written to disk as a json. + Defaults to False. + + write_location: + String with the location where runs history should be written to disk. + + Returns + ------- + + run_history: + List of dictionaries generated for each run containing info + about that run such as mae history, rmse history, etc.. + """ + if number_parallel_jobs is not None: + runs_history = Parallel(n_jobs=number_parallel_jobs)( + delayed(simulated_sequential_learning)( + structure_corrector=structure_corrector, + training_base_structures=training_base_structures, + **sl_kwargs, + ) + for i in range(number_of_runs) + ) + + else: + runs_history = [ + simulated_sequential_learning( + structure_corrector=structure_corrector, + training_base_structures=training_base_structures, + **sl_kwargs, + ) + for i in range(number_of_runs) + ] + + if write_to_disk: + if not os.path.isdir(write_location): + os.makedirs(write_location) + write_path = os.path.join(write_location, "sl_runs_history.json") + with open(write_path, "w") as f: + json.dump(runs_history, f) + print(f"SL histories written to {write_path}") + + return runs_history + + def simulated_sequential_learning( structure_corrector: AutoCatStructureCorrector, training_base_structures: List[Atoms], @@ -76,11 +151,11 @@ def simulated_sequential_learning( Integer specifying the number of sequential learning loops to be conducted write_to_disk: - Boolean specifying whether X should be written to disk as a json. + Boolean specifying whether the sl dictionary should be written to disk as a json. Defaults to False. write_location: - String with the location where X should be written to disk. + String with the location where sl_dict should be written to disk. Returns ------- diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 54ed6fd0..8ac54aff 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -9,6 +9,7 @@ from autocat.learning.predictors import AutoCatStructureCorrector from autocat.learning.sequential import simulated_sequential_learning +from autocat.learning.sequential import multiple_sequential_learning_runs from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate @@ -145,3 +146,64 @@ def test_simulated_sequential_write_to_disk(): with open(os.path.join(_tmp_dir, "sl_dict.json"), "r") as f: sl_written = json.load(f) assert sl_dict == sl_written + + +def test_multiple_sequential_learning_serial(): + # Tests serial implementation + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + runs_history = multiple_sequential_learning_runs( + acsc, + [base_struct1], + 3, + batch_num_of_perturbations_per_base_structure=1, + number_of_sl_loops=2, + ) + assert len(runs_history) == 3 + assert isinstance(runs_history[0], dict) + assert "mae_train_history" in runs_history[1] + + +def test_multiple_sequential_learning_parallel(): + # Tests parallel implementation + sub1 = generate_surface_structures(["Cu"], facets={"Cu": ["111"]})["Cu"]["fcc111"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "Li")["custom"]["structure"] + acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + runs_history = multiple_sequential_learning_runs( + acsc, + [base_struct1], + 3, + batch_num_of_perturbations_per_base_structure=1, + number_of_sl_loops=2, + number_parallel_jobs=2, + ) + assert len(runs_history) == 3 + assert isinstance(runs_history[2], dict) + assert "rmse_train_history" in runs_history[0] + + +def test_multiple_sequential_learning_write_to_disk(): + # Tests writing run history to disk + _tmp_dir = tempfile.TemporaryDirectory().name + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "N")["custom"]["structure"] + acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + runs_history = multiple_sequential_learning_runs( + acsc, + [base_struct1], + 3, + batch_num_of_perturbations_per_base_structure=1, + number_of_sl_loops=2, + write_to_disk=True, + write_location=_tmp_dir, + ) + with open(os.path.join(_tmp_dir, "sl_runs_history.json"), "r") as f: + runs_history_written = json.load(f) + assert runs_history == runs_history_written From 03787cbbf13abbf8b0edac27f9ea2defb447d3a1 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 20 Apr 2021 16:51:59 -0400 Subject: [PATCH 071/239] add featurization tutorial --- .../learning/featurization_tutorial.ipynb | 434 ++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 examples/learning/featurization_tutorial.ipynb diff --git a/examples/learning/featurization_tutorial.ipynb b/examples/learning/featurization_tutorial.ipynb new file mode 100644 index 00000000..8492d5ef --- /dev/null +++ b/examples/learning/featurization_tutorial.ipynb @@ -0,0 +1,434 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from autocat.adsorption import generate_rxn_structures\n", + "from autocat.surface import generate_surface_structures\n", + "from autocat.learning.featurizers import full_structure_featurization\n", + "from autocat.learning.featurizers import adsorbate_featurization\n", + "from autocat.learning.featurizers import catalyst_featurization\n", + "from autocat.learning.featurizers import _get_number_of_features\n", + "from autocat.learning.featurizers import get_X" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Adsorbate Featurization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example we are going to show how to featurize a catalyst structure solely based on local features of the adsorbate. First, let's start with making our adsorbate+slab structure" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# generate the surface structure to place an adsorbate on\n", + "surf = generate_surface_structures([\"Fe\"])[\"Fe\"][\"bcc100\"][\"structure\"]\n", + "\n", + "# place an adsorbate onto the surface\n", + "ads_struct = generate_rxn_structures(surf, ads=[\"Li\"])[\"Li\"][\"ontop\"][\"0.0_0.0\"][\n", + " \"structure\"\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have our structure, we can now featurize the OOH adsorbate using a multitude of techniques. Let's use SOAP:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adsorbate featurized into a vector of shape: (1950,)\n" + ] + } + ], + "source": [ + "soap_feat = adsorbate_featurization(\n", + " ads_struct,\n", + " featurizer=\"soap\",\n", + " species_list = [\"Fe\", \"Li\", \"H\"], # species that should be accounted for by the representation\n", + " rcut = 6, # cutoff radius\n", + " nmax=4,\n", + " lmax=4, # maximum order of spherical harmonic\n", + " maximum_adsorbate_size = 5, # maximum adsorbate size that can be handled by the representation\n", + " refine_structure = True # this refines the structure to only include surface and adsorbate atoms\n", + ")\n", + "print(f\"Adsorbate featurized into a vector of shape: {soap_feat.shape}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarly, we can use the same function with a different featurization technique. For example we can use the order parameter site fingerprint:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adsorbate featurized into a vector of shape: (185,)\n" + ] + } + ], + "source": [ + "opsf_feat = adsorbate_featurization(\n", + " ads_struct,\n", + " featurizer=\"op_sitefingerprint\",\n", + " maximum_adsorbate_size=5,\n", + " refine_structure=False,\n", + ")\n", + "print(f\"Adsorbate featurized into a vector of shape: {opsf_feat.shape}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that different featurization techniques will result in varying representation sizes, but will also have varying degrees of learning success. We encourage the user to try different approaches for their specific application" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Long-range full structure featurization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the previous section we looked at featurizing the adsorbate by its local environment. Now we can take a look at how we might want to capture longer-range phenomena via full structure featurization. Again, we start with making an example structure" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# generate the surface structure to place an adsorbate on\n", + "surf = generate_surface_structures([\"Cu\"])[\"Cu\"][\"fcc111\"][\"structure\"]\n", + "\n", + "# place an adsorbate onto the surface\n", + "ads_struct = generate_rxn_structures(surf, ads=[\"OH\"])[\"OH\"][\"ontop\"][\"0.0_0.0\"][\n", + " \"structure\"\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One of the simpler forms of larger scale featurization is just taking elemental properties based upon the structure composition:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1.00000000e+00 4.40000000e+01 4.30000000e+01 4.08461538e+01\n", + " 5.82248521e+00 4.40000000e+01 5.60000000e+01 9.20000000e+01\n", + " 3.60000000e+01 5.85128205e+01 4.63905325e+00 5.60000000e+01\n", + " 1.00794000e+00 1.01070000e+02 1.00062060e+02 9.37062200e+01\n", + " 1.35946708e+01 1.01070000e+02 1.40100000e+01 2.60700000e+03\n", + " 2.59299000e+03 2.40879667e+03 3.65913846e+02 2.60700000e+03\n", + " 1.00000000e+00 1.50000000e+01 1.40000000e+01 7.82051282e+00\n", + " 6.99539776e-01 8.00000000e+00 1.00000000e+00 5.00000000e+00\n", + " 4.00000000e+00 4.71794872e+00 5.20710059e-01 5.00000000e+00\n", + " 3.10000000e+01 1.46000000e+02 1.15000000e+02 1.38179487e+02\n", + " 1.44378698e+01 1.46000000e+02 2.20000000e+00 3.04000000e+00\n", + " 8.40000000e-01 2.22153846e+00 4.19723866e-02 2.20000000e+00\n", + " 1.00000000e+00 2.00000000e+00 1.00000000e+00 1.02564103e+00\n", + " 4.99671269e-02 1.00000000e+00 0.00000000e+00 3.00000000e+00\n", + " 3.00000000e+00 7.69230769e-02 1.49901381e-01 0.00000000e+00\n", + " 0.00000000e+00 7.00000000e+00 7.00000000e+00 6.46153846e+00\n", + " 9.94082840e-01 7.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 1.00000000e+00 8.00000000e+00 7.00000000e+00 7.56410256e+00\n", + " 8.04733728e-01 8.00000000e+00 0.00000000e+00 1.00000000e+00\n", + " 1.00000000e+00 9.74358974e-01 4.99671269e-02 1.00000000e+00\n", + " 0.00000000e+00 3.00000000e+00 3.00000000e+00 7.69230769e-02\n", + " 1.49901381e-01 0.00000000e+00 0.00000000e+00 3.00000000e+00\n", + " 3.00000000e+00 2.76923077e+00 4.26035503e-01 3.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 1.00000000e+00 4.00000000e+00\n", + " 3.00000000e+00 3.82051282e+00 3.31360947e-01 4.00000000e+00\n", + " 6.61500000e+00 1.47687500e+01 8.15375000e+00 1.31886859e+01\n", + " 6.74224195e-01 1.35100000e+01 0.00000000e+00 7.85300000e+00\n", + " 7.85300000e+00 5.67769231e-01 1.04818935e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 1.94000000e+02 1.94000000e+02\n", + " 0.00000000e+00 1.94000000e+02 0.00000000e+00 1.94000000e+02]\n" + ] + } + ], + "source": [ + "elem_prop = full_structure_featurization(\n", + " ads_struct,\n", + " featurizer=\"elemental_property\",\n", + " elementalproperty_preset=\"magpie\", # here we can choose other presets as implemented within `matminer`\n", + " refine_structure = False,\n", + " maximum_structure_size = None, # by default we will just take the max size as the size of the slab+adsorbate\n", + ")\n", + "print(f\"Full structure featurized into representation of shape {elem_prop.shape}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also featurize the full structure through use of a sine matrix, an extension of the coulomb matrix:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Full structure featurized into representation of shape (1600,)\n" + ] + } + ], + "source": [ + "sine_matrix = full_structure_featurization(\n", + " ads_struct,\n", + " maximum_structure_size = None,\n", + " refine_structure = True # this helps reduce the number of features to only\n", + ")\n", + "print(f\"Full structure featurized into representation of shape {sine_matrix.shape}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We note here the use of refine_structure for the sine matrix. The main motivation for this is that since the sine matrix consists of pairwise interactions, and we are most interested in the adsorbate + slab interactions, only considering the surface and adsorbate atoms is an approach to reduce the representation length " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Catalyst Featurization (Adsorbate + Full Structure)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As a means to incorporate both the short-range and longer-range features into a single representation, we can combine them in a consistent, generalizable manner using autocat. First, let's make our structure:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "# generate the surface structure to place an adsorbate on\n", + "surf = generate_surface_structures([\"Ru\"])[\"Ru\"][\"hcp0001\"][\"structure\"]\n", + "\n", + "# place an adsorbate onto the surface\n", + "ads_struct = generate_rxn_structures(surf, ads=[\"NH2\"])[\"NH2\"][\"ontop\"][\"0.0_0.0\"][\n", + " \"structure\"\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we have the freedom to combine any of autocat's local featurization techniques with any of its longer range features. As an example, let's combine the sine matrix with SOAP" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Catalyst featurized into a representation of shape (3160,)\n" + ] + } + ], + "source": [ + "cat = catalyst_featurization(\n", + " ads_struct,\n", + " maximum_structure_size=40,\n", + " maximum_adsorbate_size=4,\n", + " structure_featurizer = \"sine_matrix\",\n", + " adsorbate_featurizer = \"soap\",\n", + " adsorbate_featurization_kwargs={\"rcut\": 6.0, \"nmax\": 4, \"lmax\": 4},\n", + " refine_structure=False,\n", + ")\n", + "print(f\"Catalyst featurized into a representation of shape {cat.shape}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is where the use of maximum structure size and maximum adsorbate size becomes most useful, as it allows for consistently sized vectors when incorporating structures and adsorbates of different sizes into a single representation. This is done through the use of zero-padding for both full-structure and short-range components of the representation. This leads into our next section of featurizing into an input matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Featurizing many structures at once" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As is often the case, we are generally most interested in featurizing multiple structures at once rather than just a single structure in isolation. While if the user opts to use AutoCats structure corrector predictor this is done under-the-hood, but we showcase this feature here to show how it can be used if one then wants to feed this into an alternate ML software" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "# generate multiple slab+adsorbate structures\n", + "structs = []\n", + "surf1 = generate_surface_structures([\"Pt\"])[\"Pt\"][\"fcc111\"][\"structure\"]\n", + "ads1 = generate_rxn_structures(\n", + " surf1,\n", + " ads=[\"NH3\", \"CO\"],\n", + " all_sym_sites=False,\n", + " sites={\"origin\": [(0.0, 0.0)]},\n", + " height={\"CO\": 1.5},\n", + " rots={\"NH3\": [[180.0, \"x\"], [90.0, \"z\"]], \"CO\": [[180.0, \"y\"]]},\n", + ")\n", + "structs.append(ads1[\"NH3\"][\"origin\"][\"0.0_0.0\"][\"structure\"])\n", + "structs.append(ads1[\"CO\"][\"origin\"][\"0.0_0.0\"][\"structure\"])\n", + "surf2 = generate_surface_structures([\"Ru\"])[\"Ru\"][\"hcp0001\"][\"structure\"]\n", + "ads2 = generate_rxn_structures(\n", + " surf2, ads=[\"N\"], all_sym_sites=False, sites={\"origin\": [(0.0, 0.0)]},\n", + ")\n", + "structs.append(ads2[\"N\"][\"origin\"][\"0.0_0.0\"][\"structure\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Input matrix, X of shape (3, 10000) generated\n" + ] + } + ], + "source": [ + "X = get_X(\n", + " structs,\n", + " maximum_structure_size=50,\n", + " maximum_adsorbate_size=5,\n", + " structure_featurizer = \"sine_matrix\",\n", + " adsorbate_featurizer = \"soap\",\n", + " refine_structures = True,\n", + " adsorbate_featurization_kwargs={\"rcut\": 5.0, \"nmax\": 4, \"lmax\": 4},\n", + " write_to_disk = False, # we can write the matrix to disk as a json\n", + " write_location = \".\",\n", + ")\n", + "print(f\"Input matrix, X of shape {X.shape} generated\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We note that the shape of the matrix X generated is (# of structures, full_structure_feat(max_structure_size) + adsorbate_feat(max_adsorbate_size))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 15895816e6c2abdafa934e0e73b731dbb006b57e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 20 Apr 2021 21:28:00 -0400 Subject: [PATCH 072/239] add prediction tutorial --- examples/learning/predictor_tutorial.ipynb | 371 +++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 examples/learning/predictor_tutorial.ipynb diff --git a/examples/learning/predictor_tutorial.ipynb b/examples/learning/predictor_tutorial.ipynb new file mode 100644 index 00000000..aa47e9e0 --- /dev/null +++ b/examples/learning/predictor_tutorial.ipynb @@ -0,0 +1,371 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "cdcaeeef", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from sklearn.gaussian_process import GaussianProcessRegressor\n", + "\n", + "from autocat.adsorption import place_adsorbate\n", + "from autocat.surface import generate_surface_structures\n", + "from autocat.perturbations import generate_perturbed_dataset\n", + "from autocat.learning.predictors import AutoCatStructureCorrector" + ] + }, + { + "cell_type": "markdown", + "id": "42ad423a", + "metadata": {}, + "source": [ + "In this tutorial we show how to use the AutoCatStructureCorrector for both training on relaxed structures and predicting corrections to initial structure guesses" + ] + }, + { + "cell_type": "markdown", + "id": "37fb7a41", + "metadata": {}, + "source": [ + "# Creating an AutoCatStructureCorrector object" + ] + }, + { + "cell_type": "markdown", + "id": "053de3bb", + "metadata": {}, + "source": [ + "Let's start with creating our AutoCatStructureCorrector object" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "78ebf3b3", + "metadata": {}, + "outputs": [], + "source": [ + "acsc = AutoCatStructureCorrector(\n", + " model_class = GaussianProcessRegressor, # regressor model class\n", + " structure_featurizer=\"sine_matrix\",\n", + " adsorbate_featurizer=\"soap\",\n", + " adsorbate_featurization_kwargs={\"rcut\": 5.0, \"nmax\": 8, \"lmax\": 6},\n", + " refine_structures = True,\n", + " maximum_structure_size = None,\n", + " maximum_adsorbate_size = None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "baa10924", + "metadata": {}, + "source": [ + "The model class provided should have fit and predict (with return_std) methods. The AutoCatStructureCorrector class behaves similarly to sci-kit learn regressor objects with fit, predict and score methods." + ] + }, + { + "cell_type": "markdown", + "id": "33d69603", + "metadata": {}, + "source": [ + "# Generating Perturbed Structure Datasets" + ] + }, + { + "cell_type": "markdown", + "id": "9930927d", + "metadata": {}, + "source": [ + "Before we can demonstrate the capabilities of the AutoCatStructureCorrector class, we need to create a perturbed dataset from given base structures. These base structures would typically be already relaxed with DFT. For simplicity let's consider a single base structure, which would correspond to an interpolative prediction on the surface, generated directly by AutoCat (in practice this would ideally be relaxed before use)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "225e7954", + "metadata": {}, + "outputs": [], + "source": [ + "sub = generate_surface_structures([\"Pd\"], facets={\"Pd\": [\"100\"]})[\"Pd\"][\"fcc100\"][\n", + " \"structure\"\n", + "]\n", + "base_struct = place_adsorbate(sub, \"CO\")[\"custom\"][\"structure\"]" + ] + }, + { + "cell_type": "markdown", + "id": "4ea5e3f3", + "metadata": {}, + "source": [ + "Now that we have the base structure, we can now perturb it to create the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "42d1ffe5", + "metadata": {}, + "outputs": [], + "source": [ + "train_set = generate_perturbed_dataset(\n", + " [base_struct],\n", + " num_of_perturbations=20,\n", + " minimum_perturbation_distance = 0.01,\n", + " maximum_perturbation_distance = 0.75,\n", + " maximum_adsorbate_size = None,\n", + ")\n", + "\n", + "# separating out the corrected structures and corresponding correction matrix\n", + "train_structures = train_set[\"collected_structures\"]\n", + "train_correction_matrix = train_set[\"correction_matrix\"] # matrix which has padding\n", + "train_corrections_list = train_set[\"corrections_list\"] # list of corrections (which will have variable length)" + ] + }, + { + "cell_type": "markdown", + "id": "44df37cc", + "metadata": {}, + "source": [ + "**A note on directionality:** Perturbations are controlled via the tags associated with each atom in the Atoms object. By default, slabs generated with ASE/AutoCat will have the tags going from 1 (top-layer) to # of layers (bottom layer) and adsorbates will have a tag of 0. Atoms given a tag of 0 will be free to move and atoms with tags >=1 will be fixed. Constraints in directionality may be specified via the following specially assigned tag-values (see documentation for further details)" + ] + }, + { + "cell_type": "markdown", + "id": "2e8e7e28", + "metadata": {}, + "source": [ + "For future use let's also make a test set" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "234bd52a", + "metadata": {}, + "outputs": [], + "source": [ + "test_set = generate_perturbed_dataset(\n", + " [base_struct],\n", + " num_of_perturbations=20,\n", + " minimum_perturbation_distance = 0.01,\n", + " maximum_perturbation_distance = 0.75,\n", + " maximum_adsorbate_size = None,\n", + ")\n", + "\n", + "# separating out the corrected structures and corresponding correction matrix\n", + "test_structures = test_set[\"collected_structures\"]\n", + "test_corrections_list = test_set[\"corrections_list\"]" + ] + }, + { + "cell_type": "markdown", + "id": "364c6359", + "metadata": {}, + "source": [ + "# Fitting to perturbation data" + ] + }, + { + "cell_type": "markdown", + "id": "20312914", + "metadata": {}, + "source": [ + "To fit our model to the generated perturbation data, we can use the fit method" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b8803318", + "metadata": {}, + "outputs": [], + "source": [ + "acsc.fit(\n", + " train_structures,\n", + " correction_matrix = train_correction_matrix\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c2c69d9e", + "metadata": {}, + "source": [ + "Alternatively, we can also provide the corrections as a list" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6bd60f27", + "metadata": {}, + "outputs": [], + "source": [ + "acsc.fit(\n", + " train_structures,\n", + " corrections_list = train_corrections_list\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "0d67ceb0", + "metadata": {}, + "source": [ + "If we want to check whether our model has been fit to data, we can use the is_fit method. Note that changing any of the model or featurizer settings will automatically change this value back to False" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "977af7d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Has the model been fit? True\n", + "Is the model still been fit? False\n" + ] + } + ], + "source": [ + "print(f\"Has the model been fit? {acsc.is_fit}\")\n", + "\n", + "acsc.structure_featurizer = \"elemental_property\"\n", + "print(f\"Is the model still been fit? {acsc.is_fit}\")" + ] + }, + { + "cell_type": "markdown", + "id": "f113fce5", + "metadata": {}, + "source": [ + "Let's refit the model to be used for the next section" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "021a19e7", + "metadata": {}, + "outputs": [], + "source": [ + "acsc.structure_featurizer = \"sine_matrix\"\n", + "\n", + "acsc.fit(\n", + " train_structures,\n", + " corrections_list = train_corrections_list\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "158eefad", + "metadata": {}, + "source": [ + "# Structure Correction Predictions and Performance Metrics" + ] + }, + { + "cell_type": "markdown", + "id": "7ab75c93", + "metadata": {}, + "source": [ + "Now that we have a fit model, we can now make predictions on the test set we made earlier. To do this we can make use of the predict method" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1a1c2303", + "metadata": {}, + "outputs": [], + "source": [ + "predicted_corrections, corrected_structures, unc = (\n", + " acsc.predict(\n", + " test_structures\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "adb13e4d", + "metadata": {}, + "source": [ + "When we make predictions we are given three quantities: a list of the predicted corrections for each structure, Atoms objects of the corrected structures, and the uncertainties attributed to each prediction." + ] + }, + { + "cell_type": "markdown", + "id": "3226da93", + "metadata": {}, + "source": [ + "Since we know what the corrections need to be for each of the test structures, we can evaluate performance metrics on each of the predictions. At present, both MAE and RMSE are implemented within the score method" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "9d1b78e5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAE = 0.7263375790848622\n", + "RMSE = 0.5777037271424246\n" + ] + } + ], + "source": [ + "MAE = acsc.score(test_structures, test_corrections_list)\n", + "print(f\"MAE = {MAE}\")\n", + "\n", + "RMSE = acsc.score(test_structures, test_corrections_list, metric=\"rmse\")\n", + "print(f\"RMSE = {RMSE}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From d8dbbe7522dd9a1eb864206650f8ac31068c13b1 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 20 Apr 2021 22:43:03 -0400 Subject: [PATCH 073/239] adds sl tutorial --- .../sequential_learning_tutorial.ipynb | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 examples/learning/sequential_learning_tutorial.ipynb diff --git a/examples/learning/sequential_learning_tutorial.ipynb b/examples/learning/sequential_learning_tutorial.ipynb new file mode 100644 index 00000000..1e98871c --- /dev/null +++ b/examples/learning/sequential_learning_tutorial.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "b2f4b043", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from autocat.adsorption import place_adsorbate\n", + "from autocat.surface import generate_surface_structures\n", + "from autocat.learning.predictors import AutoCatStructureCorrector\n", + "from autocat.learning.sequential import simulated_sequential_learning\n", + "from autocat.learning.sequential import multiple_sequential_learning_runs" + ] + }, + { + "cell_type": "markdown", + "id": "2ceb554b", + "metadata": {}, + "source": [ + "In this tutorial we show how to conduct sequential learning runs for training an `AutoCatStructureCorrector`" + ] + }, + { + "cell_type": "markdown", + "id": "c2d963c5", + "metadata": {}, + "source": [ + "# Sequential Learning" + ] + }, + { + "cell_type": "markdown", + "id": "016adce8", + "metadata": {}, + "source": [ + "In order to conduct a sequential learning run, we need three things: i) an `AutoCatStructureCorrector` object with our desired featurizer settings, ii) base structures to be used for training, iii) base_structures to be used for testing (this one is optional)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c77eca10", + "metadata": {}, + "outputs": [], + "source": [ + "acsc = AutoCatStructureCorrector(\n", + " structure_featurizer=\"sine_matrix\",\n", + " adsorbate_featurizer=\"soap\",\n", + " adsorbate_featurization_kwargs={\"rcut\": 5.0, \"nmax\": 4, \"lmax\": 4},\n", + " refine_structures = True,\n", + " maximum_structure_size = None, # will default to max structure encountered\n", + " maximum_adsorbate_size = None, # will default to max adsorbate encountered\n", + " species_list = [\"Pd\", \"C\", \"O\", \"Cu\"] # This is important to include!\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b86e5552", + "metadata": {}, + "outputs": [], + "source": [ + "sub = generate_surface_structures([\"Pd\"], facets={\"Pd\": [\"100\"]})[\"Pd\"][\"fcc100\"][\n", + " \"structure\"\n", + "]\n", + "train_base_struct = place_adsorbate(sub, \"CO\")[\"custom\"][\"structure\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cd42d052", + "metadata": {}, + "outputs": [], + "source": [ + "sub2 = generate_surface_structures([\"Cu\"], facets={\"Cu\": [\"100\"]})[\"Cu\"][\"fcc100\"][\n", + " \"structure\"\n", + "]\n", + "test_base_struct = place_adsorbate(sub2, \"O\")[\"custom\"][\"structure\"]" + ] + }, + { + "cell_type": "markdown", + "id": "e185c3d8", + "metadata": {}, + "source": [ + "We're now ready to conduct a sequential learning run" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "764fc7bb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sequential Learning Iteration #1\n", + "Sequential Learning Iteration #2\n", + "Sequential Learning Iteration #3\n", + "Sequential Learning Iteration #4\n" + ] + } + ], + "source": [ + "sl_dict = simulated_sequential_learning(\n", + " acsc,\n", + " [train_base_struct],\n", + " testing_base_structures = [test_base_struct],\n", + " initial_num_of_perturbations_per_base_structure = 4, # for generating the initial training set\n", + " batch_num_of_perturbations_per_base_structure = 3, # how large of a pool to predict on for each loop\n", + " batch_size_to_add = 3, # how many structures to add to training on each loop\n", + " number_of_sl_loops = 4,\n", + " write_to_disk = False # if we want to save the history info to disk\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "164c2d27", + "metadata": {}, + "source": [ + "Contained within the returned disk is multiple histories as a function of sequential learning iteration including MAE & RMSE on the validation and testing sets" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "49a10d8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAE training history: [0.6085485197361659, 0.6085485197361659, 0.6085485197361659, 0.6085485197361659]\n", + "RMSE training history: [0.40434634537932845, 0.40434634537932845, 0.40434634537932845, 0.40434634537932845]\n", + "\n", + "\n", + "MAE test history: [0.6661825694029918, 0.6661825694029918, 0.6661825694029918, 0.6661825694029918]\n", + "RMSE test history: [0.46687185319246804, 0.46687185319246804, 0.46687185319246804, 0.46687185319246804]\n" + ] + } + ], + "source": [ + "print(f\"MAE training history: {sl_dict['mae_train_history']}\")\n", + "print(f\"RMSE training history: {sl_dict['rmse_train_history']}\")\n", + "print('\\n')\n", + "print(f\"MAE test history: {sl_dict['mae_test_history']}\")\n", + "print(f\"RMSE test history: {sl_dict['rmse_test_history']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "493fde74", + "metadata": {}, + "source": [ + "Moreover, the uncertainties of each batch that is added to the training set is also kept (note here it will be very large as the training set was kept small for easier demonstration purposes)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "0e005797", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Full Maximum Uncertainty History: [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]\n" + ] + } + ], + "source": [ + "print(f\"Full Maximum Uncertainty History: {sl_dict['max_unc_history']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "817d0951", + "metadata": {}, + "source": [ + "For additional information about the other quantities that are tracked, we refer the reader to the documentation" + ] + }, + { + "cell_type": "markdown", + "id": "a94d5c80", + "metadata": {}, + "source": [ + "# Running Multiple Sequential Learning Runs" + ] + }, + { + "cell_type": "markdown", + "id": "8ee96e20", + "metadata": {}, + "source": [ + "In most cases we will want to run multiple sequential learning runs in order to remove any effects from data initialization of the first training set. Through the use of `joblib`, `AutoCat` can parallelize this process across multiple cores where each core runs a separate sl run. The example below runs 2 sequential learning runs on 2 cores (1 on each core)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "bbf1ab53", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of runs information stored: 2\n" + ] + } + ], + "source": [ + "runs_history = multiple_sequential_learning_runs(\n", + " acsc,\n", + " [train_base_struct],\n", + " testing_base_structures = [test_base_struct],\n", + " number_of_runs = 2,\n", + " number_parallel_jobs = 2,\n", + " initial_num_of_perturbations_per_base_structure = 3, # for generating the initial training set\n", + " batch_num_of_perturbations_per_base_structure = 3, # how large of a pool to predict on for each loop\n", + " batch_size_to_add = 1, # how many structures to add to training on each loop\n", + " number_of_sl_loops = 4,\n", + " write_to_disk = False # if we want to save the runs history to disk\n", + ")\n", + "print(f\"Number of runs information stored: {len(runs_history)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "74b194b9", + "metadata": {}, + "source": [ + "The returned list contains the sequential learning dictionary of each separate run, thus allowing for averaging of any of the quantities across runs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f401661", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 11609f7279e78bb8dcc10900f94be48ad0a1dc28 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 21 Apr 2021 14:44:19 -0400 Subject: [PATCH 074/239] bugfix: subtracts disp vecs to get corr structs --- src/autocat/learning/predictors.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 68cc201b..3a0cc458 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -279,7 +279,9 @@ def fit( correction_list: List of np.arrays of correction vectors - where each item is of shape (# of adsorbate atoms, 3) + where each item is of shape (# of adsorbate atoms, 3). + Adding the negative of these vectors to any perturbed + structure should return it to the base structure Returns ------- @@ -403,7 +405,7 @@ def predict( idx, : 3 * num_of_adsorbates ].reshape(num_of_adsorbates, 3) predicted_corrections.append(corr) - cs.positions[list_of_adsorbate_indices] += corr + cs.positions[list_of_adsorbate_indices] -= corr corrected_structures.append(cs) return predicted_corrections, corrected_structures, unc From ce2f2a0c3a7560c79a3c3423a1f09fc96c66f822 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 21 Apr 2021 16:16:17 -0400 Subject: [PATCH 075/239] add CrystalNN site fingerprint to ads feat --- src/autocat/learning/featurizers.py | 18 +++++++++++++++--- tests/learning/test_featurizers.py | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 704512d1..2d56d0a7 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -7,6 +7,7 @@ from matminer.featurizers.composition import ElementProperty from matminer.featurizers.site import ChemicalSRO from matminer.featurizers.site import OPSiteFingerprint +from matminer.featurizers.site import CrystalNNFingerprint import tempfile import os @@ -427,6 +428,16 @@ def adsorbate_featurization( representation = np.concatenate((representation, feat)) num_of_features = len(opsf.feature_labels()) + elif featurizer == "crystalnn_sitefingerprint": + cnn = CrystalNNFingerprint.from_preset("cn") + conv = AseAtomsAdaptor() + pym_struct = conv.get_structure(structure) + representation = np.array([]) + for idx in adsorbate_indices: + feat = cnn.featurize(pym_struct, idx) + representation = np.concatenate((representation, feat)) + num_of_features = len(cnn.feature_labels()) + else: raise NotImplementedError("selected featurizer not implemented") @@ -557,6 +568,7 @@ def _get_number_of_features( "elemental_property": ElementProperty.from_preset(elementalproperty_preset), "chemical_sro": None, "op_sitefingerprint": OPSiteFingerprint, + "crystalnn_sitefingerprint": CrystalNNFingerprint, } if featurizer in supported_dscribe_featurizers: @@ -569,9 +581,9 @@ def _get_number_of_features( return len(ep.features) * len(ep.stats) elif featurizer == "chemical_sro": return len(species) - elif featurizer == "op_sitefingerprint": - opsf = supported_matminer_featurizers[featurizer](**kwargs) - return len(opsf.feature_labels()) + elif featurizer in ["op_sitefingerprint", "crystalnn_sitefingerprint"]: + f = supported_matminer_featurizers[featurizer](**kwargs) + return len(f.feature_labels()) else: raise NotImplementedError( diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 12b85e0f..3564ad12 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -16,6 +16,7 @@ from matminer.featurizers.composition import ElementProperty from matminer.featurizers.site import ChemicalSRO from matminer.featurizers.site import OPSiteFingerprint +from matminer.featurizers.site import CrystalNNFingerprint # from autocat.io.qml import ase_atoms_to_qml_compound from autocat.adsorption import generate_rxn_structures @@ -155,6 +156,28 @@ def test_adsorbate_featurization_op_sitefingerprint(): assert np.allclose(opsf_feat, manual_feat) +def test_adsorbate_featurization_crystalnn_fingerprint(): + # Test CrystalNN site fingerprint + surf = generate_surface_structures(["Ag"])["Ag"]["fcc100"]["structure"] + ads_struct = generate_rxn_structures(surf, ads=["OH"])["OH"]["ontop"]["0.0_0.0"][ + "structure" + ] + cnn_feat = adsorbate_featurization( + ads_struct, + featurizer="crystalnn_sitefingerprint", + maximum_adsorbate_size=4, + refine_structure=False, + ) + cnn = CrystalNNFingerprint.from_preset("cn") + conv = AseAtomsAdaptor() + pym_struct = conv.get_structure(ads_struct) + manual_feat = cnn.featurize(pym_struct, -2) + manual_feat = np.concatenate((manual_feat, cnn.featurize(pym_struct, -1))) + manual_feat = np.concatenate((manual_feat, np.zeros(2 * len(cnn.feature_labels())))) + assert np.allclose(cnn_feat, manual_feat) + assert cnn_feat.shape[0] == 4 * len(cnn.feature_labels()) + + def test_adsorbate_featurization_padding(): # Tests that padding is properly applied surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] From 1088d1078dd051dd27c7568811a4234fb7f15346 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 21 Apr 2021 16:22:14 -0400 Subject: [PATCH 076/239] bugfix: use preset for crysnn in num features func --- src/autocat/learning/featurizers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 2d56d0a7..e5338e61 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -581,9 +581,12 @@ def _get_number_of_features( return len(ep.features) * len(ep.stats) elif featurizer == "chemical_sro": return len(species) - elif featurizer in ["op_sitefingerprint", "crystalnn_sitefingerprint"]: + elif featurizer == "op_sitefingerprint": f = supported_matminer_featurizers[featurizer](**kwargs) return len(f.feature_labels()) + elif featurizer == "crystalnn_sitefingerprint": + f = supported_matminer_featurizers[featurizer].from_preset("cn") + return len(f.feature_labels()) else: raise NotImplementedError( From 8614e0fd51ef132efdfd8dbc87b80c12723ac1c7 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 22 Apr 2021 16:26:28 -0400 Subject: [PATCH 077/239] bugfix: typo in rmse score calculation --- src/autocat/learning/predictors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 3a0cc458..13e75039 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -464,7 +464,7 @@ def score( np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) ** 2 ) all_sq_vec_diff.append(sq_vec_diff / N_i) - return np.sum(all_sq_vec_diff) / len(all_sq_vec_diff) + return np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)) else: msg = f"Metric: {metric} is not supported" raise AutocatStructureCorrectorError(msg) From 6dccbc976a233dfb1559755f2bc7a40736712d5b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 28 Apr 2021 17:29:12 -0400 Subject: [PATCH 078/239] generalize predict for model class without unc --- src/autocat/learning/predictors.py | 11 +++++++---- tests/learning/test_predictors.py | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 13e75039..f325b3d5 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -385,10 +385,13 @@ def predict( structure_featurization_kwargs=self.structure_featurization_kwargs, adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - - predicted_correction_matrix_full, unc = self.regressor.predict( - featurized_input, return_std=True - ) + try: + predicted_correction_matrix_full, unc = self.regressor.predict( + featurized_input, return_std=True + ) + except TypeError: + predicted_correction_matrix_full = self.regressor.predict(featurized_input,) + unc = None corrected_structures = [ init_struct.copy() for init_struct in initial_structure_guesses diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 8ca64ed6..471f801b 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -130,3 +130,27 @@ def test_model_class_and_kwargs(): acsc.model_kwargs = {"alpha": 2.5} assert acsc.model_kwargs == {"alpha": 2.5} assert acsc.regressor.alpha == 2.5 + + +def test_model_without_unc(): + # Test that predictions are still made when the model class + # provided does not have uncertainty + sub = generate_surface_structures(["Li"], facets={"Li": ["100"]})["Li"]["bcc100"][ + "structure" + ] + base_struct = place_adsorbate(sub, "S")["custom"]["structure"] + p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) + p_structures = p_set["collected_structures"] + correction_matrix = p_set["correction_matrix"] + acsc = AutoCatStructureCorrector( + model_class=KernelRidge, + structure_featurizer="sine_matrix", + adsorbate_featurizer=None, + ) + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) + predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + assert uncs is None + assert predicted_corrections is not None + assert corrected_structures is not None From b6b13864644c2f330bccd77ffac687dfe4f2b51a Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 3 May 2021 15:09:10 -0400 Subject: [PATCH 079/239] can train multi separate models, add padding check --- src/autocat/learning/predictors.py | 69 +++++++++++++++++++--- tests/learning/test_predictors.py | 93 ++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 8 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index f325b3d5..009352cd 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -21,6 +21,7 @@ class AutoCatStructureCorrector: def __init__( self, model_class=None, + multiple_separate_models: bool = None, structure_featurizer: str = None, adsorbate_featurizer: str = None, maximum_structure_size: int = None, @@ -43,6 +44,11 @@ def __init__( model_kwargs will be removed. N.B. must have fit and predict methods + multiple_separate_models: + Bool indicating whether to train separate models for each target output. + If this is true, when fit to data, `acsc.regressor` will become the list + of regressors with length of number of targets + structure_featurizer: String giving featurizer to be used for full structure which will be fed into `autocat.learning.featurizers.full_structure_featurization` @@ -74,6 +80,9 @@ def __init__( """ self.is_fit = False + self._multiple_separate_models = False + self.multiple_separate_models = multiple_separate_models + self._model_class = GaussianProcessRegressor self.model_class = model_class @@ -122,6 +131,17 @@ def model_class(self, model_class): # generates new regressor with default settings self.regressor = self._model_class() + @property + def multiple_separate_models(self): + return self._multiple_separate_models + + @multiple_separate_models.setter + def multiple_separate_models(self, multiple_separate_models): + if multiple_separate_models is not None: + self._multiple_separate_models = multiple_separate_models + if self.is_fit: + self.is_fit = False + @property def model_kwargs(self): return self._model_kwargs @@ -338,12 +358,23 @@ def fit( for idx, row in enumerate(corrections_list): correction_matrix[idx, : 3 * len(row)] = row.flatten() elif correction_matrix is not None: - pass + if correction_matrix.shape[1] != 3 * self.maximum_adsorbate_size: + msg = f"Correction matrix must have {3 * self.maximum_adsorbate_size} targets, got {correction_matrix.shape[1]}" + raise AutocatStructureCorrectorError(msg) else: msg = "Must specify either corrections list or matrix" raise AutocatStructureCorrectorError(msg) - self.regressor.fit(X, correction_matrix) + if not self.multiple_separate_models: + self.regressor.fit(X, correction_matrix) + else: + regs = [] + for i in range(correction_matrix.shape[1]): + reg = self.model_class(**self.model_kwargs or {}) + reg.fit(X, correction_matrix[:, i]) + regs.append(reg) + assert regs[0] is not regs[1] + self.regressor = regs self.is_fit = True @@ -385,13 +416,35 @@ def predict( structure_featurization_kwargs=self.structure_featurization_kwargs, adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - try: - predicted_correction_matrix_full, unc = self.regressor.predict( - featurized_input, return_std=True + if not self.multiple_separate_models: + try: + predicted_correction_matrix_full, unc = self.regressor.predict( + featurized_input, return_std=True + ) + except TypeError: + predicted_correction_matrix_full = self.regressor.predict( + featurized_input, + ) + unc = None + else: + predicted_correction_matrix_full = np.zeros( + (len(initial_structure_guesses), 3 * self.maximum_adsorbate_size) ) - except TypeError: - predicted_correction_matrix_full = self.regressor.predict(featurized_input,) - unc = None + try: + uncs = np.zeros((len(initial_structure_guesses), len(self.regressor))) + for idx, r in enumerate(self.regressor): + preds = r.predict(featurized_input, return_std=True) + predicted_correction_matrix_full[:, idx] = preds[0] + # uncertainties for target r for each struct to predict on + uncs[:, idx] = preds[1] + # take average uncertainty for each struct + unc = np.mean(uncs, axis=1) + except TypeError: + for idx, r in enumerate(self.regressor): + predicted_correction_matrix_full[:, idx] = r.predict( + featurized_input + ) + unc = None corrected_structures = [ init_struct.copy() for init_struct in initial_structure_guesses diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 471f801b..b80441bd 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -6,6 +6,7 @@ from sklearn.kernel_ridge import KernelRidge from sklearn.gaussian_process import GaussianProcessRegressor +from sklearn.linear_model import BayesianRidge from ase import Atoms @@ -64,6 +65,17 @@ def test_predict_initial_configuration_formats(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] + acsc = AutoCatStructureCorrector( + structure_featurizer="sine_matrix", + adsorbate_featurizer="soap", + adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + maximum_adsorbate_size=3, + ) + with pytest.raises(AutocatStructureCorrectorError): + # check that supplied correction matrix is properly padded + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) acsc = AutoCatStructureCorrector( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", @@ -154,3 +166,84 @@ def test_model_without_unc(): assert uncs is None assert predicted_corrections is not None assert corrected_structures is not None + + +def test_multi_separate_models_fit(): + # Tests fitting multiple separate models for each output + sub = generate_surface_structures(["Fe"], facets={"Fe": ["100"]})["Fe"]["bcc100"][ + "structure" + ] + base_struct = place_adsorbate(sub, "O")["custom"]["structure"] + p_set = generate_perturbed_dataset( + [base_struct], num_of_perturbations=20, maximum_adsorbate_size=2, + ) + p_structures = p_set["collected_structures"] + correction_matrix = p_set["correction_matrix"] + acsc = AutoCatStructureCorrector( + structure_featurizer="sine_matrix", + adsorbate_featurizer=None, + multiple_separate_models=True, + maximum_adsorbate_size=2, + ) + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) + assert len(acsc.regressor) == 6 + assert isinstance(acsc.regressor[0], GaussianProcessRegressor) + assert hasattr(acsc.regressor[-1], "alpha_") + assert acsc.regressor[1] is not acsc.regressor[2] + # Check fitting to supplied model class + acsc.model_class = BayesianRidge + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) + assert len(acsc.regressor) == 6 + assert hasattr(acsc.regressor[2], "coef_") + assert acsc.regressor[0] is not acsc.regressor[-1] + + +def test_multi_separate_models_predict(): + # Tests predicting with multiple separate models + sub = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"]["hcp0001"][ + "structure" + ] + base_struct = place_adsorbate(sub, "NH")["custom"]["structure"] + p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) + p_structures = p_set["collected_structures"] + correction_matrix = p_set["correction_matrix"] + acsc = AutoCatStructureCorrector( + structure_featurizer="sine_matrix", + adsorbate_featurizer=None, + multiple_separate_models=True, + ) + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) + predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + assert len(predicted_corrections) == 5 + assert len(predicted_corrections[0]) == 2 + assert len(predicted_corrections[0][0]) == 3 + assert len(corrected_structures) == 5 + assert len(uncs) == 5 + # Test for model class without unc + acsc.model_class = KernelRidge + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) + predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + assert len(predicted_corrections) == 5 + assert len(predicted_corrections[0]) == 2 + assert len(predicted_corrections[0][0]) == 3 + assert len(corrected_structures) == 5 + assert uncs is None + # Test for model class with uncertainty + acsc.model_class = BayesianRidge + acsc.fit( + p_structures[:15], correction_matrix=correction_matrix[:15, :], + ) + predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + assert len(predicted_corrections) == 5 + assert len(predicted_corrections[0]) == 2 + assert len(predicted_corrections[0][0]) == 3 + assert len(corrected_structures) == 5 + assert len(uncs) == 5 From 321040b5e21d3d22ecc10a95b92eefed88b483b1 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 6 May 2021 18:12:24 -0400 Subject: [PATCH 080/239] start making surface and adsorption examples --- .../adsorbing_molecules_on_surfaces.ipynb | 334 ++++++++++++++++++ examples/generating_surface_slabs.ipynb | 183 ++++++++++ 2 files changed, 517 insertions(+) create mode 100644 examples/adsorbing_molecules_on_surfaces.ipynb create mode 100644 examples/generating_surface_slabs.ipynb diff --git a/examples/adsorbing_molecules_on_surfaces.ipynb b/examples/adsorbing_molecules_on_surfaces.ipynb new file mode 100644 index 00000000..1e3f5541 --- /dev/null +++ b/examples/adsorbing_molecules_on_surfaces.ipynb @@ -0,0 +1,334 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 36, + "id": "eaee58ba", + "metadata": {}, + "outputs": [], + "source": [ + "from autocat.surface import generate_surface_structures\n", + "\n", + "from autocat.adsorption import generate_rxn_structures\n", + "from autocat.adsorption import generate_molecule_object\n", + "\n", + "from autocat.data.intermediates import ORR_INTERMEDIATE_NAMES" + ] + }, + { + "cell_type": "markdown", + "id": "f89805bd", + "metadata": {}, + "source": [ + "In this example we show how to use `AutoCat` to generate adsorption structures given a surface structure" + ] + }, + { + "cell_type": "markdown", + "id": "69d3233d", + "metadata": {}, + "source": [ + "# Generating Reaction Structures" + ] + }, + { + "cell_type": "markdown", + "id": "ccd545c4", + "metadata": {}, + "source": [ + "Let's start by making a `Pt111` slab for demonstration purposes. But in general this can be any surface you'd like as long as you have it in the form of an `ase.Atoms` object or written to disk in an `ase` readable format." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6711809e", + "metadata": {}, + "outputs": [], + "source": [ + "slab_dictionary = generate_surface_structures(\n", + " species_list = [\"Pt\"],\n", + " facets = {\"Pt\": [\"111\"]},\n", + " n_fixed_layers = 2\n", + ")\n", + "\n", + "clean_slab = slab_dictionary[\"Pt\"][\"fcc111\"][\"structure\"]" + ] + }, + { + "cell_type": "markdown", + "id": "e5bc6f7e", + "metadata": {}, + "source": [ + "Now that we have our clean slab, we can start adsorbing molecules onto the surface." + ] + }, + { + "cell_type": "markdown", + "id": "75ab5a3d", + "metadata": {}, + "source": [ + "To fully characterize this surface for its activity toward evolving hydrogen (HER), we'd need to adsorb `H` onto every symmetry site of the surface. As the choice of `Pt111` was arbitrary, the function demo'd here works for any surface (through `pymatgen`'s implementation of Delaunay Triangulation)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "4d48cb62", + "metadata": {}, + "outputs": [], + "source": [ + "h_adsorption_structure_dictionary = generate_rxn_structures(\n", + " clean_slab,\n", + " all_sym_sites = True, # to consider all identified sites\n", + " ads = [\"H\"],\n", + " height = {\"H\" : 1.5}, # manually specify height. default guess based on covalent radii of nearest neighbors\n", + " refs = [\"H2\"], # reference molecule for the reaction\n", + " write_to_disk = False\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e7e1e3ce", + "metadata": {}, + "source": [ + "This will generate a dictionary of all generated adsorption structures with the following structure:\n", + "\n", + "- Adsorbate Names\n", + " - Symmetry Site types (ie. hollow, ontop, bridge) or Custom Label\n", + " - `x-y` coordinate of each site\n", + " - `ase.Atoms` structure\n", + " - Path to structure file (in the `ase.traj` format)\n", + "- references\n", + " - Reference Molecule Name\n", + " - `ase.Atoms` structure\n", + " - Path to structure file (in the `ase.traj` format)" + ] + }, + { + "cell_type": "markdown", + "id": "830e6d48", + "metadata": {}, + "source": [ + "Here we have all three types of symmetry sites present" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "6afb4a44", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['ontop', 'bridge', 'hollow'])\n" + ] + } + ], + "source": [ + "print(h_adsorption_structure_dictionary[\"H\"].keys())" + ] + }, + { + "cell_type": "markdown", + "id": "70b5335a", + "metadata": {}, + "source": [ + "And we can confirm that it identified both hollow sites:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "e846d5a8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['6.93_5.601', '9.702_4.001'])\n" + ] + } + ], + "source": [ + "print(h_adsorption_structure_dictionary[\"H\"][\"hollow\"].keys())" + ] + }, + { + "cell_type": "markdown", + "id": "5c3bc7ac", + "metadata": {}, + "source": [ + "Instead of exhaustively considering all sites, it can be restricted to specific types via `site_type`. \n", + "\n", + "Or alternatively, if we want to consider only manually specified sites, that can be done via `sites`. When specifying the sites manually in this way, we need to provide them as a dictionary with keys as to how we'd like the site labelled. This is solely used for organizing the output dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "eae177e2", + "metadata": {}, + "outputs": [], + "source": [ + "h_manual_adsorption_structure_dictionary = generate_rxn_structures(\n", + " clean_slab,\n", + " all_sym_sites = False,\n", + " ads = [\"H\"],\n", + " sites = {\"custom\": [(0.,0.)]},\n", + " write_to_disk = False\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "7eeddd64", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['custom'])\n", + "dict_keys(['0.0_0.0'])\n" + ] + } + ], + "source": [ + "print(h_manual_adsorption_structure_dictionary[\"H\"].keys())\n", + "print(h_manual_adsorption_structure_dictionary[\"H\"][\"custom\"].keys())" + ] + }, + { + "cell_type": "markdown", + "id": "13e24624", + "metadata": {}, + "source": [ + "`AutoCat` also has some defaults for generating structures for considering the Oxygen Reduction/Evolution Reaction as well as Nitrogen Reduction. These can be found within `autocat.data.intermediates`. Let's generate the ORR adsorption structures on this slab as an example." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "dd3d8ea1", + "metadata": {}, + "outputs": [], + "source": [ + "orr_adsorption_structure_dictionary = generate_rxn_structures(\n", + " clean_slab,\n", + " all_sym_sites = True, # to consider all identified sites (can also manually specify via `sites`)\n", + " ads = ORR_INTERMEDIATE_NAMES,\n", + " refs = [\"H2\", \"H2O\"], # reference molecules for the reaction\n", + " write_to_disk = False\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "da19d412", + "metadata": {}, + "source": [ + "This places all of the relevant adsorbate molecules at all of the identified sites." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "78748e9b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['OOH', 'O', 'OH', 'references'])\n" + ] + } + ], + "source": [ + "print(orr_adsorption_structure_dictionary.keys())" + ] + }, + { + "cell_type": "markdown", + "id": "51561b67", + "metadata": {}, + "source": [ + "It's important to note that if you already have the adsorbate molecule you'd like to consider as an `ase.Atoms` object, that can be supplied as well. We are going to use `autocat.adsorption.generate_molecule_object` to generate an example, but this can be anything (e.g. an `*.sdf` read by `ase.io.read`)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "7be25d8e", + "metadata": {}, + "outputs": [], + "source": [ + "nh2_mol = generate_molecule_object(\"NH2\").get(\"structure\")\n", + "\n", + "nh2_adsorption_structure_dictionary = generate_rxn_structures(\n", + " clean_slab,\n", + " all_sym_sites = True,\n", + " ads = [nh2_mol],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "99a12fe8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['H2N'])\n" + ] + } + ], + "source": [ + "print(nh2_adsorption_structure_dictionary.keys())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/generating_surface_slabs.ipynb b/examples/generating_surface_slabs.ipynb new file mode 100644 index 00000000..2cd86a3b --- /dev/null +++ b/examples/generating_surface_slabs.ipynb @@ -0,0 +1,183 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "319554e9", + "metadata": {}, + "outputs": [], + "source": [ + "from autocat.surface import generate_surface_structures" + ] + }, + { + "cell_type": "markdown", + "id": "3bbcebd9", + "metadata": {}, + "source": [ + "In this tutorial we show how to generate slabs using `AutoCat`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6d42c7dc", + "metadata": {}, + "outputs": [], + "source": [ + "slab_dictionary = generate_surface_structures(\n", + " species_list = [\"Pt\", \"Fe\", \"Ru\"],\n", + " facets = {\"Pt\": [\"100\"], \"Fe\": [\"111\"]}, # If we want to specify only specific facets\n", + " supercell_dim = (2,2,5), # dimensions of the supercell\n", + " default_lat_param_lib = \"pbe_pw\", # where default lattice parameters are pulled from\n", + " vacuum = 10.,\n", + " n_fixed_layers = 3, # fixes bottom 3 layers\n", + " write_to_disk = False # if we want to write the slabs to disk in the AutoCat directory format\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1818f3f0", + "metadata": {}, + "source": [ + "This generates a dictionary containing the structures of the slabs. The organization of this dictionary is as follows:\n", + "\n", + "- Species\n", + " - Crystal Structure/Facet\n", + " - `ase.Atoms` structure\n", + " - Path to structure file (in the `ase.traj` format)" + ] + }, + { + "cell_type": "markdown", + "id": "d49b437a", + "metadata": {}, + "source": [ + "Thus, going layer by layer for this example, the first keys correspond to:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "91f530b4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['Pt', 'Fe', 'Ru'])\n" + ] + } + ], + "source": [ + "print(slab_dictionary.keys())" + ] + }, + { + "cell_type": "markdown", + "id": "7c4a89fd", + "metadata": {}, + "source": [ + "Continuing down `Pt` for example, the next level is then:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "17fb6812", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['fcc100'])\n" + ] + } + ], + "source": [ + "print(slab_dictionary[\"Pt\"].keys())" + ] + }, + { + "cell_type": "markdown", + "id": "8521948b", + "metadata": {}, + "source": [ + "Going down another level, we get both the `ase.Atoms` structure object as well as the file location. Since we didn't write to disk, the latter returns `None`" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "db7b9248", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Atoms(symbols='Pt20', pbc=[True, True, False], cell=[5.612606335552851, 5.612606335552851, 27.937424], tags=..., constraint=FixAtoms(indices=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]))\n" + ] + } + ], + "source": [ + "print(slab_dictionary[\"Pt\"][\"fcc100\"][\"structure\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f9d94169", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "print(slab_dictionary[\"Pt\"][\"fcc100\"][\"traj_file_path\"])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From ff874dba865b7ce8c2b082f564324b52b4bea81e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 6 May 2021 20:37:59 -0400 Subject: [PATCH 081/239] collect candidate structs on each sl iteration --- src/autocat/learning/sequential.py | 56 ++++++++++++++++++++++++------ tests/learning/test_sequential.py | 46 ++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 707ea1bf..0905875e 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -8,6 +8,7 @@ from typing import Union from ase import Atoms +from ase.io import write as ase_write from autocat.perturbations import generate_perturbed_dataset from autocat.learning.predictors import AutoCatStructureCorrector @@ -85,10 +86,27 @@ def multiple_sequential_learning_runs( if write_to_disk: if not os.path.isdir(write_location): os.makedirs(write_location) - write_path = os.path.join(write_location, "sl_runs_history.json") - with open(write_path, "w") as f: - json.dump(runs_history, f) - print(f"SL histories written to {write_path}") + json_write_path = os.path.join(write_location, "sl_runs_history.json") + data_runs_history = [] + for r_idx, run in enumerate(runs_history): + # get dict excluding selected candidate structures + j_dict = { + key: run[key] for key in run if key != "selected_candidate_history" + } + data_runs_history.append(j_dict) + # write out selected candidates for each iteration of each run + c_hist = run["selected_candidate_history"] + for i, c in enumerate(c_hist): + traj_file_path = os.path.join( + write_location, f"run{r_idx+1}_candidates_sl_iter{i+1}.traj" + ) + ase_write(traj_file_path, c) + print( + f"Selected SL Candidates for run {r_idx+1}, iteration {i+1} written to {traj_file_path}" + ) + with open(json_write_path, "w") as f: + json.dump(data_runs_history, f) + print(f"SL histories written to {json_write_path}") return runs_history @@ -178,9 +196,7 @@ def simulated_sequential_learning( batch_num_of_perturbations_per_base_structure * len(training_base_structures) < batch_size_to_add ): - msg = ( - "Batch size to add must be greater than the number of candidates generated" - ) + msg = "Batch size to add must be less than the number of candidates generated" raise AutoCatSequentialLearningError(msg) if initial_num_of_perturbations_per_base_structure is None: @@ -229,6 +245,7 @@ def simulated_sequential_learning( rmse_train_history = [] mae_test_history = [] rmse_test_history = [] + selected_candidate_history = [] ctr = 0 while len(max_unc_history) < number_of_sl_loops: ctr += 1 @@ -281,6 +298,10 @@ def simulated_sequential_learning( # add new perturbed struct to training set train_pert_structures.extend(next_candidate_struct) train_pert_corr_list.extend([new_pert_corr_list[idx] for idx in high_unc_idx],) + + # keeps all candidate structures added for each loop + selected_candidate_history.append(next_candidate_struct) + # keeps as lists to make writing to disk easier pred_corrs_history.append( [p.tolist() for p in [_pred_corrs[idx] for idx in high_unc_idx]] @@ -296,6 +317,7 @@ def simulated_sequential_learning( "full_unc_history": [unc.tolist() for unc in full_unc_history], "pred_corrs_history": pred_corrs_history, "real_corrs_history": [r for r in real_corrs_history], + "selected_candidate_history": selected_candidate_history, "mae_train_history": mae_train_history, "rmse_train_history": rmse_train_history, } @@ -307,9 +329,21 @@ def simulated_sequential_learning( if write_to_disk: if not os.path.isdir(write_location): os.makedirs(write_location) - write_path = os.path.join(write_location, "sl_dict.json") - with open(write_path, "w") as f: - json.dump(sl_dict, f) - print(f"SL dictionary written to {write_path}") + json_write_path = os.path.join(write_location, "sl_dict.json") + data_sl_dict = { + key: sl_dict[key] for key in sl_dict if key != "selected_candidate_history" + } + with open(json_write_path, "w") as f: + json.dump(data_sl_dict, f) + print(f"SL dictionary written to {json_write_path}") + candidate_struct_hist = sl_dict["selected_candidate_history"] + for i, c in enumerate(candidate_struct_hist): + traj_file_path = os.path.join( + write_location, f"candidates_sl_iter{i+1}.traj" + ) + ase_write(traj_file_path, c) + print( + f"Selected SL Candidates for iteration {i+1} written to {traj_file_path}" + ) return sl_dict diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 8ac54aff..7637060b 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -7,6 +7,8 @@ import tempfile +from ase.io import read as ase_read + from autocat.learning.predictors import AutoCatStructureCorrector from autocat.learning.sequential import simulated_sequential_learning from autocat.learning.sequential import multiple_sequential_learning_runs @@ -112,6 +114,7 @@ def test_simulated_sequential_outputs_testing(): [base_struct1, base_struct2], testing_base_structures=[base_struct3], batch_num_of_perturbations_per_base_structure=3, + batch_size_to_add=3, number_of_sl_loops=2, ) # Check lenght of testing scores @@ -120,6 +123,10 @@ def test_simulated_sequential_outputs_testing(): assert len(sl_dict["mae_train_history"]) == 2 assert sl_dict["mae_test_history"] != sl_dict["mae_train_history"] + # check selected_candidate_history + assert len(sl_dict["selected_candidate_history"]) == 2 + assert len(sl_dict["selected_candidate_history"][0]) == 3 + def test_simulated_sequential_write_to_disk(): # Test writing out sl dict @@ -139,13 +146,25 @@ def test_simulated_sequential_write_to_disk(): [base_struct1, base_struct2], testing_base_structures=[base_struct3], batch_num_of_perturbations_per_base_structure=1, - number_of_sl_loops=1, + batch_size_to_add=2, + number_of_sl_loops=2, write_to_disk=True, write_location=_tmp_dir, ) + # when writing to disk, candidate history separated out + sl_dict_data = { + key: sl_dict[key] for key in sl_dict if key != "selected_candidate_history" + } + # check data written as json with open(os.path.join(_tmp_dir, "sl_dict.json"), "r") as f: sl_written = json.load(f) - assert sl_dict == sl_written + assert sl_dict_data == sl_written + + # check written sl selected candidates + cands1 = ase_read(f"{_tmp_dir}/candidates_sl_iter1.traj", index=":") + assert sl_dict["selected_candidate_history"][0] == cands1 + cands2 = ase_read(f"{_tmp_dir}/candidates_sl_iter2.traj", index=":") + assert sl_dict["selected_candidate_history"][1] == cands2 def test_multiple_sequential_learning_serial(): @@ -204,6 +223,27 @@ def test_multiple_sequential_learning_write_to_disk(): write_to_disk=True, write_location=_tmp_dir, ) + runs_history_data = [] + for r in runs_history: + data_dict = {key: r[key] for key in r if key != "selected_candidate_history"} + runs_history_data.append(data_dict) + + # check data history with open(os.path.join(_tmp_dir, "sl_runs_history.json"), "r") as f: runs_history_written = json.load(f) - assert runs_history == runs_history_written + assert runs_history_data == runs_history_written + + # check selected candidate history + # check run 1 iteration 1 + cands1_1 = ase_read(f"{_tmp_dir}/run1_candidates_sl_iter1.traj", index=":") + assert len(cands1_1) == 1 + assert runs_history[0]["selected_candidate_history"][0] == cands1_1 + # check run 1 iteration 2 + cands1_2 = ase_read(f"{_tmp_dir}/run1_candidates_sl_iter2.traj", index=":") + assert runs_history[0]["selected_candidate_history"][1] == cands1_2 + # check run 2 iteration 1 + cands2_1 = ase_read(f"{_tmp_dir}/run2_candidates_sl_iter1.traj", index=":") + assert runs_history[1]["selected_candidate_history"][0] == cands2_1 + # check run 2 iteration 2 + cands2_2 = ase_read(f"{_tmp_dir}/run2_candidates_sl_iter2.traj", index=":") + assert runs_history[1]["selected_candidate_history"][1] == cands2_2 From 5e7b2080cd402fd71965ba5f0a532294d6cca508 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 11 May 2021 16:06:26 -0400 Subject: [PATCH 082/239] writes candidate structures to disk in nested dirs --- src/autocat/learning/sequential.py | 26 +++++++++++++++++-------- tests/learning/test_sequential.py | 31 +++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 0905875e..554e39b4 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -27,6 +27,7 @@ def multiple_sequential_learning_runs( number_parallel_jobs: int = None, write_to_disk: bool = False, write_location: str = ".", + dirs_exist_ok_structures: bool = False, **sl_kwargs, ): """ @@ -56,6 +57,9 @@ def multiple_sequential_learning_runs( write_location: String with the location where runs history should be written to disk. + dirs_exist_ok_structures: + Boolean indicating if existing candidate structure files can be overwritten + Returns ------- @@ -96,11 +100,13 @@ def multiple_sequential_learning_runs( data_runs_history.append(j_dict) # write out selected candidates for each iteration of each run c_hist = run["selected_candidate_history"] + traj_file_path = os.path.join( + write_location, f"candidate_structures/run{r_idx+1}", + ) + os.makedirs(traj_file_path, exist_ok=dirs_exist_ok_structures) for i, c in enumerate(c_hist): - traj_file_path = os.path.join( - write_location, f"run{r_idx+1}_candidates_sl_iter{i+1}.traj" - ) - ase_write(traj_file_path, c) + traj_filename = os.path.join(traj_file_path, f"sl_iter{i+1}.traj") + ase_write(traj_filename, c) print( f"Selected SL Candidates for run {r_idx+1}, iteration {i+1} written to {traj_file_path}" ) @@ -123,6 +129,7 @@ def simulated_sequential_learning( number_of_sl_loops: int = 100, write_to_disk: bool = False, write_location: str = ".", + dirs_exist_ok_structures: bool = False, ): """ Conducts a simulated sequential learning loop given @@ -175,6 +182,9 @@ def simulated_sequential_learning( write_location: String with the location where sl_dict should be written to disk. + dirs_exist_ok_structures: + Boolean indicating if existing candidate structure files can be overwritten + Returns ------- @@ -337,11 +347,11 @@ def simulated_sequential_learning( json.dump(data_sl_dict, f) print(f"SL dictionary written to {json_write_path}") candidate_struct_hist = sl_dict["selected_candidate_history"] + traj_file_path = os.path.join(write_location, f"candidate_structures") + os.makedirs(traj_file_path, exist_ok=dirs_exist_ok_structures) for i, c in enumerate(candidate_struct_hist): - traj_file_path = os.path.join( - write_location, f"candidates_sl_iter{i+1}.traj" - ) - ase_write(traj_file_path, c) + traj_filename = os.path.join(traj_file_path, f"sl_iter{i+1}.traj") + ase_write(traj_filename, c) print( f"Selected SL Candidates for iteration {i+1} written to {traj_file_path}" ) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 7637060b..f6262e7b 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -161,9 +161,13 @@ def test_simulated_sequential_write_to_disk(): assert sl_dict_data == sl_written # check written sl selected candidates - cands1 = ase_read(f"{_tmp_dir}/candidates_sl_iter1.traj", index=":") - assert sl_dict["selected_candidate_history"][0] == cands1 - cands2 = ase_read(f"{_tmp_dir}/candidates_sl_iter2.traj", index=":") + cands1 = ase_read(f"{_tmp_dir}/candidate_structures/sl_iter1.traj", index=":") + # print(cands1) + assert len(cands1) == 2 + # assert sl_dict["selected_candidate_history"][0] == cands1 + cands2 = ase_read(f"{_tmp_dir}/candidate_structures/sl_iter2.traj", index=":") + print(cands2) + print(sl_dict["selected_candidate_history"]) assert sl_dict["selected_candidate_history"][1] == cands2 @@ -218,8 +222,9 @@ def test_multiple_sequential_learning_write_to_disk(): acsc, [base_struct1], 3, - batch_num_of_perturbations_per_base_structure=1, + batch_num_of_perturbations_per_base_structure=2, number_of_sl_loops=2, + batch_size_to_add=2, write_to_disk=True, write_location=_tmp_dir, ) @@ -235,15 +240,23 @@ def test_multiple_sequential_learning_write_to_disk(): # check selected candidate history # check run 1 iteration 1 - cands1_1 = ase_read(f"{_tmp_dir}/run1_candidates_sl_iter1.traj", index=":") - assert len(cands1_1) == 1 + cands1_1 = ase_read( + f"{_tmp_dir}/candidate_structures/run1/sl_iter1.traj", index=":" + ) + assert len(cands1_1) == 2 assert runs_history[0]["selected_candidate_history"][0] == cands1_1 # check run 1 iteration 2 - cands1_2 = ase_read(f"{_tmp_dir}/run1_candidates_sl_iter2.traj", index=":") + cands1_2 = ase_read( + f"{_tmp_dir}/candidate_structures/run1/sl_iter2.traj", index=":" + ) assert runs_history[0]["selected_candidate_history"][1] == cands1_2 # check run 2 iteration 1 - cands2_1 = ase_read(f"{_tmp_dir}/run2_candidates_sl_iter1.traj", index=":") + cands2_1 = ase_read( + f"{_tmp_dir}/candidate_structures/run2/sl_iter1.traj", index=":" + ) assert runs_history[1]["selected_candidate_history"][0] == cands2_1 # check run 2 iteration 2 - cands2_2 = ase_read(f"{_tmp_dir}/run2_candidates_sl_iter2.traj", index=":") + cands2_2 = ase_read( + f"{_tmp_dir}/candidate_structures/run2/sl_iter2.traj", index=":" + ) assert runs_history[1]["selected_candidate_history"][1] == cands2_2 From c2980203172fe424531e7887c0b7af2f933c0add Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 14 May 2021 13:45:01 -0400 Subject: [PATCH 083/239] keep test predictions and uncs for each loop --- src/autocat/learning/predictors.py | 23 ++++++++++++--- src/autocat/learning/sequential.py | 45 +++++++++++++++++++----------- tests/learning/test_predictors.py | 6 ++++ tests/learning/test_sequential.py | 40 +++++++++++++++----------- 4 files changed, 76 insertions(+), 38 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 009352cd..574cf30d 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -470,7 +470,8 @@ def score( self, test_structure_guesses: List[Atoms], corrections_list: List[np.ndarray], - metric="mae", + metric: str = "mae", + return_predictions: bool = False, ): """ Returns a prediction score given the actual corrections. @@ -491,6 +492,10 @@ def score( - rmse: square root of the average of the average norm displacement vector difference squared + return_predictions: + Bool indicating whether the predictions and uncertainties should + be returned in addition to the score + Returns ------- @@ -499,7 +504,7 @@ def score( """ assert self.is_fit - pred_corr, _, _ = self.predict(test_structure_guesses) + pred_corr, _, unc = self.predict(test_structure_guesses) if metric == "mae": all_abs_vec_diff = [] @@ -510,7 +515,10 @@ def score( np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) ) all_abs_vec_diff.append(abs_vec_diff / N_i) - return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff) + if return_predictions: + return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff), pred_corr, unc + else: + return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff) elif metric == "rmse": all_sq_vec_diff = [] for i in range(len(pred_corr)): @@ -520,7 +528,14 @@ def score( np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) ** 2 ) all_sq_vec_diff.append(sq_vec_diff / N_i) - return np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)) + if return_predictions: + return ( + np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)), + pred_corr, + unc, + ) + else: + return np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)) else: msg = f"Metric: {metric} is not supported" raise AutocatStructureCorrectorError(msg) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 554e39b4..2e1f66db 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -191,14 +191,16 @@ def simulated_sequential_learning( sl_dict: Dictionary containing histories of different quantities throughout the calculation: - - maximum uncertainty history - - full uncertainty history + - candidate maximum uncertainty history + - candidate full uncertainty history - predicted corrections training history - real corrections training history - mae training history - rmse training history - mae testing history (if test structures given) - rmse testing history (if test structures given) + - testing predictions (if test structures given) + - testing uncertainties (if test structures given) For the corrections histories, the dimensions are as follows: num of loops -> num of candidates added -> corrections applied """ @@ -247,17 +249,19 @@ def simulated_sequential_learning( validation_pert_structures = validation_pert_dataset["collected_structures"] validation_pert_corr_list = validation_pert_dataset["corrections_list"] - full_unc_history = [] - max_unc_history = [] - pred_corrs_history = [] - real_corrs_history = [] + candidate_full_unc_history = [] + candidate_max_unc_history = [] + candidate_pred_corrs_history = [] + candidate_real_corrs_history = [] mae_train_history = [] rmse_train_history = [] mae_test_history = [] rmse_test_history = [] selected_candidate_history = [] + test_preds_history = [] + test_unc_history = [] ctr = 0 - while len(max_unc_history) < number_of_sl_loops: + while len(candidate_max_unc_history) < number_of_sl_loops: ctr += 1 print(f"Sequential Learning Iteration #{ctr}") # generate new perturbations to predict on @@ -289,20 +293,23 @@ def simulated_sequential_learning( # get scores on test perturbations if testing_base_structures is not None: - mae_test_history.append( - structure_corrector.score(test_pert_structures, test_pert_corr_list) + mae_test_score, test_preds, test_unc = structure_corrector.score( + test_pert_structures, test_pert_corr_list, return_predictions=True ) + mae_test_history.append(mae_test_score) + test_preds_history.append([p.tolist() for p in test_preds]) + test_unc_history.append([u.tolist() for u in test_unc]) rmse_test_history.append( structure_corrector.score( test_pert_structures, test_pert_corr_list, metric="rmse" ) ) - full_unc_history.append(_uncs) + candidate_full_unc_history.append(_uncs) # find candidate with highest uncertainty high_unc_idx = np.argsort(_uncs)[-batch_size_to_add:] - max_unc_history.append(_uncs[high_unc_idx]) + candidate_max_unc_history.append(_uncs[high_unc_idx]) next_candidate_struct = [new_pert_structs[idx] for idx in high_unc_idx] # add new perturbed struct to training set @@ -313,20 +320,22 @@ def simulated_sequential_learning( selected_candidate_history.append(next_candidate_struct) # keeps as lists to make writing to disk easier - pred_corrs_history.append( + candidate_pred_corrs_history.append( [p.tolist() for p in [_pred_corrs[idx] for idx in high_unc_idx]] ) - real_corrs_history.append( + candidate_real_corrs_history.append( [new_pert_corr_list[idx].tolist() for idx in high_unc_idx] ) structure_corrector.fit( train_pert_structures, corrections_list=train_pert_corr_list ) sl_dict = { - "max_unc_history": [mu.tolist() for mu in max_unc_history], - "full_unc_history": [unc.tolist() for unc in full_unc_history], - "pred_corrs_history": pred_corrs_history, - "real_corrs_history": [r for r in real_corrs_history], + "candidate_max_unc_history": [mu.tolist() for mu in candidate_max_unc_history], + "candidate_full_unc_history": [ + unc.tolist() for unc in candidate_full_unc_history + ], + "candidate_pred_corrs_history": candidate_pred_corrs_history, + "candidate_real_corrs_history": [r for r in candidate_real_corrs_history], "selected_candidate_history": selected_candidate_history, "mae_train_history": mae_train_history, "rmse_train_history": rmse_train_history, @@ -335,6 +344,8 @@ def simulated_sequential_learning( if testing_base_structures is not None: sl_dict["mae_test_history"] = mae_test_history sl_dict["rmse_test_history"] = rmse_test_history + sl_dict["test_preds_history"] = test_preds_history + sl_dict["test_unc_history"] = test_unc_history if write_to_disk: if not os.path.isdir(write_location): diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index b80441bd..3e440cc6 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -123,6 +123,12 @@ def test_score_on_perturbed_systems(): mae = acsc.score(p_structures[15:], corrections_list) assert isinstance(mae, float) rmse = acsc.score(p_structures[15:], corrections_list, metric="rmse") + # Test returning predictions + _, pred_corr, unc = acsc.score( + p_structures[15:], corrections_list, return_predictions=True + ) + assert len(pred_corr) == 5 + assert len(unc) == 5 assert mae != rmse with pytest.raises(AutocatStructureCorrectorError): acsc.score(p_structures[15:], corrections_list, metric="msd") diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index f6262e7b..1dad3833 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -35,9 +35,9 @@ def test_simulated_sequential_outputs(): number_of_sl_loops=4, ) # Test that number of max uncertainties equals number of sl loops - assert len(sl_dict["max_unc_history"]) == 4 + assert len(sl_dict["candidate_max_unc_history"]) == 4 # Test the number of total uncertainties collected - assert len(sl_dict["full_unc_history"][-1]) == 6 + assert len(sl_dict["candidate_full_unc_history"][-1]) == 6 sl_dict = simulated_sequential_learning( acsc, [base_struct1, base_struct2], @@ -45,19 +45,21 @@ def test_simulated_sequential_outputs(): number_of_sl_loops=4, ) # check default for initial number of training perturbations - assert len(sl_dict["full_unc_history"][0]) == 2 + assert len(sl_dict["candidate_full_unc_history"][0]) == 2 # check all mae and rmse training scores collected assert len(sl_dict["mae_train_history"]) == 4 assert len(sl_dict["rmse_train_history"]) == 4 # check prediction and correction history - assert len(sl_dict["pred_corrs_history"]) == len(sl_dict["real_corrs_history"]) - assert len(sl_dict["pred_corrs_history"][-1]) == len( - sl_dict["real_corrs_history"][-1] + assert len(sl_dict["candidate_pred_corrs_history"]) == len( + sl_dict["candidate_real_corrs_history"] ) - assert len(sl_dict["pred_corrs_history"]) == 4 - assert len(sl_dict["real_corrs_history"]) == 4 + assert len(sl_dict["candidate_pred_corrs_history"][-1]) == len( + sl_dict["candidate_real_corrs_history"][-1] + ) + assert len(sl_dict["candidate_pred_corrs_history"]) == 4 + assert len(sl_dict["candidate_real_corrs_history"]) == 4 def test_simulated_sequential_batch_added(): @@ -81,20 +83,20 @@ def test_simulated_sequential_batch_added(): number_of_sl_loops=num_loops, ) # each max unc history should be equal to number of candidates added - assert len(sl_dict["max_unc_history"][0]) == bsta + assert len(sl_dict["candidate_max_unc_history"][0]) == bsta # first dimension is number of loops - assert len(sl_dict["pred_corrs_history"]) == num_loops + assert len(sl_dict["candidate_pred_corrs_history"]) == num_loops # next dimension is size of candidates added on each loop - assert len(sl_dict["pred_corrs_history"][0]) == bsta + assert len(sl_dict["candidate_pred_corrs_history"][0]) == bsta # next dimension is size of adsorbate - assert len(sl_dict["pred_corrs_history"][0][0]) == 2 + assert len(sl_dict["candidate_pred_corrs_history"][0][0]) == 2 # next dimension is vector correction to atom in adsorbate - assert len(sl_dict["pred_corrs_history"][0][0][0]) == 3 + assert len(sl_dict["candidate_pred_corrs_history"][0][0][0]) == 3 # check same holds for real history - assert len(sl_dict["real_corrs_history"]) == num_loops - assert len(sl_dict["real_corrs_history"][0]) == bsta - assert len(sl_dict["real_corrs_history"][0][0]) == 2 - assert len(sl_dict["real_corrs_history"][0][0][0]) == 3 + assert len(sl_dict["candidate_real_corrs_history"]) == num_loops + assert len(sl_dict["candidate_real_corrs_history"][0]) == bsta + assert len(sl_dict["candidate_real_corrs_history"][0][0]) == 2 + assert len(sl_dict["candidate_real_corrs_history"][0][0][0]) == 3 def test_simulated_sequential_outputs_testing(): @@ -121,6 +123,10 @@ def test_simulated_sequential_outputs_testing(): assert len(sl_dict["mae_test_history"]) == 2 assert len(sl_dict["rmse_train_history"]) == 2 assert len(sl_dict["mae_train_history"]) == 2 + assert len(sl_dict["test_preds_history"]) == 2 + assert len(sl_dict["test_preds_history"][0]) == 3 + assert len(sl_dict["test_unc_history"]) == 2 + assert len(sl_dict["test_unc_history"][0]) == 3 assert sl_dict["mae_test_history"] != sl_dict["mae_train_history"] # check selected_candidate_history From bef1ee2bd3f37b082b42327728b6f22e337ac08f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 18 May 2021 22:46:11 -0400 Subject: [PATCH 084/239] add num test perts param, return real test corrs --- src/autocat/learning/sequential.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 2e1f66db..007261d2 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -125,6 +125,7 @@ def simulated_sequential_learning( maximum_perturbation_distance: float = 0.75, initial_num_of_perturbations_per_base_structure: int = None, batch_num_of_perturbations_per_base_structure: int = 2, + test_num_of_perturbations_per_base_structure: int = None, batch_size_to_add: int = 1, number_of_sl_loops: int = 100, write_to_disk: bool = False, @@ -231,9 +232,13 @@ def simulated_sequential_learning( ) if testing_base_structures is not None: + if test_num_of_perturbations_per_base_structure is None: + test_num_of_perturbations_per_base_structure = ( + batch_num_of_perturbations_per_base_structure + ) test_pert_dataset = generate_perturbed_dataset( testing_base_structures, - num_of_perturbations=initial_num_of_perturbations_per_base_structure, + num_of_perturbations=test_num_of_perturbations_per_base_structure, maximum_perturbation_distance=maximum_perturbation_distance, minimum_perturbation_distance=minimum_perturbation_distance, ) @@ -345,6 +350,7 @@ def simulated_sequential_learning( sl_dict["mae_test_history"] = mae_test_history sl_dict["rmse_test_history"] = rmse_test_history sl_dict["test_preds_history"] = test_preds_history + sl_dict["test_real_corrections"] = [t.tolist() for t in test_pert_corr_list] sl_dict["test_unc_history"] = test_unc_history if write_to_disk: From 403d69fc8a9b096a9a470a11e72ffe22c5bc7487 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 25 Jun 2021 14:14:07 -0400 Subject: [PATCH 085/239] start generalizing predictor - AutoCatStructureCorrector -> AutoCatPredictor - arbitrary labels (no longer has to be specific to perturbation approach) - will no longer automatically apply corrections to structure --- src/autocat/learning/predictors.py | 126 +++++++++-------------------- tests/learning/test_predictors.py | 88 +++++++------------- 2 files changed, 64 insertions(+), 150 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 574cf30d..dc54a301 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -13,11 +13,11 @@ from autocat.learning.featurizers import _get_number_of_features -class AutocatStructureCorrectorError(Exception): +class AutoCatPredictorError(Exception): pass -class AutoCatStructureCorrector: +class AutoCatPredictor: def __init__( self, model_class=None, @@ -276,10 +276,7 @@ def get_total_number_of_features(self): return num_struct_feat, num_ads_feat def fit( - self, - perturbed_structures: List[Union[Atoms, str]], - corrections_list: List[np.ndarray] = None, - correction_matrix: np.ndarray = None, + self, training_structures: List[Union[Atoms, str]], y: np.ndarray, ): """ Given a list of perturbed structures @@ -288,20 +285,12 @@ def fit( Parameters ---------- - perturbed_structures: + training_structures: List of perturbed structures to be trained upon - correction_matrix: - Numpy array of collected matrices of perturbations corresponding to - each of the perturbed structures. - This can be generated via `autocat.perturbations.generate_perturbed_dataset`. - Shape should be (# of structures, 3 * # of atoms in the largest structure) - - correction_list: - List of np.arrays of correction vectors - where each item is of shape (# of adsorbate atoms, 3). - Adding the negative of these vectors to any perturbed - structure should return it to the base structure + y: + Numpy array of labels corresponding to training structures + of shape (# of training structures, # of targets) Returns ------- @@ -310,7 +299,7 @@ def fit( Trained `sklearn` model object """ X = get_X( - perturbed_structures, + training_structures, maximum_structure_size=self.maximum_structure_size, structure_featurizer=self.structure_featurizer, maximum_adsorbate_size=self.maximum_adsorbate_size, @@ -325,17 +314,15 @@ def fit( if self.refine_structures: ref_structures = [ structure[np.where(structure.get_tags() < 2)[0].tolist()] - for structure in perturbed_structures + for structure in training_structures ] self.maximum_structure_size = max([len(ref) for ref in ref_structures]) else: - self.maximum_structure_size = max( - [len(s) for s in perturbed_structures] - ) + self.maximum_structure_size = max([len(s) for s in training_structures]) if self.maximum_adsorbate_size is None: adsorbate_sizes = [] - for struct in perturbed_structures: + for struct in training_structures: adsorbate_sizes.append( len(np.where(struct.get_tags() <= 0)[0].tolist()) ) @@ -343,7 +330,7 @@ def fit( if self.species_list is None: species_list = [] - for s in perturbed_structures: + for s in training_structures: found_species = np.unique(s.get_chemical_symbols()).tolist() new_species = [ spec for spec in found_species if spec not in species_list @@ -351,27 +338,17 @@ def fit( species_list.extend(new_species) self.species_list = species_list - if corrections_list is not None: - correction_matrix = np.zeros( - (len(corrections_list), 3 * self.maximum_adsorbate_size) - ) - for idx, row in enumerate(corrections_list): - correction_matrix[idx, : 3 * len(row)] = row.flatten() - elif correction_matrix is not None: - if correction_matrix.shape[1] != 3 * self.maximum_adsorbate_size: - msg = f"Correction matrix must have {3 * self.maximum_adsorbate_size} targets, got {correction_matrix.shape[1]}" - raise AutocatStructureCorrectorError(msg) - else: - msg = "Must specify either corrections list or matrix" - raise AutocatStructureCorrectorError(msg) + if len(y.shape) == 1: + y = y.reshape(-1, 1) if not self.multiple_separate_models: - self.regressor.fit(X, correction_matrix) + self.regressor.fit(X, y) else: + assert y.shape[1] > 1 regs = [] - for i in range(correction_matrix.shape[1]): + for i in range(y.shape[1]): reg = self.model_class(**self.model_kwargs or {}) - reg.fit(X, correction_matrix[:, i]) + reg.fit(X, y[:, i]) regs.append(reg) assert regs[0] is not regs[1] self.regressor = regs @@ -379,7 +356,7 @@ def fit( self.is_fit = True def predict( - self, initial_structure_guesses: List[Atoms], + self, testing_structures: List[Atoms], ): """ From a trained model, will predict corrected structure @@ -388,7 +365,7 @@ def predict( Parameters ---------- - initial_structure_guesses: + testing_structures: List of Atoms objects of initial guesses for adsorbate placement to be optimized @@ -398,16 +375,13 @@ def predict( predicted_corrections: List of corrections to be applied to each input structure - corrected_structures: - List of Atoms object with corrections applied - unc: List of uncertainties for each prediction """ assert self.is_fit featurized_input = get_X( - structures=initial_structure_guesses, + structures=testing_structures, structure_featurizer=self.structure_featurizer, adsorbate_featurizer=self.adsorbate_featurizer, maximum_structure_size=self.maximum_structure_size, @@ -418,58 +392,34 @@ def predict( ) if not self.multiple_separate_models: try: - predicted_correction_matrix_full, unc = self.regressor.predict( + predicted_labels, unc = self.regressor.predict( featurized_input, return_std=True ) except TypeError: - predicted_correction_matrix_full = self.regressor.predict( - featurized_input, - ) + predicted_labels = self.regressor.predict(featurized_input,) unc = None else: - predicted_correction_matrix_full = np.zeros( - (len(initial_structure_guesses), 3 * self.maximum_adsorbate_size) - ) + predicted_labels = np.zeros((len(testing_structures), len(self.regressor))) try: - uncs = np.zeros((len(initial_structure_guesses), len(self.regressor))) + uncs = np.zeros((len(testing_structures), len(self.regressor))) for idx, r in enumerate(self.regressor): preds = r.predict(featurized_input, return_std=True) - predicted_correction_matrix_full[:, idx] = preds[0] + predicted_labels[:, idx] = preds[0] # uncertainties for target r for each struct to predict on uncs[:, idx] = preds[1] # take average uncertainty for each struct unc = np.mean(uncs, axis=1) except TypeError: for idx, r in enumerate(self.regressor): - predicted_correction_matrix_full[:, idx] = r.predict( - featurized_input - ) + predicted_labels[:, idx] = r.predict(featurized_input) unc = None - corrected_structures = [ - init_struct.copy() for init_struct in initial_structure_guesses - ] - - corrected_structures = [] - predicted_corrections = [] - for idx, struct in enumerate(initial_structure_guesses): - cs = struct.copy() - list_of_adsorbate_indices = np.where(struct.get_tags() <= 0)[0].tolist() - list_of_adsorbate_indices.sort() - num_of_adsorbates = len(list_of_adsorbate_indices) - corr = predicted_correction_matrix_full[ - idx, : 3 * num_of_adsorbates - ].reshape(num_of_adsorbates, 3) - predicted_corrections.append(corr) - cs.positions[list_of_adsorbate_indices] -= corr - corrected_structures.append(cs) - - return predicted_corrections, corrected_structures, unc + return predicted_labels, unc def score( self, - test_structure_guesses: List[Atoms], - corrections_list: List[np.ndarray], + testing_structures: List[Atoms], + y: List[np.ndarray], metric: str = "mae", return_predictions: bool = False, ): @@ -504,16 +454,14 @@ def score( """ assert self.is_fit - pred_corr, _, unc = self.predict(test_structure_guesses) + pred_corr, unc = self.predict(testing_structures) if metric == "mae": all_abs_vec_diff = [] for i in range(len(pred_corr)): - assert pred_corr[i].shape == corrections_list[i].shape + assert pred_corr[i].shape == y[i].shape N_i = len(pred_corr[i]) - abs_vec_diff = np.sum( - np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) - ) + abs_vec_diff = np.sum(np.linalg.norm(y[i] - pred_corr[i], axis=1)) all_abs_vec_diff.append(abs_vec_diff / N_i) if return_predictions: return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff), pred_corr, unc @@ -522,11 +470,9 @@ def score( elif metric == "rmse": all_sq_vec_diff = [] for i in range(len(pred_corr)): - assert pred_corr[i].shape == corrections_list[i].shape + assert pred_corr[i].shape == y[i].shape N_i = len(pred_corr[i]) - sq_vec_diff = np.sum( - np.linalg.norm(corrections_list[i] - pred_corr[i], axis=1) ** 2 - ) + sq_vec_diff = np.sum(np.linalg.norm(y[i] - pred_corr[i], axis=1) ** 2) all_sq_vec_diff.append(sq_vec_diff / N_i) if return_predictions: return ( @@ -538,4 +484,4 @@ def score( return np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)) else: msg = f"Metric: {metric} is not supported" - raise AutocatStructureCorrectorError(msg) + raise AutoCatPredictorError(msg) diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 3e440cc6..19cd36ec 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -15,8 +15,8 @@ from autocat.perturbations import generate_perturbed_dataset from autocat.learning.featurizers import get_X from autocat.learning.featurizers import catalyst_featurization -from autocat.learning.predictors import AutoCatStructureCorrector -from autocat.learning.predictors import AutocatStructureCorrectorError +from autocat.learning.predictors import AutoCatPredictor +from autocat.learning.predictors import AutoCatPredictorError def test_fit_model_on_perturbed_systems(): @@ -28,13 +28,13 @@ def test_fit_model_on_perturbed_systems(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatStructureCorrector( + acsc = AutoCatPredictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures, correction_matrix=correction_matrix, + p_structures, y=correction_matrix, ) assert acsc.adsorbate_featurizer == "soap" assert acsc.is_fit @@ -49,11 +49,9 @@ def test_fit_model_on_perturbed_systems(): acsc.adsorbate_featurizer = "soap" acsc.adsorbate_featurization_kwargs = {"rcut": 3.0, "nmax": 6, "lmax": 6} acsc.fit( - p_structures, correction_matrix=correction_matrix, + p_structures, y=correction_matrix, ) assert acsc.is_fit - with pytest.raises(AutocatStructureCorrectorError): - acsc.fit(p_structures) def test_predict_initial_configuration_formats(): @@ -65,39 +63,16 @@ def test_predict_initial_configuration_formats(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatStructureCorrector( - structure_featurizer="sine_matrix", - adsorbate_featurizer="soap", - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, - maximum_adsorbate_size=3, - ) - with pytest.raises(AutocatStructureCorrectorError): - # check that supplied correction matrix is properly padded - acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], - ) - acsc = AutoCatStructureCorrector( + acsc = AutoCatPredictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) - predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) - assert isinstance(corrected_structures[0], Atoms) - assert len(corrected_structures) == 5 - # check that even with refining, corrected structure is - # returned to full size - assert len(corrected_structures[2]) == len(p_structures[17]) + predicted_corrections, uncs = acsc.predict(p_structures[15:],) assert len(predicted_corrections) == 5 - # check that predicted correction matrix is applied correctly - manual = p_structures[15].copy() - manual_corr_mat = predicted_corrections[0] - assert predicted_corrections[0].shape == (2, 3) - manual.positions[-1] += manual_corr_mat[-1] - manual.positions[-2] += manual_corr_mat[-2] - assert np.allclose(manual.positions, corrected_structures[0].positions) # check dimension of uncertainty estimates assert len(uncs) == 5 @@ -112,7 +87,7 @@ def test_score_on_perturbed_systems(): p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] corrections_list = p_set["corrections_list"] - acsc = AutoCatStructureCorrector( + acsc = AutoCatPredictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, @@ -130,13 +105,13 @@ def test_score_on_perturbed_systems(): assert len(pred_corr) == 5 assert len(unc) == 5 assert mae != rmse - with pytest.raises(AutocatStructureCorrectorError): + with pytest.raises(AutoCatPredictorError): acsc.score(p_structures[15:], corrections_list, metric="msd") def test_model_class_and_kwargs(): # Tests providing regression model class and kwargs - acsc = AutoCatStructureCorrector(KernelRidge, model_kwargs={"gamma": 0.5}) + acsc = AutoCatPredictor(KernelRidge, model_kwargs={"gamma": 0.5}) assert isinstance(acsc.regressor, KernelRidge) # check that regressor created with correct kwarg assert acsc.regressor.gamma == 0.5 @@ -144,7 +119,7 @@ def test_model_class_and_kwargs(): acsc.model_class = GaussianProcessRegressor # check that kwargs are removed when class is changed assert acsc.model_kwargs is None - acsc = AutoCatStructureCorrector() + acsc = AutoCatPredictor() acsc.model_kwargs = {"alpha": 2.5} assert acsc.model_kwargs == {"alpha": 2.5} assert acsc.regressor.alpha == 2.5 @@ -160,18 +135,17 @@ def test_model_without_unc(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatStructureCorrector( + acsc = AutoCatPredictor( model_class=KernelRidge, structure_featurizer="sine_matrix", adsorbate_featurizer=None, ) acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) - predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + predicted_corrections, uncs = acsc.predict(p_structures[15:],) assert uncs is None assert predicted_corrections is not None - assert corrected_structures is not None def test_multi_separate_models_fit(): @@ -185,14 +159,14 @@ def test_multi_separate_models_fit(): ) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatStructureCorrector( + acsc = AutoCatPredictor( structure_featurizer="sine_matrix", adsorbate_featurizer=None, multiple_separate_models=True, maximum_adsorbate_size=2, ) acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) assert len(acsc.regressor) == 6 assert isinstance(acsc.regressor[0], GaussianProcessRegressor) @@ -201,7 +175,7 @@ def test_multi_separate_models_fit(): # Check fitting to supplied model class acsc.model_class = BayesianRidge acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) assert len(acsc.regressor) == 6 assert hasattr(acsc.regressor[2], "coef_") @@ -217,39 +191,33 @@ def test_multi_separate_models_predict(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatStructureCorrector( + acsc = AutoCatPredictor( structure_featurizer="sine_matrix", adsorbate_featurizer=None, multiple_separate_models=True, ) acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) - predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + predicted_corrections, uncs = acsc.predict(p_structures[15:],) assert len(predicted_corrections) == 5 - assert len(predicted_corrections[0]) == 2 - assert len(predicted_corrections[0][0]) == 3 - assert len(corrected_structures) == 5 + assert len(predicted_corrections[0]) == correction_matrix.shape[1] assert len(uncs) == 5 # Test for model class without unc acsc.model_class = KernelRidge acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) - predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + predicted_corrections, uncs = acsc.predict(p_structures[15:],) assert len(predicted_corrections) == 5 - assert len(predicted_corrections[0]) == 2 - assert len(predicted_corrections[0][0]) == 3 - assert len(corrected_structures) == 5 + assert len(predicted_corrections[0]) == correction_matrix.shape[1] assert uncs is None # Test for model class with uncertainty acsc.model_class = BayesianRidge acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) - predicted_corrections, corrected_structures, uncs = acsc.predict(p_structures[15:],) + predicted_corrections, uncs = acsc.predict(p_structures[15:],) assert len(predicted_corrections) == 5 - assert len(predicted_corrections[0]) == 2 - assert len(predicted_corrections[0][0]) == 3 - assert len(corrected_structures) == 5 + assert len(predicted_corrections[0]) == correction_matrix.shape[1] assert len(uncs) == 5 From 478ed4dcdf395ce9e421a5cc750bbfadeb65483d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 25 Jun 2021 17:32:07 -0400 Subject: [PATCH 086/239] generalize score method --- src/autocat/learning/predictors.py | 35 ++++++++++++++++++++---------- tests/learning/test_predictors.py | 20 ++++++++++++----- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index dc54a301..c99e3df3 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -419,7 +419,7 @@ def predict( def score( self, testing_structures: List[Atoms], - y: List[np.ndarray], + y: np.ndarray, metric: str = "mae", return_predictions: bool = False, ): @@ -454,30 +454,41 @@ def score( """ assert self.is_fit - pred_corr, unc = self.predict(testing_structures) + pred_label, unc = self.predict(testing_structures) + + if len(y.shape) == 1: + y = y.reshape(-1, 1) if metric == "mae": all_abs_vec_diff = [] - for i in range(len(pred_corr)): - assert pred_corr[i].shape == y[i].shape - N_i = len(pred_corr[i]) - abs_vec_diff = np.sum(np.linalg.norm(y[i] - pred_corr[i], axis=1)) + for i in range(len(pred_label)): + assert pred_label[i].shape == y[i].shape + N_i = len(pred_label[i]) + # check if multi-target and takes norm if it is + if len(y[i]) > 1: + abs_vec_diff = np.sum(np.linalg.norm(y[i] - pred_label[i])) + else: + abs_vec_diff = np.sum(y[i] - pred_label[i]) all_abs_vec_diff.append(abs_vec_diff / N_i) if return_predictions: - return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff), pred_corr, unc + return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff), pred_label, unc else: return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff) elif metric == "rmse": all_sq_vec_diff = [] - for i in range(len(pred_corr)): - assert pred_corr[i].shape == y[i].shape - N_i = len(pred_corr[i]) - sq_vec_diff = np.sum(np.linalg.norm(y[i] - pred_corr[i], axis=1) ** 2) + for i in range(len(pred_label)): + assert pred_label[i].shape == y[i].shape + N_i = len(pred_label[i]) + # check if multi-target and takes norm if it is + if len(y[i]) > 1: + sq_vec_diff = np.sum(np.linalg.norm(y[i] - pred_label[i]) ** 2) + else: + sq_vec_diff = np.sum((y[i] - pred_label[i]) ** 2) all_sq_vec_diff.append(sq_vec_diff / N_i) if return_predictions: return ( np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)), - pred_corr, + pred_label, unc, ) else: diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 19cd36ec..4e0d46cb 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -86,27 +86,35 @@ def test_score_on_perturbed_systems(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - corrections_list = p_set["corrections_list"] acsc = AutoCatPredictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures[:15], correction_matrix=correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15, :], ) - mae = acsc.score(p_structures[15:], corrections_list) + mae = acsc.score(p_structures[15:], correction_matrix) assert isinstance(mae, float) - rmse = acsc.score(p_structures[15:], corrections_list, metric="rmse") + rmse = acsc.score(p_structures[15:], correction_matrix, metric="rmse") + assert isinstance(rmse, float) + # Test returning predictions _, pred_corr, unc = acsc.score( - p_structures[15:], corrections_list, return_predictions=True + p_structures[15:], correction_matrix, return_predictions=True ) assert len(pred_corr) == 5 assert len(unc) == 5 assert mae != rmse with pytest.raises(AutoCatPredictorError): - acsc.score(p_structures[15:], corrections_list, metric="msd") + acsc.score(p_structures[15:], correction_matrix, metric="msd") + + # Test with single target + acsc.fit( + p_structures[:15], np.arange(15), + ) + mae = acsc.score(p_structures[15:], np.arange(15)) + assert isinstance(mae, float) def test_model_class_and_kwargs(): From 21d8e54f559b183b30d6dd6e2f5f875d3955e90c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Jul 2021 18:16:52 -0400 Subject: [PATCH 087/239] add sign of perturbation constraint parameter --- src/autocat/perturbations.py | 20 ++++++++++++++++++++ tests/test_perturbations.py | 26 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 45f93cac..5163fe6e 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -194,6 +194,7 @@ def perturb_structure( base_structure: Union[str, Atoms], minimum_perturbation_distance: float = 0.1, maximum_perturbation_distance: float = 1.0, + direction_sign_constraint: int = None, ): """ @@ -209,6 +210,13 @@ def perturb_structure( -3: free in x only -4: free in y only + The direction sign can also be constrained (e.g. + only perturb in the + direction) + + For example, to perturb an atom in only the + +z direction, its tag should be -1 and + direction_sign_constraint = 1 + Parameters ---------- @@ -225,6 +233,13 @@ def perturb_structure( Float of maximum acceptable perturbation distance Default: 1.0 Angstrom + direction_sign_constraint: + Int used to restrict the sign of the perturbation. + Options: + 1 = perturb in only the + direction + -1 = perturb in only the - direction + Default: allows both +/- directions. + Returns ------- @@ -254,6 +269,11 @@ def perturb_structure( for idx in atom_indices_to_perturb: # randomize +/- direction of each perturbation signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=11, size=(1, 3)) + if direction_sign_constraint is not None: + if direction_sign_constraint not in [-1, 1]: + msg = f"direction_sign_constraint must be either -1 or 1, got {direction_sign_constraint}" + raise AutocatPerturbationError(msg) + signs = direction_sign_constraint * abs(signs) # generate perturbation matrix pert_matrix[idx, :] = ( constr[ase_obj[idx].tag] diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 3e054e83..1ea7215d 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -10,7 +10,7 @@ from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate -from autocat.perturbations import perturb_structure +from autocat.perturbations import AutocatPerturbationError, perturb_structure from autocat.perturbations import generate_perturbed_dataset @@ -31,6 +31,30 @@ def test_perturb_structure_directions(): assert p_struct["structure"].positions[-1][-1] != base_struct.positions[-1][-1] +def test_perturb_structure_sign_constraint(): + # Tests fixing sign of perturbation + sub = generate_surface_structures(["Cu"], facets={"Cu": ["111"]})["Cu"]["fcc111"][ + "structure" + ] + base_struct = place_adsorbate(sub, "O")["custom"]["structure"] + # free in +z + base_struct[-1].tag = -1 + p_struct = perturb_structure(base_struct, direction_sign_constraint=1) + assert p_struct["structure"].positions[-1][0] == base_struct.positions[-1][0] + assert p_struct["structure"].positions[-1][-1] - base_struct.positions[-1][-1] > 0.0 + # free in -x + base_struct[-1].tag = -3 + p_struct = perturb_structure(base_struct, direction_sign_constraint=-1) + assert p_struct["structure"].positions[-1][0] - base_struct.positions[-1][0] < 0.0 + # free in +xy + base_struct[-1].tag = -2 + p_struct = perturb_structure(base_struct, direction_sign_constraint=1) + assert p_struct["structure"].positions[-1][0] - base_struct.positions[-1][0] > 0.0 + assert p_struct["structure"].positions[-1][1] - base_struct.positions[-1][1] > 0.0 + with pytest.raises(AutocatPerturbationError): + p_struct = perturb_structure(base_struct, direction_sign_constraint=2) + + def test_perturb_structure_matrix(): # Tests matrix matches perturbed structure sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ From 268b245961c3b87d1da92081af0574a56fcd03e0 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 15 Jul 2021 18:20:52 -0400 Subject: [PATCH 088/239] propagate new direction sign parameter --- src/autocat/perturbations.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 5163fe6e..3828a7ff 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -18,6 +18,7 @@ def generate_perturbed_dataset( maximum_perturbation_distance: float = 0.75, maximum_adsorbate_size: int = None, num_of_perturbations: int = 10, + direction_sign_constraint: int = None, write_to_disk: bool = False, write_location: str = ".", dirs_exist_ok: bool = False, @@ -37,6 +38,13 @@ def generate_perturbed_dataset( -3: free in x only -4: free in y only + The direction sign can also be constrained (e.g. + only perturb in the + direction) + + For example, to perturb an atom in only the + +z direction, its tag should be -1 and + direction_sign_constraint = 1 + Parameters ---------- @@ -63,6 +71,13 @@ def generate_perturbed_dataset( Int specifying number of perturbations to generate. Default 10 + direction_sign_constraint: + Int used to restrict the sign of the perturbation. + Options: + 1 = perturb in only the + direction + -1 = perturb in only the - direction + Default: allows both +/- directions. + write_to_disk: Boolean specifying whether the perturbed structures generated should be written to disk. @@ -78,7 +93,6 @@ def generate_perturbed_dataset( Defaults to False (raises an error if directories corresponding the species and crystal structure already exist). - Returns ------- @@ -116,6 +130,7 @@ def generate_perturbed_dataset( structure, minimum_perturbation_distance=minimum_perturbation_distance, maximum_perturbation_distance=maximum_perturbation_distance, + direction_sign_constraint=direction_sign_constraint, ) # keeps flattened atomic coordinates difference vector corrections_list.append(perturbed_dict[name][str(i)]["perturbation_matrix"]) From e505ede726cf38f6156f167421a4e18787d69d33 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 16 Jul 2021 11:50:23 -0400 Subject: [PATCH 089/239] generalize score method, rm multi model option --- src/autocat/learning/predictors.py | 122 ++++++----------------------- tests/learning/test_predictors.py | 94 +++------------------- 2 files changed, 35 insertions(+), 181 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index c99e3df3..5783b258 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -5,9 +5,12 @@ from typing import Union from ase import Atoms +from sklearn import metrics from sklearn.model_selection import KFold from sklearn.gaussian_process import GaussianProcessRegressor +from sklearn.metrics import mean_absolute_error +from sklearn.metrics import mean_squared_error from autocat.learning.featurizers import get_X from autocat.learning.featurizers import _get_number_of_features @@ -44,11 +47,6 @@ def __init__( model_kwargs will be removed. N.B. must have fit and predict methods - multiple_separate_models: - Bool indicating whether to train separate models for each target output. - If this is true, when fit to data, `acsc.regressor` will become the list - of regressors with length of number of targets - structure_featurizer: String giving featurizer to be used for full structure which will be fed into `autocat.learning.featurizers.full_structure_featurization` @@ -131,17 +129,6 @@ def model_class(self, model_class): # generates new regressor with default settings self.regressor = self._model_class() - @property - def multiple_separate_models(self): - return self._multiple_separate_models - - @multiple_separate_models.setter - def multiple_separate_models(self, multiple_separate_models): - if multiple_separate_models is not None: - self._multiple_separate_models = multiple_separate_models - if self.is_fit: - self.is_fit = False - @property def model_kwargs(self): return self._model_kwargs @@ -338,21 +325,7 @@ def fit( species_list.extend(new_species) self.species_list = species_list - if len(y.shape) == 1: - y = y.reshape(-1, 1) - - if not self.multiple_separate_models: - self.regressor.fit(X, y) - else: - assert y.shape[1] > 1 - regs = [] - for i in range(y.shape[1]): - reg = self.model_class(**self.model_kwargs or {}) - reg.fit(X, y[:, i]) - regs.append(reg) - assert regs[0] is not regs[1] - self.regressor = regs - + self.regressor.fit(X, y) self.is_fit = True def predict( @@ -390,29 +363,13 @@ def predict( structure_featurization_kwargs=self.structure_featurization_kwargs, adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, ) - if not self.multiple_separate_models: - try: - predicted_labels, unc = self.regressor.predict( - featurized_input, return_std=True - ) - except TypeError: - predicted_labels = self.regressor.predict(featurized_input,) - unc = None - else: - predicted_labels = np.zeros((len(testing_structures), len(self.regressor))) - try: - uncs = np.zeros((len(testing_structures), len(self.regressor))) - for idx, r in enumerate(self.regressor): - preds = r.predict(featurized_input, return_std=True) - predicted_labels[:, idx] = preds[0] - # uncertainties for target r for each struct to predict on - uncs[:, idx] = preds[1] - # take average uncertainty for each struct - unc = np.mean(uncs, axis=1) - except TypeError: - for idx, r in enumerate(self.regressor): - predicted_labels[:, idx] = r.predict(featurized_input) - unc = None + try: + predicted_labels, unc = self.regressor.predict( + featurized_input, return_std=True + ) + except TypeError: + predicted_labels = self.regressor.predict(featurized_input,) + unc = None return predicted_labels, unc @@ -422,6 +379,7 @@ def score( y: np.ndarray, metric: str = "mae", return_predictions: bool = False, + **kwargs, ): """ Returns a prediction score given the actual corrections. @@ -432,15 +390,14 @@ def score( test_structure_guesses: List of Atoms objects of structures to be tested o - corrections_list: - List of actual corrections as `np.arrays` + y: + Labels for the testing structures metric: How the performance metric should be calculated Options: - - mae: average of the average norm displacement vector difference - - rmse: square root of the average of the average norm displacement - vector difference squared + - mae + - mse return_predictions: Bool indicating whether the predictions and uncertainties should @@ -456,43 +413,14 @@ def score( pred_label, unc = self.predict(testing_structures) - if len(y.shape) == 1: - y = y.reshape(-1, 1) - - if metric == "mae": - all_abs_vec_diff = [] - for i in range(len(pred_label)): - assert pred_label[i].shape == y[i].shape - N_i = len(pred_label[i]) - # check if multi-target and takes norm if it is - if len(y[i]) > 1: - abs_vec_diff = np.sum(np.linalg.norm(y[i] - pred_label[i])) - else: - abs_vec_diff = np.sum(y[i] - pred_label[i]) - all_abs_vec_diff.append(abs_vec_diff / N_i) - if return_predictions: - return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff), pred_label, unc - else: - return np.sum(all_abs_vec_diff) / len(all_abs_vec_diff) - elif metric == "rmse": - all_sq_vec_diff = [] - for i in range(len(pred_label)): - assert pred_label[i].shape == y[i].shape - N_i = len(pred_label[i]) - # check if multi-target and takes norm if it is - if len(y[i]) > 1: - sq_vec_diff = np.sum(np.linalg.norm(y[i] - pred_label[i]) ** 2) - else: - sq_vec_diff = np.sum((y[i] - pred_label[i]) ** 2) - all_sq_vec_diff.append(sq_vec_diff / N_i) - if return_predictions: - return ( - np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)), - pred_label, - unc, - ) - else: - return np.sqrt(np.sum(all_sq_vec_diff) / len(all_sq_vec_diff)) - else: + score_func = {"mae": mean_absolute_error, "mse": mean_squared_error} + + if metric not in score_func: msg = f"Metric: {metric} is not supported" raise AutoCatPredictorError(msg) + + score = score_func[metric](y, pred_label, **kwargs) + + if return_predictions: + return score, pred_label, unc + return score diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 4e0d46cb..4e016328 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -85,27 +85,27 @@ def test_score_on_perturbed_systems(): base_struct = place_adsorbate(sub, "CO")["custom"]["structure"] p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] - correction_matrix = p_set["correction_matrix"] + correction_matrix = p_set["correction_matrix"][:, 0] acsc = AutoCatPredictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, ) acsc.fit( - p_structures[:15], correction_matrix[:15, :], + p_structures[:15], correction_matrix[:15], ) - mae = acsc.score(p_structures[15:], correction_matrix) + mae = acsc.score(p_structures[15:], correction_matrix[15:]) assert isinstance(mae, float) - rmse = acsc.score(p_structures[15:], correction_matrix, metric="rmse") - assert isinstance(rmse, float) + mse = acsc.score(p_structures[15:], correction_matrix[15:], metric="mse") + assert isinstance(mse, float) # Test returning predictions - _, pred_corr, unc = acsc.score( - p_structures[15:], correction_matrix, return_predictions=True + score, pred_corr, unc = acsc.score( + p_structures[15:], correction_matrix[15:], return_predictions=True ) assert len(pred_corr) == 5 assert len(unc) == 5 - assert mae != rmse + assert mae != mse with pytest.raises(AutoCatPredictorError): acsc.score(p_structures[15:], correction_matrix, metric="msd") @@ -113,7 +113,8 @@ def test_score_on_perturbed_systems(): acsc.fit( p_structures[:15], np.arange(15), ) - mae = acsc.score(p_structures[15:], np.arange(15)) + + mae = acsc.score(p_structures[15:], np.arange(5)) assert isinstance(mae, float) @@ -154,78 +155,3 @@ def test_model_without_unc(): predicted_corrections, uncs = acsc.predict(p_structures[15:],) assert uncs is None assert predicted_corrections is not None - - -def test_multi_separate_models_fit(): - # Tests fitting multiple separate models for each output - sub = generate_surface_structures(["Fe"], facets={"Fe": ["100"]})["Fe"]["bcc100"][ - "structure" - ] - base_struct = place_adsorbate(sub, "O")["custom"]["structure"] - p_set = generate_perturbed_dataset( - [base_struct], num_of_perturbations=20, maximum_adsorbate_size=2, - ) - p_structures = p_set["collected_structures"] - correction_matrix = p_set["correction_matrix"] - acsc = AutoCatPredictor( - structure_featurizer="sine_matrix", - adsorbate_featurizer=None, - multiple_separate_models=True, - maximum_adsorbate_size=2, - ) - acsc.fit( - p_structures[:15], correction_matrix[:15, :], - ) - assert len(acsc.regressor) == 6 - assert isinstance(acsc.regressor[0], GaussianProcessRegressor) - assert hasattr(acsc.regressor[-1], "alpha_") - assert acsc.regressor[1] is not acsc.regressor[2] - # Check fitting to supplied model class - acsc.model_class = BayesianRidge - acsc.fit( - p_structures[:15], correction_matrix[:15, :], - ) - assert len(acsc.regressor) == 6 - assert hasattr(acsc.regressor[2], "coef_") - assert acsc.regressor[0] is not acsc.regressor[-1] - - -def test_multi_separate_models_predict(): - # Tests predicting with multiple separate models - sub = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"]["hcp0001"][ - "structure" - ] - base_struct = place_adsorbate(sub, "NH")["custom"]["structure"] - p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) - p_structures = p_set["collected_structures"] - correction_matrix = p_set["correction_matrix"] - acsc = AutoCatPredictor( - structure_featurizer="sine_matrix", - adsorbate_featurizer=None, - multiple_separate_models=True, - ) - acsc.fit( - p_structures[:15], correction_matrix[:15, :], - ) - predicted_corrections, uncs = acsc.predict(p_structures[15:],) - assert len(predicted_corrections) == 5 - assert len(predicted_corrections[0]) == correction_matrix.shape[1] - assert len(uncs) == 5 - # Test for model class without unc - acsc.model_class = KernelRidge - acsc.fit( - p_structures[:15], correction_matrix[:15, :], - ) - predicted_corrections, uncs = acsc.predict(p_structures[15:],) - assert len(predicted_corrections) == 5 - assert len(predicted_corrections[0]) == correction_matrix.shape[1] - assert uncs is None - # Test for model class with uncertainty - acsc.model_class = BayesianRidge - acsc.fit( - p_structures[:15], correction_matrix[:15, :], - ) - predicted_corrections, uncs = acsc.predict(p_structures[15:],) - assert len(predicted_corrections) == 5 - assert len(predicted_corrections[0]) == correction_matrix.shape[1] - assert len(uncs) == 5 From 2cc597715e88169f2903e8e199d469d150cbe93e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 30 Aug 2021 22:02:36 -0400 Subject: [PATCH 090/239] save training X and y --- src/autocat/learning/predictors.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 5783b258..e1f204ff 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -126,6 +126,8 @@ def model_class(self, model_class): self._model_kwargs = None if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None # generates new regressor with default settings self.regressor = self._model_class() @@ -139,6 +141,8 @@ def model_kwargs(self, model_kwargs): self._model_kwargs = model_kwargs if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None self.regressor = self.model_class(**model_kwargs) @property @@ -151,6 +155,8 @@ def refine_structures(self, refine_structures): self._refine_structures = refine_structures if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None @property def structure_featurizer(self): @@ -162,6 +168,8 @@ def structure_featurizer(self, structure_featurizer): self._structure_featurizer = structure_featurizer if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None @property def adsorbate_featurizer(self): @@ -173,6 +181,8 @@ def adsorbate_featurizer(self, adsorbate_featurizer): self._adsorbate_featurizer = adsorbate_featurizer if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None @property def structure_featurization_kwargs(self): @@ -186,6 +196,8 @@ def structure_featurization_kwargs(self, structure_featurization_kwargs): self._structure_featurization_kwargs = structure_featurization_kwargs if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None @property def adsorbate_featurization_kwargs(self): @@ -198,6 +210,8 @@ def adsorbate_featurization_kwargs(self, adsorbate_featurization_kwargs): self._adsorbate_featurization_kwargs = adsorbate_featurization_kwargs if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None @property def maximum_structure_size(self): @@ -209,6 +223,8 @@ def maximum_structure_size(self, maximum_structure_size): self._maximum_structure_size = maximum_structure_size if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None @property def maximum_adsorbate_size(self): @@ -220,6 +236,8 @@ def maximum_adsorbate_size(self, maximum_adsorbate_size): self._maximum_adsorbate_size = maximum_adsorbate_size if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None @property def species_list(self): @@ -231,6 +249,8 @@ def species_list(self, species_list): self._species_list = species_list if self.is_fit: self.is_fit = False + self.X_ = None + self.y_ = None def get_total_number_of_features(self): # get specified kwargs for featurizers @@ -285,7 +305,7 @@ def fit( trained_model: Trained `sklearn` model object """ - X = get_X( + self.X_ = get_X( training_structures, maximum_structure_size=self.maximum_structure_size, structure_featurizer=self.structure_featurizer, @@ -324,8 +344,8 @@ def fit( ] species_list.extend(new_species) self.species_list = species_list - - self.regressor.fit(X, y) + self.y_ = y + self.regressor.fit(self.X_, self.y_) self.is_fit = True def predict( From e98e1d81699b17ec9997eea3b1824899df4c7ae4 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 31 Aug 2021 16:23:52 -0400 Subject: [PATCH 091/239] start generalizing sl framework - refactors to AutoCatPredictor - adds function to calculate overlap score - adds function to choose the candidate(s) on each loop --- src/autocat/learning/sequential.py | 276 ++++++++++++++++++----------- 1 file changed, 172 insertions(+), 104 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 007261d2..2579b470 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -9,9 +9,10 @@ from ase import Atoms from ase.io import write as ase_write +from scipy import stats from autocat.perturbations import generate_perturbed_dataset -from autocat.learning.predictors import AutoCatStructureCorrector +from autocat.learning.predictors import AutoCatPredictor Array = List[float] @@ -21,7 +22,7 @@ class AutoCatSequentialLearningError(Exception): def multiple_sequential_learning_runs( - structure_corrector: AutoCatStructureCorrector, + predictor: AutoCatPredictor, training_base_structures: List[Atoms], number_of_runs: int = 5, number_parallel_jobs: int = None, @@ -36,8 +37,8 @@ def multiple_sequential_learning_runs( Parameters ---------- - structure_corrector: - AutoCatStructureCorrector object to be used for fitting and prediction + predictor: + AutoCatPredictor object to be used for fitting and prediction training_base_structures: List of Atoms objects for all base structures to be perturbed for training @@ -70,7 +71,7 @@ def multiple_sequential_learning_runs( if number_parallel_jobs is not None: runs_history = Parallel(n_jobs=number_parallel_jobs)( delayed(simulated_sequential_learning)( - structure_corrector=structure_corrector, + predictor=predictor, training_base_structures=training_base_structures, **sl_kwargs, ) @@ -80,7 +81,7 @@ def multiple_sequential_learning_runs( else: runs_history = [ simulated_sequential_learning( - structure_corrector=structure_corrector, + predictor=predictor, training_base_structures=training_base_structures, **sl_kwargs, ) @@ -118,14 +119,12 @@ def multiple_sequential_learning_runs( def simulated_sequential_learning( - structure_corrector: AutoCatStructureCorrector, - training_base_structures: List[Atoms], - testing_base_structures: List[Atoms] = None, - minimum_perturbation_distance: float = 0.01, - maximum_perturbation_distance: float = 0.75, - initial_num_of_perturbations_per_base_structure: int = None, - batch_num_of_perturbations_per_base_structure: int = 2, - test_num_of_perturbations_per_base_structure: int = None, + predictor: AutoCatPredictor, + all_training_structures: List[Atoms], + all_training_y: np.ndarray, + init_training_size: int = 10, + testing_structures: List[Atoms] = None, + testing_y: np.ndarray = None, batch_size_to_add: int = 1, number_of_sl_loops: int = 100, write_to_disk: bool = False, @@ -134,14 +133,13 @@ def simulated_sequential_learning( ): """ Conducts a simulated sequential learning loop given - a set of base structures. For each loop, new perturbations - are generated. Maximum Uncertainty is used as the acquisition function + a set of base training structures. Parameters ---------- - structure_corrector: - AutoCatStructureCorrector object to be used for fitting and prediction + predictor: + AutoCatPredictor object to be used for fitting and prediction training_base_structures: List of Atoms objects for all base structures to be perturbed for training @@ -150,23 +148,6 @@ def simulated_sequential_learning( testing_base_structures: List of Atoms objects for base structures that are - minimum_perturbation_distance: - Float of minimum acceptable perturbation distance - Default: 0.01 Angstrom - - maximum_perturbation_distance: - Float of maximum acceptable perturbation distance - Default: 0.75 Angstrom - - initial_num_of_perturbations_per_base_structure: - Integer giving the number of perturbations to generate initially - for each of the given base structures for initial training purposes. - Default: uses same value as `batch_num_of_perturbations_per_base_structure` - - batch_num_of_perturbations_per_base_structure: - Integer giving the number of perturbations to generate - on each loop when finding the next candidate - batch_size_to_add: Integer giving the number of candidates to be added to the training set on each loop. (ie. N candidates with @@ -205,107 +186,60 @@ def simulated_sequential_learning( For the corrections histories, the dimensions are as follows: num of loops -> num of candidates added -> corrections applied """ - if ( - batch_num_of_perturbations_per_base_structure * len(training_base_structures) - < batch_size_to_add - ): - msg = "Batch size to add must be less than the number of candidates generated" + + if init_training_size > len(all_training_structures): + msg = f"Initial training size ({init_training_size}) larger than design space ({len(all_training_structures)})" raise AutoCatSequentialLearningError(msg) - if initial_num_of_perturbations_per_base_structure is None: - initial_num_of_perturbations_per_base_structure = ( - batch_num_of_perturbations_per_base_structure + # generate initial training set + train_idx = np.zeros(len(all_training_structures), dtype=bool) + train_idx[ + np.random.choice( + len(all_training_structures), init_training_size, replace=False ) + ] = 1 + train_history = [train_idx] - initial_pert_dataset = generate_perturbed_dataset( - training_base_structures, - num_of_perturbations=initial_num_of_perturbations_per_base_structure, - maximum_perturbation_distance=maximum_perturbation_distance, - minimum_perturbation_distance=minimum_perturbation_distance, - ) - - train_pert_structures = initial_pert_dataset["collected_structures"] - train_pert_corr_list = initial_pert_dataset["corrections_list"] - - structure_corrector.fit( - train_pert_structures, corrections_list=train_pert_corr_list - ) - - if testing_base_structures is not None: - if test_num_of_perturbations_per_base_structure is None: - test_num_of_perturbations_per_base_structure = ( - batch_num_of_perturbations_per_base_structure - ) - test_pert_dataset = generate_perturbed_dataset( - testing_base_structures, - num_of_perturbations=test_num_of_perturbations_per_base_structure, - maximum_perturbation_distance=maximum_perturbation_distance, - minimum_perturbation_distance=minimum_perturbation_distance, - ) - test_pert_structures = test_pert_dataset["collected_structures"] - test_pert_corr_list = test_pert_dataset["corrections_list"] - - validation_pert_dataset = generate_perturbed_dataset( - training_base_structures, - num_of_perturbations=initial_num_of_perturbations_per_base_structure, - maximum_perturbation_distance=maximum_perturbation_distance, - minimum_perturbation_distance=minimum_perturbation_distance, - ) - validation_pert_structures = validation_pert_dataset["collected_structures"] - validation_pert_corr_list = validation_pert_dataset["corrections_list"] + # fit on initial training set + predictor.fit(all_training_structures[train_idx], all_training_y[train_idx]) candidate_full_unc_history = [] candidate_max_unc_history = [] - candidate_pred_corrs_history = [] - candidate_real_corrs_history = [] mae_train_history = [] rmse_train_history = [] mae_test_history = [] rmse_test_history = [] - selected_candidate_history = [] test_preds_history = [] test_unc_history = [] ctr = 0 while len(candidate_max_unc_history) < number_of_sl_loops: ctr += 1 print(f"Sequential Learning Iteration #{ctr}") - # generate new perturbations to predict on - new_perturbations = generate_perturbed_dataset( - training_base_structures, - num_of_perturbations=batch_num_of_perturbations_per_base_structure, - maximum_perturbation_distance=maximum_perturbation_distance, - minimum_perturbation_distance=minimum_perturbation_distance, - ) - new_pert_structs = new_perturbations["collected_structures"] - new_pert_corr_list = new_perturbations["corrections_list"] + # select new candidates to predict on # make predictions - _pred_corrs, _pred_corr_structs, _uncs = structure_corrector.predict( - new_pert_structs - ) + _pred_corrs, _uncs = predictor.predict(new_pert_structs) # get scores on new perturbations (training) mae_train_history.append( - structure_corrector.score( - validation_pert_structures, validation_pert_corr_list - ) + predictor.score(validation_pert_structures, validation_pert_corr_list) ) rmse_train_history.append( - structure_corrector.score( + predictor.score( validation_pert_structures, validation_pert_corr_list, metric="rmse" ) ) # get scores on test perturbations if testing_base_structures is not None: - mae_test_score, test_preds, test_unc = structure_corrector.score( + mae_test_score, test_preds, test_unc = predictor.score( test_pert_structures, test_pert_corr_list, return_predictions=True ) mae_test_history.append(mae_test_score) test_preds_history.append([p.tolist() for p in test_preds]) test_unc_history.append([u.tolist() for u in test_unc]) rmse_test_history.append( - structure_corrector.score( + predictor.score( test_pert_structures, test_pert_corr_list, metric="rmse" ) ) @@ -319,7 +253,12 @@ def simulated_sequential_learning( next_candidate_struct = [new_pert_structs[idx] for idx in high_unc_idx] # add new perturbed struct to training set train_pert_structures.extend(next_candidate_struct) - train_pert_corr_list.extend([new_pert_corr_list[idx] for idx in high_unc_idx],) + train_pert_corr_list = np.concatenate( + ( + train_pert_corr_list, + np.array([new_pert_corr_list[idx] for idx in high_unc_idx]), + ) + ) # keeps all candidate structures added for each loop selected_candidate_history.append(next_candidate_struct) @@ -331,9 +270,7 @@ def simulated_sequential_learning( candidate_real_corrs_history.append( [new_pert_corr_list[idx].tolist() for idx in high_unc_idx] ) - structure_corrector.fit( - train_pert_structures, corrections_list=train_pert_corr_list - ) + predictor.fit(train_pert_structures, train_pert_corr_list) sl_dict = { "candidate_max_unc_history": [mu.tolist() for mu in candidate_max_unc_history], "candidate_full_unc_history": [ @@ -374,3 +311,134 @@ def simulated_sequential_learning( ) return sl_dict + + +def choose_next_candidate( + labels: Array = None, + train_idx: Array = None, + pred: Array = None, + unc: Array = None, + aq: str = "MLI", + num_candidates_to_pick: int = None, + target_min: float = None, + target_max: float = None, +): + """ + Chooses the next candidate(s) from a given acquisition function + + Parameters + ---------- + + labels: + Array of the labels for the data + + train_idx: + Indices of all data entries already in the training set + + pred: + Predictions for all structures in the dataset + + unc: + Uncertainties for all structures in the dataset + + aq: + Acquisition function to be used to select the next candidates + Options + - MLI: maximum likelihood of improvement (default) + - Random + - MU: maximum uncertainty + + num_candidates_to_pick: + Number of candidates to choose from the dataset + + target_min: + Minimum target value to optimize for + + target_max: + Maximum target value to optimize for + + Returns + ------- + + parent_idx: + Index/indices of the selected candidates + + max_scores: + Maximum scores (corresponding to the selected candidates for given `aq`) + + aq_scores: + Calculated scores based on the selected `aq` for the entire training set + """ + if aq == "Random": + if labels is None: + msg = "For aq = 'Random', the labels must be supplied" + raise AutoCatSequentialLearningError(msg) + + next_idx = np.random.choice( + len(labels[~train_idx]), size=num_candidates_to_pick, replace=False + ) + if num_candidates_to_pick is None: + next_idx = np.array([next_idx]) + parent_idx = np.arange(labels.shape[0])[~train_idx][next_idx] + + max_scores = [] + aq_scores = [] + + elif aq == "MU": + if unc is None: + msg = "For aq = 'MU', the uncertainties must be supplied" + raise AutoCatSequentialLearningError(msg) + + if num_candidates_to_pick is None: + next_idx = np.array([np.argmax(unc[~train_idx])]) + max_scores = [np.max(unc[~train_idx])] + + else: + next_idx = np.argsort(unc[~train_idx])[-num_candidates_to_pick:] + sorted_array = unc[~train_idx][next_idx] + max_scores = list(sorted_array[-num_candidates_to_pick:]) + parent_idx = np.arange(unc.shape[0])[~train_idx][next_idx] + aq_scores = unc + + elif aq == "MLI": + if unc is None or pred is None: + msg = "For aq = 'MLI', both uncertainties and predictions must be supplied" + raise AutoCatSequentialLearningError(msg) + + aq_scores = np.array( + [ + get_overlap_score(mean, std, x2=target_max, x1=target_min) + for mean, std in zip(pred, unc) + ] + ) + if num_candidates_to_pick is None: + next_idx = np.array([np.argmax(aq_scores[~train_idx])]) + max_scores = [np.max(aq_scores[~train_idx])] + + else: + next_idx = np.argsort(aq_scores[~train_idx])[-num_candidates_to_pick:] + sorted_array = aq_scores[~train_idx][next_idx] + max_scores = list(sorted_array[-num_candidates_to_pick:]) + parent_idx = np.arange(aq_scores.shape[0])[~train_idx][next_idx] + + else: + msg = f"Acquisition function {aq} is not supported" + raise NotImplementedError(msg) + + return parent_idx, max_scores, aq_scores + + +def get_overlap_score(mean: float, std: float, x2: float = None, x1: float = None): + """Calculate overlap score given targets x2 (max) and x1 (min)""" + if x2 is not None and x1 is None: + x1 = 0.0 + + elif x2 is None and x1 is not None: + x2 = np.inf + + else: + msg = "Please specify at least either a minimum or maximum target for MLI" + raise AutoCatSequentialLearningError(msg) + + norm_dist = stats.norm(loc=mean, scale=std) + return norm_dist.cdf(x2) - norm_dist.cdf(x1) From de54e5b7e1efd98fa01187b2dc6484db6bbc9c19 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 1 Sep 2021 18:05:29 -0400 Subject: [PATCH 092/239] continue generalizing - fix overlap score default minimum - train_idx default to full list if none for choose_next_candidate --- src/autocat/learning/sequential.py | 75 ++++++++++++++++++------------ 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 2579b470..5dba8fc8 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -125,8 +125,11 @@ def simulated_sequential_learning( init_training_size: int = 10, testing_structures: List[Atoms] = None, testing_y: np.ndarray = None, + acquisition_function: str = "MLI", batch_size_to_add: int = 1, number_of_sl_loops: int = 100, + target_min: float = None, + target_max: float = None, write_to_disk: bool = False, write_location: str = ".", dirs_exist_ok_structures: bool = False, @@ -203,54 +206,58 @@ def simulated_sequential_learning( # fit on initial training set predictor.fit(all_training_structures[train_idx], all_training_y[train_idx]) - candidate_full_unc_history = [] - candidate_max_unc_history = [] + pred_history = [] + unc_history = [] mae_train_history = [] rmse_train_history = [] mae_test_history = [] rmse_test_history = [] - test_preds_history = [] + test_pred_history = [] test_unc_history = [] ctr = 0 - while len(candidate_max_unc_history) < number_of_sl_loops: + while ctr < number_of_sl_loops: ctr += 1 print(f"Sequential Learning Iteration #{ctr}") # select new candidates to predict on # make predictions - _pred_corrs, _uncs = predictor.predict(new_pert_structs) + _preds, _uncs = predictor.predict(all_training_structures) - # get scores on new perturbations (training) + # get scores on full training set mae_train_history.append( - predictor.score(validation_pert_structures, validation_pert_corr_list) + predictor.score(all_training_structures, all_training_y) ) rmse_train_history.append( - predictor.score( - validation_pert_structures, validation_pert_corr_list, metric="rmse" - ) + predictor.score(all_training_structures, all_training_y, metric="rmse") ) # get scores on test perturbations - if testing_base_structures is not None: + if testing_structures is not None: mae_test_score, test_preds, test_unc = predictor.score( - test_pert_structures, test_pert_corr_list, return_predictions=True + testing_structures, testing_y, return_predictions=True ) mae_test_history.append(mae_test_score) - test_preds_history.append([p.tolist() for p in test_preds]) + test_pred_history.append([p.tolist() for p in test_preds]) test_unc_history.append([u.tolist() for u in test_unc]) rmse_test_history.append( - predictor.score( - test_pert_structures, test_pert_corr_list, metric="rmse" - ) + predictor.score(testing_structures, testing_y, metric="rmse") ) - candidate_full_unc_history.append(_uncs) - - # find candidate with highest uncertainty - high_unc_idx = np.argsort(_uncs)[-batch_size_to_add:] - candidate_max_unc_history.append(_uncs[high_unc_idx]) + unc_history.append(_uncs) + pred_history.append(_preds) + + # select next candidate(s) + next_candidate_idx, max_scores, aq_scores = choose_next_candidate( + all_training_y, + train_idx, + _preds, + _uncs, + acquisition_function, + batch_size_to_add, + target_min, + target_max, + ) - next_candidate_struct = [new_pert_structs[idx] for idx in high_unc_idx] # add new perturbed struct to training set train_pert_structures.extend(next_candidate_struct) train_pert_corr_list = np.concatenate( @@ -334,6 +341,7 @@ def choose_next_candidate( train_idx: Indices of all data entries already in the training set + Default: consider entire training set pred: Predictions for all structures in the dataset @@ -374,6 +382,9 @@ def choose_next_candidate( msg = "For aq = 'Random', the labels must be supplied" raise AutoCatSequentialLearningError(msg) + if train_idx is None: + train_idx = np.zeros(len(labels), dtype=bool) + next_idx = np.random.choice( len(labels[~train_idx]), size=num_candidates_to_pick, replace=False ) @@ -389,6 +400,9 @@ def choose_next_candidate( msg = "For aq = 'MU', the uncertainties must be supplied" raise AutoCatSequentialLearningError(msg) + if train_idx is None: + train_idx = np.zeros(len(unc), dtype=bool) + if num_candidates_to_pick is None: next_idx = np.array([np.argmax(unc[~train_idx])]) max_scores = [np.max(unc[~train_idx])] @@ -405,6 +419,9 @@ def choose_next_candidate( msg = "For aq = 'MLI', both uncertainties and predictions must be supplied" raise AutoCatSequentialLearningError(msg) + if train_idx is None: + train_idx = np.zeros(len(unc), dtype=bool) + aq_scores = np.array( [ get_overlap_score(mean, std, x2=target_max, x1=target_min) @@ -430,15 +447,15 @@ def choose_next_candidate( def get_overlap_score(mean: float, std: float, x2: float = None, x1: float = None): """Calculate overlap score given targets x2 (max) and x1 (min)""" - if x2 is not None and x1 is None: - x1 = 0.0 - - elif x2 is None and x1 is not None: - x2 = np.inf - - else: + if x1 is None and x2 is None: msg = "Please specify at least either a minimum or maximum target for MLI" raise AutoCatSequentialLearningError(msg) + if x1 is None: + x1 = -np.inf + + if x2 is None: + x2 = np.inf + norm_dist = stats.norm(loc=mean, scale=std) return norm_dist.cdf(x2) - norm_dist.cdf(x1) From 39ace32664ccae7d0bca8b99050556d52188edb4 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 2 Sep 2021 10:24:04 -0400 Subject: [PATCH 093/239] generalize simulated sl func --- src/autocat/learning/sequential.py | 110 ++++++++++++++--------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 5dba8fc8..d58a0518 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -132,11 +132,12 @@ def simulated_sequential_learning( target_max: float = None, write_to_disk: bool = False, write_location: str = ".", - dirs_exist_ok_structures: bool = False, ): """ Conducts a simulated sequential learning loop given - a set of base training structures. + a data set to explore. Can optionally provide a holdout + test set that is never added to the training set + to measure transferability at each iteration Parameters ---------- @@ -144,22 +145,48 @@ def simulated_sequential_learning( predictor: AutoCatPredictor object to be used for fitting and prediction - training_base_structures: - List of Atoms objects for all base structures to be perturbed for training - and candidate selection upon each loop + all_training_structures: + List of all Atoms objects that make up the design space + to be considered + + all_training_y: + Labels corresponding to each structure in the design + space to be explored (ie. correspond to + `all_training_structures`) - testing_base_structures: - List of Atoms objects for base structures that are + init_training_size: + Size of the initial training set to be selected from + the full space + + testing_structures: + List of all Atoms objects to be excluded from the + SL search (ie. structures to never be added to the set). + Used for testing transferability of the model at each iteration + + testing_y: + Labels for corresponding to `testing_structures` + + acquisition_function: + Acquisition function to be used to determine candidates to be + added to the training set at each iteration of the sl loop batch_size_to_add: - Integer giving the number of candidates to be added - to the training set on each loop. (ie. N candidates with - the highest uncertainties added for each iteration) + Number of candidates to be added to the training set on each loop. + (ie. N candidates with the highest uncertainties added + for each iteration) Default: 1 (ie. adds candidate with max unc on each loop) number_of_sl_loops: Integer specifying the number of sequential learning loops to be conducted + target_min: + Label value that ideal candidates should be greater than + Default: -inf + + target_max: + Label value that ideal candidates should be less than + Default: +inf + write_to_disk: Boolean specifying whether the sl dictionary should be written to disk as a json. Defaults to False. @@ -167,9 +194,6 @@ def simulated_sequential_learning( write_location: String with the location where sl_dict should be written to disk. - dirs_exist_ok_structures: - Boolean indicating if existing candidate structure files can be overwritten - Returns ------- @@ -208,6 +232,8 @@ def simulated_sequential_learning( pred_history = [] unc_history = [] + max_scores_history = [] + aq_scores_history = [] mae_train_history = [] rmse_train_history = [] mae_test_history = [] @@ -257,65 +283,39 @@ def simulated_sequential_learning( target_min, target_max, ) + max_scores_history.append(max_scores) + aq_scores_history.append(aq_scores) - # add new perturbed struct to training set - train_pert_structures.extend(next_candidate_struct) - train_pert_corr_list = np.concatenate( - ( - train_pert_corr_list, - np.array([new_pert_corr_list[idx] for idx in high_unc_idx]), - ) - ) + # add next candidates to training set + train_idx[next_candidate_idx] = True + train_history.append(train_idx) - # keeps all candidate structures added for each loop - selected_candidate_history.append(next_candidate_struct) + # retrain on training set with new additions + predictor.fit(all_training_structures[train_idx], all_training_y[train_idx]) - # keeps as lists to make writing to disk easier - candidate_pred_corrs_history.append( - [p.tolist() for p in [_pred_corrs[idx] for idx in high_unc_idx]] - ) - candidate_real_corrs_history.append( - [new_pert_corr_list[idx].tolist() for idx in high_unc_idx] - ) - predictor.fit(train_pert_structures, train_pert_corr_list) sl_dict = { - "candidate_max_unc_history": [mu.tolist() for mu in candidate_max_unc_history], - "candidate_full_unc_history": [ - unc.tolist() for unc in candidate_full_unc_history - ], - "candidate_pred_corrs_history": candidate_pred_corrs_history, - "candidate_real_corrs_history": [r for r in candidate_real_corrs_history], - "selected_candidate_history": selected_candidate_history, + "training_history": [th.tolist() for th in train_history], + "uncertainty_history": [mu.tolist() for mu in unc_history], + "prediction_history": [p.tolist() for p in pred_history], "mae_train_history": mae_train_history, "rmse_train_history": rmse_train_history, + "max_scores_history": max_scores_history, + "aq_scores_history": aq_scores_history, } - if testing_base_structures is not None: + if testing_structures is not None: sl_dict["mae_test_history"] = mae_test_history sl_dict["rmse_test_history"] = rmse_test_history - sl_dict["test_preds_history"] = test_preds_history - sl_dict["test_real_corrections"] = [t.tolist() for t in test_pert_corr_list] - sl_dict["test_unc_history"] = test_unc_history + sl_dict["test_prediction_history"] = test_pred_history + sl_dict["test_uncertainty_history"] = test_unc_history if write_to_disk: if not os.path.isdir(write_location): os.makedirs(write_location) json_write_path = os.path.join(write_location, "sl_dict.json") - data_sl_dict = { - key: sl_dict[key] for key in sl_dict if key != "selected_candidate_history" - } with open(json_write_path, "w") as f: - json.dump(data_sl_dict, f) + json.dump(sl_dict, f) print(f"SL dictionary written to {json_write_path}") - candidate_struct_hist = sl_dict["selected_candidate_history"] - traj_file_path = os.path.join(write_location, f"candidate_structures") - os.makedirs(traj_file_path, exist_ok=dirs_exist_ok_structures) - for i, c in enumerate(candidate_struct_hist): - traj_filename = os.path.join(traj_file_path, f"sl_iter{i+1}.traj") - ase_write(traj_filename, c) - print( - f"Selected SL Candidates for iteration {i+1} written to {traj_file_path}" - ) return sl_dict From 73317de2ceee12e050f4d30d17e5245d9e979898 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 2 Sep 2021 15:26:29 -0400 Subject: [PATCH 094/239] generalize multi sl wrapper --- src/autocat/learning/sequential.py | 51 +++--------------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index d58a0518..d7916b9c 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -21,29 +21,19 @@ class AutoCatSequentialLearningError(Exception): pass -def multiple_sequential_learning_runs( - predictor: AutoCatPredictor, - training_base_structures: List[Atoms], +def multiple_simulated_sequential_learning_runs( number_of_runs: int = 5, number_parallel_jobs: int = None, write_to_disk: bool = False, write_location: str = ".", - dirs_exist_ok_structures: bool = False, **sl_kwargs, ): """ - Conducts multiple sequential learning runs + Conducts multiple simulated sequential learning runs Parameters ---------- - predictor: - AutoCatPredictor object to be used for fitting and prediction - - training_base_structures: - List of Atoms objects for all base structures to be perturbed for training - and candidate selection upon each loop - number_of_runs: Integer of number of runs to be done @@ -58,9 +48,6 @@ def multiple_sequential_learning_runs( write_location: String with the location where runs history should be written to disk. - dirs_exist_ok_structures: - Boolean indicating if existing candidate structure files can be overwritten - Returns ------- @@ -70,49 +57,21 @@ def multiple_sequential_learning_runs( """ if number_parallel_jobs is not None: runs_history = Parallel(n_jobs=number_parallel_jobs)( - delayed(simulated_sequential_learning)( - predictor=predictor, - training_base_structures=training_base_structures, - **sl_kwargs, - ) + delayed(simulated_sequential_learning)(**sl_kwargs,) for i in range(number_of_runs) ) else: runs_history = [ - simulated_sequential_learning( - predictor=predictor, - training_base_structures=training_base_structures, - **sl_kwargs, - ) - for i in range(number_of_runs) + simulated_sequential_learning(**sl_kwargs,) for i in range(number_of_runs) ] if write_to_disk: if not os.path.isdir(write_location): os.makedirs(write_location) json_write_path = os.path.join(write_location, "sl_runs_history.json") - data_runs_history = [] - for r_idx, run in enumerate(runs_history): - # get dict excluding selected candidate structures - j_dict = { - key: run[key] for key in run if key != "selected_candidate_history" - } - data_runs_history.append(j_dict) - # write out selected candidates for each iteration of each run - c_hist = run["selected_candidate_history"] - traj_file_path = os.path.join( - write_location, f"candidate_structures/run{r_idx+1}", - ) - os.makedirs(traj_file_path, exist_ok=dirs_exist_ok_structures) - for i, c in enumerate(c_hist): - traj_filename = os.path.join(traj_file_path, f"sl_iter{i+1}.traj") - ase_write(traj_filename, c) - print( - f"Selected SL Candidates for run {r_idx+1}, iteration {i+1} written to {traj_file_path}" - ) with open(json_write_path, "w") as f: - json.dump(data_runs_history, f) + json.dump(runs_history, f) print(f"SL histories written to {json_write_path}") return runs_history From 7331507eda9b4f5d35b992e9a98d11b61173317e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 2 Sep 2021 17:40:50 -0400 Subject: [PATCH 095/239] various bugfixes - properly mask training structures by train_idx - add logic for if less unseen data than batch size to add - add check that number of sl loops is no greater than provided dataset - default num sl loops is size of training set divided by size to add --- src/autocat/learning/sequential.py | 41 +++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index d7916b9c..da12cdcc 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -86,7 +86,7 @@ def simulated_sequential_learning( testing_y: np.ndarray = None, acquisition_function: str = "MLI", batch_size_to_add: int = 1, - number_of_sl_loops: int = 100, + number_of_sl_loops: int = None, target_min: float = None, target_max: float = None, write_to_disk: bool = False, @@ -115,7 +115,8 @@ def simulated_sequential_learning( init_training_size: Size of the initial training set to be selected from - the full space + the full space. + Default: 10 testing_structures: List of all Atoms objects to be excluded from the @@ -137,6 +138,7 @@ def simulated_sequential_learning( number_of_sl_loops: Integer specifying the number of sequential learning loops to be conducted + Default: Size of training set target_min: Label value that ideal candidates should be greater than @@ -177,6 +179,12 @@ def simulated_sequential_learning( msg = f"Initial training size ({init_training_size}) larger than design space ({len(all_training_structures)})" raise AutoCatSequentialLearningError(msg) + if number_of_sl_loops is None: + number_of_sl_loops = np.ceil(len(all_training_structures) / batch_size_to_add) + + if number_of_sl_loops > len(all_training_structures): + msg = f"Number of SL loops ({number_of_sl_loops}) cannot be greater than the design space ({len(all_training_structures)})" + # generate initial training set train_idx = np.zeros(len(all_training_structures), dtype=bool) train_idx[ @@ -187,7 +195,9 @@ def simulated_sequential_learning( train_history = [train_idx] # fit on initial training set - predictor.fit(all_training_structures[train_idx], all_training_y[train_idx]) + X = [s for s, i in zip(all_training_structures, train_idx) if i] + y = all_training_y[train_idx] + predictor.fit(X, y) pred_history = [] unc_history = [] @@ -213,11 +223,17 @@ def simulated_sequential_learning( predictor.score(all_training_structures, all_training_y) ) rmse_train_history.append( - predictor.score(all_training_structures, all_training_y, metric="rmse") + predictor.score( + all_training_structures, all_training_y, metric="mse", squared=False + ) ) # get scores on test perturbations if testing_structures is not None: + if testing_y is None: + msg = f"Labels for the test structures must be provided" + raise AutoCatSequentialLearningError(msg) + mae_test_score, test_preds, test_unc = predictor.score( testing_structures, testing_y, return_predictions=True ) @@ -225,12 +241,21 @@ def simulated_sequential_learning( test_pred_history.append([p.tolist() for p in test_preds]) test_unc_history.append([u.tolist() for u in test_unc]) rmse_test_history.append( - predictor.score(testing_structures, testing_y, metric="rmse") + predictor.score( + testing_structures, testing_y, metric="mse", squared=False + ) ) unc_history.append(_uncs) pred_history.append(_preds) + # check that enough data pts left to adhere to batch_size_to_add + # otherwise just add the leftover data + if batch_size_to_add < len(all_training_y[~train_idx]): + bsa = batch_size_to_add + else: + bsa = len(all_training_y[~train_idx]) + # select next candidate(s) next_candidate_idx, max_scores, aq_scores = choose_next_candidate( all_training_y, @@ -238,7 +263,7 @@ def simulated_sequential_learning( _preds, _uncs, acquisition_function, - batch_size_to_add, + bsa, target_min, target_max, ) @@ -250,7 +275,9 @@ def simulated_sequential_learning( train_history.append(train_idx) # retrain on training set with new additions - predictor.fit(all_training_structures[train_idx], all_training_y[train_idx]) + X = [s for s, i in zip(all_training_structures, train_idx) if i] + y = all_training_y[train_idx] + predictor.fit(X, y) sl_dict = { "training_history": [th.tolist() for th in train_history], From 32d16a232047fbc826de649694e0d9a49dc014aa Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 3 Sep 2021 13:30:47 -0400 Subject: [PATCH 096/239] various fixes - fix how multi func handles kwargs - add helper func for pred stats - redefine number of sl loops - update sl func docstring - fix max num sl loops check - rm unused import --- src/autocat/learning/sequential.py | 77 +++++++++++++++++++----------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index da12cdcc..2cbdf479 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -8,7 +8,6 @@ from typing import Union from ase import Atoms -from ase.io import write as ase_write from scipy import stats from autocat.perturbations import generate_perturbed_dataset @@ -26,7 +25,7 @@ def multiple_simulated_sequential_learning_runs( number_parallel_jobs: int = None, write_to_disk: bool = False, write_location: str = ".", - **sl_kwargs, + sl_kwargs=None, ): """ Conducts multiple simulated sequential learning runs @@ -48,6 +47,10 @@ def multiple_simulated_sequential_learning_runs( write_location: String with the location where runs history should be written to disk. + sl_kwargs: + Mapping of keywords for `simulated_sequential_learning`. + Note: Do not use the `write_to_disk` keyword here + Returns ------- @@ -55,6 +58,9 @@ def multiple_simulated_sequential_learning_runs( List of dictionaries generated for each run containing info about that run such as mae history, rmse history, etc.. """ + if sl_kwargs is None: + sl_kwargs = {} + if number_parallel_jobs is not None: runs_history = Parallel(n_jobs=number_parallel_jobs)( delayed(simulated_sequential_learning)(**sl_kwargs,) @@ -137,8 +143,10 @@ def simulated_sequential_learning( Default: 1 (ie. adds candidate with max unc on each loop) number_of_sl_loops: - Integer specifying the number of sequential learning loops to be conducted - Default: Size of training set + Integer specifying the number of sequential learning loops to be conducted. + This value cannot be greater than + `(len(all_training_structures) - init_training_size)/batch_size_to_add` + Default: maximum number of sl loops calculated above target_min: Label value that ideal candidates should be greater than @@ -161,29 +169,34 @@ def simulated_sequential_learning( sl_dict: Dictionary containing histories of different quantities throughout the calculation: - - candidate maximum uncertainty history - - candidate full uncertainty history - - predicted corrections training history - - real corrections training history - - mae training history - - rmse training history - - mae testing history (if test structures given) - - rmse testing history (if test structures given) - - testing predictions (if test structures given) - - testing uncertainties (if test structures given) - For the corrections histories, the dimensions are as follows: - num of loops -> num of candidates added -> corrections applied + - training_history: indices that are included in the training set + - uncertainty_history: prediction uncertainties at each iteration + - predicted_history: all predictions at each iteration + - mae_train_history: mae scores on predicting training set + - rmse_train_history: rmse scores on predicting training set + - max_scores_history: max aq scores for candidate selection + - aq_scores_history: all aq scores at each iteration + If testing structures and labels given: + - mae_test_history: mae scores on testing set + - rmse_test_history: rmse scores on testing set + - test_prediction_history: predictions on testing set + - test_uncertainty_history: uncertainties on predicting on test set """ if init_training_size > len(all_training_structures): msg = f"Initial training size ({init_training_size}) larger than design space ({len(all_training_structures)})" raise AutoCatSequentialLearningError(msg) + max_num_sl_loops = int( + np.ceil((len(all_training_structures) - init_training_size) / batch_size_to_add) + ) + if number_of_sl_loops is None: - number_of_sl_loops = np.ceil(len(all_training_structures) / batch_size_to_add) + number_of_sl_loops = max_num_sl_loops - if number_of_sl_loops > len(all_training_structures): - msg = f"Number of SL loops ({number_of_sl_loops}) cannot be greater than the design space ({len(all_training_structures)})" + if number_of_sl_loops > max_num_sl_loops: + msg = f"Number of SL loops ({number_of_sl_loops}) cannot be greater than ({max_num_sl_loops})" + raise AutoCatSequentialLearningError(msg) # generate initial training set train_idx = np.zeros(len(all_training_structures), dtype=bool) @@ -192,7 +205,7 @@ def simulated_sequential_learning( len(all_training_structures), init_training_size, replace=False ) ] = 1 - train_history = [train_idx] + train_history = [train_idx.copy()] # fit on initial training set X = [s for s, i in zip(all_training_structures, train_idx) if i] @@ -209,13 +222,10 @@ def simulated_sequential_learning( rmse_test_history = [] test_pred_history = [] test_unc_history = [] - ctr = 0 - while ctr < number_of_sl_loops: - ctr += 1 - print(f"Sequential Learning Iteration #{ctr}") - # select new candidates to predict on - # make predictions + def _collect_pred_stats( + all_training_structures, all_training_y, testing_structures, testing_y + ): _preds, _uncs = predictor.predict(all_training_structures) # get scores on full training set @@ -248,6 +258,14 @@ def simulated_sequential_learning( unc_history.append(_uncs) pred_history.append(_preds) + return _preds, _uncs + + for i in range(number_of_sl_loops): + print(f"Sequential Learning Iteration #{i+1}") + # make predictions + _preds, _uncs = _collect_pred_stats( + all_training_structures, all_training_y, testing_structures, testing_y + ) # check that enough data pts left to adhere to batch_size_to_add # otherwise just add the leftover data @@ -272,13 +290,18 @@ def simulated_sequential_learning( # add next candidates to training set train_idx[next_candidate_idx] = True - train_history.append(train_idx) + train_history.append(train_idx.copy()) # retrain on training set with new additions X = [s for s, i in zip(all_training_structures, train_idx) if i] y = all_training_y[train_idx] predictor.fit(X, y) + # make preds on final model + _, _ = _collect_pred_stats( + all_training_structures, all_training_y, testing_structures, testing_y + ) + sl_dict = { "training_history": [th.tolist() for th in train_history], "uncertainty_history": [mu.tolist() for mu in unc_history], From 3fabcca0961940237b610572862e6b76344560cb Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 3 Sep 2021 13:31:31 -0400 Subject: [PATCH 097/239] update tests for generalized sl funcs --- tests/learning/test_sequential.py | 342 ++++++++++++++++++------------ 1 file changed, 206 insertions(+), 136 deletions(-) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 1dad3833..44292f52 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -7,11 +7,16 @@ import tempfile -from ase.io import read as ase_read +from scipy import stats -from autocat.learning.predictors import AutoCatStructureCorrector +from autocat.learning.predictors import AutoCatPredictor +from autocat.learning.sequential import ( + AutoCatSequentialLearningError, + choose_next_candidate, + get_overlap_score, +) from autocat.learning.sequential import simulated_sequential_learning -from autocat.learning.sequential import multiple_sequential_learning_runs +from autocat.learning.sequential import multiple_simulated_sequential_learning_runs from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate @@ -26,40 +31,37 @@ def test_simulated_sequential_outputs(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] + acsc = AutoCatPredictor(structure_featurizer="elemental_property") sl_dict = simulated_sequential_learning( acsc, - [base_struct1, base_struct2], - initial_num_of_perturbations_per_base_structure=4, - batch_num_of_perturbations_per_base_structure=3, - number_of_sl_loops=4, + [base_struct1, base_struct2, base_struct3, sub1, sub2,], + np.array([0.0, 1.0, 2.0, 3.0, 4.0]), + init_training_size=1, + batch_size_to_add=2, + number_of_sl_loops=2, + acquisition_function="MLI", + target_min=0.9, + target_max=2.1, ) - # Test that number of max uncertainties equals number of sl loops - assert len(sl_dict["candidate_max_unc_history"]) == 4 + + # Test number of train histories equals number of sl loops + 1 + assert len(sl_dict["training_history"]) == 3 + + # Test initial training size + assert len([a for a in sl_dict["training_history"][0] if a]) == 1 + + # Test keeping track of history + assert sl_dict["training_history"][0] != sl_dict["training_history"][1] + + # Test that number of max uncertainties equals number of sl loops + 1 + assert len(sl_dict["uncertainty_history"]) == 3 # Test the number of total uncertainties collected - assert len(sl_dict["candidate_full_unc_history"][-1]) == 6 - sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2], - batch_num_of_perturbations_per_base_structure=1, - number_of_sl_loops=4, - ) - # check default for initial number of training perturbations - assert len(sl_dict["candidate_full_unc_history"][0]) == 2 + assert len(sl_dict["uncertainty_history"][-1]) == 5 # check all mae and rmse training scores collected - assert len(sl_dict["mae_train_history"]) == 4 - assert len(sl_dict["rmse_train_history"]) == 4 - - # check prediction and correction history - assert len(sl_dict["candidate_pred_corrs_history"]) == len( - sl_dict["candidate_real_corrs_history"] - ) - assert len(sl_dict["candidate_pred_corrs_history"][-1]) == len( - sl_dict["candidate_real_corrs_history"][-1] - ) - assert len(sl_dict["candidate_pred_corrs_history"]) == 4 - assert len(sl_dict["candidate_real_corrs_history"]) == 4 + assert len(sl_dict["mae_train_history"]) == 3 + assert len(sl_dict["rmse_train_history"]) == 3 def test_simulated_sequential_batch_added(): @@ -72,31 +74,71 @@ def test_simulated_sequential_batch_added(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + acsc = AutoCatPredictor(structure_featurizer="elemental_property") bsta = 2 - num_loops = 3 + num_loops = 2 sl_dict = simulated_sequential_learning( acsc, - [base_struct1, base_struct2], - batch_num_of_perturbations_per_base_structure=4, + [base_struct1, base_struct2, sub1, sub2], + np.array([5.0, 6.0, 7.0, 8.0]), batch_size_to_add=bsta, number_of_sl_loops=num_loops, + acquisition_function="Random", + init_training_size=1, + ) + assert len(sl_dict["training_history"]) == num_loops + 1 + num_in_tr_set0 = len([a for a in sl_dict["training_history"][0] if a]) + num_in_tr_set1 = len([a for a in sl_dict["training_history"][1] if a]) + num_in_tr_set2 = len([a for a in sl_dict["training_history"][2] if a]) + # should add 2 candidates on first loop + assert num_in_tr_set1 - num_in_tr_set0 == bsta + # since only 1 left, should add it on the next + assert num_in_tr_set2 - num_in_tr_set1 == 1 + + +def test_simulated_sequential_num_loops(): + # Tests the number of loops + sub1 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "H")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "N")["custom"]["structure"] + acsc = AutoCatPredictor(structure_featurizer="elemental_property") + # Test default number of loops + bsta = 3 + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2, sub1, sub2], + np.array([5.0, 6.0, 7.0, 8.0]), + batch_size_to_add=bsta, + acquisition_function="Random", + init_training_size=1, + ) + assert len(sl_dict["training_history"]) == 2 + + # Test catches maximum number of loops + with pytest.raises(AutoCatSequentialLearningError): + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2, sub1, sub2], + np.array([5.0, 6.0, 7.0, 8.0]), + batch_size_to_add=bsta, + acquisition_function="Random", + init_training_size=1, + number_of_sl_loops=3, + ) + + sl_dict = simulated_sequential_learning( + acsc, + [base_struct1, base_struct2, sub2], + np.array([5.0, 6.0, 7.0]), + acquisition_function="Random", + init_training_size=1, ) - # each max unc history should be equal to number of candidates added - assert len(sl_dict["candidate_max_unc_history"][0]) == bsta - # first dimension is number of loops - assert len(sl_dict["candidate_pred_corrs_history"]) == num_loops - # next dimension is size of candidates added on each loop - assert len(sl_dict["candidate_pred_corrs_history"][0]) == bsta - # next dimension is size of adsorbate - assert len(sl_dict["candidate_pred_corrs_history"][0][0]) == 2 - # next dimension is vector correction to atom in adsorbate - assert len(sl_dict["candidate_pred_corrs_history"][0][0][0]) == 3 - # check same holds for real history - assert len(sl_dict["candidate_real_corrs_history"]) == num_loops - assert len(sl_dict["candidate_real_corrs_history"][0]) == bsta - assert len(sl_dict["candidate_real_corrs_history"][0][0]) == 2 - assert len(sl_dict["candidate_real_corrs_history"][0][0][0]) == 3 + assert len(sl_dict["training_history"]) == 3 def test_simulated_sequential_outputs_testing(): @@ -110,29 +152,31 @@ def test_simulated_sequential_outputs_testing(): base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] - acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + acsc = AutoCatPredictor(structure_featurizer="elemental_property") sl_dict = simulated_sequential_learning( acsc, - [base_struct1, base_struct2], - testing_base_structures=[base_struct3], - batch_num_of_perturbations_per_base_structure=3, - batch_size_to_add=3, + [base_struct1, base_struct2, sub1], + np.array([0.0, 2.0, 4.0]), + init_training_size=1, + testing_structures=[base_struct3, sub2], + testing_y=np.array([8.0, 10.0]), + batch_size_to_add=1, number_of_sl_loops=2, + acquisition_function="MU", ) - # Check lenght of testing scores - assert len(sl_dict["mae_test_history"]) == 2 - assert len(sl_dict["rmse_train_history"]) == 2 - assert len(sl_dict["mae_train_history"]) == 2 - assert len(sl_dict["test_preds_history"]) == 2 - assert len(sl_dict["test_preds_history"][0]) == 3 - assert len(sl_dict["test_unc_history"]) == 2 - assert len(sl_dict["test_unc_history"][0]) == 3 + # Check length of testing scores + assert len(sl_dict["training_history"]) == 3 + assert len(sl_dict["aq_scores_history"]) == 2 + assert len(sl_dict["max_scores_history"]) == 2 + assert len(sl_dict["mae_test_history"]) == 3 + assert len(sl_dict["rmse_train_history"]) == 3 + assert len(sl_dict["mae_train_history"]) == 3 + assert len(sl_dict["test_prediction_history"]) == 3 + assert len(sl_dict["test_prediction_history"][0]) == 2 + assert len(sl_dict["test_uncertainty_history"]) == 3 + assert len(sl_dict["test_uncertainty_history"][0]) == 2 assert sl_dict["mae_test_history"] != sl_dict["mae_train_history"] - # check selected_candidate_history - assert len(sl_dict["selected_candidate_history"]) == 2 - assert len(sl_dict["selected_candidate_history"][0]) == 3 - def test_simulated_sequential_write_to_disk(): # Test writing out sl dict @@ -146,35 +190,24 @@ def test_simulated_sequential_write_to_disk(): base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] base_struct3 = place_adsorbate(sub2, "N")["custom"]["structure"] - acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") + acsc = AutoCatPredictor(structure_featurizer="elemental_property") sl_dict = simulated_sequential_learning( acsc, - [base_struct1, base_struct2], - testing_base_structures=[base_struct3], - batch_num_of_perturbations_per_base_structure=1, + [base_struct1, base_struct2, base_struct3], + np.array([0, 1, 2]), + init_training_size=2, + testing_structures=[base_struct3], + testing_y=np.array([2]), batch_size_to_add=2, - number_of_sl_loops=2, + number_of_sl_loops=1, write_to_disk=True, write_location=_tmp_dir, + acquisition_function="Random", ) - # when writing to disk, candidate history separated out - sl_dict_data = { - key: sl_dict[key] for key in sl_dict if key != "selected_candidate_history" - } # check data written as json with open(os.path.join(_tmp_dir, "sl_dict.json"), "r") as f: sl_written = json.load(f) - assert sl_dict_data == sl_written - - # check written sl selected candidates - cands1 = ase_read(f"{_tmp_dir}/candidate_structures/sl_iter1.traj", index=":") - # print(cands1) - assert len(cands1) == 2 - # assert sl_dict["selected_candidate_history"][0] == cands1 - cands2 = ase_read(f"{_tmp_dir}/candidate_structures/sl_iter2.traj", index=":") - print(cands2) - print(sl_dict["selected_candidate_history"]) - assert sl_dict["selected_candidate_history"][1] == cands2 + assert sl_dict == sl_written def test_multiple_sequential_learning_serial(): @@ -183,13 +216,17 @@ def test_multiple_sequential_learning_serial(): "structure" ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") - runs_history = multiple_sequential_learning_runs( - acsc, - [base_struct1], - 3, - batch_num_of_perturbations_per_base_structure=1, - number_of_sl_loops=2, + acsc = AutoCatPredictor(structure_featurizer="elemental_property") + runs_history = multiple_simulated_sequential_learning_runs( + number_of_runs=3, + sl_kwargs={ + "predictor": acsc, + "all_training_structures": [base_struct1, sub1], + "all_training_y": np.array([0.0, 0.0]), + "number_of_sl_loops": 1, + "acquisition_function": "MU", + "init_training_size": 1, + }, ) assert len(runs_history) == 3 assert isinstance(runs_history[0], dict) @@ -202,14 +239,18 @@ def test_multiple_sequential_learning_parallel(): "structure" ] base_struct1 = place_adsorbate(sub1, "Li")["custom"]["structure"] - acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") - runs_history = multiple_sequential_learning_runs( - acsc, - [base_struct1], - 3, - batch_num_of_perturbations_per_base_structure=1, - number_of_sl_loops=2, + acsc = AutoCatPredictor(structure_featurizer="elemental_property") + runs_history = multiple_simulated_sequential_learning_runs( + number_of_runs=3, number_parallel_jobs=2, + sl_kwargs={ + "predictor": acsc, + "all_training_structures": [base_struct1, sub1], + "all_training_y": np.array([0.0, 0.0]), + "number_of_sl_loops": 1, + "acquisition_function": "Random", + "init_training_size": 1, + }, ) assert len(runs_history) == 3 assert isinstance(runs_history[2], dict) @@ -223,46 +264,75 @@ def test_multiple_sequential_learning_write_to_disk(): "structure" ] base_struct1 = place_adsorbate(sub1, "N")["custom"]["structure"] - acsc = AutoCatStructureCorrector(structure_featurizer="elemental_property") - runs_history = multiple_sequential_learning_runs( - acsc, - [base_struct1], - 3, - batch_num_of_perturbations_per_base_structure=2, - number_of_sl_loops=2, - batch_size_to_add=2, + acsc = AutoCatPredictor(structure_featurizer="elemental_property") + runs_history = multiple_simulated_sequential_learning_runs( + number_of_runs=3, + number_parallel_jobs=2, write_to_disk=True, write_location=_tmp_dir, + sl_kwargs={ + "predictor": acsc, + "all_training_structures": [base_struct1, sub1], + "all_training_y": np.array([0.0, 0.0]), + "number_of_sl_loops": 1, + "acquisition_function": "Random", + "init_training_size": 1, + "batch_size_to_add": 2, + }, ) - runs_history_data = [] - for r in runs_history: - data_dict = {key: r[key] for key in r if key != "selected_candidate_history"} - runs_history_data.append(data_dict) # check data history with open(os.path.join(_tmp_dir, "sl_runs_history.json"), "r") as f: runs_history_written = json.load(f) - assert runs_history_data == runs_history_written + assert runs_history == runs_history_written - # check selected candidate history - # check run 1 iteration 1 - cands1_1 = ase_read( - f"{_tmp_dir}/candidate_structures/run1/sl_iter1.traj", index=":" - ) - assert len(cands1_1) == 2 - assert runs_history[0]["selected_candidate_history"][0] == cands1_1 - # check run 1 iteration 2 - cands1_2 = ase_read( - f"{_tmp_dir}/candidate_structures/run1/sl_iter2.traj", index=":" - ) - assert runs_history[0]["selected_candidate_history"][1] == cands1_2 - # check run 2 iteration 1 - cands2_1 = ase_read( - f"{_tmp_dir}/candidate_structures/run2/sl_iter1.traj", index=":" - ) - assert runs_history[1]["selected_candidate_history"][0] == cands2_1 - # check run 2 iteration 2 - cands2_2 = ase_read( - f"{_tmp_dir}/candidate_structures/run2/sl_iter2.traj", index=":" - ) - assert runs_history[1]["selected_candidate_history"][1] == cands2_2 + +def test_choose_next_candidate_input_minimums(): + # Tests that appropriately catches minimum necessary inputs + labels = np.random.rand(5) + train_idx = np.zeros(5, dtype=bool) + train_idx[np.random.choice(5, size=2, replace=False)] = 1 + unc = np.random.rand(5) + pred = np.random.rand(5) + + with pytest.raises(AutoCatSequentialLearningError): + choose_next_candidate() + + with pytest.raises(AutoCatSequentialLearningError): + choose_next_candidate(unc=unc, pred=pred, num_candidates_to_pick=2, aq="Random") + + with pytest.raises(AutoCatSequentialLearningError): + choose_next_candidate( + labels=labels, pred=pred, num_candidates_to_pick=2, aq="MU" + ) + + with pytest.raises(AutoCatSequentialLearningError): + choose_next_candidate(pred=pred, num_candidates_to_pick=2, aq="MLI") + + with pytest.raises(AutoCatSequentialLearningError): + choose_next_candidate(unc=unc, num_candidates_to_pick=2, aq="MLI") + + +def test_get_overlap_score(): + # Tests default behavior + mean = 0.0 + std = 0.1 + x1 = -0.4 + x2 = 0.8 + norm = stats.norm(loc=mean, scale=std) + + # checks that at least target min or max is provided + with pytest.raises(AutoCatSequentialLearningError): + get_overlap_score(mean, std) + + # test default min + overlap_score = get_overlap_score(mean, std, x2=x2) + assert np.isclose(overlap_score, norm.cdf(x2)) + + # test default max + overlap_score = get_overlap_score(mean, std, x1=x1) + assert np.isclose(overlap_score, 1.0 - norm.cdf(x1)) + + # test both max and min + overlap_score = get_overlap_score(mean, std, x1=x1, x2=x2) + assert np.isclose(overlap_score, norm.cdf(x2) - norm.cdf(x1)) From 2eea3d841e93abda35d3e1883977ff3aaa84f41f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 7 Sep 2021 13:08:05 -0400 Subject: [PATCH 098/239] add hhi data --- src/autocat/data/hhi/__init__.py | 23 ++++++++++ src/autocat/data/hhi/hhi_p.json | 79 ++++++++++++++++++++++++++++++++ src/autocat/data/hhi/hhi_r.json | 79 ++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 src/autocat/data/hhi/__init__.py create mode 100644 src/autocat/data/hhi/hhi_p.json create mode 100644 src/autocat/data/hhi/hhi_r.json diff --git a/src/autocat/data/hhi/__init__.py b/src/autocat/data/hhi/__init__.py new file mode 100644 index 00000000..fba5356b --- /dev/null +++ b/src/autocat/data/hhi/__init__.py @@ -0,0 +1,23 @@ +import json +import pkg_resources + +__all__ = ["HHI_PRODUCTION", "HHI_RESERVES"] +""" +Values obtained from dx.doi.org/10.1021/cm400893e + +HHI_PRODUCTION: + Calculated based on elemental production + +HHI_RESERVES: + Calculated based on known elemental reserves +""" + +raw_hhi_p = pkg_resources.resource_filename("autocat.data.hhi", "hhi_p.json") + +with open(raw_hhi_p) as fr: + HHI_PRODUCTION = json.load(fr) + +raw_hhi_r = pkg_resources.resource_filename("autocat.data.hhi", "hhi_r.json") + +with open(raw_hhi_r) as fr: + HHI_RESERVES = json.load(fr) diff --git a/src/autocat/data/hhi/hhi_p.json b/src/autocat/data/hhi/hhi_p.json new file mode 100644 index 00000000..3681cacf --- /dev/null +++ b/src/autocat/data/hhi/hhi_p.json @@ -0,0 +1,79 @@ +{ + "He": 3200, + "Li": 2900, + "Be": 8000, + "B": 2900, + "C": 500, + "N": 1300, + "O": 500, + "F": 1500, + "Na": 1100, + "Mg": 5300, + "Al": 1600, + "Si": 4700, + "P": 2000, + "S": 700, + "Cl": 1500, + "K": 1700, + "Ca": 3900, + "Sc": 5500, + "Ti": 1100, + "V": 3300, + "Cr": 3100, + "Mn": 1600, + "Fe": 2400, + "Co": 3100, + "Ni": 1000, + "Cu": 1600, + "Zn": 1600, + "Ga": 5500, + "Ge": 5300, + "As": 3300, + "Se": 2200, + "Br": 3300, + "Rb": 6000, + "Sr": 4200, + "Y": 9800, + "Zr": 3400, + "Nb": 8500, + "Mo": 2400, + "Ru": 3200, + "Rh": 3200, + "Pd": 3200, + "Ag": 1200, + "Cd": 1700, + "In": 3300, + "Sn": 2600, + "Sb": 7900, + "Te": 2900, + "I": 4900, + "Cs": 6000, + "Ba": 3000, + "La": 9500, + "Ce": 9500, + "Pr": 9500, + "Nd": 9500, + "Pm": 9500, + "Sm": 9500, + "Eu": 9500, + "Gd": 9500, + "Tb": 9500, + "Dy": 9500, + "Ho": 9500, + "Er": 9500, + "Tm": 9500, + "Yb": 9500, + "Lu": 9500, + "Hf": 3400, + "Ta": 2300, + "W": 7000, + "Re": 3300, + "Os": 5500, + "Ir": 5500, + "Pt": 5500, + "Au": 1100, + "Hg": 5500, + "Tl": 6500, + "Pb": 2700, + "Bi": 5300 +} diff --git a/src/autocat/data/hhi/hhi_r.json b/src/autocat/data/hhi/hhi_r.json new file mode 100644 index 00000000..79079bd1 --- /dev/null +++ b/src/autocat/data/hhi/hhi_r.json @@ -0,0 +1,79 @@ +{ + "He": 3900, + "Li": 4200, + "Be": 4000, + "B": 2000, + "C": 500, + "N": 500, + "O": 500, + "F": 1500, + "Na": 500, + "Mg": 500, + "Al": 1000, + "Si": 1000, + "P": 5100, + "S": 1000, + "Cl": 1500, + "K": 7200, + "Ca": 1500, + "Sc": 4500, + "Ti": 1600, + "V": 3400, + "Cr": 4100, + "Mn": 1800, + "Fe": 1400, + "Co": 2700, + "Ni": 1500, + "Cu": 1500, + "Zn": 1900, + "Ga": 1900, + "Ge": 1900, + "As": 4000, + "Se": 1900, + "Br": 6900, + "Rb": 6000, + "Sr": 3000, + "Y": 2600, + "Zr": 2600, + "Nb": 8800, + "Mo": 5300, + "Ru": 8000, + "Rh": 8000, + "Pd": 8000, + "Ag": 1400, + "Cd": 1300, + "In": 2000, + "Sn": 1600, + "Sb": 3400, + "Te": 4900, + "I": 4800, + "Cs": 6000, + "Ba": 2300, + "La": 3100, + "Ce": 3100, + "Pr": 3100, + "Nd": 3100, + "Pm": 3100, + "Sm": 3100, + "Eu": 3100, + "Gd": 3100, + "Tb": 3100, + "Dy": 3100, + "Ho": 3100, + "Er": 3100, + "Tm": 3100, + "Yb": 3100, + "Lu": 3100, + "Hf": 2600, + "Ta": 4800, + "W": 4300, + "Re": 3300, + "Os": 9100, + "Ir": 9100, + "Pt": 9100, + "Au": 1000, + "Hg": 3100, + "Tl": 6500, + "Pb": 1800, + "Bi": 6000 +} From 9918f401d534564309a16ab586c4f88fac8a497f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 16 Sep 2021 11:37:08 -0400 Subject: [PATCH 099/239] add hhi score calculator --- src/autocat/learning/sequential.py | 50 +++++++++++++++++++++++++++ tests/learning/test_sequential.py | 55 ++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 2cbdf479..ab2e959c 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -468,3 +468,53 @@ def get_overlap_score(mean: float, std: float, x2: float = None, x1: float = Non norm_dist = stats.norm(loc=mean, scale=std) return norm_dist.cdf(x2) - norm_dist.cdf(x1) + + +def calculate_hhi_scores(structures: List[Atoms], hhi_type: str = "production"): + """ + Calculates HHI scores for structures weighted by their composition. + The scores are normalized and inverted such that these should + be maximized in the interest of finding a low cost system + + Parameters + ---------- + + structures: + List of Atoms objects for which to calculate the scores + + hhi_type: + Type of HHI index to be used for the score + Options + - production (default) + - reserves + + Returns + ------- + + hhi_scores: + Scores corresponding to each of the provided structures + + """ + if structures is None: + msg = "To include HHI, the structures must be provided" + raise AutoCatSequentialLearningError(msg) + + raw_hhi_data = {"production": HHI_PRODUCTION, "reserves": HHI_RESERVES} + max_hhi = np.max([raw_hhi_data[hhi_type][r] for r in raw_hhi_data[hhi_type]]) + min_hhi = np.min([raw_hhi_data[hhi_type][r] for r in raw_hhi_data[hhi_type]]) + # normalize and invert (so that this score is to be maximized) + norm_hhi_data = { + el: 1.0 - (raw_hhi_data[hhi_type][el] - min_hhi) / (max_hhi - min_hhi) + for el in raw_hhi_data[hhi_type] + } + + hhi_scores = np.zeros(len(structures)) + for idx, struct in enumerate(structures): + hhi = 0 + el_counts = struct.symbols.formula.count() + tot_size = len(struct) + # weight calculated hhi score by composition + for el in el_counts: + hhi += norm_hhi_data[el] * el_counts[el] / tot_size + hhi_scores[idx] = hhi + return hhi_scores diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 44292f52..bd193a83 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -9,6 +9,8 @@ from scipy import stats +from autocat.data.hhi import HHI_PRODUCTION +from autocat.data.hhi import HHI_RESERVES from autocat.learning.predictors import AutoCatPredictor from autocat.learning.sequential import ( AutoCatSequentialLearningError, @@ -17,8 +19,10 @@ ) from autocat.learning.sequential import simulated_sequential_learning from autocat.learning.sequential import multiple_simulated_sequential_learning_runs +from autocat.learning.sequential import calculate_hhi_scores from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate +from autocat.saa import generate_saa_structures def test_simulated_sequential_outputs(): @@ -336,3 +340,54 @@ def test_get_overlap_score(): # test both max and min overlap_score = get_overlap_score(mean, std, x1=x1, x2=x2) assert np.isclose(overlap_score, norm.cdf(x2) - norm.cdf(x1)) + + +def test_calculate_hhi_scores(): + # Tests calculating the HHI scores + saa_dict = generate_saa_structures( + ["Pt", "Cu", "Ni"], + ["Ru"], + facets={"Pt": ["111"], "Cu": ["111"], "Ni": ["111"]}, + ) + saa_structs = [saa_dict[host]["Ru"]["fcc111"]["structure"] for host in saa_dict] + # test production + hhi_prod_scores = calculate_hhi_scores(saa_structs) + norm_hhi_prod = { + el: 1.0 - (HHI_PRODUCTION[el] - 500.0) / 9300.0 for el in HHI_PRODUCTION + } + # check approach properly normalizes and inverts + assert np.isclose(norm_hhi_prod["Y"], 0.0) + assert np.isclose(norm_hhi_prod["O"], 1.0) + # test scores calculated on SAAs + assert np.isclose( + hhi_prod_scores[0], (35 * norm_hhi_prod["Pt"] + norm_hhi_prod["Ru"]) / 36 + ) + assert np.isclose( + hhi_prod_scores[1], (35 * norm_hhi_prod["Cu"] + norm_hhi_prod["Ru"]) / 36 + ) + assert np.isclose( + hhi_prod_scores[2], (35 * norm_hhi_prod["Ni"] + norm_hhi_prod["Ru"]) / 36 + ) + # check scores normalized + assert (hhi_prod_scores <= 1.0).all() + assert (hhi_prod_scores >= 0.0).all() + # test reserves + hhi_res_scores = calculate_hhi_scores(saa_structs, "reserves") + norm_hhi_res = { + el: 1.0 - (HHI_RESERVES[el] - 500.0) / 8600.0 for el in HHI_RESERVES + } + # check approach properly normalizes and inverts + assert np.isclose(norm_hhi_res["Pt"], 0.0) + assert np.isclose(norm_hhi_res["C"], 1.0) + assert np.isclose( + hhi_res_scores[0], (35 * norm_hhi_res["Pt"] + norm_hhi_res["Ru"]) / 36 + ) + assert np.isclose( + hhi_res_scores[1], (35 * norm_hhi_res["Cu"] + norm_hhi_res["Ru"]) / 36 + ) + assert np.isclose( + hhi_res_scores[2], (35 * norm_hhi_res["Ni"] + norm_hhi_res["Ru"]) / 36 + ) + # check normalized + assert (hhi_res_scores <= 1.0).all() + assert (hhi_res_scores >= 0.0).all() From db87e5e9fe17f30746b3139066e758cfab39da60 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 16 Sep 2021 12:12:02 -0400 Subject: [PATCH 100/239] fix imports --- src/autocat/learning/sequential.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index ab2e959c..85e25a63 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -10,8 +10,9 @@ from ase import Atoms from scipy import stats -from autocat.perturbations import generate_perturbed_dataset from autocat.learning.predictors import AutoCatPredictor +from autocat.data.hhi import HHI_PRODUCTION +from autocat.data.hhi import HHI_RESERVES Array = List[float] From aef1a2e82ed1426fa0819902d05bbb15ce988a7e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 21 Sep 2021 13:23:39 -0400 Subject: [PATCH 101/239] reorg choose cand func --- src/autocat/learning/sequential.py | 67 +++++++++++++----------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 85e25a63..677c43ed 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -277,17 +277,17 @@ def _collect_pred_stats( # select next candidate(s) next_candidate_idx, max_scores, aq_scores = choose_next_candidate( - all_training_y, - train_idx, - _preds, - _uncs, - acquisition_function, - bsa, - target_min, - target_max, + labels=all_training_y, + train_idx=train_idx, + pred=_preds, + unc=_uncs, + aq=acquisition_function, + num_candidates_to_pick=bsa, + target_min=target_min, + target_max=target_max, ) - max_scores_history.append(max_scores) - aq_scores_history.append(aq_scores) + max_scores_history.append([int(i) for i in max_scores]) + aq_scores_history.append(aq_scores.tolist()) # add next candidates to training set train_idx[next_candidate_idx] = True @@ -331,6 +331,7 @@ def _collect_pred_stats( def choose_next_candidate( + structures: List[Atoms] = None, labels: Array = None, train_idx: Array = None, pred: Array = None, @@ -339,6 +340,8 @@ def choose_next_candidate( num_candidates_to_pick: int = None, target_min: float = None, target_max: float = None, + include_hhi: bool = None, + hhi_type: str = "production", ): """ Chooses the next candidate(s) from a given acquisition function @@ -387,6 +390,12 @@ def choose_next_candidate( aq_scores: Calculated scores based on the selected `aq` for the entire training set """ + if include_hhi: + if structures is None: + msg = "Structures must be provided to include HHI scores" + raise AutoCatSequentialLearningError(msg) + hhi_scores = calculate_hhi_scores(structures, hhi_type) + if aq == "Random": if labels is None: msg = "For aq = 'Random', the labels must be supplied" @@ -395,15 +404,7 @@ def choose_next_candidate( if train_idx is None: train_idx = np.zeros(len(labels), dtype=bool) - next_idx = np.random.choice( - len(labels[~train_idx]), size=num_candidates_to_pick, replace=False - ) - if num_candidates_to_pick is None: - next_idx = np.array([next_idx]) - parent_idx = np.arange(labels.shape[0])[~train_idx][next_idx] - - max_scores = [] - aq_scores = [] + aq_scores = np.random.choice(len(labels), size=len(labels), replace=False) elif aq == "MU": if unc is None: @@ -413,15 +414,6 @@ def choose_next_candidate( if train_idx is None: train_idx = np.zeros(len(unc), dtype=bool) - if num_candidates_to_pick is None: - next_idx = np.array([np.argmax(unc[~train_idx])]) - max_scores = [np.max(unc[~train_idx])] - - else: - next_idx = np.argsort(unc[~train_idx])[-num_candidates_to_pick:] - sorted_array = unc[~train_idx][next_idx] - max_scores = list(sorted_array[-num_candidates_to_pick:]) - parent_idx = np.arange(unc.shape[0])[~train_idx][next_idx] aq_scores = unc elif aq == "MLI": @@ -438,20 +430,21 @@ def choose_next_candidate( for mean, std in zip(pred, unc) ] ) - if num_candidates_to_pick is None: - next_idx = np.array([np.argmax(aq_scores[~train_idx])]) - max_scores = [np.max(aq_scores[~train_idx])] - - else: - next_idx = np.argsort(aq_scores[~train_idx])[-num_candidates_to_pick:] - sorted_array = aq_scores[~train_idx][next_idx] - max_scores = list(sorted_array[-num_candidates_to_pick:]) - parent_idx = np.arange(aq_scores.shape[0])[~train_idx][next_idx] else: msg = f"Acquisition function {aq} is not supported" raise NotImplementedError(msg) + if num_candidates_to_pick is None: + next_idx = np.array([np.argmax(aq_scores[~train_idx])]) + max_scores = [np.max(aq_scores[~train_idx])] + + else: + next_idx = np.argsort(aq_scores[~train_idx])[-num_candidates_to_pick:] + sorted_array = aq_scores[~train_idx][next_idx] + max_scores = list(sorted_array[-num_candidates_to_pick:]) + parent_idx = np.arange(aq_scores.shape[0])[~train_idx][next_idx] + return parent_idx, max_scores, aq_scores From 073aa0d024f4ac339cf1fb4fc63dbb4c298942e2 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 21 Sep 2021 14:06:42 -0400 Subject: [PATCH 102/239] implement hhi weighting --- src/autocat/learning/sequential.py | 43 ++++++++++++++++++++++++------ tests/learning/test_sequential.py | 37 +++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 677c43ed..bb39a35a 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -340,7 +340,7 @@ def choose_next_candidate( num_candidates_to_pick: int = None, target_min: float = None, target_max: float = None, - include_hhi: bool = None, + include_hhi: bool = False, hhi_type: str = "production", ): """ @@ -349,6 +349,9 @@ def choose_next_candidate( Parameters ---------- + structures: + List of `Atoms` objects to be used for HHI weighting if desired + labels: Array of the labels for the data @@ -378,6 +381,15 @@ def choose_next_candidate( target_max: Maximum target value to optimize for + include_hhi: + Whether HHI scores should be used to weight aq scores + + hhi_type: + Type of HHI index to be used for weighting + Options + - production (default) + - reserves + Returns ------- @@ -390,6 +402,7 @@ def choose_next_candidate( aq_scores: Calculated scores based on the selected `aq` for the entire training set """ + hhi_scores = None if include_hhi: if structures is None: msg = "Structures must be provided to include HHI scores" @@ -404,7 +417,12 @@ def choose_next_candidate( if train_idx is None: train_idx = np.zeros(len(labels), dtype=bool) - aq_scores = np.random.choice(len(labels), size=len(labels), replace=False) + if hhi_scores is None: + hhi_scores = np.ones(len(train_idx)) + + aq_scores = ( + np.random.choice(len(labels), size=len(labels), replace=False) * hhi_scores + ) elif aq == "MU": if unc is None: @@ -414,7 +432,10 @@ def choose_next_candidate( if train_idx is None: train_idx = np.zeros(len(unc), dtype=bool) - aq_scores = unc + if hhi_scores is None: + hhi_scores = np.ones(len(train_idx)) + + aq_scores = unc.copy() * hhi_scores elif aq == "MLI": if unc is None or pred is None: @@ -424,11 +445,17 @@ def choose_next_candidate( if train_idx is None: train_idx = np.zeros(len(unc), dtype=bool) - aq_scores = np.array( - [ - get_overlap_score(mean, std, x2=target_max, x1=target_min) - for mean, std in zip(pred, unc) - ] + if hhi_scores is None: + hhi_scores = np.ones(len(train_idx)) + + aq_scores = ( + np.array( + [ + get_overlap_score(mean, std, x2=target_max, x1=target_min) + for mean, std in zip(pred, unc) + ] + ) + * hhi_scores ) else: diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index bd193a83..009b7139 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -317,6 +317,43 @@ def test_choose_next_candidate_input_minimums(): choose_next_candidate(unc=unc, num_candidates_to_pick=2, aq="MLI") +def test_choose_next_candidate_hhi_weighting(): + # Tests that the HHI weighting is properly applied + unc = np.array([0.1, 0.1]) + pred = np.array([4.0, 4.0]) + # Tests using production HHI values and MU + y_struct = generate_surface_structures(["Y"], facets={"Y": ["0001"]})["Y"][ + "hcp0001" + ]["structure"] + ni_struct = generate_surface_structures(["Ni"], facets={"Ni": ["111"]})["Ni"][ + "fcc111" + ]["structure"] + parent_idx, _, aq_scores = choose_next_candidate( + [y_struct, ni_struct], unc=unc, include_hhi=True, aq="MU" + ) + assert parent_idx[0] == 1 + assert aq_scores[0] < aq_scores[1] + + # Tests using reserves HHI values and MLI + nb_struct = generate_surface_structures(["Nb"], facets={"Nb": ["111"]})["Nb"][ + "bcc111" + ]["structure"] + na_struct = generate_surface_structures(["Na"], facets={"Na": ["110"]})["Na"][ + "bcc110" + ]["structure"] + parent_idx, _, aq_scores = choose_next_candidate( + [na_struct, nb_struct], + unc=unc, + pred=pred, + target_min=3, + target_max=5, + include_hhi=True, + hhi_type="reserves", + ) + assert parent_idx[0] == 0 + assert aq_scores[0] > aq_scores[1] + + def test_get_overlap_score(): # Tests default behavior mean = 0.0 From d419278607fb1aef886dd864e32945ab900dd377 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 21 Sep 2021 14:15:41 -0400 Subject: [PATCH 103/239] propagate hhi thru sl func --- src/autocat/learning/sequential.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index bb39a35a..10470423 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -96,6 +96,8 @@ def simulated_sequential_learning( number_of_sl_loops: int = None, target_min: float = None, target_max: float = None, + include_hhi: bool = False, + hhi_type: str = "production", write_to_disk: bool = False, write_location: str = ".", ): @@ -157,6 +159,15 @@ def simulated_sequential_learning( Label value that ideal candidates should be less than Default: +inf + include_hhi: + Whether HHI scores should be used to weight aq scores + + hhi_type: + Type of HHI index to be used for weighting + Options + - production (default) + - reserves + write_to_disk: Boolean specifying whether the sl dictionary should be written to disk as a json. Defaults to False. @@ -285,6 +296,8 @@ def _collect_pred_stats( num_candidates_to_pick=bsa, target_min=target_min, target_max=target_max, + include_hhi=include_hhi, + hhi_type=hhi_type, ) max_scores_history.append([int(i) for i in max_scores]) aq_scores_history.append(aq_scores.tolist()) From 424ef01a8c55b56353c53da65431936505191a68 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 28 Sep 2021 19:24:31 -0400 Subject: [PATCH 104/239] adds design space object --- src/autocat/learning/sequential.py | 105 ++++++++++++++++++++++++++++- tests/learning/test_sequential.py | 64 +++++++++++++++++- 2 files changed, 167 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 10470423..fe857e7e 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -2,12 +2,14 @@ import os import json from joblib import Parallel, delayed - +import tempfile from typing import List from typing import Dict from typing import Union from ase import Atoms +from ase.io import write as ase_write +from ase.io import read as ase_read from scipy import stats from autocat.learning.predictors import AutoCatPredictor @@ -17,6 +19,107 @@ Array = List[float] +class AutoCatDesignSpace: + def __init__( + self, + design_space_structures: List[Atoms], + design_space_labels: Array, + write_location: str = None, + ): + """ + Constructor. + + Parameters + ---------- + + design_space_structures: + List of all structures within the design space + + design_space_labels: + Labels corresponding to all structures within the design space. + If label not yet known, set to np.nan + + """ + self._design_space_structures = None + self.design_space_structures = design_space_structures + + self._design_space_labels = None + self.design_space_labels = design_space_labels + + self._write_location = "." + self.write_location = write_location + + @property + def design_space_structures(self): + return self._design_space_structures + + @design_space_structures.setter + def design_space_structures(self, design_space_structures): + if design_space_structures is not None: + if all(isinstance(struct, Atoms) for struct in design_space_structures): + self._design_space_structures = design_space_structures + + @property + def design_space_labels(self): + return self._design_space_labels + + @design_space_labels.setter + def design_space_labels(self, design_space_labels): + if design_space_labels is not None: + self._design_space_labels = design_space_labels + + @property + def write_location(self): + return self._write_location + + @write_location.setter + def write_location(self, write_location): + if write_location is not None: + self._write_location = write_location + + def write_json(self, json_name: str = None): + with tempfile.TemporaryDirectory() as _tmp_dir: + # write out all individual structure jsons + for i, struct in enumerate(self.design_space_structures): + tmp_filename = os.path.join(_tmp_dir, f"{i}.json") + struct.write(tmp_filename) + # load individual jsons and collect in list + collected_jsons = [] + for i in range(len(self.design_space_structures)): + tmp_filename = os.path.join(_tmp_dir, f"{i}.json") + with open(tmp_filename, "r") as f: + collected_jsons.append(json.load(f)) + # append labels to list of collected jsons + jsonified_labels = [float(x) for x in self.design_space_labels] + collected_jsons.append(jsonified_labels) + # set default json name if needed + if json_name is None: + json_name = "acds.json" + # write out single json + json_path = os.path.join(self.write_location, json_name) + with open(json_path, "w") as f: + json.dump(collected_jsons, f) + + @staticmethod + def from_json(json_name: str, **kwargs): + with open(json_name, "r") as f: + all_data = json.load(f) + structures = [] + with tempfile.TemporaryDirectory() as _tmp_dir: + for i in range(len(all_data) - 1): + # write temp json for each individual structure + _tmp_json = os.path.join(_tmp_dir, "tmp.json") + with open(_tmp_json, "w") as tmp: + json.dump(all_data[i], tmp) + # read individual tmp json using ase + atoms = ase_read(_tmp_json, format="json") + structures.append(atoms) + labels = np.array(all_data[-1]) + return AutoCatDesignSpace( + design_space_structures=structures, design_space_labels=labels, **kwargs + ) + + class AutoCatSequentialLearningError(Exception): pass diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 009b7139..14f2bbfc 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -8,11 +8,12 @@ import tempfile from scipy import stats - +from ase.io import read as ase_read from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES from autocat.learning.predictors import AutoCatPredictor from autocat.learning.sequential import ( + AutoCatDesignSpace, AutoCatSequentialLearningError, choose_next_candidate, get_overlap_score, @@ -25,6 +26,67 @@ from autocat.saa import generate_saa_structures +def test_write_design_space_as_json(): + # Tests writing out the AutoCatDesignSpace to disk + sub1 = generate_surface_structures(["Pd"], facets={"Pd": ["111"]})["Pd"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["V"], facets={"V": ["110"]})["V"]["bcc110"][ + "structure" + ] + structs = [sub1, sub2] + labels = np.array([0.3, 0.8]) + with tempfile.TemporaryDirectory() as _tmp_dir: + acds = AutoCatDesignSpace( + design_space_structures=structs, + design_space_labels=labels, + write_location=_tmp_dir, + ) + acds.write_json() + # loads back written json + with open(os.path.join(_tmp_dir, "acds.json"), "r") as f: + ds = json.load(f) + # collects structs by writing each json individually + # and reading with ase + written_structs = [] + for i in range(2): + _tmp_json = os.path.join(_tmp_dir, "tmp.json") + with open(_tmp_json, "w") as tmp: + json.dump(ds[i], tmp) + written_structs.append(ase_read(_tmp_json)) + assert structs == written_structs + assert (labels == ds[-1]).all() + + +def test_get_design_space_from_json(): + # Tests generating AutoCatDesignSpace from a json + sub1 = generate_surface_structures(["Au"], facets={"Au": ["100"]})["Au"]["fcc100"][ + "structure" + ] + sub2 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ + "structure" + ] + sub3 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ + "hcp0001" + ]["structure"] + structs = [sub1, sub2, sub3] + labels = np.array([30.0, 900.0, np.nan]) + with tempfile.TemporaryDirectory() as _tmp_dir: + acds = AutoCatDesignSpace( + design_space_structures=structs, + design_space_labels=labels, + write_location=_tmp_dir, + ) + acds.write_json("testing.json") + + tmp_json_dir = os.path.join(_tmp_dir, "testing.json") + acds_from_json = AutoCatDesignSpace.from_json(tmp_json_dir) + assert acds_from_json.design_space_structures == structs + assert np.array_equal( + acds_from_json.design_space_labels, labels, equal_nan=True + ) + + def test_simulated_sequential_outputs(): # Test outputs without any testing structures sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ From 441f73a6012933d7582a038286f9d45dbd2c23ad Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 29 Sep 2021 18:40:14 -0400 Subject: [PATCH 105/239] add logic for updating design space --- src/autocat/learning/sequential.py | 49 ++++++++++++++++++++++++------ tests/learning/test_sequential.py | 37 ++++++++++++++++++++++ 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index fe857e7e..511dfb29 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -8,7 +8,6 @@ from typing import Union from ase import Atoms -from ase.io import write as ase_write from ase.io import read as ase_read from scipy import stats @@ -19,6 +18,10 @@ Array = List[float] +class AutoCatDesignSpaceError(Exception): + pass + + class AutoCatDesignSpace: def __init__( self, @@ -40,11 +43,9 @@ def __init__( If label not yet known, set to np.nan """ - self._design_space_structures = None - self.design_space_structures = design_space_structures + self._design_space_structures = design_space_structures - self._design_space_labels = None - self.design_space_labels = design_space_labels + self._design_space_labels = design_space_labels self._write_location = "." self.write_location = write_location @@ -55,9 +56,8 @@ def design_space_structures(self): @design_space_structures.setter def design_space_structures(self, design_space_structures): - if design_space_structures is not None: - if all(isinstance(struct, Atoms) for struct in design_space_structures): - self._design_space_structures = design_space_structures + msg = "Please use `update` method to update the design space." + raise AutoCatDesignSpaceError(msg) @property def design_space_labels(self): @@ -65,8 +65,8 @@ def design_space_labels(self): @design_space_labels.setter def design_space_labels(self, design_space_labels): - if design_space_labels is not None: - self._design_space_labels = design_space_labels + msg = "Please use `update` method to update the design space." + raise AutoCatDesignSpaceError(msg) @property def write_location(self): @@ -77,6 +77,35 @@ def write_location(self, write_location): if write_location is not None: self._write_location = write_location + def update(self, structures: List[Atoms], labels: Array): + """ + Updates design space given structures and corresponding labels. + If structure already in design space, the label is updated. + + Parameters + ---------- + + structures: + List of Atoms objects structures to be added + + labels: + Corresponding labels to `structures` + """ + if (structures is not None) and (labels is not None): + assert len(structures) == len(labels) + assert all(isinstance(struct, Atoms) for struct in structures) + for i, struct in enumerate(structures): + # if structure already in design space, update label + if struct in self.design_space_structures: + idx = self.design_space_structures.index(struct) + self._design_space_labels[idx] = labels[i] + # otherwise extend design space + else: + self._design_space_structures.append(struct) + self._design_space_labels = np.append( + self.design_space_labels, labels[i] + ) + def write_json(self, json_name: str = None): with tempfile.TemporaryDirectory() as _tmp_dir: # write out all individual structure jsons diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 14f2bbfc..b4ef168a 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -14,6 +14,7 @@ from autocat.learning.predictors import AutoCatPredictor from autocat.learning.sequential import ( AutoCatDesignSpace, + AutoCatDesignSpaceError, AutoCatSequentialLearningError, choose_next_candidate, get_overlap_score, @@ -26,6 +27,42 @@ from autocat.saa import generate_saa_structures +def test_updating_design_space(): + sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["100"]})["Ag"]["fcc100"][ + "structure" + ] + sub2 = generate_surface_structures(["Li"], facets={"Li": ["110"]})["Li"]["bcc110"][ + "structure" + ] + sub3 = generate_surface_structures(["Na"], facets={"Na": ["110"]})["Na"]["bcc110"][ + "structure" + ] + sub4 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ + "hcp0001" + ]["structure"] + structs = [sub1, sub2, sub3] + labels = np.array([4.0, 5.0, 6.0]) + acds = AutoCatDesignSpace(structs, labels) + + # Test trying to update just structures + with pytest.raises(AutoCatDesignSpaceError): + acds.design_space_structures = [sub4] + + # Test trying to update just labels + with pytest.raises(AutoCatDesignSpaceError): + acds.design_space_structures = np.array([4.0]) + + # Test updating label already in DS and extending + acds.update([sub1, sub4], np.array([10.0, 20.0])) + assert np.isclose(acds.design_space_labels[0], 10.0) + assert sub4 in acds.design_space_structures + assert np.isclose(acds.design_space_labels[-1], 20.0) + + # Test trying to give structures that are not Atoms objects + with pytest.raises(AssertionError): + acds.update([sub1, np.array(20.0)], np.array([3.0, 4.0])) + + def test_write_design_space_as_json(): # Tests writing out the AutoCatDesignSpace to disk sub1 = generate_surface_structures(["Pd"], facets={"Pd": ["111"]})["Pd"]["fcc111"][ From 0f074e5553d7016e29dee5ae81acc7747dd07dd5 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 30 Sep 2021 11:58:58 -0400 Subject: [PATCH 106/239] add segregation energy db --- .../data/segregation_energies/__init__.py | 18 + .../segregation_energies.json | 652 ++++++++++++++++++ 2 files changed, 670 insertions(+) create mode 100644 src/autocat/data/segregation_energies/__init__.py create mode 100644 src/autocat/data/segregation_energies/segregation_energies.json diff --git a/src/autocat/data/segregation_energies/__init__.py b/src/autocat/data/segregation_energies/__init__.py new file mode 100644 index 00000000..63e0b849 --- /dev/null +++ b/src/autocat/data/segregation_energies/__init__.py @@ -0,0 +1,18 @@ +import json +import pkg_resources + +__all__ = ["SEGREGATION_ENERGIES"] +""" +Values obtained from https://doi.org/10.1103/PhysRevB.59.15990 + +SEGREGATION_ENERGIES: + Segregation energies for different host/dopant combinations + For hosts used fcc: 111, bcc:110 (Fe100 also available), hcp:0001 +""" + +raw_seg_ener = pkg_resources.resource_filename( + "autocat.data.segregation_energies", "segregation_energies.json" +) + +with open(raw_seg_ener) as fr: + SEGREGATION_ENERGIES = json.load(fr) diff --git a/src/autocat/data/segregation_energies/segregation_energies.json b/src/autocat/data/segregation_energies/segregation_energies.json new file mode 100644 index 00000000..a8a4e6d1 --- /dev/null +++ b/src/autocat/data/segregation_energies/segregation_energies.json @@ -0,0 +1,652 @@ +{ + "Ti": { + "Ti": 1.2, + "V": 0.1, + "Cr": -0.24, + "Mn": -0.34, + "Fe": -0.41, + "Co": -0.56, + "Ni": -0.75, + "Cu": -0.94, + "Zr": -0.38, + "Nb": 0.03, + "Mo": 0.09, + "Tc": -0.06, + "Ru": -0.31, + "Rh": -0.62, + "Pd": -0.93, + "Ag": -1.18, + "Hf": -0.14, + "Ta": 0.25, + "W": 0.35, + "Re": 0.2, + "Os": -0.04, + "Ir": -0.37, + "Pt": -0.72, + "Au": -1.05 + }, + "V": { + "Ti": -0.49, + "V": 1.16, + "Cr": 0.3, + "Mn": 0.41, + "Fe": 0.36, + "Co": 0.15, + "Ni": -0.12, + "Cu": -0.54, + "Zr": -1.08, + "Nb": -0.41, + "Mo": 0.1, + "Tc": 0.36, + "Ru": 0.39, + "Rh": 0.13, + "Pd": -0.28, + "Ag": -0.75, + "Hf": -1.0, + "Ta": -0.23, + "W": 0.31, + "Re": 0.62, + "Os": 0.68, + "Ir": 0.51, + "Pt": 0.09, + "Au": -0.39 + }, + "Cr": { + "Ti": -0.72, + "V": -0.15, + "Cr": 1.46, + "Mn": -0.14, + "Fe": -0.44, + "Co": -0.67, + "Ni": -0.8, + "Cu": -1.02, + "Zr": -2.05, + "Nb": -1.15, + "Mo": -0.62, + "Tc": -0.45, + "Ru": -0.68, + "Rh": -1.25, + "Pd": -1.7, + "Ag": -1.9, + "Hf": -1.55, + "Ta": -0.98, + "W": -0.4, + "Re": -0.17, + "Os": -0.29, + "Ir": -0.81, + "Pt": -1.58, + "Au": -1.98 + }, + "Mn": { + "Ti": -0.83, + "V": -0.32, + "Cr": -0.1, + "Mn": 1.24, + "Fe": -0.12, + "Co": -0.26, + "Ni": -0.47, + "Cu": -0.77, + "Zr": -2.15, + "Nb": -1.28, + "Mo": -0.73, + "Tc": -0.48, + "Ru": -0.52, + "Rh": -0.69, + "Pd": -0.93, + "Ag": -1.31, + "Hf": -1.83, + "Ta": -1.03, + "W": -0.56, + "Re": -0.31, + "Os": -0.32, + "Ir": -0.53, + "Pt": -0.83, + "Au": -1.23 + }, + "Fe": { + "Ti": -0.39, + "V": 0.06, + "Cr": 0.1, + "Mn": -0.16, + "Fe": 1.2, + "Co": -0.14, + "Ni": -0.65, + "Cu": -0.83, + "Zr": -1.6, + "Nb": -0.65, + "Mo": -0.06, + "Tc": 0.1, + "Ru": -0.2, + "Rh": -0.52, + "Pd": -1.05, + "Ag": -1.55, + "Hf": -1.5, + "Ta": -0.35, + "W": 0.2, + "Re": 0.45, + "Os": 0.25, + "Ir": -0.15, + "Pt": -0.66, + "Au": -1.36 + }, + "Fe_100": { + "Ti": -0.69, + "V": 0.19, + "Cr": 0.16, + "Mn": -0.38, + "Fe": 1.73, + "Co": -0.03, + "Ni": -0.77, + "Cu": -1.37, + "Zr": -2.22, + "Nb": -0.83, + "Mo": 0.03, + "Tc": 0.24, + "Ru": 0.0, + "Rh": -0.53, + "Pd": -1.43, + "Ag": -2.37, + "Hf": -2.15, + "Ta": -0.5, + "W": 0.42, + "Re": 0.78, + "Os": 0.6, + "Ir": 0.08, + "Pt": -0.78, + "Au": -1.93 + }, + "Co": { + "Ti": -0.33, + "V": 0.13, + "Cr": 0.19, + "Mn": 0.1, + "Fe": -0.01, + "Co": 1.07, + "Ni": -0.13, + "Cu": -0.48, + "Zr": -1.4, + "Nb": -0.45, + "Mo": 0.0, + "Tc": 0.49, + "Ru": 0.12, + "Rh": -0.4, + "Pd": -0.6, + "Ag": -0.93, + "Hf": -0.56, + "Ta": -0.24, + "W": 0.34, + "Re": 0.72, + "Os": 0.56, + "Ir": -0.1, + "Pt": -0.38, + "Au": -0.76 + }, + "Ni": { + "Ti": -0.12, + "V": 0.2, + "Cr": 0.25, + "Mn": 0.0, + "Fe": 0.13, + "Co": 0.13, + "Ni": 0.95, + "Cu": -0.25, + "Zr": -1.16, + "Nb": -0.26, + "Mo": 0.18, + "Tc": 0.31, + "Ru": 0.1, + "Rh": -0.1, + "Pd": -0.4, + "Ag": -0.8, + "Hf": -0.74, + "Ta": -0.01, + "W": 0.45, + "Re": 0.53, + "Os": 0.37, + "Ir": 0.16, + "Pt": -0.17, + "Au": -0.69 + }, + "Cu": { + "Ti": 0.01, + "V": 0.25, + "Cr": 0.1, + "Mn": 0.07, + "Fe": 0.28, + "Co": 0.33, + "Ni": 0.17, + "Cu": 0.77, + "Zr": -0.64, + "Nb": 0.0, + "Mo": 0.28, + "Tc": 0.3, + "Ru": 0.2, + "Rh": 0.05, + "Pd": -0.2, + "Ag": -0.42, + "Hf": -0.35, + "Ta": 0.22, + "W": 0.57, + "Re": 0.62, + "Os": 0.48, + "Ir": 0.23, + "Pt": -0.04, + "Au": -0.29 + }, + "Zr": { + "Ti": 0.06, + "V": 0.01, + "Cr": -0.47, + "Mn": -0.36, + "Fe": -0.4, + "Co": -0.45, + "Ni": -0.55, + "Cu": -0.72, + "Zr": 1.22, + "Nb": 0.19, + "Mo": 0.13, + "Tc": -0.01, + "Ru": -0.19, + "Rh": -0.41, + "Pd": -0.68, + "Ag": -0.88, + "Hf": 0.15, + "Ta": 0.33, + "W": 0.3, + "Re": 0.15, + "Os": -0.02, + "Ir": -0.25, + "Pt": -0.5, + "Au": -0.8 + }, + "Nb": { + "Ti": -0.24, + "V": 0.12, + "Cr": 0.32, + "Mn": 0.23, + "Fe": 0.29, + "Co": 0.31, + "Ni": 0.08, + "Cu": -0.2, + "Zr": -0.65, + "Nb": 1.21, + "Mo": 0.48, + "Tc": 0.7, + "Ru": 0.65, + "Rh": 0.42, + "Pd": 0.05, + "Ag": -0.32, + "Hf": -0.47, + "Ta": 0.17, + "W": 0.7, + "Re": 0.98, + "Os": 1.0, + "Ir": 0.77, + "Pt": 0.4, + "Au": -0.03 + }, + "Mo": { + "Ti": -0.14, + "V": 0.08, + "Cr": -0.01, + "Mn": -0.5, + "Fe": -0.52, + "Co": -0.72, + "Ni": -0.82, + "Cu": -1.16, + "Zr": -0.9, + "Nb": -0.22, + "Mo": 1.6, + "Tc": -0.21, + "Ru": -0.81, + "Rh": -1.28, + "Pd": -1.47, + "Ag": -1.75, + "Hf": -0.98, + "Ta": -0.02, + "W": 0.22, + "Re": 0.1, + "Os": -0.45, + "Ir": -1.15, + "Pt": -1.6, + "Au": -1.94 + }, + "Tc": { + "Ti": -0.82, + "V": -0.4, + "Cr": -0.11, + "Mn": -0.2, + "Fe": -0.11, + "Co": -0.01, + "Ni": -0.24, + "Cu": -0.7, + "Zr": -1.57, + "Nb": -0.77, + "Mo": -0.27, + "Tc": 1.47, + "Ru": 0.02, + "Rh": -0.11, + "Pd": -0.46, + "Ag": -0.97, + "Hf": -1.26, + "Ta": -0.65, + "W": -0.06, + "Re": 0.26, + "Os": 0.37, + "Ir": 0.21, + "Pt": -0.16, + "Au": -0.7 + }, + "Ru": { + "Ti": -0.3, + "V": 0.15, + "Cr": 0.24, + "Mn": -0.4, + "Fe": -0.39, + "Co": -0.37, + "Ni": -0.71, + "Cu": -1.21, + "Zr": -1.12, + "Nb": -0.31, + "Mo": 0.1, + "Tc": 0.17, + "Ru": 1.48, + "Rh": -0.43, + "Pd": -1.03, + "Ag": -1.72, + "Hf": -0.83, + "Ta": -0.17, + "W": 0.24, + "Re": 0.37, + "Os": 0.23, + "Ir": -0.2, + "Pt": -0.82, + "Au": -1.62 + }, + "Rh": { + "Ti": 0.12, + "V": 0.35, + "Cr": 0.31, + "Mn": -0.08, + "Fe": -0.01, + "Co": 0.02, + "Ni": -0.08, + "Cu": -0.38, + "Zr": -0.46, + "Nb": 0.09, + "Mo": 0.44, + "Tc": 0.46, + "Ru": 0.31, + "Rh": 1.15, + "Pd": -0.45, + "Ag": -0.92, + "Hf": -0.15, + "Ta": 0.36, + "W": 0.66, + "Re": 0.71, + "Os": 0.56, + "Ir": 0.23, + "Pt": -0.27, + "Au": -0.87 + }, + "Pd": { + "Ti": 0.58, + "V": 0.78, + "Cr": 0.3, + "Mn": 0.3, + "Fe": 0.35, + "Co": 0.29, + "Ni": 0.21, + "Cu": 0.04, + "Zr": 0.32, + "Nb": 0.87, + "Mo": 1.08, + "Tc": 1.02, + "Ru": 0.74, + "Rh": 0.36, + "Pd": 0.84, + "Ag": -0.26, + "Hf": 0.44, + "Ta": 1.04, + "W": 1.37, + "Re": 1.34, + "Os": 1.11, + "Ir": 0.7, + "Pt": 0.19, + "Au": -0.22 + }, + "Ag": { + "Ti": 0.45, + "V": 0.63, + "Cr": 0.29, + "Mn": 0.23, + "Fe": 0.41, + "Co": 0.48, + "Ni": 0.49, + "Cu": 0.22, + "Zr": 0.33, + "Nb": 0.67, + "Mo": 0.74, + "Tc": 0.69, + "Ru": 0.6, + "Rh": 0.42, + "Pd": 0.28, + "Ag": 0.58, + "Hf": 0.4, + "Ta": 0.83, + "W": 0.93, + "Re": 0.88, + "Os": 0.72, + "Ir": 0.55, + "Pt": 0.34, + "Au": 0.03 + }, + "Hf": { + "Ti": -0.03, + "V": -0.04, + "Cr": -0.52, + "Mn": -0.49, + "Fe": -0.51, + "Co": -0.62, + "Ni": -0.75, + "Cu": -0.93, + "Zr": -0.14, + "Nb": 0.08, + "Mo": -0.12, + "Tc": -0.25, + "Ru": -0.44, + "Rh": -0.68, + "Pd": -0.92, + "Ag": -1.16, + "Hf": 1.35, + "Ta": 0.25, + "W": 0.17, + "Re": -0.01, + "Os": -0.26, + "Ir": -0.53, + "Pt": -0.8, + "Au": -1.11 + }, + "Ta": { + "Ti": -0.45, + "V": -0.03, + "Cr": 0.16, + "Mn": 0.1, + "Fe": 0.13, + "Co": 0.06, + "Ni": -0.18, + "Cu": -0.52, + "Zr": -0.85, + "Nb": -0.21, + "Mo": 0.25, + "Tc": 0.44, + "Ru": 0.4, + "Rh": 0.11, + "Pd": -0.26, + "Ag": -0.67, + "Hf": -0.6, + "Ta": 1.37, + "W": 0.47, + "Re": 0.75, + "Os": 0.73, + "Ir": 0.49, + "Pt": 0.11, + "Au": -0.37 + }, + "W": { + "Ti": 0.02, + "V": 0.04, + "Cr": -0.14, + "Mn": -0.25, + "Fe": -0.35, + "Co": -0.45, + "Ni": -0.42, + "Cu": -0.75, + "Zr": -0.81, + "Nb": -0.31, + "Mo": -0.24, + "Tc": -0.55, + "Ru": -1.07, + "Rh": -1.22, + "Pd": -1.27, + "Ag": -1.56, + "Hf": -0.78, + "Ta": -0.13, + "W": 1.87, + "Re": -0.27, + "Os": -0.85, + "Ir": -1.34, + "Pt": -1.66, + "Au": -1.85 + }, + "Re": { + "Ti": -0.89, + "V": -0.42, + "Cr": -0.13, + "Mn": -0.28, + "Fe": -0.18, + "Co": -0.14, + "Ni": -0.36, + "Cu": -0.83, + "Zr": -1.75, + "Nb": -0.94, + "Mo": -0.43, + "Tc": -0.19, + "Ru": -0.17, + "Rh": -0.32, + "Pd": -0.68, + "Ag": -1.24, + "Hf": -1.51, + "Ta": -0.77, + "W": -0.27, + "Re": 1.69, + "Os": 0.04, + "Ir": -0.11, + "Pt": -0.46, + "Au": -1.05 + }, + "Os": { + "Ti": -0.22, + "V": 0.18, + "Cr": 0.36, + "Mn": -0.21, + "Fe": -0.31, + "Co": -0.3, + "Ni": -0.62, + "Cu": -1.21, + "Zr": -1.07, + "Nb": -0.27, + "Mo": 0.07, + "Tc": 0.09, + "Ru": -0.2, + "Rh": -0.7, + "Pd": -1.31, + "Ag": -2.0, + "Hf": -1.04, + "Ta": -0.17, + "W": 0.13, + "Re": 0.23, + "Os": 1.81, + "Ir": -0.48, + "Pt": -1.25, + "Au": -2.14 + }, + "Ir": { + "Ti": 0.29, + "V": 0.51, + "Cr": 0.35, + "Mn": 0.09, + "Fe": 0.11, + "Co": 0.16, + "Ni": 0.12, + "Cu": -0.12, + "Zr": -0.43, + "Nb": 0.1, + "Mo": 0.35, + "Tc": 0.35, + "Ru": 0.23, + "Rh": -0.08, + "Pd": -0.55, + "Ag": -1.0, + "Hf": -0.17, + "Ta": 0.26, + "W": 0.47, + "Re": 0.48, + "Os": 0.32, + "Ir": 1.44, + "Pt": -0.58, + "Au": -1.2 + }, + "Pt": { + "Ti": 0.66, + "V": 0.98, + "Cr": 0.6, + "Mn": 0.38, + "Fe": 0.37, + "Co": 0.46, + "Ni": 0.43, + "Cu": 0.32, + "Zr": 0.3, + "Nb": 0.76, + "Mo": 0.93, + "Tc": 0.85, + "Ru": 0.6, + "Rh": 0.26, + "Pd": 0.0, + "Ag": -0.27, + "Hf": 0.47, + "Ta": 0.95, + "W": 1.16, + "Re": 1.11, + "Os": 0.86, + "Ir": 0.44, + "Pt": 1.03, + "Au": -0.36 + }, + "Au": { + "Ti": 0.46, + "V": 0.59, + "Cr": 0.33, + "Mn": 0.3, + "Fe": 0.45, + "Co": 0.54, + "Ni": 0.56, + "Cu": 0.34, + "Zr": 0.3, + "Nb": 0.61, + "Mo": 0.67, + "Tc": 0.59, + "Ru": 0.52, + "Rh": 0.44, + "Pd": 0.28, + "Ag": 0.0, + "Hf": 0.47, + "Ta": 0.79, + "W": 0.92, + "Re": 0.81, + "Os": 0.65, + "Ir": 0.5, + "Pt": 0.34, + "Au": 0.72 + } +} \ No newline at end of file From da86d96281bf64b2b3bd84606704a417ae8eb157 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 4 Oct 2021 11:50:10 -0400 Subject: [PATCH 107/239] start adding learner obj --- src/autocat/learning/sequential.py | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 511dfb29..8224ca34 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -153,6 +153,65 @@ class AutoCatSequentialLearningError(Exception): pass +class AutoCatSequentialLearner: + def __init__(self, design_space: AutoCatDesignSpace, **predictor_kwargs): + self._design_space = design_space + dstructs = self.design_space.design_space_structures + dlabels = self.design_space.design_space_labels + + self.predictor = AutoCatPredictor(**predictor_kwargs) + mask_nans = ~np.isnan(dlabels) + self.predictor.fit( + dstructs[np.where(mask_nans)], dlabels[np.where(mask_nans)], + ) + + self._iteration_count = 0 + preds, unc = self.predictor.predict(dstructs) + self._predictions = preds + self._uncertainties = unc + + @property + def iteration_count(self): + return self._iteration_count + + @iteration_count.setter + def iteration_count(self, num): + msg = "Cannot manually update iteration count value" + raise AutoCatSequentialLearningError(msg) + + @property + def design_space(self): + return self._design_space + + @property + def predictions(self): + return self._predictions + + @property + def uncertainties(self): + return self._uncertainties + + def compare(self, other_design_space): + """ + Compare contained `AutoCatDesignSpace` object to another. + Returns True if they are the same, False otherwise. + + Parameters + ---------- + + other_design_space: + `AutoCatDesignSpace` object to be compared to + """ + ds = self.design_space + same_structs = ( + ds.design_space_structures == other_design_space.design_space_structures + ) + same_labels = np.array_equal( + ds.design_space_labels, other_design_space.design_space_labels + ) + return same_structs and same_labels + + def multiple_simulated_sequential_learning_runs( number_of_runs: int = 5, number_parallel_jobs: int = None, From a2f3c7ea59cd7a671d3474df5d18eb7043a3dff5 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 4 Oct 2021 16:57:10 -0400 Subject: [PATCH 108/239] add util func for extracting structs from dict --- src/autocat/utils.py | 11 +++++++++ tests/test_utils.py | 57 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/test_utils.py diff --git a/src/autocat/utils.py b/src/autocat/utils.py index 6595e424..bf13550a 100644 --- a/src/autocat/utils.py +++ b/src/autocat/utils.py @@ -1,5 +1,6 @@ import os from contextlib import contextmanager +from ase import Atoms @contextmanager @@ -10,3 +11,13 @@ def change_working_dir(new_dir): yield finally: os.chdir(current_dir) + + +def extract_structures(autocat_dict: dict): + structure_list = [] + for element in autocat_dict: + if isinstance(autocat_dict[element], dict): + structure_list.extend(extract_structures(autocat_dict[element])) + elif isinstance(autocat_dict[element], Atoms): + structure_list.append(autocat_dict[element]) + return structure_list diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..ca6713d1 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,57 @@ +"""Unit tests for the `autocat.utils` module""" + +from ase import Atoms +from ase.build import fcc100 +from ase.build import fcc111 +from ase.build import bcc110 + +from autocat.surface import generate_surface_structures +from autocat.saa import generate_saa_structures +from autocat.adsorption import generate_rxn_structures +from autocat.utils import extract_structures + + +def test_extract_surfaces(): + # Tests extracting structures from `autocat.surface.generate_surface_structures` + surfaces = generate_surface_structures( + ["Pt", "Cu", "Li"], facets={"Pt": ["100", "111"], "Cu": ["111"], "Li": ["110"]} + ) + ex_structures = extract_structures(surfaces) + assert all(isinstance(struct, Atoms) for struct in ex_structures) + # checks atoms objects left untouched during extraction + pt_struct100 = fcc100("Pt", size=(3, 3, 4), vacuum=10) + assert pt_struct100 in ex_structures + pt_struct111 = fcc111("Pt", size=(3, 3, 4), vacuum=10) + assert pt_struct111 in ex_structures + cu_struct = fcc111("Cu", size=(3, 3, 4), vacuum=10) + assert cu_struct in ex_structures + li_struct = bcc110("Li", size=(3, 3, 4), vacuum=10) + assert li_struct in ex_structures + + +def test_extract_saas(): + # Tests extracting saa structures + saas = generate_saa_structures( + ["Cu", "Au"], + ["Fe"], + facets={"Cu": ["110"], "Au": ["100"]}, + supercell_dim=(2, 2, 5), + ) + ex_structures = extract_structures(saas) + assert all(isinstance(struct, Atoms) for struct in ex_structures) + assert saas["Cu"]["Fe"]["fcc110"]["structure"] in ex_structures + assert saas["Au"]["Fe"]["fcc100"]["structure"] in ex_structures + + +def test_extract_adsorption(): + # Test extracting adsorption structures + saa = generate_saa_structures(["Ru"], ["Pd"], supercell_dim=(2, 2, 5),)["Ru"]["Pd"][ + "hcp0001" + ]["structure"] + ads_dict = generate_rxn_structures( + saa, ads=["NH2", "Li"], sites={"custom": [(0.0, 0.0)]}, all_sym_sites=False + ) + ex_structures = extract_structures(ads_dict) + assert all(isinstance(struct, Atoms) for struct in ex_structures) + assert ads_dict["NH2"]["custom"]["0.0_0.0"]["structure"] in ex_structures + assert ads_dict["Li"]["custom"]["0.0_0.0"]["structure"] in ex_structures From ce5b4534b4ee0485663ba30e0bf7eed6c497b4f0 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Oct 2021 15:23:16 -0400 Subject: [PATCH 109/239] propagate elementalproperty_presets --- src/autocat/learning/featurizers.py | 1 + src/autocat/learning/predictors.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index e5338e61..c68755be 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -187,6 +187,7 @@ def get_X( ase_struct, structure_featurizer=structure_featurizer, adsorbate_featurizer=adsorbate_featurizer, + elementalproperty_preset=elementalproperty_preset, maximum_structure_size=maximum_structure_size, maximum_adsorbate_size=maximum_adsorbate_size, species_list=species_list, diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index e1f204ff..6d801b48 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -29,6 +29,7 @@ def __init__( adsorbate_featurizer: str = None, maximum_structure_size: int = None, maximum_adsorbate_size: int = None, + elementalproperty_preset: str = None, species_list: List[str] = None, refine_structures: bool = None, structure_featurization_kwargs: Dict = None, @@ -107,6 +108,9 @@ def __init__( self._maximum_structure_size = None self.maximum_structure_size = maximum_structure_size + self._elementalproperty_preset = "magpie" + self.elementalproperty_preset = elementalproperty_preset + self._maximum_adsorbate_size = None self.maximum_adsorbate_size = maximum_adsorbate_size @@ -199,6 +203,19 @@ def structure_featurization_kwargs(self, structure_featurization_kwargs): self.X_ = None self.y_ = None + @property + def elementalproperty_preset(self): + return self._elementalproperty_preset + + @elementalproperty_preset.setter + def elementalproperty_preset(self, elementalproperty_preset): + if elementalproperty_preset is not None: + self._elementalproperty_preset = elementalproperty_preset + if self.is_fit: + self.is_fit = False + self.X_ = None + self.y_ = None + @property def adsorbate_featurization_kwargs(self): return self._adsorbate_featurization_kwargs From 63060ae7ca7c9448b686b4d472916b93069109e6 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Oct 2021 15:26:06 -0400 Subject: [PATCH 110/239] add iterate method, store candidate data --- src/autocat/learning/sequential.py | 116 ++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 11 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 8224ca34..b05734ef 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -154,31 +154,50 @@ class AutoCatSequentialLearningError(Exception): class AutoCatSequentialLearner: - def __init__(self, design_space: AutoCatDesignSpace, **predictor_kwargs): + def __init__( + self, + design_space: AutoCatDesignSpace, + predictor_kwargs: Dict[str, Union[str, float]] = None, + candidate_selection_kwargs: Dict[str, Union[str, float]] = None, + ): + self._predictor_kwargs = predictor_kwargs or { + "structure_featurizer": "sine_matrix" + } + self._candidate_selection_kwargs = candidate_selection_kwargs or { + "aq": "Random" + } + self._design_space = design_space dstructs = self.design_space.design_space_structures dlabels = self.design_space.design_space_labels - - self.predictor = AutoCatPredictor(**predictor_kwargs) mask_nans = ~np.isnan(dlabels) - self.predictor.fit( - dstructs[np.where(mask_nans)], dlabels[np.where(mask_nans)], - ) + + train_idx = np.zeros(len(dlabels), dtype=bool) + train_idx[np.where(mask_nans)] = 1 + self._train_idx = train_idx + + masked_structs = [struct for i, struct in enumerate(dstructs) if mask_nans[i]] + masked_labels = dlabels[np.where(mask_nans)] + + self.predictor = AutoCatPredictor(**self.predictor_kwargs) + self.predictor.fit(masked_structs, masked_labels) self._iteration_count = 0 preds, unc = self.predictor.predict(dstructs) self._predictions = preds self._uncertainties = unc + candidate_idx, _, aq_scores = choose_next_candidate( + dstructs, dlabels, train_idx, preds, unc, **self.candidate_selection_kwargs, + ) + + self._candidate_indices = candidate_idx + self._acquisition_scores = aq_scores + @property def iteration_count(self): return self._iteration_count - @iteration_count.setter - def iteration_count(self, num): - msg = "Cannot manually update iteration count value" - raise AutoCatSequentialLearningError(msg) - @property def design_space(self): return self._design_space @@ -191,6 +210,32 @@ def predictions(self): def uncertainties(self): return self._uncertainties + @property + def candidate_indices(self): + return self._candidate_indices + + @property + def acquisition_scores(self): + return self._acquisition_scores + + @property + def candidate_structures(self): + idxs = self.candidate_indices + structs = self.design_space.design_space_structures + return [structs[i] for i in idxs] + + @property + def predictor_kwargs(self): + return self._predictor_kwargs + + @property + def candidate_selection_kwargs(self): + return self._candidate_selection_kwargs + + @property + def train_idx(self): + return self._train_idx + def compare(self, other_design_space): """ Compare contained `AutoCatDesignSpace` object to another. @@ -211,6 +256,55 @@ def compare(self, other_design_space): ) return same_structs and same_labels + def iterate(self, other_design_space): + """ + Iterates the SL loop if the proposed design space is different than the + contained one. + This consists of: + - retraining the predictor + - obtaining new acquisition scores + - selecting next batch of candidates + + Parameters + ---------- + + other_design_space: + `AutoCatDesignSpace` object to be compared to + """ + if self.compare(other_design_space): + self._iteration_count += 1 + self._design_space = other_design_space + + dstructs = self.design_space.design_space_structures + dlabels = self.design_space.design_space_labels + + mask_nans = ~np.isnan(dlabels) + masked_structs = [ + struct for i, struct in enumerate(dstructs) if mask_nans[i] + ] + masked_labels = dlabels[np.where(mask_nans)] + + self.predictor.fit(masked_structs, masked_labels) + train_idx = np.zeros(len(dlabels), dtype=bool) + train_idx[np.where(mask_nans)] = 1 + self._train_idx = train_idx + + preds, unc = self.predictor.predict(dstructs) + self._predictions = preds + self._uncertainties = unc + + candidate_idx, _, aq_scores = choose_next_candidate( + dstructs, + dlabels, + train_idx, + preds, + unc, + **self.candidate_selection_kwargs or {}, + ) + + self._candidate_indices = candidate_idx + self._acquisition_scores = aq_scores + def multiple_simulated_sequential_learning_runs( number_of_runs: int = 5, From 834709f92d234259f31396b667691e40f67ed43a Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Oct 2021 17:43:39 -0400 Subject: [PATCH 111/239] add comparison method logic --- src/autocat/learning/sequential.py | 33 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index b05734ef..d2a1e85c 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -236,10 +236,12 @@ def candidate_selection_kwargs(self): def train_idx(self): return self._train_idx - def compare(self, other_design_space): + def check_design_space_different(self, other_design_space): """ Compare contained `AutoCatDesignSpace` object to another. - Returns True if they are the same, False otherwise. + The other design space must be of the same size to the + one that is contained. + Returns True if they are different, False otherwise. Parameters ---------- @@ -248,13 +250,26 @@ def compare(self, other_design_space): `AutoCatDesignSpace` object to be compared to """ ds = self.design_space - same_structs = ( - ds.design_space_structures == other_design_space.design_space_structures + assert len(ds.design_space_structures) == len( + other_design_space.design_space_structures ) - same_labels = np.array_equal( - ds.design_space_labels, other_design_space.design_space_labels - ) - return same_structs and same_labels + for idx, struct in enumerate(ds.design_space_structures): + # check same structures + if struct not in other_design_space.design_space_structures: + return True + o_idx = other_design_space.design_space_structures.index(struct) + # check if both nans + if np.isnan(ds.design_space_labels[idx]) and np.isnan( + other_design_space.design_space_labels[o_idx] + ): + continue + # check same labels + if ( + ds.design_space_labels[idx] + != other_design_space.design_space_labels[o_idx] + ): + return True + return False def iterate(self, other_design_space): """ @@ -271,7 +286,7 @@ def iterate(self, other_design_space): other_design_space: `AutoCatDesignSpace` object to be compared to """ - if self.compare(other_design_space): + if self.check_design_space_different(other_design_space): self._iteration_count += 1 self._design_space = other_design_space From 38014c65caaa849f402074d6f79804a74a0e980d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Oct 2021 17:48:44 -0400 Subject: [PATCH 112/239] tests for learner construction and comparison --- tests/learning/test_sequential.py | 108 ++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index b4ef168a..fa3261b0 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -16,6 +16,7 @@ AutoCatDesignSpace, AutoCatDesignSpaceError, AutoCatSequentialLearningError, + AutoCatSequentialLearner, choose_next_candidate, get_overlap_score, ) @@ -27,6 +28,113 @@ from autocat.saa import generate_saa_structures +def test_sequential_learner_compare(): + # Tests comparison method + sub1 = generate_surface_structures(["Ca"], facets={"Ca": ["111"]})["Ca"]["fcc111"][ + "structure" + ] + sub1 = place_adsorbate(sub1, "Li")["custom"]["structure"] + sub2 = generate_surface_structures(["Nb"], facets={"Nb": ["110"]})["Nb"]["bcc110"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "P")["custom"]["structure"] + sub3 = generate_surface_structures(["Ta"], facets={"Ta": ["110"]})["Ta"]["bcc110"][ + "structure" + ] + sub3 = place_adsorbate(sub3, "F")["custom"]["structure"] + sub4 = generate_surface_structures(["Sr"], facets={"Sr": ["110"]})["Sr"]["fcc110"][ + "structure" + ] + sub4 = place_adsorbate(sub4, "F")["custom"]["structure"] + structs = [sub1, sub2, sub3] + labels = np.array([np.nan, 6.0, 15.0]) + acds1 = AutoCatDesignSpace(structs, labels) + acsl = AutoCatSequentialLearner(acds1) + + assert not acsl.check_design_space_different(acds1) + + structs_reorder = [sub2, sub3, sub1] + labels_reorder = np.array([6.0, 15.0, np.nan]) + acds2 = AutoCatDesignSpace(structs_reorder, labels_reorder) + + assert not acsl.check_design_space_different(acds2) + + structs_new_label = [sub1, sub2, sub3] + labels_new_label = np.array([3.0, 6.0, 15.0]) + acds3 = AutoCatDesignSpace(structs_new_label, labels_new_label) + + assert acsl.check_design_space_different(acds3) + + structs_new_label_reorder = [sub1, sub3, sub2] + labels_new_label_reorder = np.array([3.0, 15.0, 6.0]) + acds4 = AutoCatDesignSpace(structs_new_label_reorder, labels_new_label_reorder) + + assert acsl.check_design_space_different(acds4) + + +def test_sequential_learner_setup(): + # Tests setting up an SL object + sub1 = generate_surface_structures(["Ir"], facets={"Ir": ["100"]})["Ir"]["fcc100"][ + "structure" + ] + sub1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + sub2 = generate_surface_structures(["Mo"], facets={"Mo": ["110"]})["Mo"]["bcc110"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "H")["custom"]["structure"] + sub3 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ + "structure" + ] + sub3 = place_adsorbate(sub3, "O")["custom"]["structure"] + sub4 = generate_surface_structures(["Re"], facets={"Re": ["0001"]})["Re"][ + "hcp0001" + ]["structure"] + sub4 = place_adsorbate(sub4, "N")["custom"]["structure"] + structs = [sub1, sub2, sub3, sub4] + labels = np.array([4.0, np.nan, 6.0, np.nan]) + acds = AutoCatDesignSpace(structs, labels) + acsl = AutoCatSequentialLearner(acds) + + assert acsl.design_space == acds + assert acsl.iteration_count == 0 + assert (acsl.train_idx == np.array([True, False, True, False])).all() + assert acsl.candidate_selection_kwargs == {"aq": "Random"} + assert acsl.candidate_structures[0] == structs[acsl.candidate_indices[0]] + # test default kwargs + assert acsl.predictor_kwargs == {"structure_featurizer": "sine_matrix"} + # test specifying kwargs + acsl = AutoCatSequentialLearner( + acds, + predictor_kwargs={ + "structure_featurizer": "elemental_property", + "elementalproperty_preset": "pymatgen", + "adsorbate_featurizer": "soap", + "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, + "species_list": ["Ir", "O", "H", "Mo", "Fe", "N", "Re"], + }, + candidate_selection_kwargs={"aq": "MU"}, + ) + # test passing predictor kwargs + assert acsl.predictor_kwargs == { + "structure_featurizer": "elemental_property", + "elementalproperty_preset": "pymatgen", + "adsorbate_featurizer": "soap", + "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, + "species_list": ["Ir", "O", "H", "Mo", "Fe", "N", "Re"], + } + assert acsl.predictor.structure_featurizer == "elemental_property" + assert acsl.predictor.elementalproperty_preset == "pymatgen" + assert acsl.predictor.adsorbate_featurizer == "soap" + assert acsl.predictor.adsorbate_featurization_kwargs == { + "rcut": 5.0, + "nmax": 8, + "lmax": 6, + } + + # test passing candidate selection kwargs + assert acsl.candidate_selection_kwargs == {"aq": "MU"} + + def test_updating_design_space(): sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["100"]})["Ag"]["fcc100"][ "structure" From feebf64055ff52136f6e1918dd675381f8baa2ac Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 14 Oct 2021 16:05:43 -0400 Subject: [PATCH 113/239] add logic for fully explored design space --- src/autocat/learning/sequential.py | 39 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index d2a1e85c..3fc3c773 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -216,13 +216,19 @@ def candidate_indices(self): @property def acquisition_scores(self): - return self._acquisition_scores + if self.candidate_indices is not None: + return self._acquisition_scores + else: + print("Design space fully explored, no more candidates") @property def candidate_structures(self): idxs = self.candidate_indices - structs = self.design_space.design_space_structures - return [structs[i] for i in idxs] + if idxs is not None: + structs = self.design_space.design_space_structures + return [structs[i] for i in idxs] + else: + print("Design space fully explored, no more candidates") @property def predictor_kwargs(self): @@ -277,8 +283,8 @@ def iterate(self, other_design_space): contained one. This consists of: - retraining the predictor - - obtaining new acquisition scores - - selecting next batch of candidates + - obtaining new acquisition scores (if fully explored returns None) + - selecting next batch of candidates (if fully explored returns None) Parameters ---------- @@ -308,14 +314,21 @@ def iterate(self, other_design_space): self._predictions = preds self._uncertainties = unc - candidate_idx, _, aq_scores = choose_next_candidate( - dstructs, - dlabels, - train_idx, - preds, - unc, - **self.candidate_selection_kwargs or {}, - ) + # make sure haven't fully searched design space + if True in [np.isnan(l) for l in dlabels]: + candidate_idx, _, aq_scores = choose_next_candidate( + dstructs, + dlabels, + train_idx, + preds, + unc, + **self.candidate_selection_kwargs or {}, + ) + + # if fully searched, no more candidate structures + else: + candidate_idx = None + aq_scores = None self._candidate_indices = candidate_idx self._acquisition_scores = aq_scores From be9ce538050056c93916cbabb4ade056271e306c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 14 Oct 2021 16:06:30 -0400 Subject: [PATCH 114/239] add iterate tests --- tests/learning/test_sequential.py | 65 ++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index fa3261b0..d99eecb1 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -28,6 +28,61 @@ from autocat.saa import generate_saa_structures +def test_sequential_learner_iterate(): + # Tests iterate method + sub1 = generate_surface_structures(["Ca"], facets={"Ca": ["111"]})["Ca"]["fcc111"][ + "structure" + ] + sub1 = place_adsorbate(sub1, "Na")["custom"]["structure"] + sub2 = generate_surface_structures(["Nb"], facets={"Nb": ["110"]})["Nb"]["bcc110"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "K")["custom"]["structure"] + sub3 = generate_surface_structures(["Ta"], facets={"Ta": ["110"]})["Ta"]["bcc110"][ + "structure" + ] + sub3 = place_adsorbate(sub3, "H")["custom"]["structure"] + sub4 = generate_surface_structures(["Sr"], facets={"Sr": ["110"]})["Sr"]["fcc110"][ + "structure" + ] + sub4 = place_adsorbate(sub4, "Fe")["custom"]["structure"] + structs = [sub1, sub2, sub3, sub4] + labels = np.array([11.0, 25.0, np.nan, np.nan]) + acds = AutoCatDesignSpace(structs, labels) + acsl = AutoCatSequentialLearner(acds) + + old_candidate_structures = acsl.candidate_structures + assert acsl.iteration_count == 0 + + # make sure doesn't iterate if no changes to design space + acsl.iterate(acds) + assert acsl.iteration_count == 0 + assert acsl.design_space is acds + assert acsl.candidate_structures == old_candidate_structures + + # check updates for first iteration + new_labels = np.array([11.0, 25.0, 13.0, np.nan]) + new_acds = AutoCatDesignSpace(structs, new_labels) + + acsl.iterate(new_acds) + + assert acsl.iteration_count == 1 + assert acsl.design_space is new_acds + assert acsl.candidate_structures[0] == sub4 + + new_labels2 = np.array([11.0, 25.0, 13.0, 30.0]) + new_acds2 = AutoCatDesignSpace(structs, new_labels2) + + # checks being iterated a second time to fully explore the design space + acsl.iterate(new_acds2) + + assert acsl.iteration_count == 2 + assert acsl.design_space is new_acds2 + assert acsl.candidate_structures is None + assert acsl.candidate_indices is None + assert acsl.acquisition_scores is None + + def test_sequential_learner_compare(): # Tests comparison method sub1 = generate_surface_structures(["Ca"], facets={"Ca": ["111"]})["Ca"]["fcc111"][ @@ -42,10 +97,6 @@ def test_sequential_learner_compare(): "structure" ] sub3 = place_adsorbate(sub3, "F")["custom"]["structure"] - sub4 = generate_surface_structures(["Sr"], facets={"Sr": ["110"]})["Sr"]["fcc110"][ - "structure" - ] - sub4 = place_adsorbate(sub4, "F")["custom"]["structure"] structs = [sub1, sub2, sub3] labels = np.array([np.nan, 6.0, 15.0]) acds1 = AutoCatDesignSpace(structs, labels) @@ -112,7 +163,7 @@ def test_sequential_learner_setup(): "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, "species_list": ["Ir", "O", "H", "Mo", "Fe", "N", "Re"], }, - candidate_selection_kwargs={"aq": "MU"}, + candidate_selection_kwargs={"aq": "MU", "num_candidates_to_pick": 2}, ) # test passing predictor kwargs assert acsl.predictor_kwargs == { @@ -132,7 +183,9 @@ def test_sequential_learner_setup(): } # test passing candidate selection kwargs - assert acsl.candidate_selection_kwargs == {"aq": "MU"} + assert acsl.candidate_selection_kwargs == {"aq": "MU", "num_candidates_to_pick": 2} + assert len(acsl.candidate_indices) == 2 + assert len(acsl.candidate_structures) == 2 def test_updating_design_space(): From bbe36f10405129f3209a4d584068bfb1ad0a677c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 14 Oct 2021 17:32:00 -0400 Subject: [PATCH 115/239] add sl and ds write json, update explored ds logic --- src/autocat/learning/sequential.py | 72 +++++++++++++++++-------- tests/learning/test_sequential.py | 86 +++++++++++++++++++++++++++--- 2 files changed, 127 insertions(+), 31 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 3fc3c773..d2579834 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -24,10 +24,7 @@ class AutoCatDesignSpaceError(Exception): class AutoCatDesignSpace: def __init__( - self, - design_space_structures: List[Atoms], - design_space_labels: Array, - write_location: str = None, + self, design_space_structures: List[Atoms], design_space_labels: Array, ): """ Constructor. @@ -47,9 +44,6 @@ def __init__( self._design_space_labels = design_space_labels - self._write_location = "." - self.write_location = write_location - @property def design_space_structures(self): return self._design_space_structures @@ -68,15 +62,6 @@ def design_space_labels(self, design_space_labels): msg = "Please use `update` method to update the design space." raise AutoCatDesignSpaceError(msg) - @property - def write_location(self): - return self._write_location - - @write_location.setter - def write_location(self, write_location): - if write_location is not None: - self._write_location = write_location - def update(self, structures: List[Atoms], labels: Array): """ Updates design space given structures and corresponding labels. @@ -106,7 +91,13 @@ def update(self, structures: List[Atoms], labels: Array): self.design_space_labels, labels[i] ) - def write_json(self, json_name: str = None): + def write_json( + self, + json_name: str = None, + write_location: str = ".", + write_to_disk: bool = True, + return_jsonified_list: bool = False, + ): with tempfile.TemporaryDirectory() as _tmp_dir: # write out all individual structure jsons for i, struct in enumerate(self.design_space_structures): @@ -125,9 +116,13 @@ def write_json(self, json_name: str = None): if json_name is None: json_name = "acds.json" # write out single json - json_path = os.path.join(self.write_location, json_name) - with open(json_path, "w") as f: - json.dump(collected_jsons, f) + if write_to_disk: + json_path = os.path.join(write_location, json_name) + with open(json_path, "w") as f: + json.dump(collected_jsons, f) + # write to jsonified list to memory + if return_jsonified_list: + return collected_jsons @staticmethod def from_json(json_name: str, **kwargs): @@ -160,6 +155,7 @@ def __init__( predictor_kwargs: Dict[str, Union[str, float]] = None, candidate_selection_kwargs: Dict[str, Union[str, float]] = None, ): + self._predictor_kwargs = predictor_kwargs or { "structure_featurizer": "sine_matrix" } @@ -187,9 +183,18 @@ def __init__( self._predictions = preds self._uncertainties = unc - candidate_idx, _, aq_scores = choose_next_candidate( - dstructs, dlabels, train_idx, preds, unc, **self.candidate_selection_kwargs, - ) + if False in mask_nans: + candidate_idx, _, aq_scores = choose_next_candidate( + dstructs, + dlabels, + train_idx, + preds, + unc, + **self.candidate_selection_kwargs, + ) + else: + candidate_idx = None + aq_scores = None self._candidate_indices = candidate_idx self._acquisition_scores = aq_scores @@ -333,6 +338,27 @@ def iterate(self, other_design_space): self._candidate_indices = candidate_idx self._acquisition_scores = aq_scores + def write_json(self, write_location: str = ".", json_name: str = None): + """ + Writes `AutocatSequentialLearner` to disk as a json + """ + jsonified_list = self.design_space.write_json( + write_to_disk=False, return_jsonified_list=True + ) + + # append kwargs for predictor + jsonified_list.append(self.predictor_kwargs) + # append kwargs for candidate selection + jsonified_list.append(self.candidate_selection_kwargs) + + if json_name is None: + json_name = "acsl.json" + + json_path = os.path.join(write_location, json_name) + + with open(json_path, "w") as f: + json.dump(jsonified_list, f) + def multiple_simulated_sequential_learning_runs( number_of_runs: int = 5, diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index d99eecb1..653660e3 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -28,6 +28,78 @@ from autocat.saa import generate_saa_structures +def test_sequential_learner_write_json(): + # Tests writing a AutoCatSequentialLearner to disk as a json + sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["110"]})["Ag"]["fcc110"][ + "structure" + ] + sub1 = place_adsorbate(sub1, "B")["custom"]["structure"] + sub2 = generate_surface_structures(["Li"], facets={"Li": ["100"]})["Li"]["bcc100"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "Al")["custom"]["structure"] + sub3 = generate_surface_structures(["Ti"], facets={"Ti": ["0001"]})["Ti"][ + "hcp0001" + ]["structure"] + sub3 = place_adsorbate(sub3, "H")["custom"]["structure"] + structs = [sub1, sub2, sub3] + labels = np.array([0.1, 0.2, 0.3]) + predictor_kwargs = { + "structure_featurizer": "sine_matrix", + "elementalproperty_preset": "deml", + "adsorbate_featurizer": "soap", + "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, + "species_list": ["Ag", "Li", "Al", "B", "Ti", "H"], + } + + candidate_selection_kwargs = {"aq": "MU", "num_candidates_to_pick": 2} + acds = AutoCatDesignSpace(structs, labels) + acsl = AutoCatSequentialLearner( + acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + ) + with tempfile.TemporaryDirectory() as _tmp_dir: + acsl.write_json(_tmp_dir) + with open(os.path.join(_tmp_dir, "acsl.json"), "r") as f: + sl = json.load(f) + # collects structs by writing each json individually + # and reading with ase + written_structs = [] + for i in range(3): + _tmp_json = os.path.join(_tmp_dir, "tmp.json") + with open(_tmp_json, "w") as tmp: + json.dump(sl[i], tmp) + written_structs.append(ase_read(_tmp_json)) + assert structs == written_structs + assert (labels == sl[3]).all() + # check predictor kwargs kept + assert predictor_kwargs == sl[4] + # check candidate selection kwargs kept + assert candidate_selection_kwargs == sl[-1] + + # test writing when no kwargs given + acsl = AutoCatSequentialLearner(acds) + with tempfile.TemporaryDirectory() as _tmp_dir: + acsl.write_json(_tmp_dir) + with open(os.path.join(_tmp_dir, "acsl.json"), "r") as f: + sl = json.load(f) + # collects structs by writing each json individually + # and reading with ase + written_structs = [] + for i in range(3): + _tmp_json = os.path.join(_tmp_dir, "tmp.json") + with open(_tmp_json, "w") as tmp: + json.dump(sl[i], tmp) + written_structs.append(ase_read(_tmp_json)) + assert structs == written_structs + assert (labels == sl[3]).all() + # check default predictor kwargs kept + assert sl[4] == {"structure_featurizer": "sine_matrix"} + # check default candidate selection kwargs kept + assert sl[-1] == {"aq": "Random"} + + def test_sequential_learner_iterate(): # Tests iterate method sub1 = generate_surface_structures(["Ca"], facets={"Ca": ["111"]})["Ca"]["fcc111"][ @@ -162,6 +234,7 @@ def test_sequential_learner_setup(): "adsorbate_featurizer": "soap", "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, "species_list": ["Ir", "O", "H", "Mo", "Fe", "N", "Re"], + "model_kwargs": {"n_restarts_optimizer": 9}, }, candidate_selection_kwargs={"aq": "MU", "num_candidates_to_pick": 2}, ) @@ -172,6 +245,7 @@ def test_sequential_learner_setup(): "adsorbate_featurizer": "soap", "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, "species_list": ["Ir", "O", "H", "Mo", "Fe", "N", "Re"], + "model_kwargs": {"n_restarts_optimizer": 9}, } assert acsl.predictor.structure_featurizer == "elemental_property" assert acsl.predictor.elementalproperty_preset == "pymatgen" @@ -236,11 +310,9 @@ def test_write_design_space_as_json(): labels = np.array([0.3, 0.8]) with tempfile.TemporaryDirectory() as _tmp_dir: acds = AutoCatDesignSpace( - design_space_structures=structs, - design_space_labels=labels, - write_location=_tmp_dir, + design_space_structures=structs, design_space_labels=labels, ) - acds.write_json() + acds.write_json(write_location=_tmp_dir) # loads back written json with open(os.path.join(_tmp_dir, "acds.json"), "r") as f: ds = json.load(f) @@ -271,11 +343,9 @@ def test_get_design_space_from_json(): labels = np.array([30.0, 900.0, np.nan]) with tempfile.TemporaryDirectory() as _tmp_dir: acds = AutoCatDesignSpace( - design_space_structures=structs, - design_space_labels=labels, - write_location=_tmp_dir, + design_space_structures=structs, design_space_labels=labels, ) - acds.write_json("testing.json") + acds.write_json("testing.json", write_location=_tmp_dir) tmp_json_dir = os.path.join(_tmp_dir, "testing.json") acds_from_json = AutoCatDesignSpace.from_json(tmp_json_dir) From 2011f1b231b7fa8e50fe1efea7c3633c7757d570 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 15 Oct 2021 15:40:32 -0400 Subject: [PATCH 116/239] add from_json method --- src/autocat/learning/sequential.py | 30 ++++++++++++++++++-- tests/learning/test_sequential.py | 44 ++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index d2579834..e860d408 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -125,7 +125,7 @@ def write_json( return collected_jsons @staticmethod - def from_json(json_name: str, **kwargs): + def from_json(json_name: str): with open(json_name, "r") as f: all_data = json.load(f) structures = [] @@ -140,7 +140,7 @@ def from_json(json_name: str, **kwargs): structures.append(atoms) labels = np.array(all_data[-1]) return AutoCatDesignSpace( - design_space_structures=structures, design_space_labels=labels, **kwargs + design_space_structures=structures, design_space_labels=labels, ) @@ -359,6 +359,32 @@ def write_json(self, write_location: str = ".", json_name: str = None): with open(json_path, "w") as f: json.dump(jsonified_list, f) + @staticmethod + def from_json(json_name: str): + with open(json_name, "r") as f: + all_data = json.load(f) + structures = [] + with tempfile.TemporaryDirectory() as _tmp_dir: + for i in range(len(all_data) - 3): + # write temp json for each individual structure + _tmp_json = os.path.join(_tmp_dir, "tmp.json") + with open(_tmp_json, "w") as tmp: + json.dump(all_data[i], tmp) + # read individual tmp json using ase + atoms = ase_read(_tmp_json, format="json") + structures.append(atoms) + labels = np.array(all_data[-3]) + acds = AutoCatDesignSpace( + design_space_structures=structures, design_space_labels=labels, + ) + predictor_kwargs = all_data[-2] + candidate_selection_kwargs = all_data[-1] + return AutoCatSequentialLearner( + design_space=acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + ) + def multiple_simulated_sequential_learning_runs( number_of_runs: int = 5, diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 653660e3..67e3d4a1 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -28,6 +28,46 @@ from autocat.saa import generate_saa_structures +def test_sequential_learner_from_json(): + # Tests generation of an AutoCatSequentialLearner from a json + sub1 = generate_surface_structures(["Au"], facets={"Au": ["110"]})["Au"]["fcc110"][ + "structure" + ] + sub1 = place_adsorbate(sub1, "C")["custom"]["structure"] + sub2 = generate_surface_structures(["Li"], facets={"Li": ["100"]})["Li"]["bcc100"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "Mg")["custom"]["structure"] + sub3 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ + "hcp0001" + ]["structure"] + sub3 = place_adsorbate(sub3, "N")["custom"]["structure"] + structs = [sub1, sub2, sub3] + labels = np.array([0.1, 0.2, 0.3]) + predictor_kwargs = { + "structure_featurizer": "coulomb_matrix", + "elementalproperty_preset": "megnet_el", + "adsorbate_featurizer": "soap", + "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, + "species_list": ["Au", "Li", "Mg", "C", "Ru", "N"], + } + + candidate_selection_kwargs = {"aq": "Random", "num_candidates_to_pick": 3} + acds = AutoCatDesignSpace(structs, labels) + acsl = AutoCatSequentialLearner( + acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + ) + with tempfile.TemporaryDirectory() as _tmp_dir: + acsl.write_json(_tmp_dir, "testing_acsl.json") + json_path = os.path.join(_tmp_dir, "testing_acsl.json") + written_acsl = AutoCatSequentialLearner.from_json(json_path) + assert not written_acsl.check_design_space_different(acds) + assert written_acsl.predictor_kwargs == predictor_kwargs + assert written_acsl.candidate_selection_kwargs == candidate_selection_kwargs + + def test_sequential_learner_write_json(): # Tests writing a AutoCatSequentialLearner to disk as a json sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["110"]})["Ag"]["fcc110"][ @@ -60,8 +100,8 @@ def test_sequential_learner_write_json(): candidate_selection_kwargs=candidate_selection_kwargs, ) with tempfile.TemporaryDirectory() as _tmp_dir: - acsl.write_json(_tmp_dir) - with open(os.path.join(_tmp_dir, "acsl.json"), "r") as f: + acsl.write_json(_tmp_dir, "testing_acsl.json") + with open(os.path.join(_tmp_dir, "testing_acsl.json"), "r") as f: sl = json.load(f) # collects structs by writing each json individually # and reading with ase From 38bbc771dfc8b494cf7774ce22049d1645988f1c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 19 Oct 2021 11:58:39 -0400 Subject: [PATCH 117/239] add more lattice parameters --- .../lattice_parameters/bulk_beefvdw_fd.json | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json b/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json index 920ad171..38c52cb0 100644 --- a/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json +++ b/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json @@ -1,7 +1,36 @@ { - "Pt": {"a": 4.013732}, - "Pd": {"a": 3.998072}, - "Fe": {"a": 2.893316}, - "Ni": {"a": 3.577644}, - "Ru": {"a": 2.748451, "c": 4.314765} -} + "Pt": { + "a": 4.013732 + }, + "Pd": { + "a": 3.998072 + }, + "Fe": { + "a": 2.893316 + }, + "Ni": { + "a": 3.577644 + }, + "Ag": { + "a": 4.26015 + }, + "Au": { + "a": 4.235948 + }, + "Cu": { + "a": 3.70834 + }, + "Rh": { + "a": 3.882724 + }, + "V": { + "a": 3.01497 + }, + "W": { + "a": 3.192674 + }, + "Ru": { + "a": 2.748451, + "c": 4.314765 + } +} \ No newline at end of file From 8dedaa65cab232aaef195327c77f40b6d5f31d68 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 19 Oct 2021 16:08:31 -0400 Subject: [PATCH 118/239] more lattice parameters --- .../lattice_parameters/bulk_beefvdw_fd.json | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json b/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json index 38c52cb0..5b9b211f 100644 --- a/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json +++ b/src/autocat/data/lattice_parameters/bulk_beefvdw_fd.json @@ -29,6 +29,45 @@ "W": { "a": 3.192674 }, + "Mo": { + "a": 3.17331 + }, + "Nb": { + "a": 3.316336 + }, + "Cr": { + "a": 2.863204 + }, + "Ir": { + "a": 3.893 + }, + "Ta": { + "a": 3.334218 + }, + "Hf": { + "a": 3.218418, + "c": 5.081832 + }, + "Zr": { + "a": 3.246790, + "c": 5.172850 + }, + "Co": { + "a": 2.500673, + "c": 4.097051 + }, + "Ti": { + "a": 2.942273, + "c": 4.667081 + }, + "Os": { + "a": 2.773357, + "c": 4.360051 + }, + "Re": { + "a": 2.798497, + "c": 4.497198 + }, "Ru": { "a": 2.748451, "c": 4.314765 From a5006d580c6e73e360a280fbc41f014c088cbf1c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 28 Oct 2021 16:53:24 -0400 Subject: [PATCH 119/239] test acds setup, add logic for acds len --- src/autocat/learning/sequential.py | 7 +++++++ tests/learning/test_sequential.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index e860d408..3f998b5f 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -40,10 +40,17 @@ def __init__( If label not yet known, set to np.nan """ + if len(design_space_structures) != design_space_labels.shape[0]: + msg = f"Number of structures ({len(design_space_structures)}) and labels ({design_space_labels.shape[0]}) must match" + raise AutoCatDesignSpaceError(msg) + self._design_space_structures = design_space_structures self._design_space_labels = design_space_labels + def __len__(self): + return len(self.design_space_structures) + @property def design_space_structures(self): return self._design_space_structures diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 67e3d4a1..f275276c 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -302,6 +302,26 @@ def test_sequential_learner_setup(): assert len(acsl.candidate_structures) == 2 +def test_design_space_setup(): + # test setting up an AutoCatDesignSpace + sub1 = generate_surface_structures( + ["Pt"], supercell_dim=[2, 2, 5], facets={"Pt": ["100"]} + )["Pt"]["fcc100"]["structure"] + sub1 = place_adsorbate(sub1, "H2")["custom"]["structure"] + sub2 = generate_surface_structures(["Na"], facets={"Na": ["110"]})["Na"]["bcc110"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "F")["custom"]["structure"] + labels = np.array([3.0, 7.0]) + acds = AutoCatDesignSpace([sub1, sub2], labels) + assert acds.design_space_structures == [sub1, sub2] + assert np.array_equal(acds.design_space_labels, labels) + assert len(acds) == 2 + # test different number of structures and labels + with pytest.raises(AutoCatDesignSpaceError): + acds = AutoCatDesignSpace([sub1], labels) + + def test_updating_design_space(): sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["100"]})["Ag"]["fcc100"][ "structure" From 35bd575cff2916051fd3ab27aa98f95edeef36e9 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 28 Oct 2021 17:20:48 -0400 Subject: [PATCH 120/239] fix: ensure structs and labels are copies --- src/autocat/learning/sequential.py | 5 ++--- tests/learning/test_sequential.py | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 3f998b5f..84b382aa 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -44,9 +44,8 @@ def __init__( msg = f"Number of structures ({len(design_space_structures)}) and labels ({design_space_labels.shape[0]}) must match" raise AutoCatDesignSpaceError(msg) - self._design_space_structures = design_space_structures - - self._design_space_labels = design_space_labels + self._design_space_structures = design_space_structures.copy() + self._design_space_labels = design_space_labels.copy() def __len__(self): return len(self.design_space_structures) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index f275276c..3ce221c0 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -312,10 +312,13 @@ def test_design_space_setup(): "structure" ] sub2 = place_adsorbate(sub2, "F")["custom"]["structure"] + structs = [sub1, sub2] labels = np.array([3.0, 7.0]) - acds = AutoCatDesignSpace([sub1, sub2], labels) + acds = AutoCatDesignSpace(structs, labels) assert acds.design_space_structures == [sub1, sub2] + assert acds.design_space_structures is not structs assert np.array_equal(acds.design_space_labels, labels) + assert acds.design_space_labels is not labels assert len(acds) == 2 # test different number of structures and labels with pytest.raises(AutoCatDesignSpaceError): From c4d4f3ebedcda493f0df54c7efd9e80ef7c5ae37 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 28 Oct 2021 18:43:32 -0400 Subject: [PATCH 121/239] adds delitem to acds --- src/autocat/learning/sequential.py | 15 ++++++++ tests/learning/test_sequential.py | 56 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 84b382aa..5529e47e 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -50,6 +50,21 @@ def __init__( def __len__(self): return len(self.design_space_structures) + def __delitem__(self, i): + """ + Deletes systems from the design space. If mask provided, deletes wherever True + """ + if isinstance(i, list): + i = np.array(i) + elif isinstance(i, int): + i = [i] + mask = np.ones(len(self), dtype=bool) + mask[i] = 0 + self._design_space_labels = self.design_space_labels[mask] + structs = self.design_space_structures + masked_structs = [structs[j] for j in range(len(self)) if mask[j]] + self._design_space_structures = masked_structs + @property def design_space_structures(self): return self._design_space_structures diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 3ce221c0..032fa662 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -325,6 +325,62 @@ def test_design_space_setup(): acds = AutoCatDesignSpace([sub1], labels) +def test_delitem_design_space(): + # tests deleting items from the design space + sub0 = generate_surface_structures(["Pd"], facets={"Pd": ["100"]})["Pd"]["fcc100"][ + "structure" + ] + sub0 = place_adsorbate(sub0, "O")["custom"]["structure"] + sub1 = generate_surface_structures(["V"], facets={"V": ["110"]})["V"]["bcc110"][ + "structure" + ] + sub1 = place_adsorbate(sub1, "H")["custom"]["structure"] + sub2 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "S")["custom"]["structure"] + sub3 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ + "hcp0001" + ]["structure"] + sub3 = place_adsorbate(sub3, "P")["custom"]["structure"] + structs = [sub0, sub1, sub2] + labels = np.array([-2.5, np.nan, 600.0]) + # test deleting by single idx + acds = AutoCatDesignSpace(structs, labels) + del acds[1] + assert len(acds) == 2 + assert np.array_equal(acds.design_space_labels, np.array([-2.5, 600.0])) + assert acds.design_space_structures == [sub0, sub2] + # test deleting using a mask + acds = AutoCatDesignSpace(structs, labels) + mask = np.zeros(len(acds), bool) + mask[0] = 1 + mask[1] = 1 + # n.b. deletes wherever mask is True + del acds[mask] + assert len(acds) == 1 + assert acds.design_space_structures == [sub2] + assert np.array_equal(acds.design_space_labels, np.array([600.0])) + # test deleting by providing list of idx + structs = [sub0, sub1, sub2, sub3] + labels = np.array([-20, 8, np.nan, 0.3]) + acds = AutoCatDesignSpace(structs, labels) + del acds[[1, 3]] + assert len(acds) == 2 + assert np.array_equal( + acds.design_space_labels, np.array([-20, np.nan]), equal_nan=True + ) + assert acds.design_space_structures == [sub0, sub2] + # test deleting by providing list with a single idx + acds = AutoCatDesignSpace(structs, labels) + del acds[[0]] + assert len(acds) == 3 + assert np.array_equal( + acds._design_space_labels, np.array([8, np.nan, 0.3]), equal_nan=True + ) + assert acds.design_space_structures == [sub1, sub2, sub3] + + def test_updating_design_space(): sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["100"]})["Ag"]["fcc100"][ "structure" From 43356da48fcdbb47d4671854c616c0316f8bb449 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Fri, 29 Oct 2021 10:13:42 -0700 Subject: [PATCH 122/239] add sl driver mvp --- src/autocat/learning/driver.py | 165 +++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/autocat/learning/driver.py diff --git a/src/autocat/learning/driver.py b/src/autocat/learning/driver.py new file mode 100644 index 00000000..dbfa518e --- /dev/null +++ b/src/autocat/learning/driver.py @@ -0,0 +1,165 @@ +from typing import List +from typing import Dict +from typing import Union + +from pypif.pif import System +from citrination_client import CitrinationClient, data +from citrination_client.search import Filter +from citrination_client.search import FieldQuery +from citrination_client.search import DatasetQuery +from citrination_client.search import DataQuery +from citrination_client.search import PifSystemQuery +from citrination_client.search import PifSearchHit +from citrination_client.search import PifSystemReturningQuery + +from autocat.learning.predictors import AutoCatPredictor +from autocat.learning.sequential import ( + AutoCatDesignSpace, + AutoCatSequentialLearner, + AutoCatSequentialLearningError, +) + + +def AutoCatSequentialDriverError(Exception): + pass + + +def get_pif_query( + dataset_id: int = None, tags: List[str] = None +) -> PifSystemReturningQuery: + """ + Creates a Citrination query object that can be used to search for PIFs + with the specified tags in the specified dataset. + """ + dataset_q = DatasetQuery(id=Filter(equal=dataset_id)) + tags_q = [FieldQuery(filter=Filter(equal=tag), extract_all=True) for tag in tags] + system_q = PifSystemQuery(tags=tags_q) + data_q = DataQuery(dataset=dataset_q, system=system_q) + return PifSystemReturningQuery(query=data_q) + + +def query_pifs_from_citrination( + client: CitrinationClient = None, + dataset_id: int = None, + host: str = None, + dopant: str = None, + termination: str = None, +) -> List[PifSearchHit]: + """ + Queries the specified Citrination dataset for a specified SAA catalyst + system (defined by the host lattice, single atom dopant, and surface + termination). + """ + print(f"Querying dataset (Citrination ID {dataset_id}) for:") + print(f" Host = {host}") + print(f" SA Dopant = {dopant}") + print(f" Termination = ({termination})") + tags = [ + f"host:{host}", + f"sa-dopant:{dopant}", + f"termination:{termination}", + ] + query = get_pif_query(dataset_id=dataset_id, tags=tags) + pif_search_result = client.search.pif_search(query) + print(f"Number of search hits: {pif_search_result.total_num_hits}") + print("") + return pif_search_result.hits + + +def get_system_total_energy(system: System) -> float: + """Returns the total energy value in the input PIF object.""" + total_energy_filter = filter(lambda x: "Total Energy" in x.name, system.properties) + return list(total_energy_filter)[0].scalars[0].value + + +def get_reference_energies( + pif_search_hits: List[PifSearchHit], references: List[str] = None +) -> Dict[str, List[Dict[str, float]]]: + """ + For each specified reference, filters the total energy from the input + list of PIFs queried from Citrination, and returns them as lists in a + dictionary. + """ + reference_energies = {} + for reference in references: + f_search_hits = filter( + lambda x: f"reference:{reference}" in x.system.tags, pif_search_hits + ) + reference_energies[reference] = [ + {"total_energy": get_system_total_energy(f_search_hit.system)} + for f_search_hit in f_search_hits + ] + return reference_energies + + +def get_configuration_tags(system: System) -> List[str]: + """Returns site configuration tags in the input PIF object as a list.""" + configuration_tags = filter( + lambda x: "site-type" in x or "coordinates" in x, system.tags + ) + return list(configuration_tags) + + +def get_intermediate_energies( + pif_search_hits: List[PifSearchHit], intermediates: List[str] = None +) -> Dict[str, List[Dict[str, Union[List[str], float]]]]: + """ + For each specified intermediate species, filters the total energy and + configuration tags from the input list of PIFs queried from Citrination, + and returns them as lists of dictionaries. + """ + intermediate_energies = {} + for intermediate in intermediates: + f_search_hits = filter( + lambda x: f"intermediate:{intermediate}" in x.system.tags, pif_search_hits + ) + intermediate_energies[intermediate] = [ + { + "configuration_tags": get_configuration_tags(f_search_hit.system), + "total_energy": get_system_total_energy(f_search_hit.system), + } + for f_search_hit in f_search_hits + ] + return intermediate_energies + + +class AutoCatSequentialDriver(object): + """Driver for sequential learning.""" + + def __init__( + self, dataset_id: int = None, current_learner: AutoCatSequentialLearner = None, + ): + """Constructor docstring goes here.""" + self._dataset_id = None + self.dataset_id = dataset_id + + self._current_learner = None + self.current_learner = current_learner + + @property + def dataset_id(self): + return self._dataset_id + + @dataset_id.setter + def dataset_id(self, dataset_id): + if dataset_id is None: + msg = "Citrination dataset ID is not valid (None)" + raise AutoCatSequentialDriverError(msg) + self._dataset_id = dataset_id + + @property + def current_learner(self): + return self._current_learner + + @current_learner.setter + def current_learner(self, current_learner): + if isinstance(current_learner, AutoCatSequentialLearner): + self._current_learner = current_learner + elif isinstance(current_learner, str): + self._current_learner = AutoCatSequentialLearner.from_json(current_learner) + else: + msg = "Input AutoCat sequential learner is not valid" + raise AutoCatSequentialDriverError(msg) + + def drive(self, new_design_space: AutoCatDesignSpace = None): + self.current_learner.iterate(new_design_space) From 2431a738df46ea6a5fb10493c53cde5bbdf0b096 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 10 Nov 2021 17:25:49 -0500 Subject: [PATCH 123/239] add seg energies option to aq func --- src/autocat/learning/sequential.py | 82 +++++++++++++++++++++++++++++- tests/learning/test_sequential.py | 62 +++++++++++++++++++++- 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 5529e47e..5655a4b3 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -14,6 +14,7 @@ from autocat.learning.predictors import AutoCatPredictor from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES +from autocat.data.segregation_energies import SEGREGATION_ENERGIES Array = List[float] @@ -741,6 +742,7 @@ def choose_next_candidate( target_max: float = None, include_hhi: bool = False, hhi_type: str = "production", + include_seg_ener: bool = False, ): """ Chooses the next candidate(s) from a given acquisition function @@ -789,6 +791,9 @@ def choose_next_candidate( - production (default) - reserves + include_seg_ener: + Whether segregation energies should be used to weight aq scores + Returns ------- @@ -808,6 +813,13 @@ def choose_next_candidate( raise AutoCatSequentialLearningError(msg) hhi_scores = calculate_hhi_scores(structures, hhi_type) + segreg_energy_scores = None + if include_seg_ener: + if structures is None: + msg = "Structures must be provided to include segregation energy scores" + raise AutoCatSequentialLearningError(msg) + segreg_energy_scores = calculate_segregation_energy_scores(structures) + if aq == "Random": if labels is None: msg = "For aq = 'Random', the labels must be supplied" @@ -819,8 +831,13 @@ def choose_next_candidate( if hhi_scores is None: hhi_scores = np.ones(len(train_idx)) + if segreg_energy_scores is None: + segreg_energy_scores = np.ones(len(train_idx)) + aq_scores = ( - np.random.choice(len(labels), size=len(labels), replace=False) * hhi_scores + np.random.choice(len(labels), size=len(labels), replace=False) + * hhi_scores + * segreg_energy_scores ) elif aq == "MU": @@ -834,7 +851,10 @@ def choose_next_candidate( if hhi_scores is None: hhi_scores = np.ones(len(train_idx)) - aq_scores = unc.copy() * hhi_scores + if segreg_energy_scores is None: + segreg_energy_scores = np.ones(len(train_idx)) + + aq_scores = unc.copy() * hhi_scores * segreg_energy_scores elif aq == "MLI": if unc is None or pred is None: @@ -847,6 +867,9 @@ def choose_next_candidate( if hhi_scores is None: hhi_scores = np.ones(len(train_idx)) + if segreg_energy_scores is None: + segreg_energy_scores = np.ones(len(train_idx)) + aq_scores = ( np.array( [ @@ -855,6 +878,7 @@ def choose_next_candidate( ] ) * hhi_scores + * segreg_energy_scores ) else: @@ -938,3 +962,57 @@ def calculate_hhi_scores(structures: List[Atoms], hhi_type: str = "production"): hhi += norm_hhi_data[el] * el_counts[el] / tot_size hhi_scores[idx] = hhi return hhi_scores + + +def calculate_segregation_energy_scores(structures: List[Atoms]): + """ + Calculates HHI scores for structures weighted by their composition. + The scores are normalized and inverted such that these should + be maximized in the interest of finding a low cost system + + Parameters + ---------- + + structures: + List of Atoms objects for which to calculate the scores + + hhi_type: + Type of HHI index to be used for the score + Options + - production (default) + - reserves + + Returns + ------- + + hhi_scores: + Scores corresponding to each of the provided structures + + """ + if structures is None: + msg = "To include segregation energies, the structures must be provided" + raise AutoCatSequentialLearningError(msg) + + # won't consider surface energies (ie. dop == host) for normalization + max_seg_ener = SEGREGATION_ENERGIES["Pd"]["W"] + min_seg_ener = SEGREGATION_ENERGIES["Fe_100"]["Ag"] + # normalize and invert (so that this score is to be maximized) + norm_seg_ener_data = {} + for hsp in SEGREGATION_ENERGIES: + norm_seg_ener_data[hsp] = {} + for dsp in SEGREGATION_ENERGIES[hsp]: + norm_seg_ener_data[hsp][dsp] = 1.0 - ( + SEGREGATION_ENERGIES[hsp][dsp] - min_seg_ener + ) / (max_seg_ener - min_seg_ener) + + seg_ener_scores = np.zeros(len(structures)) + for idx, struct in enumerate(structures): + el_counts = struct.symbols.formula.count() + assert len(el_counts) == 2 + for el in el_counts: + if el_counts[el] == 1: + dsp = el + else: + hsp = el + seg_ener_scores[idx] = norm_seg_ener_data[hsp][dsp] + return seg_ener_scores diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 032fa662..c20dd7eb 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -11,12 +11,14 @@ from ase.io import read as ase_read from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES +from autocat.data.segregation_energies import SEGREGATION_ENERGIES from autocat.learning.predictors import AutoCatPredictor from autocat.learning.sequential import ( AutoCatDesignSpace, AutoCatDesignSpaceError, AutoCatSequentialLearningError, AutoCatSequentialLearner, + calculate_segregation_energy_scores, choose_next_candidate, get_overlap_score, ) @@ -26,6 +28,7 @@ from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate from autocat.saa import generate_saa_structures +from autocat.utils import extract_structures def test_sequential_learner_from_json(): @@ -778,7 +781,11 @@ def test_choose_next_candidate_hhi_weighting(): "fcc111" ]["structure"] parent_idx, _, aq_scores = choose_next_candidate( - [y_struct, ni_struct], unc=unc, include_hhi=True, aq="MU" + [y_struct, ni_struct], + unc=unc, + include_hhi=True, + aq="MU", + include_seg_ener=False, ) assert parent_idx[0] == 1 assert aq_scores[0] < aq_scores[1] @@ -798,6 +805,32 @@ def test_choose_next_candidate_hhi_weighting(): target_max=5, include_hhi=True, hhi_type="reserves", + include_seg_ener=False, + ) + assert parent_idx[0] == 0 + assert aq_scores[0] > aq_scores[1] + + +def test_choose_next_candidate_segregation_energy_weighting(): + # Tests that the segregation energy weighting is properly applied + unc = np.array([0.3, 0.3]) + pred = np.array([2.0, 2.0]) + structs = extract_structures( + generate_saa_structures(["Cr"], ["Rh"], facets={"Cr": ["110"]}) + ) + structs.extend( + extract_structures( + generate_saa_structures(["Co"], ["Re"], facets={"Co": ["0001"]}) + ) + ) + parent_idx, _, aq_scores = choose_next_candidate( + structs, + unc=unc, + pred=pred, + target_min=0, + target_max=4, + include_hhi=False, + include_seg_ener=True, ) assert parent_idx[0] == 0 assert aq_scores[0] > aq_scores[1] @@ -877,3 +910,30 @@ def test_calculate_hhi_scores(): # check normalized assert (hhi_res_scores <= 1.0).all() assert (hhi_res_scores >= 0.0).all() + + +def test_calculate_segregation_energy_scores(): + # Tests calculating segregation energy scores + saa_structs = extract_structures( + generate_saa_structures( + ["Ag", "Ni"], ["Pt"], facets={"Ag": ["111"], "Ni": ["111"]}, + ) + ) + saa_structs.extend( + extract_structures( + generate_saa_structures(["Pd"], ["W"], facets={"Pd": ["111"]}) + ) + ) + # saa_structs = [saa_dict[host]["Pt"]["fcc111"]["structure"] for host in saa_dict] + se_scores = calculate_segregation_energy_scores(saa_structs) + assert np.isclose(se_scores[-1], 0.0) + min_seg = SEGREGATION_ENERGIES["Fe_100"]["Ag"] + max_seg = SEGREGATION_ENERGIES["Pd"]["W"] + assert np.isclose( + se_scores[0], + 1.0 - (SEGREGATION_ENERGIES["Ag"]["Pt"] - min_seg) / (max_seg - min_seg), + ) + assert np.isclose( + se_scores[1], + 1.0 - (SEGREGATION_ENERGIES["Ni"]["Pt"] - min_seg) / (max_seg - min_seg), + ) From 7111308ef677607e2a0e1c2476b9d9f40ddf9277 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Wed, 17 Nov 2021 10:24:50 -0800 Subject: [PATCH 124/239] remove duplicate intermediate from nrr list --- src/autocat/data/intermediates/nrr.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/autocat/data/intermediates/nrr.py b/src/autocat/data/intermediates/nrr.py index e6d7dfab..1d1f713e 100644 --- a/src/autocat/data/intermediates/nrr.py +++ b/src/autocat/data/intermediates/nrr.py @@ -23,7 +23,6 @@ [7.52e-01, 1.67e-01, 1.85], ], ), - "NH": Atoms("NH", [(0.0, 0.0, 0.0), (0.0, 0.0, 1.0)]), "NHNH": Atoms( "N2H2", [ From de127bfcc173b45d22e44b799959a86115df500d Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Wed, 17 Nov 2021 18:25:42 -0800 Subject: [PATCH 125/239] mv citrination content out of driver --- src/autocat/learning/driver.py | 109 ++------------------------------- 1 file changed, 5 insertions(+), 104 deletions(-) diff --git a/src/autocat/learning/driver.py b/src/autocat/learning/driver.py index dbfa518e..16a3c7b9 100644 --- a/src/autocat/learning/driver.py +++ b/src/autocat/learning/driver.py @@ -13,116 +13,15 @@ from citrination_client.search import PifSystemReturningQuery from autocat.learning.predictors import AutoCatPredictor -from autocat.learning.sequential import ( - AutoCatDesignSpace, - AutoCatSequentialLearner, - AutoCatSequentialLearningError, -) +from autocat.learning.sequential import AutoCatDesignSpace +from autocat.learning.sequential import AutoCatSequentialLearner +from autocat.learning.sequential import AutoCatSequentialLearningError def AutoCatSequentialDriverError(Exception): pass -def get_pif_query( - dataset_id: int = None, tags: List[str] = None -) -> PifSystemReturningQuery: - """ - Creates a Citrination query object that can be used to search for PIFs - with the specified tags in the specified dataset. - """ - dataset_q = DatasetQuery(id=Filter(equal=dataset_id)) - tags_q = [FieldQuery(filter=Filter(equal=tag), extract_all=True) for tag in tags] - system_q = PifSystemQuery(tags=tags_q) - data_q = DataQuery(dataset=dataset_q, system=system_q) - return PifSystemReturningQuery(query=data_q) - - -def query_pifs_from_citrination( - client: CitrinationClient = None, - dataset_id: int = None, - host: str = None, - dopant: str = None, - termination: str = None, -) -> List[PifSearchHit]: - """ - Queries the specified Citrination dataset for a specified SAA catalyst - system (defined by the host lattice, single atom dopant, and surface - termination). - """ - print(f"Querying dataset (Citrination ID {dataset_id}) for:") - print(f" Host = {host}") - print(f" SA Dopant = {dopant}") - print(f" Termination = ({termination})") - tags = [ - f"host:{host}", - f"sa-dopant:{dopant}", - f"termination:{termination}", - ] - query = get_pif_query(dataset_id=dataset_id, tags=tags) - pif_search_result = client.search.pif_search(query) - print(f"Number of search hits: {pif_search_result.total_num_hits}") - print("") - return pif_search_result.hits - - -def get_system_total_energy(system: System) -> float: - """Returns the total energy value in the input PIF object.""" - total_energy_filter = filter(lambda x: "Total Energy" in x.name, system.properties) - return list(total_energy_filter)[0].scalars[0].value - - -def get_reference_energies( - pif_search_hits: List[PifSearchHit], references: List[str] = None -) -> Dict[str, List[Dict[str, float]]]: - """ - For each specified reference, filters the total energy from the input - list of PIFs queried from Citrination, and returns them as lists in a - dictionary. - """ - reference_energies = {} - for reference in references: - f_search_hits = filter( - lambda x: f"reference:{reference}" in x.system.tags, pif_search_hits - ) - reference_energies[reference] = [ - {"total_energy": get_system_total_energy(f_search_hit.system)} - for f_search_hit in f_search_hits - ] - return reference_energies - - -def get_configuration_tags(system: System) -> List[str]: - """Returns site configuration tags in the input PIF object as a list.""" - configuration_tags = filter( - lambda x: "site-type" in x or "coordinates" in x, system.tags - ) - return list(configuration_tags) - - -def get_intermediate_energies( - pif_search_hits: List[PifSearchHit], intermediates: List[str] = None -) -> Dict[str, List[Dict[str, Union[List[str], float]]]]: - """ - For each specified intermediate species, filters the total energy and - configuration tags from the input list of PIFs queried from Citrination, - and returns them as lists of dictionaries. - """ - intermediate_energies = {} - for intermediate in intermediates: - f_search_hits = filter( - lambda x: f"intermediate:{intermediate}" in x.system.tags, pif_search_hits - ) - intermediate_energies[intermediate] = [ - { - "configuration_tags": get_configuration_tags(f_search_hit.system), - "total_energy": get_system_total_energy(f_search_hit.system), - } - for f_search_hit in f_search_hits - ] - return intermediate_energies - - class AutoCatSequentialDriver(object): """Driver for sequential learning.""" @@ -163,3 +62,5 @@ def current_learner(self, current_learner): def drive(self, new_design_space: AutoCatDesignSpace = None): self.current_learner.iterate(new_design_space) + # self.current_learner.candidate_structures + # write new input files From 8ee9b8f87a1798fb637f7d180ce07e0ff7d7b808 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Wed, 17 Nov 2021 18:26:04 -0800 Subject: [PATCH 126/239] rm superfluous driver module (replacement in integrate) --- src/autocat/learning/driver.py | 66 ---------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 src/autocat/learning/driver.py diff --git a/src/autocat/learning/driver.py b/src/autocat/learning/driver.py deleted file mode 100644 index 16a3c7b9..00000000 --- a/src/autocat/learning/driver.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import List -from typing import Dict -from typing import Union - -from pypif.pif import System -from citrination_client import CitrinationClient, data -from citrination_client.search import Filter -from citrination_client.search import FieldQuery -from citrination_client.search import DatasetQuery -from citrination_client.search import DataQuery -from citrination_client.search import PifSystemQuery -from citrination_client.search import PifSearchHit -from citrination_client.search import PifSystemReturningQuery - -from autocat.learning.predictors import AutoCatPredictor -from autocat.learning.sequential import AutoCatDesignSpace -from autocat.learning.sequential import AutoCatSequentialLearner -from autocat.learning.sequential import AutoCatSequentialLearningError - - -def AutoCatSequentialDriverError(Exception): - pass - - -class AutoCatSequentialDriver(object): - """Driver for sequential learning.""" - - def __init__( - self, dataset_id: int = None, current_learner: AutoCatSequentialLearner = None, - ): - """Constructor docstring goes here.""" - self._dataset_id = None - self.dataset_id = dataset_id - - self._current_learner = None - self.current_learner = current_learner - - @property - def dataset_id(self): - return self._dataset_id - - @dataset_id.setter - def dataset_id(self, dataset_id): - if dataset_id is None: - msg = "Citrination dataset ID is not valid (None)" - raise AutoCatSequentialDriverError(msg) - self._dataset_id = dataset_id - - @property - def current_learner(self): - return self._current_learner - - @current_learner.setter - def current_learner(self, current_learner): - if isinstance(current_learner, AutoCatSequentialLearner): - self._current_learner = current_learner - elif isinstance(current_learner, str): - self._current_learner = AutoCatSequentialLearner.from_json(current_learner) - else: - msg = "Input AutoCat sequential learner is not valid" - raise AutoCatSequentialDriverError(msg) - - def drive(self, new_design_space: AutoCatDesignSpace = None): - self.current_learner.iterate(new_design_space) - # self.current_learner.candidate_structures - # write new input files From bb9f6f2a7fa6646d136fddad0d0e0be6d0c4c8e5 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Wed, 17 Nov 2021 18:26:21 -0800 Subject: [PATCH 127/239] fix typi in docstring --- src/autocat/learning/featurizers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index c68755be..4562e457 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -479,7 +479,7 @@ def full_structure_featurization( Options: - sine_matrix (default) - coulomb_matrix (N.B.: does not support periodicity) - - element property + - elemental_property permutation: String specifying how ordering is handled. This is fed into From 02a7f8d9d3f32d3c1f2b70dade1b4ca88cc38620 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Wed, 17 Nov 2021 18:26:43 -0800 Subject: [PATCH 128/239] rm design space comparison in iterate --- src/autocat/learning/sequential.py | 81 +++++++++++++----------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 5529e47e..99815155 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -303,61 +303,50 @@ def check_design_space_different(self, other_design_space): return True return False - def iterate(self, other_design_space): + def iterate(self): """ - Iterates the SL loop if the proposed design space is different than the - contained one. + Iterates the SL loop. This consists of: - retraining the predictor - obtaining new acquisition scores (if fully explored returns None) - selecting next batch of candidates (if fully explored returns None) + """ + self._iteration_count += 1 - Parameters - ---------- + dstructs = self.design_space.design_space_structures + dlabels = self.design_space.design_space_labels - other_design_space: - `AutoCatDesignSpace` object to be compared to - """ - if self.check_design_space_different(other_design_space): - self._iteration_count += 1 - self._design_space = other_design_space - - dstructs = self.design_space.design_space_structures - dlabels = self.design_space.design_space_labels - - mask_nans = ~np.isnan(dlabels) - masked_structs = [ - struct for i, struct in enumerate(dstructs) if mask_nans[i] - ] - masked_labels = dlabels[np.where(mask_nans)] - - self.predictor.fit(masked_structs, masked_labels) - train_idx = np.zeros(len(dlabels), dtype=bool) - train_idx[np.where(mask_nans)] = 1 - self._train_idx = train_idx - - preds, unc = self.predictor.predict(dstructs) - self._predictions = preds - self._uncertainties = unc - - # make sure haven't fully searched design space - if True in [np.isnan(l) for l in dlabels]: - candidate_idx, _, aq_scores = choose_next_candidate( - dstructs, - dlabels, - train_idx, - preds, - unc, - **self.candidate_selection_kwargs or {}, - ) + mask_nans = ~np.isnan(dlabels) + masked_structs = [struct for i, struct in enumerate(dstructs) if mask_nans[i]] + masked_labels = dlabels[np.where(mask_nans)] + + self.predictor.fit(masked_structs, masked_labels) + train_idx = np.zeros(len(dlabels), dtype=bool) + train_idx[np.where(mask_nans)] = 1 + self._train_idx = train_idx - # if fully searched, no more candidate structures - else: - candidate_idx = None - aq_scores = None + preds, unc = self.predictor.predict(dstructs) + self._predictions = preds + self._uncertainties = unc - self._candidate_indices = candidate_idx - self._acquisition_scores = aq_scores + # make sure haven't fully searched design space + if True in [np.isnan(l) for l in dlabels]: + candidate_idx, _, aq_scores = choose_next_candidate( + dstructs, + dlabels, + train_idx, + preds, + unc, + **self.candidate_selection_kwargs or {}, + ) + + # if fully searched, no more candidate structures + else: + candidate_idx = None + aq_scores = None + + self._candidate_indices = candidate_idx + self._acquisition_scores = aq_scores def write_json(self, write_location: str = ".", json_name: str = None): """ From b649688cdb58f2da5aef3f99a5ad1c2514b47b4d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 1 Dec 2021 18:55:25 -0500 Subject: [PATCH 129/239] sphinx to mkdocs --- docs/API/Learning/featurizers.md | 1 + docs/API/Learning/predictors.md | 1 + docs/API/Learning/sequential.md | 1 + docs/API/Structure_Generation/adsorption.md | 1 + docs/API/Structure_Generation/saa.md | 3 ++ docs/API/Structure_Generation/surface.md | 1 + docs/Makefile | 20 ------- docs/img/autocat_logo.png | Bin 0 -> 111373 bytes docs/index.md | 17 ++++++ docs/make.bat | 35 ------------- docs/src/conf.py | 55 -------------------- docs/src/index.rst | 22 -------- docs/src/module_reference/adsorption.rst | 10 ---- docs/src/module_reference/bulk.rst | 10 ---- docs/src/module_reference/index.rst | 14 ----- docs/src/module_reference/mpea.rst | 10 ---- docs/src/module_reference/saa.rst | 10 ---- docs/src/module_reference/surface.rst | 10 ---- mkdocs.yml | 36 +++++++++++++ 19 files changed, 61 insertions(+), 196 deletions(-) create mode 100644 docs/API/Learning/featurizers.md create mode 100644 docs/API/Learning/predictors.md create mode 100644 docs/API/Learning/sequential.md create mode 100644 docs/API/Structure_Generation/adsorption.md create mode 100644 docs/API/Structure_Generation/saa.md create mode 100644 docs/API/Structure_Generation/surface.md delete mode 100644 docs/Makefile create mode 100644 docs/img/autocat_logo.png create mode 100644 docs/index.md delete mode 100644 docs/make.bat delete mode 100644 docs/src/conf.py delete mode 100644 docs/src/index.rst delete mode 100644 docs/src/module_reference/adsorption.rst delete mode 100644 docs/src/module_reference/bulk.rst delete mode 100644 docs/src/module_reference/index.rst delete mode 100644 docs/src/module_reference/mpea.rst delete mode 100644 docs/src/module_reference/saa.rst delete mode 100644 docs/src/module_reference/surface.rst create mode 100644 mkdocs.yml diff --git a/docs/API/Learning/featurizers.md b/docs/API/Learning/featurizers.md new file mode 100644 index 00000000..e0cf1037 --- /dev/null +++ b/docs/API/Learning/featurizers.md @@ -0,0 +1 @@ +::: autocat.learning.featurizers diff --git a/docs/API/Learning/predictors.md b/docs/API/Learning/predictors.md new file mode 100644 index 00000000..2ce437cc --- /dev/null +++ b/docs/API/Learning/predictors.md @@ -0,0 +1 @@ +::: autocat.learning.predictors diff --git a/docs/API/Learning/sequential.md b/docs/API/Learning/sequential.md new file mode 100644 index 00000000..ca81144c --- /dev/null +++ b/docs/API/Learning/sequential.md @@ -0,0 +1 @@ +::: autocat.learning.sequential diff --git a/docs/API/Structure_Generation/adsorption.md b/docs/API/Structure_Generation/adsorption.md new file mode 100644 index 00000000..daca8e34 --- /dev/null +++ b/docs/API/Structure_Generation/adsorption.md @@ -0,0 +1 @@ +::: autocat.adsorption diff --git a/docs/API/Structure_Generation/saa.md b/docs/API/Structure_Generation/saa.md new file mode 100644 index 00000000..f67a49c6 --- /dev/null +++ b/docs/API/Structure_Generation/saa.md @@ -0,0 +1,3 @@ +# Single Atom Alloys + +::: autocat.saa diff --git a/docs/API/Structure_Generation/surface.md b/docs/API/Structure_Generation/surface.md new file mode 100644 index 00000000..d826fc1f --- /dev/null +++ b/docs/API/Structure_Generation/surface.md @@ -0,0 +1 @@ +::: autocat.surface diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index c7c06965..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = src -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/img/autocat_logo.png b/docs/img/autocat_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..131058a8b51c9ac80c406745cc7a4030fbb03dea GIT binary patch literal 111373 zcmeGEg;!K<_dkx~I4EJzAR#!UfP|ot0Mu<2jCdX3 z2d1UCstp<%F&^r3$soQ9Y#WXyA^cdu@zU~$d%m8s2FW@v6w$y1h2p7r*gUJo>!8u4 z6`7BDR!w!14$<4bX_LeyPFqhYN&{nZFYO`~)+Ags5!D+0hog&}? z1d4$}1%HEf>EAE^GT~oM_}2>lwSs@G;9o2F*9!i%f`6^xUn}_63jVc%f34tOEBMz6 z{9lP_}2>lwSxbDtzf>Y8gU}qc@6UdmLaa7H}D>e?z=QLC)M{74+Zg#ZN;D= z0-*$0yl**~FF<4&#I?ywlyDcUcmwOU>;}`gjJ{FNZtxZCF9;q#egijwV0mUeBlErh z&?TT|^O0)ARjeyT|G{PVB@D|ut(96|EodVFm#X9An)PKh^Dm{-T9)A{~x^j(>vck)jE{k<741_foD;9650XQ2!%+BG^)}!#f0fi!oEM zmJiXQ|KQgW#1T%fGz>188M?Oms@qB6U(AYYN?;GHu=h&+Cc2edmgN6T-FKp|urfL< zeW6tw+?1(*lr=mIqWJ3J^jpnP8?ARMG*776xDb9OEq9P$|H$6l$>$#p#U%&*E zJ#>796IL+c&FbZPKfLwN@*u^J5NpTs+`pA<*NK6AKK@y=?XTnuR1U5R*%IFsSG{|a zZpf+y39sY($6d%p!wLBZBqEA_zy1H)Yq%5yxjvo zns--`alB$l&Z~om71o;kglU1z63Qu17Ul#wM;&Uhi#%)!bhgt znxt>NIINf)WVj*9tVW=2zut{r%RoN5GyB2!eP1I|zgec!3TjOx$I#+rH+_LBxe}5z z)|t1zw)V>A%d=r4#27N1A-FD!-dKDc?*Zu!g_g2io&#L5d~#rVYo8*jh8-tcpsY*Qy0J z8jy|_9L8o>2@qmR!Dewu5xVFESbiEjKIiWPo(S=4It0o6K=b79S@+OpT?M?>nz-#*davD<9o` zkprG1MM!oFi)Df9w{TQ6#vfalI*#^Lu9D-YLqSGG^Z2mQ{+;Ru>Rg{UPbTPIw_TKe5EJbcU{iK=;3}*0 zqGa)&r#*AXmaNNJ@IGn9sfGsajyK6(kH&R#b;uG%@%B8Aaoe2>9Ah)4R2Xk2BI$-k zWbHj}vWR{Gjg%)74?{cW$Mp`$JNB9vcD^f4C6T;=2LSzesJrNUL6)lXxK_RE(Mgwn^#0)c?y}2w2rNadAW=bayfX`K!~OP zI$siU@8CTWVM66I^XBum*j+$|yBTJ#8U+u7tuRa(6Xc5161FbLR;x!LJ)^9gd&Yy( z{Uc_aWHJ4u8V(LiJsYG0Jf3iRz^2$-vXAGp=?Yb_xzK=lUi5mV!=Pe+>pk&|JPAip zDhohld7qg51)EK&&JWFBp>v^f=DaPCx{k;g&+rGnh(nV+?fl3A{|1@r zH>%FhSnv466#KmUM`+YR=XHcAxS4FGhx>eqTvJFeb{!pAjx%wMV!G*Azojftj|qJb zOOWIG`00`C`8H`NpD@x+&*wN`_|d-IXby|KbaA*Z%KeM68wNE_zdAh8(ng5?qTmz@ znTplQ-yF^^IgTtVJDxw`cJhML0Zv9Tzk6`L4JU8{3JoR;lji6xs)D2JKNh-E#Ay8} zgSO$yG4OfSVV}=RxHdJyI4Q4>d%#SrJ*T&IMXYg7_OCK%DX|>GvfK4n7nsR09!U{T ztdMdck9vrQoy}pVeU1l=ShPz6n}NW-0@6T#?tO?#OnHT#iZ*RYYz0Zm1vrEP z6Wju|P&uEC^I6r!J&`3V^VY*y>CN2S#&UoQ9xu#8&-Vq0QB2#dedif=#;;*T`uEKN zpL4BrFJHjtOOROXd@TlY2LS_2oZ>gY$)LX}m%hNTlnA)WCpV%gM%^x%=DEa5zkxpj zTNz;X6h2=dAi-;Z4zhj?B#ZV7#Qa5*Ag6+#Y<|(TD9!tbx~9wc=cn^+#hRF;C~Evi z^9#T{C^E5Ci(_m1zgTyL7cwO1;k-2(^|I2I#&!% zoA}Z3_HiP3zzs`szpkEd5jb2-mW#{XL~#XLXeK((-&4U8t^40($xwBALeG1+olnemeG&p;x{+jDG?Sd;>bT4-OiI}bSHo08-C{X1nFk@13e z*YM^w!S)Ux246a#xkyRv(VPXO_M@VaU-dLZ8rO`aAQ>181lgZwE-(_R;3PPwXD&JI zPFvdXP+r_1FK!NA6HpMgobBN2=Rf5mYp(a*d^HB+DjcOY#u1x2!|5%|W>wb6Lg4ND z8H;|Luc_Kp5I?241skm0xk|mlQ|HzS4vTCPe*CYpfF=P+{`1{08o~@QuoK#Qdu;Qs z-uH~g3Fh`{EWYTf5u*5nnV$HAJjR^=6NC)ovyC5>9~!SmrGDl z1Do(C5@I}Gr#hGrPV{*tQ@x#r^F>q@dc8dc_$jpUse%t6v_RZpxlgTW&Ud4D=tLp5 zheedq&qr2LB$FJ5R!cZ$1(z7T$jV6ji=n+hmDR;sxzB$rT$>v)VC~i8@{8XgK-9uX zu{D;cYeY?RK<{%N%T>-t2t^q`4qZzFKff?7M~DjFAE- z2O+Y^>P6rZGoraY&Sw(_GgGej<+74v!@t-@-aZ)dwZT{k4AmI5et`*sYBiK~U9k;4=xw{?^<<9qv|l#v@2wvZ)Ea+S3Q1{YYvB?uFS|5X2w zGFN4!&`k|)(u(zmt;iAkoP|8%ZkZYd=t|+2k#|$(@@zbaZK#Xr#B1|5*tx2333!I& z_H4;$hjc}m^yB{gt<^%#@Y6`o1rAL2g=p)3iHNXyUTWB#0Jsyxo&XgS&&Nd&_t6_d z?03tsLPvfyizdxg=sw`FRXj+~4D)?w;ixLZol!8IPin0yQ5{(GshC{qe5-@@5*72d zxjYfx?J02l^zqC!ctI;ZLd)ng=N zN}M-H!*j@O!3Mi~wZ4}4)}xlhl9;kr1aj2$zxlDj+U;Ro>6=p0O3*NnGO3UL6>@>uC9ohEc}h#N+l<%f z39scix)EHbFK}=jy|Gqe^PqGkB?kN5(1PHdIOf8tb@2oR;5wcw%Z#4Cm|ST!Go7VI zZ>TtWt&np3fR5vQ`s{P*`X5N{cMI&N_Cm>At%B0G@QQOmsPzmWzc4D}kcg!gadui! zyP6YUzo~h=S%Di8XJb{ zhjh5YoINBmLoR?w&N!3s2w{bM5G2Rk(npmQB%0s=G3OsFqkVYi{Iwb6w8^={x12oD zAr`@#Xv$a$f*ZF={J~8+?B1#Y&y*It@O;#6Woy#0dB&H zw8gjhEsewlt^wo|axrkivrPu~ic+(QBQATy6kG%vBX*z-?7vo4t{Ws1LIWGVZ4m}K zFd#HB`MxUWvEMphyi~#Q)y!2Nuf%uy;K0a=BC7x;)XKD?{udlJG%>5+e1@t+}Lc7zi$Q#>N*NAkB%z3dn32^!)aLY0pZ zZ&rtG>;?YKe~=ZFl<@0k2a%f9>$Bf(7~T6vFgXBQf&EFKNR5N2=9ZLA#t>7h4c3 z(`1drigh%flRr=RG&$U6M>S0P<;JVu&+(q0(v@KOQ-5QG@uw(>_)|wQqlxYo6w0er zv=-ea-iU}xZ6tV61o0ygC|X(&o>maa>}s&GBxLt#`eqn$a=WEa7dHri^baw~zot=+ zoSV%gFd?k2m2;%;@igj+B`cap)`!+BwK(uK$Y-G=ocu$k>2G`my}01&lrc8ar;O;S>Hu;(qEZv*MmxSH4*UTV94>z(cudT3U`h!tm z-)LtO(~ObXK;;fT+w|5l%5tQ+AFIHlLOh^fa)`6}-$v+8IxEy9=xb)%S+;QqHO6I9=Qt z8fKNN&oionO$t0Kdu$DM@)edpgFb6z7})o!;D~@6I21%4MQ&h@^2k-CII9khcJqrCB7;UglKCtow~{ zBVh$EqOp%#GhU23=i|A3F1S9VJ;257;iN0>(lUHcIA(VUcZ|YH6=mIsN(}#(ux@XW zI-W7g*zOp$7on=cgo&}ZUnlQ0ny}1#IWPAMj;zLp#jC*4s2Tlo2surS7JH_dozDaP(eBf>$7#fZb zY-I;GqHbx+o34s;u}pPLBy)`UvoFc>4BhYCnrd5j?!7I+`@fQ4cs%|rWO@iTxQCb& z(~U9s_y#5hfnH&ft8X#DHFJ@DAX?VumsOB8`K>+6_vehGS?i&{x%oNj_mn`K7t{yK%YRp|O~F7rm1Ygt+J05kbD;lMtX zPnQG9&)E!4zZzBeP@ws#<K* znU5(dQ#G82*@0@*!n?Nj_>wmM+;QGampuVWi3uX=GNfHBy6F4#vJoQc?hB-*d5Y}) z_e%;~Tk@r$FV8cs+_-Al1|zf6^>vr2<6p14Er~8cE)z#I9JTLO)H}NjmR}*V;+KI% znJZrFm@N;ZFd0SGZOfjMA;9?|MZ%oTC*wH*Q=^*g`N$`4s^QS|h&kBjfHm2M@$$uo zY(XY@zJ{L_I~UUT^R14`eSR*Y*!aIDfzXE%b()b0a;e+<-+ag+U4ci{!2ztnfau%u zE>YvR#YIvTQ;m!bIkk+>soAqOl~!{Ft#V@jy9{dKeAs#)`(OKR^L_QsH!bvLuiSwr zC?F(gM(cO!URsY$ld9C&MQ4vl4{`DI(*&io#ai&N&z+;rR2`Mor+#;$4-zLzkKNo3 zuZhyY6VMQh*6PBUhZXfs%httLX05I(<1Q6C#YEKd+0O=BcbzAH2C4B6O%8K653HR| zK6!FK4M#)FqU*B@vy3H8>)d-(f#P*rlD$fwz|u~vf$mMK8j668TSCbNJ2e_$$OVn>A) z{>5N4#0OBXEZfo24%=m?p+#W1r*$|Su=9^GLe1SZ90G?tYmvo=PK=LI`UnWam1O&z z3EBN3M3+ss_MU`Trh3l-g8FGKwfp)i|JQSb;d2pqRTz{Asqrt4H5&Rtvp$gd!-;?w zv$7$Id=*i*s;h0+Gw@ma{X(3Kk;5e`x6PKodild?-krnx|xX9sIt$-ud zSrq2$on{#^xZIi)GC6U}FH2DVo8|39@nZBzl>40nkQ`rp;O?K=Qhfp`A;KCXy(?&N znqL82a1coB!&U1-ynhs~)Mzte$YfJ6*DefwN}1FFRbz^yW5b;!L0MrIMbUk6Pq1t% zz@s!U{@5>En;)T$?O8?H?KJyZ-WnJ>Iw)3_TG40J)Y6AMe<%dKwv#4PZNA0PscHQ!E z;B#6OXvcVC={QR?sLpME(U|vYcp1$i_tVbY+l~CMpV16ou9t*jOw%XGMT|u?{W;?G z7fw@w++O&PaO-XiocW;Y2Ps`cY5^(jnZ~&7o4dDFABuNyLC^1&VcApO?V_cI=@LkwhRZ0?Y$SwC%>9m}AC{Lpv z*@6jzg;x`Ut(d`m>ht(LfN=R>AhaECmBV3Qp$%jlFAq)bp~(} z@(0j`D$|%gS0006JOJ&wevAy6KUUI4L+R$)jYF|()nf#hNJZ`2_)D!>qJ$WSPLU~Z zCzyRbIa?06s~#GB5ih2TLGe!s;M{8NNq=}&cnN?#XR>2kFsB*Wg6e!(@lx6;WBA5y z)Tpz5=h+&pytlBOlb+WHJA6*VY&}2&dPhKaY$^V%3;@&zDjpjfpv}(MEmJd}{oY}{ zb7oz}(WG$vvEHCE^%JLefE*KNJ{9PTU|&z>A9j}D1PpsNAONn-0NIK#{3cSPKoz4& z`RRj+*LU^VlK5)-AC|9O9`M-pxruErZsW5@rz>;Z2Re5JC=FLJEvL>OuJo$}pp#B~ z1cxLSPG}P^N}x)^XFU_C=Ff=JfMt9WheZ5`T*&&Rj8$w1^eLen1GC-C^q-xx|F#0< z3gLtcW9pLx%T&x(J*7XL%C!4y+u0OomS;cQ2YJJF0CqomlP>wk^GV(TwWitq3x7(MMN@8hc&E|1~^bKQ8OI2F8*ggR{}ms zy|uX$iV*`c^TcA9PHC&*0eQ_LW3#xnD`Nj=abRGOD80ExRZw6%AK_FGZ`+S`psZgN z0sTrcv412Q)o*}=uIBfrJWZfOFdp2^7I);c#{sKFWuuMwV^vWs+^okNlwabZgM%@q zn~OcBQs>lC@Hj}fc*a-MPUc;00Jy`NYMn=_*5pS=QG24HG;DibjYfau~xhRq+Wz+$RW za1@Ppl&A==(dUo{$PB-zBOt`~Q=yZ z@4F1vT#iLz@pvsR=Xyh1)93DE*O87~2Qi`wXM=XWgtbEu^zMknac17iHP5% z{HL_Ij|;jIp`O845G~NvX6AUCE{P)2Sas*NW=*Q#FYtrmngQvg&g*&1-9wSTeD*#M z?!QIl2Mmt^tKTVpp#FonX~61;sq`ivcoAfaZci%WES$+VeSv5M8rL_8R z^nd#XJ49S0tnPiI7Ip`V^twpO#Rr!?@JZsT`BGxcLfA%(kv(Gq*3bo8a}2P@wMIjt8@OB#=UEWqd1&(M%~*ocEE8$2Z6ntwhxN`8E;sDUb|(b z4&bPWY2T*TQ=^Hg(vQ8%?FHwC`0*y;oIJ0uu~w2w0TGm8oCC{7osjzvyFc69Gz*&# zln~QKi*|qeg|>^+@E8Zyq!(+meN<=@vnL7>Fa^0qo(8GU|GoDWqnwClqfN`b5%XU% zHRQ^oGSZl)#Mn7yg=x5t7I!UU_yc~=`!+oXP1|O@5L6NLhkJhmZ-C^H+sMc4$^*94M_%Rl`pbw+hpXBv;KASt(t!Vu3IhI|S#kw2 zaJkz!3LAlqtzgx(flb-Fikl{`@@3OQ^iB0KWXPTVJ~NRfg+wL$Ot#KHqm5i)3ZO2q{0$KChWfVObQ?@?Sj%PV5x|c}sB0+m=ip~t^?isUx$V~d ztA%#{Fden7hJ|SO`}!Jr9d!}rvq@!JI?IH-kn3k{Kp}!+E;?6{I{BhWi4Cqmar7UF zBe$83J+#X#i;s)F`~Cw)Cy)arupi zhe&>UCh+_YT>A}B=gaE0^1$oBu^|gvr*T9Z`?y0QKagC3>tk{7{?rOyO?R6*XZvf7-|5veHUn1C@#zg^uRX1W zIxsV&jvk7obuqUT1FH?+Pci95k z0(%A~=v?pOnSY*BhsyrkBN;=Q%0>{4sx`u22m6WMTs7V7y8<~E)VwHYJCIu zMIK}=VAY*}at?N)oF}wUaTSs#7cbDwA#!lkPGFR3VZJgdJ|9Q1QR(QWWiGMvARf8) zMiSOl@Xa;LMU>B7PB?y$UiZFx9(C7T?TJ}DogqssYU-*I3cv+#Fw#9bEZ%Nn``Q#w zEdV8}MSPWECw+s7fCXinEyGk;z~50$5y(TS7om8+;^9$+bV<|%SkQCP!VFe zKbj1uejXx1RtRU@bdjj*cBo6jrj;lfl8zsU%-$ID5ic_mBuq483AT4T_3+N0;;knZ zPOcg67;Mz7SvWYLRNF{#JTiPIs;vQ@V>c5cMrDBzNp|4!A_!9FCT-OU6{54I>JUU+ z^Yf&CQnDDIe+!gCWq@s~Of-T(EVlH)Zr(=)xLQOZal2;W5>jbS!%FnU)qm9=b2GmH=&Gy59f3ALt$5#>n*ZBNec^8pfX? z)titht_OZvB%$b@zq_L$Y-Eqd;rwYjX*;U|idV0ad!idN=%3kHhwJ}z*>N{-c8SX&78eP=|Q<6BHS z`5ozLuZcjO?v9ad6k>?fY(3{6Z}qBEK4pQeupljxuXgOc1>V*JFJgvGO{9ig>G2IA zJQ9$i5>S)^;^|F4&VC#3wCg1ytwEyY zoVr9P`uWY=S!cJ0fOBvZm7U{CHj>u;E(FT5ZJ8oUSNqqWSMIKVcT^0ql#TkNdGv$p z;2Bfw#MuDXyhk;&m&EYVBKh~=%LpC7IaC~}AMVbsF(Qf-e~X5A3{vMCx{=Z!0E3cD zYR9WY%Q+@_sc+*ZUKbjd)Qu_<9wymX*O4l5$df7>If@)@H9p21cvf6Ak~cX+IdFI{ zC!1ltiRd93P!Bue}Ra6I%$^$1vm#n;s`liip2l8Ius(V)8$GE zIIN8UvL&8(iYNs7J@fk8h@&e1z0Y^3BMy%tYd!{JI1~2CyP*Ud)11`JISjCF`wvom z8wLd&3eS>~wXSf#P-t+XKU@{h*`g$wlcp)Vcd6*M-CZ08Ep2l7;)*4sJ@uMw)E*eIFd9?sJ z7rQ()HBuyeH015q?wrhNgDYMXtVrWBZk)W-zcwh?LE{>)OyiR9Gezr*E|M_Xe$oYq z4Ts^0c9>AuR^K#A$~fYX1)}80qgbh`;zyF)s6$(ZAeQDzXt;vPAi))p@v_M_`lnWq zQTNI*kC{MtuDqKEe!o0bQae*7><0nrKn*D>ar9SeL_Jh-T-!fPVCUo}EUt60 z=?atL_|K5NHPK<55MQqsZV)4l$_G{X-<#$oR9}UN;VqobRWS!CybiCoapt7PM-A#A zMj+iItog^=-+bC>O^1dZp<#qg(_2_Tix$3f^9F+LECDsjPMReLT$rX^pA?^yrJlTT zkmR@dRQgdQcJdOf>-!m2F+Q3tb+3p~`IlWmwd)5ND5@sOm5rowz@=H5Qd43TGvyHA;vsBrq%oPT{=A+C!S^f-F zD%aby9ww{u4k*11_NwOUD$ZU~xW9ibKl_%yER_kuUm(YlRBkHs8;#f-Fmrt}p|;#5 z0ZuL=u=(!Hp2ownNsB!e`haH_+E~GD4{~&H9a%KGjM%s`w{!eUY}@rMQ_C8*GFNY6 z24=EGpBIcMBX)K^3i{!62G_`7o7zEm@UgR%@PTI<$d%RxZCwsT`jHWzvv#}-ue`8| zK{g9keV^pMk)u2w+BqU-Une%TKPG$>?>z(?B~G-XXInC#=b%3PkDiAS_n(y4ENK-S zabG8ow1>-5)1761#sJR9{3LIH(gYDIS0cqy#a9Q3k)jDQ2&T_@YOqSa$yc!yOD6Xa z?dnUJIx)5zY$+wbm?Xj<9V? z>nG=EzCE#BOoaWDcGSTeAUR`vE;%(>o-<{81g+$`;w1ErGNvGrf0$i>T>jJx*MfrO zF%idUYttmt_a>SunywoG?Mk81;x1g> zmh}aFV3L3nMjW?$lLpjBHA;EPb$q33nQ{>V;T`wHqBXQD@WDAJt2sl33R?gtDa%tc z_pAk2{d$!~O#y~65wz%FVC{q44RG`feg)F*L*6xq%ZR6+OTsUEhfsTzWCI!3&at~N z@UZ+8Yv7q1EBVFoq7f$H4kEq4hRXrr~nMT>z=ql&OF~X<9+e73jPrY)`JXosQrkdcbSJM z6gwFvCC$fFt0E*P7c4|vb_T-vOjN65DR-{Qp59zKoVXP)xofZ~a~-l^v!36IUddPL zXi(Qot)3U{yqGu!B9zq_Nr*tRoJ875LENvgnN9xEnfXnnK$_qTxLuE-t;X>C{91di zhB#X;fPCQ0CxT4oCUMV402#)@2Nge>3n3ROq9ZY9Sc2U``CTEkN!GIYsrwaGsaxqL zFR(lfXb3+pm44n?6-%_V2-nk+?wi{cPFB0igu{|C%LOrj-e{<06?S0?9)zaPr{#^j z^O`!%Obs2J{Y(vk@(?tQuUQjz%bpQW1c-k@V)x{XcsJ(P?ovtAfbVH2}KN4&}HB{>oW+#To6T7ye8Y^H@9i@G|bnh-z=?&qu6?cyHUvdpRF{TlEL^ zW^WB==Qb~pMZQdRw#W6Gwpcoy=&pR1bjV)gAFf9Beb&v7s10N})(U^lDvyS$YWJG1Q;=e`wu%UVfqZ~ItIU@E1W(k#;A2YJpVpD; zVaCSY3vA+8vF;hf-K(&F&;E1*;y@7RzPd2qdxJm|y@*fBkC6OsvQ&#-A+c1RtY&WO=9Zd(bQGx8-9Vzp2K9jI3&?!hT5I zjI{gIH_@zH)}EZv86f!vP6nYkrI0PdGB9iDCIO|68esp}k_k9h^O0%YDCEGeSz2qB zpU6?YWhM1BUsX(sy%x<@N(|ly$uLM&w4Tzj#3I~1rE5^?_HAkkGqVSmaY@3eZC;ix zJW3u!Wvlr>5zV3-eb%cbXW~OBE`jp&)j9L4ty~DV8V7 zrP3%NY2L>{hwPu04L05{b9~h9gSg{j79Sh-WoRG8LKb?x7l`9fbqle$=bl0Dm|2c< zjQzthTizO5wI?XXVJBBDiDfUF#@Q2|jolxk7}x$H3iA@=7*G5(DHXQ~G|EGU6Jzj~ zCCpE0XE$c!TIwG};pa9NajWs?u&g6{WUa2ChHw>_$a#@Mjh8GnlrbpA0&_Er$il0s z(7QmgSH$U+eFf736!E{n$hZpGmq1jimQiicGZ@{5@r%{c$d)^!N{xT@&U)6yVB?PC zh$OTS-R*vyi{l}75R`|=QL1r@EDk<~etT#avzM#ND8M!AmtyI4(BuO1X4k|}bE&*O zyv7URt*I0NW?;1cH7Er7JwzU$gRghjziPHI21$&N84VL|w0R5qbCqnn;08H}4pVIS zV64#@UfWfr8rq`+3n9u^d4BKtc&CW|h;&!% z3SEq?GV{FX;8y@76U{r)Z`9pKRT3JPDBo6O$84dh*34?LsFlj_ic_X|f_+Fz0=&aehE%yO*~9H1FcNgC%#S~xVmv7gEw7ntjc%g2CIP{g1 zF~n9b2Z>HgE0s=5LZqPU>klQeivLVso}BkirmjiRAPJRo>7p;cW3XSKk$D4eNAFtY zT*<7yI=K*C1RG&Nwo`ZAs9_nRf4Q!ZEcI>V`NphxolViC?+WMZ6Rv>$|u&B1$c+Ql>!57c?K*!DmUJW*5;|LUxge; z;)aX3xYoJ*ix&~El!}wwT^{JV_tKqdQV&-)U@y~qEkK-mw}is?r>j~#eZ;`0Oyz0Kup)lODi6dTABVBfbvTBOm{2fOr*QCy2oo3OO_U%Kt zgR+m6UWUz>$yzv4Ci_TlR&Pfs;rmi!UR^Is=}OII{h=(2JU$vzhyFDhRFW?H@404| z!noS*Kru4u*9W;;W65`lqU+QecTtQr4|4BGo_vGK>}k%>Na@qXwG@f~ow<=~W(uG? zN6^3I^_aFzs0}2v^GWbQiSVVH^PJl!74zeBFgIFV!!q>~x>*-y*b4M;)f=g5la|o! zwUfo}qriORNJ-c2!JVu&FpMzC;c;M1=OkN3Tdi)VJp-W55i!r%Dd-6)c*W}ZSJGpLF{a&v@w=lJercy|qP zp1SX>UT!PQU?kb&!^3-h{oP_&A|@7%_k(>Z{HIiJx`fm9pAZT#V^Bigw%~u#y`+$? zO{X%C1;Y~=7kI&-iwB3lSoP@Og<_C26)D{pnw6ey9WI%Psc7F{8?%@xv}w~ktfJp- z9k$!$F1QIlK^0bPpQ?va2Zw;XOlj8#9NCq2_n}D`tzvc!14X;_8jCL2>tzPnts{ zX#$uy5yILF#S%MFKLsQ?xP3i9z`zC^)s)J6j7KH+t$K^8`IsWaBJTD5s*0UyZw^@g z2KuIL@=bq7g}R`Iv8>{IluGKNpdSEKl@9$RnK7z> zM;Vx8GrhMEjOd%c>cl7qo-X=jA=Bj{&~)IkGOY(u?eie$=&hHovJ+GMyqnE8p%!>n zL*9MDG5CqnEbDLkx8rcfqK|O0R`*Rh59?pF!ch%0s*aRuEs?n~Lyk+X`11+XTe?z7 zHv&zRgkHq;AndKl`{;&!!pty{$_ZZ-J@;pPzMQ0!fdj$3AiFsG53oRBwb17w%KYx_ zunIRGP1h%N4`KHkyJ7hQnA2(E#MG61WkQealbgk4tABX`uvN z1mBl6N~IWg{4~}_oj(r*>?vR(ioHiYJcCP82aDT+@u2drI( zw&ZCj{3oP@XSd&*Po$!-r20AE$exjy+KW5+C2zoSSB4kbmY;fM1f$jh{6wb215phW z@HU5tD-x)rKn-C~Pvmr5RXE7NqZvpdJWM?~?M*3WxJA4dKR+1`+a$<&STH2~UHR)> zowJG3YAN*1*TznyD@Qe!FQg^qTLSM|;U9j-v(3@9Sok>@=E)w^}7Gy1ug_7Wz zywxN3#wLp@lQVg0AuJj?)<`xij%nGfA^6zS;ZO_GjZvSVj(Cb7i`QSpC~u7=_!B-s zX?Fu*Q+Lp;vlzD7H%|dOi8pXP5Oh9CN7#Z=tpKjMHrIx`7M=8j=<_IPNNPa2u`p5U zD4v!1yWZ?-lcdAOqUf4H=c?ww-Q3GyF$`Pj@h$d*qhs0}gBVXCkd$VjZz?36a!$^B z2U%34DGOdZ?+MRl zG)=o#@GHk06X&b&`1sS!k8~(%)Tx3~p{<|va{PhIQxjdzpINNasav8R#o3s_$hv`0 zUy1BXD=|f1(B!smk=H*qKreZEhB?i$23NVq6q9-sSJ(-=y$YkB{Hw{G^8ui*>GL=- zOvaDP?&0PpWIv|R22mfoc7Nn{;~c3G>b+3-7}M!Be8=;n-SquVr5Z%3LnGj*ldT3Q zPnI}LUBFk>@yqPRY9i{alP9>l{#>xh`?yPLZe;uZ-baeaV_XZrrW$F@UAYX|@fPv@ zBA61R#Bp(xd}8j0pC3>}krI%IXx(z+pDqY9C@(?2Z<>2*?)2!rGI50V$;mD@`fN|* z$4zD`Rho7Vk5?|7usN}4O>OoY-<%I>mzOb=@K;-!dr?*xoJwhyDA&K|Jadw?jQE>xI6kLGU;A^J<-80 z@Cunv0L@go|E%lx_+m81^j!m!;@)ZLM?Xj8&o**4l|x6U5@*{eu{-3tSMwwriW)0r z%YsX$Qns=xGs{Gm9Rp)b0k{e0T+i3D-BUv1eRXEHu_P_TKE)**o$V1tS~?!dgV>c# zOzGXPpjA}LIF3xxh*h0}$>o5%D{Sb0zX~Q8((cVwWQoOug&j||@OM|95nsiWXDmz! z?IFJu;7CU>Y6E75wxwX8_6#=Cm#RG5vbH-ywxPCY?Nq@kDA3!VMNM7lEOwo-_N!vT z#u7fyieZ)@<0hS43S3f4K!sUeNC`+8bW!_vuFc1v2|!b=$_Zcp@I(hQkLoP* zL5b@a;wOFchjC3x{kB*FCFEYLn{CHUjU*blHtMw-yq(|goo$vue!MKCH{&m<6?)YY znF(2j;s3V!Bc5nm`MS~%+AXx4Ej%EOB~CK3KE|E@1jkN&)W)iI?X{;VXgipK zu5R(Ykn3mShZ8~!#KP7XxVrts%)?o^1pnU~Kfsapep_DpjyT&)|LWbIl*SFkOT5D%&J?`U`8G=_Wu zK`!hO_2%yjL$O^L?2rGNE^Bv{Wx|@1$-Mmc22#;C2VVp{?4vjJe<22aeXpY?9FNHP zx;dCeQx}IPu0NrP`>i}ZSTSUg@k*9E0Opbn{Pnj{cw#d@ijDsyAnmsl1Q$eqQ7ogA zq*XdUmr42%Q3^u)QWWdJ+1U@o3RIwY<{b zVU-_q`!{p#WZ%Fa19d`|ey?!$uOM?SvBFy zZq(!E+FpLtWG+A=9^3*=eU#>N;501}gbikbfkFN0zvn?f3+mfX0<~g&mC)L5V$?0K z5_3IwW9r{xBtLlrZw3#)C;MjgY^T-!=mL0+yao-8F+CI6k1{TE-}qaGXb2}P=FwMi zVh*N&srf8yhWwYoJ#zlQDX`1m!9C+N8$N=v?wJ68D0S-zRe0R-9g4G1h9V5)qGi$K z29l`|Q)}-7fMrr4zD$wI>UTH>@c@sk0&-p5R8igEYNU&3moA@kaVYP1xw$s~UrtaE zs+hi3_Y-?Kk^~rmddHgEzHWzM5NfLS|12PjT40#aOVGAbgNx^HGT>LyuSf^|^!;ts zV~~5U+&Uc=c1iSbZE1L-rF6}2qn;0}CdRt$tN5o9U|ox9S(>MgUY6DZd-DF`s+@>Q z7t1sTQ zN)~-%Ri=Cn^Ty=&XICD78@Wl{Hi|Pgw$7zBwl~~2S;Ac7YBjP_H0frvlG%m#B0R{1 zAiLJ`zXWPCBSv1DJxmH93X1APkfSCud2tLA<%)|vkfxxO?FGCYFJH-fR3%-5&O*Ac z!8FN(+outbQEx^B>GMT|2OXBd^ha2@YcK1wTe(hHzP!Uu=}tG9!Le*pCZrdV_bWwC zWwE8n?>{V+OQ}w1$$tg395H~EFUJtGf<_K*)ymQ=G?8pLz-jBB0Wz=aVYuWo?+*)8CN_C~;{S?#BYa{cjE>Rv1lCqY@l^cAV`u5bITN-vM&1ii0P203cg zl_^iTPuj=yc+nDLy~H>xI?tRB;v4#vmlj4sfGh=;+{1NGPggBtTO5yhvtPe0hYOhA zWQy<;pi5w)>U+pQEcGg$)snXfs7Ua+9^7zE>Tzr`=F|!rk_XK6QWZWLT{Ky;0OQmq z#s59wBbQg5@N@dAve|b|pl@&x-qjWREfXzIp?tvB!-0-h#MpZP?X2#HI5BpXWr@b! z@2(`OTR26G zkT!JRcDp8F zVED;BJQ;#*{eph?9o=N?l-^;?tF@Dq(mPq!di<6NAi7Vt`l%H!GII{BMBrinh4UWr z6C25l1_(g0eUaH}&=)xai6o>W7Y3x*E`J@-xpAZfXdnJOPfpj$pzD!|+6GT%4hIB> zkI;o=hy@M!&hq%X&&mlNdt$wgC-U~&a8^7AT*IU{e+C5DpFp06h`k;E1APT#NmSI1 zp3j=CAsT4f$CDFJ? zcYqU*g}8qGlp%F!?gKI8@Wvs=zQS*>`>(NU13;Da($qunU*E3TN--U}LtA8A!t$)P zF~6zcp%BPX+T^%d<1E*i%!Shc2gGCn5HTrsJ2ecp_n1h8Vd&Ik)CnvHK&W5a>C~@b zpQ#ifL8Pxn{h$#J9y9~Qp9GjS`~XKz;tnL%-|LtW%@4a^_CfJCqxkpCfCjJ8d(=(qmfVMIj1@f%4fKg8aK!>FejDbm@m{0MPqQ@=62?Nfav|#e z>88llfr@Q3Hg^4eYX9}^E^Xj;#uRFhWQ5=9Vi zic!&zgX~*>ALZ!+d4f5}?<}%e1T8?=>WHjVM1Is@`0qMjH59f#x=&x5MH1z%1np%c zfYbnV%;IPiEfALQB{eA#c`X8_X1n4d(ph4RE{)OWwBJz2C=MAYqd4R&^spiQ60b5Ht^U_W* zUN8Qk&EnypKj@*u(F^e-GY;dyyIxY(CkCVd^DXCHsEd*#$UF#iVn;%R~V=uuY7(To33{v{;Of0KdDG(d|x zc%8Kidq&a#Vv8lU4#3=OYE7Ku+NavlGC26;EzGy4)QAUH(69O0Vk-Tu%f7^jl9!v$ zVsB;nDcqXUZliA)KDFKT3q*`HIRLZ{W4=KS>ulzc$bg|IJ{iO^ zLn4VVnXLL`5g8x9v6JpT7`cpC3;{?87MMDzQ%NG(*@HRrYRjkL6@+WeP)!k?pyYr9 zHTrYU|6I|OyU5ARf9=ynj{KE-2jbQf-5-!*jzEKa5_n=?#qdqgi3mmo88K-d&buEx z`hyr=$Qf5=irB1~zR^gM-Ats;RhWP10JZRK7;ZXZ&8waHlSd)DUR_y28cm;Td5bPa9KnOndG_^h2!mKs8cRH2pQZ`|v%jlTmX_ z39${-P6s@~=3)dZDes>%WYJFi93l%&9_581p^f!^Q!e<~wz|kZmzJp@}v>H@$^NXMy?=<*M zXNX<32-*}D16!|j{WEX`%Rh6SGhz1A<#=GaR{>7cN+d{m>XecT!_`U8Z#BbGDL`tA zINr(iAdY``!m)-~k@m1Hq7s$)?R-QE5(LjUj>wY?*ZXb7&rIHh;Ot)owi^=W{OxD< zCwqpKFeriYF0hUQ)a{4^2i6YnqHp6AipP?T90-<+*4HqCf4&=bSZ`)O@zV>7XWL9_ z8PfKyg4Qr--8z1rQV#P!9}T2k!0}y%TEo^9u93R9EILdPY_rh@ltTPcyobmFvY)&AA& zsef)uV0B~o#ha;~HXi4r{y-k#YVF$Bj~#KZf}*Av2zRz!w?jaOQ9Su3_~`2)}7$IBRkWNSZb4^y@r&;y}-`H zVfHie(2ZD4H^1k0r#7Q$s!E21)cV=lZ&V+o7r|~p9}jl z9OW1lpN`nl@{J@fgG2x;m%2*uiXK#Sh(_SIs#VrB^};8@(917idd{Czm%*a`+%%5& zQG+jp_)=ePkRc}(mw9blo)XeZe8)v55fwXn)+=`oo=?UG@Pxj!5+a}|jJklkT-dNQ zJa*U}fnpt2kUaUfVLO1XWddv7i!v5Y^U9T&+{@oLIky0M^d97a@98lhK$+k}YZrQV(P(KHX4+9d$D=D3=lm}yzeUOQYL+;jFL>{l35w!0luzbqJ~X`a%LM!B21xd@4PLzkAe#utp5aVR zP`l;HTg2L`;DPuwwvrB7tlluBkJh=yzrb^#$1(vIAh5Dd7^gZ z%K@C%LjZI421m=y=F;~^OzPnH1^rRWW=#%ojwwz!alNGx^Jweyas8o;jhl3_VdGFR zI~(Aq*eD!urZG^6wB8}?kT=J952cnb+F&{;wLM>Ox2Kro41|J!SW#O8)G(U12w3+o zMcnuoS^qnzM(JJ@D1H7ZfnM;~6W8^$`4kB{4hm^%+hVco!lHxFfd?rV$;rojUk4&_ zDz9|y`i<_T)?zn|CPDFL(yfGt$DwzgNtA)5s^U#XfIl4|GJO4gjiRDgwnd!LHA{du zj4Z?!O_GxD78a%1UgSL}zNxgHDMLXxd_3QEB7&Ujq%JU zsV?!qF}D#0J6jV3Q5_07NYG_5MJ{yi?e@|iPuV?-%nC zOh@OgUIJmh6=89)WJUK*JD=y4{4-Pscx7j66B4k6qH5o8n#uxvpm+w=%Fb#?Ei*)Y zxlQ2UR{O%%L2L;xb}6Cs3=~`7RmCk8F&`yksIla=yU~S$UF(dUVeVK2 zDBC(f_@Crv1-QAHE#ZnPX6xdYd)OZAwoX!^P|10d?xxvmR7WVLll zeXXsmG1quM?z^I5dGaWT_E)D$$p+HAS+VE13NsFZE3i4QqOa%rSz#{2a{!lFUw^kC zE#J2^<7dlR8oP}0F%Q6jgaFoR=#_FZY4nz7qg8EVHAyI1yBp@tY+E%JGVpBny3&t8$qf3$*=&CIMQthPSj*CTHPEd%Zd0?pxov0cLKG zC^(HMgCVi5h#su0ios@+OOcT0=NI=&5AH@TnSktmpGj%mZ`AV*o-qJl(IeT&V@8Jd zN{Svu98Wtr67O9+qAi7XDyMu?4Vx(`%Z_Q1X`d#P~@ zOMZ`|ih}rQH|QHj7g`Pzy}uO*D*tj=)pe&6 zM^0BcuGjWO(pPCJzj%Gi^2bzoCiiPDvXm&fRK)oP?U-L9F3p29@SPh&`yE>mtAiTa z{pS2@sytAH{n%k$y8w^8m!ZLJoAcO6YHsg0S zon>ou>&;@(o12+f^bZyt^Rrx&1$jqBSU31Fv}2gkLHY1>I~b)pV$Dq8$Y^Z?BnO`6?Jalr9@l5SdB6*<9@1w3=* z6wG*CZf{ypE&#G>9{?;~UOM4dc`Y0#>~#yOT*G8>VJMahRnB65VyntIH$?J7 z)bTfUQ=0mGD(c<7IeSlmhL8t%%36*kRw9pl;Gw_^NyAAp9>2|Hs+PlOLAw4c1la^p zdMwpnwbHD!F1koQ9F&VeeTR&X?N6)ef3>KHIXg^L+Y=ENShG@9F zJU;sQm_!r{MEDLYO#8qED}PUmTU)gjP)~ny-*8XX)C+>wlB!Q>=ezfGvK6NTjD^Pc z^#mjOjjK!+V!|vUlR}~IeymgrDNBV?+aXhoR+}Fcu`(r3 z?06@WROCV5@uy--M+kscfaZAit=|2+YJ|SxsdI;kb)nF))Qxl=)I}UQ4)XWi1y?27 z4B3#{&1%lH7WZhOA7sGckUCDXu&4QI-V#U6#33&6@>O+;3yQ;oVx?f^;~_Wo;yG{o zjXvPLfz3}rc~eoh1^h=4M2+0ZPHlmNx%k*c(^O9hd4Y<9G;o#0@0r|FOy0Y488}cv-0ke*n9bTb*8!~+tfgrI8!*pM zKm$u-QgTaOZJxAmi?MVZ4n{Sk#b@yJM(g1pN~LT*^eqdzPjYa#bUEsVZL;j*_PcA$ z91vA}kH>+Y@dH~YJlz3e_~cBSfjU}(M;}02@rYd37aN7=UDGi$8>j@%*d9FTGqch@ zNH^HAPYNepyJ9&dP8sslrE_i`H)QKQ972Vk;QDD4wsM(HbF`|povGxS?Yr`g>JmI_ z0>}F)n-sq4L@O3g+ed8iEt&JHN6&4zjee|mZ4+V#R}QF+eKSvD%{jfw=OY%(c-lAt zV|b^Z&Z!w{(S|77pb)rn;pB(^i<#k&bh3d56LtKarP<6Y+G;|DgN)&TP)=!%Q^^vZ zVJcR(iqw3lv5RxpW(LwAkoM4*+w`df1gG?3E7(DWqJ)$#9++)@P*=uPRB2H?u*IDc7P7BK;JR*eH61jja09p# zg#h!p?X7w|=lJ@KNP46zes=p*$`oDi$9lUc%R4FVrv||lTg7ZH1*YyQ2(_b%y+8>p zP{NBp8!U`Cg&_Sf#cEO*oTwHsmfMqrsl5&$yyS!%J455<$#UmtaqKB3H~2(muOQh8 zJwC49zD`alcDpA&2qi~XQu5IErhbMcd&&j5XDB2tAoT zv1Qp867vi=k1NB*!i)U22rygM)M516`afQNNngG4ul9h`YQC~BH$(PBdU4OVen-Jy9)o@lIYTN zQT^9xJqvQiwm{Yv32Ep>YZGMf4^H{+Yt?pfV-XCf3pf|XK9%xe#Q>2TxP?kCk0z;I z_q*Y+4i*u`c`NpO;UuqzjbHh_2aB-FVZ9m0@La!YEy?-&b(i<1U`SbSTRF4QOBSqXc0glT6^K1^%okiuvpZh5gAC2$1JnmLX`*ZQG({vRgq zNk+`XSY6fA4HOVc>yGKa{^g-BtKySl?L<(;c#c{RxasJ2J64TM&tMXy!DNKN8$oCv zzfw@g#}Q&(Z6id)tgO^&-%e7g&PUm;ee;%Pxu`eL%)LbT&33<={Bz_nT@QQo*qsSy z7`^0kfKxTU4wy=EZv5ZwHTkcQt*!g#A&B8gb3Z{w z`*q9h7Qvaj%q>!la&XWrUM9p)LA3PbsV{-@AZeU}!H7O(#zGSS%aP8-NStTDdAEB9 zZvE}%zCzec3iT)h0&_hjw`)FvMkfbSLQa@RiP}5PC}FmZo!eI&TF~pn(Sm-M+-Zn^ z*#R1eFmC;enYpCAA+^F=&87*?|_Z?mkR|>ggT+p$Se) zo-6sL?*jBL49MhOqoZGa$bq%1`@k!3rCMT>t)~#)Y?gM%qSeiWr><4nxv0sAp*rb9 zJ*g#z#;7c|A~Reuo~1K1BjQ2J>gM_4y1IZM4~nxh zIFuVY*Op47ovQjlDNz@?iszwj(jx!@dX@daKZ=+em#~~*x{=>2HY3=w4 z%(iaQ7h=P7a-LXt)vrgjoj|#QqS?Nh=E*>M2|U=HPSB2iPadt{NpsXjeZ${s7p~Bx zTXTi#`@7s}Jc6@ByQQjZ9M^~Z--zF#K^z~zw9Hs->1Ew9Ilo0+>`1Bt(I=S|rR)5WppmK0kl48HyBfvJ=X6jdYaanpvgB9ut1sGI(>LdR=fOc+`UcqnqI2x6qZ#je4Zs2_ zF)FSpsa8E^I%t1$arsGZMIpENeQ&eI$HGF0h99LU?HjaX=i%eC>M;&h^D}0VQ$FTI zu^(^R?Olv}A#u~UUb|@^EU97lvb_)zZnPsF*jf~Os0s?VQM=N0rZRedQr`v;5u5h$ z0UXH#CM)|O$fbm&3$%Priq@nv`)JH2Us9F`lK6a}!eo2a-w$j*YSoD)V{7#ulRC0C zk@s(X+@ii*HO^KH+I`YkAWBn~>ZF)bDrwk(dI4X5U^TReOMl6)9cEbD0%+}QY#uOD zdt8n4OLuvh24Bz>O~mw*o$UB4N@J>Zc+)U3`X25YZ3Nbl!?yALv-|eB5?XG}I#sr| zAe>-RLS2{1xt6d&CoF;#BLsz8yYC?Y@S%y1c`8{e7Ic=^BUn7>IEH4qsB?5lYwOr$ zL`)Ek_H5?dR=~$FyxY5NVznd~v6^FXuX4hJgQlW}6Q*u4;|Y8X5sta$!#I7N$`xF* zs{VWwc>xT~$yVij0AS$abcE=Qb;~LiEis8dbGh4?*S?(}S{i_|LRaj_ zUp;a3eiKyjy;C?Hn}zNxU>FPfF6)4K?i zl-fG=muc2GpnLD)nWZr9<{>xLMaR;b8`T}?o2|U|GNWU%2sg;zy6$5cgKju4Gn&eX zhxDP<{EaWSKD2=bG#V$u+gAu36qPQs|x{^|5rrHsc_1VPdJpECisPO=<)XIy9%{mjwf~|2oPR90r zM1f04B{FdI^w*xOADwic<>3-Zx8|aVM78_L>MqUKUV$kxyp_Ke&T6Sg1@->HF~U(_mxD}ssx^_tRcY7+ zZ>Qj?xUpVJ5tiEaEiACZp2ASibZg?8@OLU?Ag;OA!?<>D=vqo(%{@6$b&_LiDZS)< zao94govakqaLU;iTITM=z@66Am(EI@;FOZFU@CoH&-6G|oWcnr33oXiR0Iz|S$e_) zS-0>Q zKV={=?js@mcdfNWt)sO$T{%a5fjmNaVLpi zQ?*1tPp9p~N@4hu!=CU)77Ewhf4kG_uL|EreDnJJj`}&m0EX;u)Ekd~(rNGXS#p3u zx>~UQW$XTryAv7kz6@y) z^Bjx96h+4~cG?d`uB*@YkEt2`aWF*Z^RK~bpAPnL(eUxY{ThJqLe7JYkg-vO6-2G2 z#IdW7>nI*>krks`8-^Z-lpAQ-&^s4}@9+7~%Fu!Pi|2|^&PJAqDl(yoLC?iTbbyEepXBObDN6bHb`HcSWYtST3e?i(7Hfb;(FkVXe-KqLP0Xb zyT8D2mf^57)WumUS)4_Tt+}q$ZMDYkrrJ(Vbfzv$ zR~8ro@mpx_s6g(UC49@9tFdeLIi`ubIju)IP&Msj@s`+-c>NFoL3F$Sq3GT$yx7;b z+S-n4NRAj*$I@&5Wr1SG>;mYs4pFhpRrEt47-z=jO=2o|$9R95&!5txU_Em1g?0KCIdH#p_B8m;~zN$?q*p-Wj#;Vmx9G|QW z`m=k5CuFF3N=hXulz~4YZ@HK9#{r z4xg>(!OU;EDSV7SDV=sVKGus{iLMw@8P;d)1pVXb6*<~QiH;}koK+-YfhY$m%5&;w z!emEeb9oY!i(zdgQ;&I)M`O823K_dxK~qy$yy+b13!DY8Y6kCa32`e&J6lJ$75{3d z$rczrncW_66$_09^b8TKW`%PRtbeSZy~6MY511YQod6KKw<5-Cpu?s+pm$lW}UYuc6FOMOjGRF&du!@C)miQ zNe*%`lnT`bY7cy7`XiW%H_0^8>+VDJ&w$m6p513Ao!t{Dp!Pk~!CoaS0f*@Bccnu) zu|h}_e?T-vhNO_wZ0ijyUQ<2>#K`=F#xyaWcY4}Q$KO5ngcv=wagt3N$nHqVAZ;3kI*XJJUVsq}M z!B^{)w!)yU-vfrUkB_~o05fTX3q1rK+J)GJ%7>uNI=33!R<(2i~;fLqLD-tRzyX}8&56}B$qHSW)n zIn9sCW3UVIFF{Z6-jmDst9X>^%Iq7%(6TMA5u#I5wRT*Ft~mUD(!47t5_@0{2wAgp zXsX$9d8jBRb7zbqqA5N2B$TCOddsAUGr8$gl{5wqJ@g2YJo?F6JlLv^*6uril%AM0 zGQ;enf1G5GbuNKi6m#NjGdG|l&M?*1=fn}xdQeiVqwKli&qi!==+P(*D{jM~r>*L% zHFqkC;AoDt8D8{#pRCG;#a(z(v3!t?Q;pESMKg05Tbh&yof*E=Y|v8JhuLZnJCa8* zNNyG+(!S^*ktlkyo76XaNLmwMB%j2`QHSf*aNcgz#lzO;yEu}K#pKGtD0Gkeb(yZP zv>I`yzf(~w+Ee;bkH^L20QMq-xTXeGcp+(2#v`kA-RnYMIiyGIx3<6Axv$~ph#(Iy z1!Rr0RkNd$Ve8{^IaQE7{v8>(SLKU4?c+!5<3rw>At!;u9=Sd-%NF{S+V1+*$|A9K z+~!5las|2>)gr+Mb~2g)!J!;iSexoEn%utNNy?HfTh81bdoW=g4EiGgsHV}OgY5uX z12u@3YbTXlQLls1!gf(?j2J)siXmH156^}vol7w%?7Eh`>t~m@b{u%y`}i6ksx!&>H=irE4;;3apH$nG`K73V}khl9F+_`MqM?b&VdUyw#fk>dYLo^PV_vbYonQ7O` z(9?5iQ0;wk_QLl9TrdTshmD&;V-Tz((*eJ53^4&++b1d;d{4yTedQeRd2G}(2L{ct z@R`x|9Tct9VSD9pI{J7TR=kIgp`u$Sa?`bXL9KC2JLWcg`Q!&ja(;e3rY!sWtR!TV zLPOBiaMH(=oV(UDqAexPk_mKhY;c^mOx~Wu9up=w>t{y}(|c}&npAH|8eBxKG$}Ak zi*UnENPFK?l|Q6|sau)jQMSU+v+zROIQ=MqtrCkZa1vLva(g0`d%v!+vi!kFog0We z4k5nZ<&j}c`2aQAY<(fGKmGHGjW~6LsnDGEm!0avLA`^X#v!X(h9ruD?c=g-y|||J z&ClcS`eEZFdE^CaM8%c)2xaJbpwskla@gmVU^z@geGi46CjB>i`Md9rI+Xi)8qviQa!f0CS zvVceH`q(2n6NixP;&RBDoASIYpm8Z-K#0^;U9XJIU!2XOMw zIMM#t2i-jqK=!;{n?iDyw>b^Ri*8Wl3c4RwC>ysFykT?bOoV)aE}@da3%s3y~&gG~i0;y&64yj~k_xf$NX5|Xhl|b0P4$zR%A6v(K9(2>a5Y@hxRyrii65akvow3> zbryz95Lc)obww|i8Dh21t|RpWL-ocVnC{Q#pjZ$|;uZ`;Qj9@;9K#q_MO}@F$hB`z zISz|WomgzU2ad(zg|azNYV`@)?`pcFO0RC6kxlO zSiEt9TXC7H4FJyaFs*iWD|+o_LgQjl!c!>zumNzjm8X}`NqWfD@GRk6@$WHwLvj<| zQ%!ydObG)oXk9%zvnU1)3*J3;qTN~DdSTq|JU3ZhC5BNZUfrn@lbu@dLCqSImj%qZ zM{qJbkQb0E-~aMlhEfbL4oU$|=pv;CQ~TT4(vFcVV6jiIm#Yg}h=TH+K5{%bwXUeH zfqz|)FoC+ILsi70}&Oky4G1G-B6sZQiEbh$02eQFGD50J74vu@s>W99@MolLDbl$4k z*6{Ftp3kasGdTzYc}8`K@vG=HkZI#txg$pl0_uiKoj=+I9iB`oK)*x!1l&j4_lmf( ztE5F(>g&ewlBT9z%*K)W*2mE$MuwCKux4q5WtFe;C4y=qdR36FcR@uUiD>sl(X99% zF9EeI9gr<*^=*n}5})2>B=YJEC^u`&6sC)loZK}~rvbwWy%L_mDl7)`_Tq)OhGfCv z3Wx$Y@-TjBU3jRWnHP{Kx-~^k6y)!IDRF>^heD4YH+D)@7dWKHA3a0ZwZ0o9a4X?HodWMyrVj!V!>{dzX!vY-YXWuR05W> zRRifT>;9 z(Etc*o!wi@b}H=aHJVaidW1LZgqSGedesC}gIXzALGU*gd(9!sEbqksQ7kMQm239> zM+a}G6S(q)l3-kQM@j38m!Mk*$8*ZQLJgOC?lgmPl?Pg{u5dadtJO@87paEUYm79W zSoeS?-7^c_-AkZ~4@7zQ&UC@ydLxqKmDw^Av=Kj!;Pj+xU$6I;rrDZ4^INoZ&FU*eG1Z{q{Qo?U~8Dj}Y$Qz?-J}%mdjV7ZAPRwc{TzBXD|pQ%*8%pA+yDyiaLXGDzehe&IF4{pI+h)XN2&NM($JpTCTh zhp=S4-o!#LLp}5c)Wr$2ml2J*@wF0X{lyQFrX_jUaYgR8pbjVU!JE?Ldk6AhxjyR} zQdrU;=W%baTx3@emy^M^G&}&nWOiX;4!D2;ImRI0y(>TNl@7I9)PVDdWQKwwG-P*# zSmwA}K#kI@cnSBIpKq^YJATke5_wp0EDmLexAm@yz8QX!E6QOs)O$5n^s`gpftK*- zkCLm%6RIOIc39pDaIlC8_X3C96Hd2|+nk%iwWIBLIp+`+ z{7#63ABcW#vMp+{d0jPp1TKw}NWpfBe|B{pwHZ&8yj>daRf*nDr@b@5U>u(u;$q#H zEsokVq}zzXw{kzj&}?JFvqWjye8!q~O~04E^;nv~<3^$2HFP)Blp(}7-zpG?xt79sjizNFt~{E z-)56z^o|K@%NvLpUJp7OH2t2L8?54Qk{3_zFa}A2pySA2lgfXHI!w&@8tM28k(Gum z_m(A*ysH8unp!oZLSl$+8q!_4%?Rmt&OBYQ>@%#ZQv!z$F#D3vV|z}0!|Aj0+Bt+P zvzv8SUmk5hYc;w%_r~Wxc#{fO-#~Xj`$Kj&{B00Vz7hndWgQ;9Z;ffHCqwhRRgl)g zY|5JBK{PizlBes3RVMfDVj1-ZfXTYtGI@Hf30W&-vKN4nuc#Rn^PLZII-H)nNz}+6 zLXBvzn)VAB+J>mZOKPADVv(OpAOYD8HRB~fH!rmX7iEajBvsv)@r zGwvwcC8Rc(wr`9eEooqD1AJ{Uf@=12?pgla`#f_=A-sxb_p|*E-#hCi7iN;!7yhvw zfNy1iEVDi;R5xV{&w9H|zgBpOp?`9QHN1bleWw5pCoh<>NR-);4%?%R9lKMjeyk;x zovI;_vH57a-(Mo>6`!Jd@=C+ryP~?lT{j=KmnhDj0ae6}c+<%TZ{w?6N_CKg3cQss zGr(mjlQDlFKjJt&jLyN{^WMdPWNlW`zZ_Bb$b1yqD!DoZTi4RFDsGX<=(J3qlG|^O zuJMz>m~oZN;D&~=Z@lQZ$2(p^@65RRa)58V#h!OETieSOeWgh330|mkBz1$?Woo}G zptv?Ca6d_J{d19ELiz)y)$~^0JWgjLsamSp*v{fY+!%OGMoc2QzJATFa44END(T(f zK3jMO&8WK@1tiet;r&P^pgwpXpzyvv5CM2_!|%E zYOyF8_^3|P&If;PLfNCLh=!~1buF18U)=R#>*Si%-Xt^7Jc%#87$Kff7Cbh{*b!%fRK|Re3dBztu&r5e`qH zT+8)OaLy4!D!)zeZh4HX8`#d-Wu1JwqM=bf@($j;Kpy|T(TxdAZ_R9E)}_^?R>ya5 zEU#j-M|m8N-9m!E3{WjoNR2-TafLYSZ2ky|s}u1}UayeDNgzTF9FZ-M1P zQ*{|vSL}8as6-s|{y+*qzq@+d&dwVE;{aP*AA!O9Hg5jm;Lw}1jxjxvBRtE(oBkY< z3upP#FlOa)KRq~ZqFD1eRNNmovceQ@^2@)LXXNzvjDd%SvD8jy%Erc`Y4O#si#;T8 zQesqxvL|bN%|tky+eWbctYGj0fY^5tcYB~=7zU}hxcCm*zc4jH#Nd$oho>&kaUaum z$=&fkJkDIDi@(1T-#wTED}44b`l{_~+1a~LyT`A5gQBCgGK$)w<-&q?A2pHCJ~mUy_ePKesmWw3UY8S4DCP8 z!xCd83?az@9)(JA4ChAK>JMBPi$^*_kM%FEJ@cnRc(A8!#^4q>0QVK(X>&sR2Z;ZZ zJ^ff555@hfkKt$=crKk?I=;Rmk&j^Up{=TIvU=4 z#~v&GWmGY-j-M%amEStcNgXG}nUb_x)ao%wvMNsWM)Z7vB~eG{`?#uNReZ{D*MrO& z{LzYc-t>lNz((D*9`H?NY3sQ2v~*Pwko8SosFk00fV>wAN9 z_ikLwy;}|dGTflFkoa2eERYguf%JzJh~I>%Gq~s{pODngcQ@UG9bZGcZ>(77iefsy zkiphxGi;h>NLu1H8hR5H!N#hR`K%Z?XTfDg$-SK@(5f#ZBAiwn&88llaLk)#vvH70 zy^)7-(I>>QGJ*ItM*zY=m%lOWFc)xlii^_1yn|fsNYy6y?Y}FO)s26FVhjZ^(1!(G z^RmXx#jgq2^4QoG13u(01ti3>wrhXJ1}iEk27*(qk5i%rQ{hp%fB739K4YYa^tUhY z<11)5x0n-wUzILXM8*x;sPOg#ht}TfwT!T!bXTo=Io8N{#D0a*%rTTI&z&XY@yWZn zc@9_-Lqx`H){^N=GNAhig~s0GZM!tR)hD3P(ZV3(qW=c^vBC0&ZH^0{dJl)Rj;LgO zqC`@yV4ctAcYl%SxnY>SZ$83+6*=v)mZ@!}gtpM+)Nutq9IbrSggK)J7nliGovvF9 zUi(U%%VQUmdJPsy5(q*t)+CWkkS&9nFn`!A!zH_Me1-&uKg@zNHX1h7r61XGDpK`r zIdxMT043XpTK)3_1{)1k2@TaRA9c(F*`6{Sc7_JQE3ol{%Iyu|px2*CvJ^vGIYg@2 z)83!pv6JnIiNM5FeyA8uwQwbYg01I#AC%AsnZkQ5+!QLnj!uO-hX@Er4p*RBjAGgF zP3&xKd>+0)rYva~2yTDK)rd=X6zX}<)l5O{$yNJqebc2Vd#9Qp&}T}abP3djuu|ur z%_4b#k8i$Mu45VPn|%0e3t_xum~MmjNgM&jFTS~_EH$omY;D`%KP-b`xFb$sH(;*A zW_{1fH89Uc+QVUMd&BPFxNlctWM;&Cl&Up(To^&e5W(==^h`nC&GEYRvx{sOW)8HD z>y#b7aBz=YMOQ-iY8}5mwR=~tj~?e85326*oI2<^Knt$iL#6;IP;@IS$Tuj)Qf$E$$TgcXHWvs_$lVdPtsWlTDRHvXSN{=q#Bb z`tNV7peKjUVs9&+OOpujR&wqhjKj7~N>Mp5BpbEXW0%R-tt7VN7b6(n*Kwg2u_dZ~ zT>GZ>9Qbl64gUvk+2PQMkpVPFvc%n=HpZS3_ZFolh6?Pw7%Qpm9HsP${PvDt>kI66 zKAjZh;mocbR6PV#BQ3twh#IY!W7-tG=zz3xmle{YY4v;HOHGMt zDVdiC-PAI*896n1p1JU;Tf`6?aMNC|AV+Qxdm_X|ewWp5whs243b#|oz56E6(xFVx ziv}HnhkUpVH!`f=-<{>f2dsBILZ3a?Pb!Tpr>%`yV8uThGCi{?6m&U@TRph_c+CZnYG;hujytPNcRL)Y~6om=*}U#~#r zOID<0l1G>nLT1-_vCV_4gqW2OS|b1cz*(UB(bm`6H$oK_H*E^loC}H^Np!B9uGVEp z4r|e5Jhb%Tmc@Cl=8Ix1q(eN=WeYx}ASO$l$}z^RPq-;vA9~VG0}x#CMD6Rb8(-a5 z*}%TS|Hc;XosgST+D6iu2X;lAK2KVXlY*lMsJWNb_mA(Be#p`6=0I8#VOHAJ12tIb zW|sP3>=Y5eiCb>*t=M=Z;5IWzB~Yx5_6>*ZY}k3_*Qn7Qs*Lnw4f-L{L-Ff#!{&X6 z(U`tF`}n=i5Ksm8f?9fJi^i5!eBAc~9e`W{Vk3z!Ku*9h?Xvf*K$x))#mmIF4qrA> z_GIdZCDq|F&Tn9b&NDr-z&!&I@yQFHEKjNMvz})US8dB@u0`~Vw3E1g7h}`+t|};x zV>r$}=@LQ1kl zK`-yMG)cs6yAQoMhqL}It-{TATAqmLvPj7s;*;oF<9ai*D0oE)eX!3Cgf^Q0^U6Zt zq8OFpQ7TL$6{-=|2{>8A{DJK&Yg%@;EAtx*>G^O5vOQv=FL#B&2cKXs2z=<32+o_| zc%)bxBOQo9F_!8D%mGnIW1Rz_2G#KCAtmLl;x=6SX@ev+9`y^`9|YnI+~JfPfTxH} zEJ3`vyfTR;V*O=~fZp7>hzWbC3`J?0sVXIHeE}VjACesIMr24GUYD^<=>t2hZh0Ka z?Z(J2@ih~t3{(vbC6-Oqzl{n^qXP1sY}G~vOtkA;HpTgkt#`sL_FTRV8-&j*j8?0o zUu0}s_f0f2M0@U~L%Fam_0zMI&=>F78unEB>(~f^&zO>v;!pr%6EXNwZ&=zCpP(-w zvf|YejXHfU1&+g$z~>9sTsErQ(eu+^_190gvn9Z-jhsq~{B3YTxG}J$ zY2a^%C?N~#3^OL0BJD8r(9y;U@6I^e@#8POFHN*r&&?dwWXyV(aAAW_`$kTz6aE9q z1+1gRr|{q6U|@;<_4~-vcUJM~^F?yXYqMU<&5><4&Y>Xd53O2T8``iav%p1dg~{j( zoG8}G^hX^G|C3?|Kxv;?WE1raCtBxI zN=h>F@HL$jwkH4g{rU4>94Ivcr%2%B#FvWqxWmO?yZ~{>Uw-hx3g#bLqfM6Noikt03$sGot|6Oh1uirt|gE{TwG)iiMwJayE z$Q|CtUVZa_jhh0W7}gSvFD-hm_mCLV8U9~}p_GbZwATxbd#~|m{CID=Gx)!Jw(AV? zcCewKWOvvnq}Mq_mN?uF1Q>tLz+ar4qr3s>S*<{71l!%&Sr@g6D4F+_b5k_YIc=A~M)sir`T8d}C#dh0XtE9+beH1wFdK8iB3vZCz>`-k#{P5{IJy_!3Fq z=!G?;uFIhIyWIXR;ve1;;FuWCl~WfuW!X|wYZuq74cY2i7G%brJ~Isa%!H@L;Kcmv zGrP_sZxb8eq*PzoDR3$0Xch}@&p@N1INn z0LrI-pZ|aT%Y7`n=xWO7fBVe;2K2uf`fv8}KO6erF8eRO>VJOdf1djP*R#xa^uE+gazo#fs5H2}esD^xM;l)j0*qi9lmy5n>_zA&CAsI(g#%dBE z3e!jG9$l`C0E!v+km<{7i=Qf}GzE-fS{#4Lx&Fr{oVk&|vgz&7ZjdV~0B#49Cel#W z*xZy|=I<{2r3DZQkZk(nJI6v~v4FVs*OUQCk56ac#>%GiBi~}j;O$G*0!@5JpHLj0NtcE0c$e3#`U*H z{r>P7AeJV;uZ8yyG2_3Z7^Pf5?5c2pW4r$UXK>?yRRW{UIr|RLGg1-p z51IPE$Aqi^ZTpUp@5ujl21WsiagF7X*5LnGgZ~^HY6MUvW)Tah|1xNt&w#ef=4}1w z|FQR$VO6zV*RX6iN~nN zI^Maq_x--l_kP!N|Nf5ec>Z|FrS7%XdCqgpF~=Np?f+0|VT>JMA6zosuwOX)zn|s* z`ppcOl42#r1pgDA4J<&GywsF&u)R`o4aC z6dt|*iU0nWEdn+6QV+Y3GdO5s%!&X0mvJ;Z*`A!z$N&6kc!OM+jNY+g2EqUQY3PTf z@NNkzp0iB<>E7_@jSEi%$$Ga2xBbU^Lldtm`^#oHmI{achbM(Q_pd{d5iHSAF<$!* zPYQSbKa2Md0rNkL_fO~Je(*82^+h|G%DX)89MteEB!E zxPB+yeQCHqSDi^>GKA@-RF_N3zU|jshTCtnBE!PM&PPlh+Su5)S&|TA72@z{MXGeD9~OPW~gyc*zQMUzmK0;cplLBYjRK z7R#!f*jC(1%hgKla_ec~6WRyXRdZ~6=G}=Rq|!6~Q`^g9lB%VmU2! zDR)a*#HbXA7aQyI9iG{Lq5c6rb1qpO}|8BQT`M0OqrQ_g#aW z|HcQQ9x2Vwmw$6hER}vS4f^ol&#$cE{AU~8XWEPoCMk+rk8m7Et8ZKOr1GRaTz~rT zV6C;durrn?`qisfheT7}{}e%JEg*JU5_a$cbL4R__fVF>DG54O1c5CPmfeY2-*W@4 zl2}xKR5;RkljT&Hd<@oJ>m@taJyo|oMq1J{(>-#nU!RUgubfO%O>{eU` zg(18BkMifEfr8S~(mzjlEDn-zU98_4wiUGOiXYS{z!z)6|EGYd$A7*1T@WZH<~3Sn z`A9qZm8(v@_r9FY&a8A~M~XQ~(e1{r;NM^KzNof*NIP7#Di8jprSHA97^zcY*+TFn z+jHl`X}Y!ogPQ!FzAnMKUvJ({tNQI}=8MnEH)f|lXr=LJ`(4mS1_ftQa;L%T{pAaITq1MunMfhh(0p?@y*_riDzBpRx2W=8_ z$xLu$p7P2mBv5UX^jwkLsyy8K(bDxMNx&?)cI^|@`meWzpJJ!ZaFhzxba!+-BVSDv zwbz{$FqFE#-p&@9V6e0G!?E+vfDxZkEceeMdPZ2V-y9p9l5l*SF|S z-kMtlm6HS#E43TU2`r5E?JX_hZ*IS3HK=xdDoyLXD3BoLXqazo{Vce|swbSs;L@Ej z%kK*ix1>pm7tPODE__cAvU;X7{B>mRA5RxOHN?cv$}e1h&lU%M{Qh);PGGjUbu5KQ zr`-DmV)3z!Y_JDcP@a=#V#``i)D zw>(WYcU5_p@mre0=AqgCRd?@AeR|$9Lh(hPw9Izk%EbNoj3>`H%IOaHg*>`{r+UQ` z$uA!J=w9-;yZY;%;I^?yCr8o~FCRgp3WrfnDp9)^fF+;mefO6tiWB-fOLo!^0a{oXGwood&-hr2Uzf*#BII#tdr8h>N;H8&=M-$M4%-#wxi4>(72 zS;5!0-rUmirktFd6U`R@qHK)wQrW;K+9WZ@aPs9(;WSh=PttVWe=Y1LGh1RxJPa&bwxA7u$kk-LiXC39gSz;J>%+vWpUU zqO{s|V*DJeW&US9q0 zEjp(!{p7xl(kr)%j5G4pkqf&>lH^w5v0~UWS*Tm~ruNTwy<#yIXYhL|m7KMcfOugsue>iB<}fx0@GR_c3~{wNO~>to|H{6Q;y`zOT~Az3=nq z+uhbHI~G?kACR#2WHENQzd8{})DbUW_PVI3_pPN(t4ToEFZs4LiV+7veF2Y==gQP7 zF<*%>=_m+H@)K*qD7d=le3vZd~JA zp4(piNYvRaHf~2go2{Jm8o+J+aC=;joZO4>h}vae|-yO%ox2k>+O1$O7ta_L*-Qf zca~5JzVNzshh>A8U6!$a2y{eCd>e4-@`>-t*AY!YS)Tf7;4w;GV-#Lh&wl=Xc-ky#2bXt`_E!jR$i58@HYRq`XdDSV$c(rWHGX`wtuibB zH;~5&!EZG7^1@%RSI}-8`S`o~cR^@fpF^ybEhNY$d#6YHD9eSR$wy(kJq-!>?=5do z^u)%-vYtG3hFCMK{C>^+t5CZ$q4Au=;?pm#nRebx)G?iP3p*R|41G>=k)Kk+**xD` zyRTR(!k-Iuyf@HbO1CM1drcJb9z7mT31hdWsx*t=-j}pT^Wj&p%=;zWl-3ph#}K7> z^yzvCKXLwHU(x=IoNF5HzCzfh|GJ0lb-MV*g`6~ATwL7umfe>q#hudQ_Zwp>EG#WM z60A~n#uzfLcx(+>g>ujrEs*fvm=hcGy!K#oK7-Agu}qd;TG}AhcW+^ET!st9h=+bi zR(Z7dJK@_@CZ96ooL$uMPOHrHBSK#GWBSw^SirA65!BRXggSxc1&FrX%Ie zXF6h>SL(KJ);;*sgco>)hB#NJB>Q05qk+`rxJgy!P^+M7Hc`Wha`?z*lJoW(g@FPC znv0S1ubm(TDdec6yo%;ec)$x^Z8|eO?K#GKmeDvAGxs@VX04U3f`xh#b0PqbAU>gM z)^~2!cf#&(EWKm_HpNZ3^;dzJ2*f@BHMT|P?~x=raxJ{%qr)9s zdcTHk3uE{i4g^I5U$B5fX@}wUJ%WDA*q`}2C8i~3#vlBCNBl~mwv$v*RV`rS<8l`A(i7hJj~@?k>(!OJO`TT(=0W zo8e<7dmXzX=kViIqmAyjkiR$n@M!Qb{*tKOyqf##S63Um1ckSoz7~nte!q1sJ~E7*tgNLh5FafGhYsKK}V@il7f$HxM@ zEVaYnorXWfG|~h>?i6w#uT;;Jd+J5{68=pGMXqMfxsqocZ0{$BWDb53-D$o`?0^qP zT?xv38BQ% zT)YS|-4!ozZMfW?Dxp@942KU5oZ;6Eq0*n3jX>iT&3+5hSOl=8ddhZ%ofbux5<2x5 zEHNn@;8k)MJ3PtwSwG|OE1Dcmk#WU|W5N>ehau;X2f#%7>sbB*J`&p!O3}JZWaVpl z?LMSBrl}JI9FS_N&JzwiH(lyHh{YuLd@Ev75tZPov}2-tk%1Ed{<;BMOFszT-pr-B zf|76a!f~+;yZ7ylL>e1D_FkExDB&023ztIz88g8aQ1bb*(ri}(6M{#tHL`BXEX`5D zRSf;bc%v!^CvOR92A{=gz;&qnd8)mwZMBAKSiX@0C$@JhC`OJ1)5wIgaeOB>avLw& z5?PohAiG``j(`0KyQ5+LUK{HHr)ghok9n&C5uv@6v-t>f9sbU+bgyMO1r^GdkQxxB z|LtAAhMEi?^g|Ny*?6;uHgWLLCvXbUmxQe+Zflle<6|NOv{m_2QNq{_ctirl%yXo) z=r1&qu_QTlO@)tyG#A#PS@3%duMmOT^SD{-aB_w#->2l96;cP2-ARlOos#Rf`ZRtaPPX?PIXY#V01PKRvlYq@l z*Q%*-$!XvV@WrP}@e!oN3cBeKL5gXePmc%|hf3tj9mkg)*-FD3eNnTH_Z=LTeZ6-s z;Oyg`lb9yn4`h~wN}==wm*@2dJBqaMsqDAmkmqqPVxcqSJtpO{n|ae?d}lZfp9Dd2P`hy36$sz5%T=7p!veD(Hw#)#ofVN zD{W0%ME0*A?YUj@?-Cp8dV?DGby${mKEK`0wo5O>Q6>bdr)=Z*$hYg@z&#_H#6wGw zS%1xq)PJy$MO&_*iMPYnnD2RPXG4jDt|c{=%kYRTeGI!;iB!EYk<3KcMSqFAzOiAk zx4GcF*(aYfa^WnJ5P6|sb-k~()WgBY*Km|Y5 zn6z^4ENdS9L=jSm(dug;jesaJz#eAm9Bd(5$)J`gca~DjA$i30i?Rdr^$U3HfFah~ zCm_}5xSv*VRUwEc+C-AGi=bGPvl65by(vXeyh_eS+nBdZN`^=jtV4)JS4`bPo)%{Y zB~R=1x)#oC|2sXgnFQi1$YX>x?yOA(E`%6Dw9`vUCg~iXPFT(KL5*bg)(bE&!aj2} zhBZa8E?$LOe8mg#`|IwBT_jtRkc;n5N6#Qk43P&&$m#9Ti)66YDN`EM-$3V>3FWJ2 zKZ-2GL$mgRDpR%DuXOTvL^!d6oWIWH_T2mDP z5xBfn)8`Ye_*7H7eO0Hexa9&5Bki!BX#x2eBH8}vD`WulWR~mcaGJ0`-CbaWkVM59^L=ti`l>^0G`Ho zhDCXLEu3Zbi<~kkKn0UgUF>0`&psmSfrp4py~fd;NDNp6_#T3(fJlni0@dR(Ug4P5 zl_GifR$qE0d!_4w#x6($7OmklsoNB)fs!7}WSU{Z5tKrf)J`2gpU}~AT}*xOE6;h% zYp%U&E|u%-C1LT2i@tD;9dwuNJrKUK0KL~z8rAVUnP0@juy~Bzm$xggl2`6OgG$Bc zS85fyc?ohP9j@oQb&|MjBtv9D+{T=vHo=m@W8 zT0=!9*SwzS{TYGvpAICt5+a@YaJ^mI4%DJB^0Uf6i!^goiW5Ta;L?0Bj#tk$>+N1S z0{r9dk={-Y_kY??iBr#g7zP!KK_$Z~4;MGP82&8JM>S-j9n2~x+ zLhLhEEK#F3CODT(?>g!>z!CEOGjYT2ELDP_#m#(v>-xkrgW+dZvWXUC-G7|nlqy+T zS}yk0dbz}D+>Ry;k`=kea_z~vm}r&DiVn4Fa?)2x^GJ~>CL>6>sJI;kfio5#MFIe1 zZf@Ruv|{E!i7g``>>;Sy+_02n;s4mw?rI=H5k4tv+iS&WQ`{z? zLHplg+?EoL+5G_2id)Bzw!~~8rRuv6S@f4!sY)zYPUVLVeDh}~`$eg&9RVF>2P0 zQA|uMq+tGJz+;4f5mTIofDS>`8U}@^4j{-7gsLSq`5zhm_r>u%r4*N*gL zN+yYeGg}#-FKInkWKx_^(IazQyEs}iP`!iKCXJ(+T%eKl%=>9%JZHwFtkqvH`=l9Mg?vDH1N(`XCm7R-HK*7Dgm< zz`uI2)V=3HUSg(y)8|9YaN)9qzNn!F+mazfCbp8BvQWqthnT5`9 zkxlt)p}WS5AOmLgqvseWs(?uKDB2L?pdX%}_|w6rorsisFA)LuveRR%0IQ}k0Ts_v zo;00x%B_5(2E$t<-?%fqpt$Uu_z>6v6jw>bc)Z?6(0x%`V&QeP={EntWGG*hZAi&Y zJn)oUedX{(r@ZoRp`iv8ja3Khoh@C0`V|f-)lztaz5%thQot0N6!4uQniS58wKJyc z`cnqIc@i;!ghvl9r}r4+*}vSH_z|YT_i!4NLZS?-ni}C2Ov!6(1_h0&Tv9uzkDQ#m z9TLbzu1krJ+GspR7~biYX~aL3X3%6Wwc^%z-dsCd3mK*O3FQFP-M3XFX2@)Y-dim8 zy}A)PxTqf+aqITw>N$^veirw$2}AjIc!TtfXK1M80xkj}qQx;RoK&B@2yje)8Nen} z=)3tdIP2nEtF1Ce>a?B{I93}8-(I?~s>?s!C4L$EoH9sP?wL3edB$)fr>1W4VI%Jfe!Px; zgv?Q;>bu{zAY#@D+70qo{Vu36Z+S+(cV3OQc+KxuUJewqbY9aW3qL9f*TWtWrOBGC z&YEm#YiqL``R-aQl2}q3ZXY5Hteq`w(XFKP?lb=?zU+kVIw)i{QY|P@9|)|u$i{%G z2zsqg8%I(vbszlLhXbw^XK;K3??MV8`88QCFmZ5jFnC(TKI~m-d}*CwOzg>hB-aB) zo1hIE)9L?vcpFhzoqfNfi`|&i3na@dsC1*EqpwBsPZKtcMDTENDZxieAg)cd+%k7qyHSr0WcI2S-QB}isk;~RcxciiRBmR(r6&e7;-L$vZfj84J1F3>iNhY|MKlWZ+N z8Us#-Hr(F-Hh4ErFIkf0vl#qI*UlzyTBz|^BPwhM{R zIW%w0qF@zhJ1L!9V~@wYSEP}Y8c!KxSB(pWBd)~n{1W6qRg;b{t?d4QvoM>o_ zl&9sel*~Aea1EIYvih3Q0@k9+1taP!TS<%g~g5YeFgWwbmClJ8aC)D)D zmiX~3{cr9T`ZgZ19~fJD#qSGTL9+H(uZfG2@zQWvF=`dW>d~9rL3Zq9+5WA083geC zk&lP{YE%4R>()WkjT1ir0v3gw(!oL-`8ZyK*RZmQvaEMn(Zb{e9Oo~)uSKfN!<_^r z8WzgZ>7$T{WTXd1h6=p(4A#Tn5@&-h{;r|djquCA*t>0*$!j>yA$q7Fba!8Wo`dg) zo_${ZwxIeZQmWJ7>{DmOQtDh0>a7(-v@cC)tiEIX_4DJCxJx-Yf=dX4{UgC6+5P5Z zRaKR46N}1eT;b77H9hr@A3B3xQj_*>Nw$Q^w(?^7k%CAAso5CmbFf5phies@uAgG9 zHY+GvVl86a!R$1VlsUxfGAR{`zdW1od2-(vULkboeXq z^2Ot_QVaReKw4pIwY7?S4VG3`N->V+TH(c^8tC# z?(I(%U0Uvm`T+ zGSq2(QmT?vr)_ujL|kTX2`ELxmhtFUs%5LB=zjmgxs^22QRDVIoAJ&gS5+iF$OuMj zj@sDuaa@eNHUd<6sDud0!pQC;8T|v$gEOBJ46%5Ao(wkr$-&`@pXw_2=o83Xx`=k@ z_4{3gE1T>lwB2-qKqoM|X>WZcfifsjo-eW3$hnVa%qz|AprrW)TgbJ zP@>xtMeb0O8!xEKpN=gh4$*Ano_c5TW%I)+p6q6%de$N8cl1zTyE0MI!$C`fwZ|F0=W0pR3wm-L5=~1v>=&w~>kBO6qN6hzS%kir zfI>E1G7E$H+F5hc6+pHn22pmkt>E^?5Xj9+OiJLk7o=nc)Qe`A5XS6DNSGAP{e z@#`SC3ucb=Iiq4l);1gLF|9^-y$?i40}H=9Y(ZG@@K~?15XnbtcoWQ@zV)+ zba$6YTsL)5!~9sJR>nPhnvX)mSqt-JC8m6UTb_@yA0g%WP%&6{s}bWNY}lWKf15?> z&sR;RK$F;jn$;#k3wWr=_Jr$G6#POlSN0c6c2u3F6)edv3%(#xn0oWJ;5^TOtyE|D z1)?En0CNE>a2=`73?RylQ0*Ww-c8Jpq~%0{OIM#W9MZpdDFMMG1PbvKx=t&3}KFgg7rg2)WUH4wD_7qKKLA&KJ=P z>o4*Ff-ZP}vbplwA?Ukb$N3O*K=D9NW%MlO|D3xCfB$UR40!U>yhIuZHsnJrSpQii zrbY@|ngB3<3`S+D?`|H`9q-5^64)!DM)Sl{f1M~%AtB8!XMgpf(yrcT9iA_&7El0M z(ePIaHraT7NJ59@vCK;iH8o?M3s0X!r=_LsC?*GBqEHZD%IjTDDqpmE@#I4cNfh)k zU+QEackJvqpF(oSd&nJe@^K@`S-61$?pTrRIr;P%qso=9HOtjXNR>KGHPO|Ek4WMU zRWq?|)7@cBZD-1UUGMI1)GfarOT+Vd;knq@3&;B1>>c?SBS`$*U?tI{l)UMen9}){;o)Q6C#3n_w#7%A-^Ao5-${qO6kd2ai15=vJB5P(P0pMDefK zsi3DSb+j{ON)W6*7_WXYNd0??84#W6XIuqpksHQzH~Q&D;_l!5Bdl1QV%HpyC%Q;`V8q&rdtfH@ELO8SbEF$1;$9_D(=E$p_#Q zQ{rznubm>I<@VlQ<~dYk@$Rnk_fl3D)b9BB^$vSjQv=&eBaO#}YX1uSptzF^2nfFc z%JAkA?<1u086owY2sr&3sKPRfyP8}yt8!Gm&;B!xts;gO`HlTqN?Fh-YELbgl^Xkc zS4b=Jtgxh{-i_rOH*Q4T%;+6qay!^r<8&b=Zd!0iI~6YKfmxy3_e=Mk2NMdtl0*+Ty3c3Ey?dGwyeM z=p4~^#2h5M#5C`I00fuZWkvGrk5Zd2NE?nLNLNkmMgOj7a&lap)wPSw6P5nuLlSW0 z!i_G!o_pb+&Eg{@C`cW}MvlFl7P9i3ZM)c&SH!1&FrOi4-bO62P2}t)v>jXxolSF) zxOj6E9dHQZb@IeUT{ZosPmPlYHBEFc2>1pd39|o-+kx0UsCL)b?tF@LtoswoZB$E=kOIrk_6hkw-U{ zM#S;XgSE>hSCUt|gnDvmZG{{l9lvp*_uR)Vt@~MEGaG%Fsrnf2APqVGPqlyizA!+E zy`~@R!eL341$J2k(Y%!tsQW&YML<*;?l2$BCOLMf8H=Iv@R2mEp{*US%M_d19VV-E zI=E;$+2wO2bkg3w@SKUcYYq%EwRsQtb)L}`Nhmoy?boKA(<24ZmGUN({EKq*vIs31 z$>Eo!V9P^NAUUMK{1YKLNUe*mCC|P6HulUV;kF-gJht6rj@2G3ywDIY=N4?buV@Sw zFhS2%<2a2y4vMj^LZ zU3$hOBYj&Nq4j=U|5i2mF5#II@U)Hcz|XEs_Je}}sIwxrmv%|aL4O_p$<_!MN(9Y5 zop~b2NUn#wT)*!C7Pz=~KUruff|_10i1*3kxHygiFz*cpdMNvW`(z!+w)eoqak;Q@ z;|bLtgqZ>ZcZKjizPxhGmA-V=!yfnf@y!hy)D{8UgsEIc-N5-sgcn|gBIt~6@)2?b z@CcLLhi&?EROxbX!5zm80@I5x5>d(0DBJJ(YtyvmTjra7kx;YPUk_=;+nn#acJF(B z9%>4VRxj$jw;x>k4Ts@t&&w4=jk5jTrAcw=TDmqg9T2TIq`<%2+wY$D=(FXR8?(sq zJ4QBMEc-NeE2iM!;Bf!u!yAp7IQHA@U+?s;x)xcV!AS!F zYMzw48>?gdKMEy&6or9nr0aa|2c0mV{Vhc(w=KZ1^LbXm)SaL!SweI6bBe8%lcHQp zSQGP4@ouqz3p~0C0Ej-eC+l@fo9NR^e0FH%!^iLUIVb1l3~Yy}S;7ige7 zeCkhHWN_>o?v7->;DSCD_*MSB5lE~*ghu=QR7i34WpwTzXX}->M_g0~O?v$v z2$r*LUXK&{yJ3rc!d8Von@#A?VnTRN4@N;|Z@hH=?vE`JG-dU!)>}pMa zpPY;g-5+6;5^rJd;^K06FV&riGu_5Aoa20=U+a4YfByNmefe@FHg+Gx-_P8N|y z&Q~O`plQ~?gww4E<=wvsXQ-b`a@Tun07=9}27Bd>v6HvPVnS2Hq9 z$9E^@t|312osyB_g!X(dq0aXAUB$B`rF`znL9+HGsf$$hjI!yjIQBuZmSnM4!WWV3 z`HCHitY?`jpAk60pbq!9DK=Q&hWQ_LKcFox0BjLyjgf8EbQ2at|PuI#F+p&mDXy5 zNgxGPShtn} zA6f6>O~$QBN#IMh7RT!Jz7f!Mv2*)S4ikN~#G^!wN> z2f7)0IAbG+M=@d-1B_magpw%MS>Qdvj!?T~7QTDO`6jWr?_ zPH#66@+qjRt52+5fOs3riKEHR%2EX7B~H2j&SPBRx}Pc;{7n>`dHcNS3)`WTLG?!G zTp`Bra-n$0wampMgJth!LJVv-A5n3?O2XPGx(1oBxODP?}+Q$Ky5$sQf z6_d0l&S4wK3O5_OJ?=-pnqbC)8Xvk#(2zpkDS$#*XW$c1wV6J7x-wo~l&rBI4n=HB zmRjX;$hyx(6w-dn48P17RzSM^g&U{Zy6@HFo{h=J@AN7o6(ia*{>$~$49sb>I9uX90 zPyGQ2nVTQ_PDvj_xkQy6%Rj{spUxxYFX<|^Goko(>Kt`$(3>uCh_cRy;cUbqcQIeO zsFj1lJP#$#;pm}-4}EPOjJ~&c`1B+a&d@#2_;;HY4@D#C{t^tE!M!EN24H=93ov&5 zSn}0rD0H-oO@p}g%DG$aG)bpW2L0TVuiL)t_DdmxR$7Z6>D$YNQ&Zl{PxCvff^vfC z>qU*Fk^JCZa42ad{X7UEw0`i>K$C%KGLC$Ki>!BZAJmH=ii=pXdVuM}Mt#h|lQPYMbbn#U%M>d;8zp-3~9e_4h%&M0AJGWza`5cM4&`>ck2|>i|A<5Tk^D<=eOQxws3^eNZ$Dl zM)-rwjzWkuaI}!e6qA!%6?z&PZF%WQKPx50gAU12=-?U05?}#6pNB@&_hgqqNN2j zvA9ew1)?A)Qhy4gWU6srde1s`HSm-oNJk=T%%tGW^%cy4-uW9K$se=wUVNAd@)!Mk z<*)8;B*@gjL>iK(u;vHikg&#?I3wOazXv>c$DxhT4!y(V&d|rN82Ignh0C65AawrC zNim^E&>+ubJ*@||=PA8X>qzUg?h$Y|+lR%mkdJK{B%(i8C;$?zY)6Fbaqd0{%kdqs`4lC)7w_B8Dd;AUU-u|@RRN}Z9&bY z`PB>G`l1F^9lds_=^pmxT+XEQL$jGQGuMMffsK)1l7wLBFUxVH*o#cfh`->Nv z>a|Ev31;%I$8nuNsDLVLo!_eoM6f>r8ZP+DxS;Bl6U@=|`3&vLX)x53UzU4xEnNzy z8*-gvWvtjasT_I8$MVWWAD<9a2!2Nt{_h0_wib44r(&Q#ZRySdMKVzBm}?pQk$p4T z=<}p-ZnBojxQZhutg%-@yiTmIT8iNoPM}!O`YFoRmNyP|rXpC@El#6!(e;O0dODR( z8L>`8A<<$y=O!keoIrQ24}l@j=7eZi3^2HV`9DNh7( z?F!=Sk%LX*8rfKGzYwbZZ=ua!B{0j4MAi+6Yu<29iNA6EHBVE%pmx0jiR&a#ncKlx zb^#mMs;Fo-)vY`Wza$H}jE4GBLk+}v_{yUF2iW8h%fd}olXhB7_7Q0Lv-B}j$Qc?M zIeF}w;Bq44wIo{OLT`C#VfjGe%hMsJ{&k4K z5Y0&_i7oLs&sR=IDZw+alG9`_@7|yK)DD^ITZ?id-b>b^`%`kr49-pOj$b~&s*5gP zdE}ym=)CvNUy|{yeYQWR63>6N9{d9$1zEU>5O^?k!Lh`##h@mO`9ZY=9gH&!lEkaG zFh5o(wmCNK=56pGaj5ZNGSvR8l1L#3Ew#~Yz<|PV?#k8yWa+)Z%gRi|wf>)G*=P0MJ)>I`?xK#ePVG8 z=0_N{eNfoRqFo%EPUytJ>+uiYKit75LRJ~($JLg4NyL#=4)^CEHuT+{`RTfBR``3M z@UDpFM|pxoceM#vgS#=`AaQH8C>34C1y?6Ch0(*}#ECL)$!eK-f1HzB$Lgcov*S__ zU%w_^~_IuMbK=p%uJHalssJ)KaF+csv>B*--d|`Jm``QElm#Lo@mwevCLdA#`?fpZdZ) z#68nd8MO_ZAgfd_%VUFI^^U#i0hN1>bZ&tr_Zt(J)pOs4n5-3%UV^y|{>&n+3+~tp zY9kHDbsR=11l~k(D8{*kN@y%UC)-eotkF;8HG;=U%D$)PQ~k~)p-fVaJsN4o_Gc?o zk)O{V%q~;2_D}O(mKdt@t|0jR{!v$FC)aO|Ehb{pUSTGAJ(57BGdT9BVc%BaI$xWb z3n54e+sh1F{Kc9C83iJKkJ*^*o&nn}o&>>4jo;58idsjQB`CUOyf|2lOw4q?;*_Q) z2+FD{8juVCf0p+^d=C$(cFMr{XI%J>L>j=nf!Ou@XwbdR;Dv8Kpb9w)b}ZQMf_EY6 zu@d_*=Vc7O<2SJfbF@L_hKkKd(*O;D#;@)805Je+5xV~VuIzSTfm`U6EvF+(=PH0gj!970?z3LU0@qoHufB^`?-4lHxR86d zFg4j{Ylx%5Oz&hDD^?J4L_BG`qc?6YH9+HlBc-PuPm38Sp=KY$cd6qJuEI93=)HT& zHiYR4vs@}6paPQ5JC-9cBrsEZ5}IXg8ET#rgKMNHUp(tQ=`WjmGp*WvSTt)qhWs&o z6JagHAxaZ>8}?#e=hXbMGDF-EKuIds=jhS;B=KO<8LWNzGlYamKZBKs5Etn$R!ux2 zyLo}RnJEDXU6=?l-@m4#B8E-LL^ZzRNl5*uvB z0+Xc(gw0bp{iZQGoVt1nNuFgWUS7$|&=%T-(kfqsjURn*z7ksO7C)PUd_~e+YoOB} z4c;utg$)T3m_jM2{>7Q61+D8RI%@||MNY#gPch>KzDL>(zI9==(!-(IvbLE}bNAPJ zZS?Ntn0ezuc`ZWDzesbR@fv*jj&g%lqG3bF-8mgBl4}9DX1{8!LLmM+8d|x$2rY9N zdcTya@c2)jNgE-$7OvueT}(AxwU9j(Vr z4*SuAHH8NTEu8yVlS6f*Tit{G_H!wp{otJZ4$eJ&(XYL z{qA}45YpcCMvqLywi8#xZ*Z@Gbxxa->S_>Mx{X|THcHNu>1Ge!Yu0p|r$&HpdCv)~ zu&vsc;OQ%pqDjTRfFlIIvT`xktHc?WJB%L{d4WGD1dL$c2m%pXB#`s`kRW=fg@lt< zp&J$r%8`TwF|^O$Y}^BE19UWSJBMDW^UoF=<{*0@b5t7A_yLvZ+oQjaS^Z%4p$7}2 zYY=o{Jx?OG9!u~YC*K_*WDJ(bfJ)f?& zLy9|&b&|Yfb^0tZrvT~iruWT1R|v3M<_SP4fCy=1*!Ov=0$@-a1fkTq0y+)c5I6<>u4GPYYc(EYCzQup^o$YD|2VU+4 z=tg}Cll>NV=>xLLsYrPl78Q&zCox=oeZ$T!^LP>4*rbzkcc5F!Z_z)9A?vhVjpYa*CL zKn#kINn(r+f8Bi#W7CC+(&;oX_ydAjBqZ5R`-)N4Su0WW0hx9G>!*lE-`D-8%@1f+ z6*&HT2>Q*Ju8xkVu&^`VB#3K00AlM(UPDbl+^fa@6>z7i?CLq>w7-Ktv8SP&L^>th z#QaL(<<@;oj}&Up23W|~{dq02js6rt#|I-YuMTF#8X}9$TC*R@2ncyQfuqfNz`&y& z884T9u=xUfE;Xm+2V%$Fu+Jiy7PLDGZq^Cf1@lZWhun|Ru^78MIf+I z3YcDBG#SIpLz!WB=R4>yJYd%dlSWh(C|=1nEJHY)7LT<{ET2CF(`#{SvV;rLJ28Bs z;EkunZX6r(wRAC;qibcbIJ2o%U)&g!{`DTeBh7O%2To-R@iX$uv@j*XNp>%Bp*beV z|IWWqs??CE8SyMy69cg`c^QJ3fbbM2HovG6i7v)JJYIbwL$;!^T?PISvpDa3&+&`A$+ zE!<=!AEc&C)^>8PJ8d;9^>0}X`A{@4^F-18_giY6zIpLs0j> zN{cVqR!hOs&?M_o2`PLGhTZ}miG#Es7Kwf$+dp)h zO~1-H3mHUqICoJ8yLi;}>G0_=?A|`v5=s;z!zuGGv8V9zC^$;Lt&u*whZ-I;ABWE2 zJ$x5Y6oeI&SDy$Xif2xM;a~84Gm={$BUDP??}0q5T|T)}v6?ubnr$6E_LWkNk^yi? zlmNTxYD)$IWN*8o)P z@AZ+3i|Z|jD-x!Y%};;Djopgr=dQVI(eY9%3IESSmu?Gl^T(G?raWO%gXwIi%POz# zE_EKTMhfQ86N9x={wGk}YYUPIDk62^EYWmx*fgRAKwD(W6pk<_Mz-E+H`_eJx@s3M zO?FoY1w|x8*%_RctLAP(G$<`rxV3`w&;Be=ZzTZ%0cxq|_h+aD`2fIHt96DoVbA4$ z0*4_BInRjN!v(BmvmGe79UYQAARv;9Mplp=8XviKjd%!K$CGVr*(WW~t5E3*{5x`E zj|>ut;3s-tM4U3pwZ5nJz?|3)ozpohiu#I_xVSlQI#=T~e-1qBp*w|!?^v`ZuVWxf zH%SG_{)wZ)N1llTlEkfq7uZsB8?Dbx_Bl;vye@!|304@s`vi?(6ykCw8noIVGW!%p z;u0Y%`cy&+w)qXmW%osP9^F#4l#?IWc-5fa(yU6b5C)bF*4L>E7Xs$-tvR}&^C-Qp zlrF#Wi`fj>wb#(%`qwZGBj1E+#tRF1CO@5%a4RIlZCzb2p@htBe`z64M9rzm{#97= zcjNqx{{DWL@7SIgJy@&+05Ryy&T`6Vi54eeSH*O$Bvs9{0Vo?7a!MgnH1xzcaNtNT znrk53`aB6T!VB*W!GD_kYEqjZh%@*pJ%n$-OXxR9LZ;Q&1qP+jF;EsA$7jS#%BG@~ zatEqJpi#L{NsyB6!0axmBtvs%1E|u(-Z{4DS1d(#LxLjsfalkg^RXL+Ow=5H4^ZpBm;qkRg#4xBRoG&(d8P3FglaHEHz6`QGwU z0rM|JVRj~H<)i?=bY|ii=#9Q+y@R@esc8gU`1U&8Vk`5K8VV_s$p|X9Rf?Lf(woDY zHnQSS#L@6T3l3Og6RV{uHm$8{r-K8IwqoAMWpS^)>YG50%3S#P}n`wquv46}@k z5geGbc>h?+AVd!W=w^rK2E~ z-hQ1M#J<{tm`5gV(Rliog+u+Kir!< z%cNFRV7(jwgf zm7W)2rlgMA0p~9A)8`jRFH`{iL8MgG;a4R4xWZ%vGJEq(`wUNsalltfM!=?$JRg5~ z0gGzPOprDxMLr`@Lrsnh5k{x^8WkA_WHQO=!qc8Wf!0)*1>1d(UfW%`KH~wHH>ckg z)<0V@R*LB8>8TJFY+Bbf9xk;(JO*whsb$;1Xy0hlek*wllARKoRW;R4kvZ#0vCqD{ z7uX(^{G#fx9~=oJ{bJrvR-_t(>I|Y-gc4Fl^+B^2w(E^~=bvpMKk!bE4e^2L{-!^P z%w|b#bO~K`U+^-T9ZLZCS*OB6~gU=D#!OQ2@IyCKixYEmCi6kwr3(-D_4>kxxD>ZT=Puj&^`s+LsGI`P{6u@gxtpGkB zY1^KaV1$!+P^gq2gBZ{OUH02^pVl+qJmJfjP!^bg6QeRuEkgvab|{>IX7XkU;jkQh zDJ2@jAwD2#&!^gq&+UTYmJg;A@Un#-U_nrg0W_`_eiCN572+`1WRN0Gxh(Y(B0rac zIo`e3fORxY$Jq`BG!)b5{*adSa4!A@D8^qOFD4u~f?pO%5wblVJ*m>g-sXw>ZY2!< zm`aq(Y*!>qCQ6OaA2oPvm;?u$JgrMYc=d1Sz?tezEv7#YPf70@U=}XHx_Pno9Lxe4 zhV~-^k;L`Ln{2Y+#($nZZ-oYvN(_=WKW9oT8si92X}T0M@$m56DUuN9^L5uu2h|$f zm<-6Yfi-^OcpOZ0wEu8ypwT_!3NLFoO>lrX;K9R9S1B}+2IBOw0?ZbnmSdKYh(Zg4 zjo>T)&1Phb8%?j4qe7DhWb=eH?hjlcg1|r7V#r8?mPOf~*G z295c_#Gl56HaxzL@C|TF=mIc!7hs#~^y$dk9_j^{Opyqnz#e)vaC^)+XCTf&nAe1Y z*WpLG@~7ui6mQ}PQz*#Da#b>sa!$)t}`k5WZD|jU>vqMH8=vx5r9|LS`WY&y6Ik@?OI~!fgALXET(7@w_gCaS#oeM9pLV#u zTkbGi;1YxcuEjw;6b&jGhw%W2@r;R%O$(IgHkwkFB0ESRrMX!N&7 zKY^J}V@T z9upKc)fj&g!mNRqQ}HjkNgp8Fp^pR{t_etaHR4Wg_r=t{q_WB@Y+pIYbAJG}~ zzOq1tNgV$F9wX*s)5WVymwa2VcBK?dZn>ybsD0SXwGD8&bGvT4q1{t*?p*y@Iv-e) z_WOx@_Q7!d;fRYv()PrGnZLq7Ht~9dv$UcQnY?e8D?FeDdv1DB#$0mk-R{T4i@$fz zgoRvTsovjzjA6H;@v%?b`XCjt>*C{bXukRtIZDWwgA?qbl?*|O6(iK(7fo}uJOkI1 z5b-@V)p@6=tJ~fs;?81yVQn7yW2F<2TAgl*WG6JFUvzrvsjfsf^$jZ$jY`P)JX}7_ z5O$_<{U}hzchMr%P?@1w58~?k9mYiTAu9w^JoZrP!9rmEb9x*R%~$~W%a2msS>IPC zO#;rd4qsB6(TMQvv2s>C-*gmwhC#z6`NgC8@UJQ|X&D;(JRd8l`}32Gi7QE$puoTw zzw}Y7Ryhx(7QYO>#5|o&DC2-K%q4}&kLWIQr`qmHeEohHPz|f@!>9}+vGcF@Bz54g zL;N~;#EU4*%x)$ixRnU*f*JfCkdr7!hAduGCcu7Gfd%0|^G}sy>Sx0{;sJ zYI(Dz(XT4R)enNCbo-AU=ph|KJBK3)amm0ktX2BJLKc_Ji^Fu#X)d!&_JKD`Ed8O} z)P!a1U;3NpK3MX@^IWREfAB8>sYpM;!7@L8Zr?c~WHY-=;>pQghqTnb>M$hl&)mb} zu1G^lvh1c*`?FB0;CkqsyXs}=9X+AffhGhCZ|Tg*NV-WUBqT8W%}U3-DvghURrAjd zwjkCxn%zlJNCf|+-mzuv&2j=(jy@;MXnr^l(TCVa#4@L`_*Hke2hZ8xP58v;0Zbze z8@d#`E|Wx!)i&8qGeT&F2C)zmRQ?Um-2c@k^=IXDKkVR&e7%0#XFj@BedhhgGf**{NWX~qA191}N8 z^O%{hiV)|1$sv+k>fzhelzp1GA6k1y+GUO_*h|2>OgQsiINiT)JQ#I5$hK(-z+$|ci+-|~a<11YZlIV_GIg5%tn8A))Q17n%# z!3zq2$V(`$L!WvEcu#?3B^auwV~QS|Bpci!F~ay^+~1U2(Ux(<9*yPuNn`n+4frn& zX?*CS$;wHrua)H_j0;coa#B@KDcikYwnH^f=WNjHgk`2z-aW;wC+>CD{{4G?Z{)At z)WL4Pi;J`}PP`Tzw*_G3UQOlkLx(>clew0NjynqK_$u05~>ubGta->oL)(|7-y*2axUZ5w#&A_>q_PWQ6H~4&@=&$$Wk~{WL2- ztKt85n|>oWDOR;(aR zM}M9;7P31)0*u|)@>w)?<|QP``!>byOFvrCQ$RwQdx;}efr*QAbns~O2(HdCXod91 zVk!$IFj^=JfN z`(N+wZpg4TL#*7j>Uh`KL!Q74MT8+`a+^w?x08}xCj$gY(awF#4ahI_DS+o5M@XK2 zcN1Ed2@6(w)N%UqMV?P-=M7>kR^1|j@GlaH-%^<4f<+GMFWW=@{!dD_2q!RBJ-V}X zSbH*05fXUh^L0kz9nW&*2Fv$(NlCOp13k}VtoI}0L+whA9#ZA_XUR@55IrAz;%4E9 z_)>9C%I6-^RxzE(!>SN-I3$tyg_GuR4G*{rNI-t5apDi^y_0H$WG%0KwsZS$aCLT4 z6f1Z0XD=IY&Q_{ctUT$EG-~KFuRB`Y-t}3c6}#yX08H>IsZ22BM9J>i)O^t-K_bKU z@)H3i-`Vq$cG)msds0w_owA|o5LWuUf8w-_!Y9>}>_Zx&YLCpr z6BiJ|ue&uh7RMS{Hqtm}3g-b)O$%^D`@Jw-uA9KC3wB94up<1&sqe?BE6!b>NH+BW zMM;E{dq(Y{@4+4QM6bk#?y>jFM`UN1IHOM1vB3KnYA|7sHE-^a$O-!7HfUV^y1RSl z+NC7W`TL?}qj=G;O)qxhMNxSI=Yo&QPR=k>9(hAFI`}aY6Gg3OkYF?yaoA7Bd(*Ou znKcp2IRWP8_Cji3%|(_j*v{UTwa}KnLD-}t&*bsFlzaunpBvoW?>!%U+?nl>{dL#Ix~+A)e%9UlQQo$-?nwFx_8a+S zU}TLiQCyHPF8DJQ;7-bEt3EPHZ?o7%QYpNa!DHsiTrh#But8AD2?KJhjA`B( z@l_SkD)W#qmgrc` zd1&k_VxZ7P_iL3#cttr*Lau7ve>24Sa$cR*;HemLc&SUo`*jc9Rpz;smC1 zS3Ek4jfw2+qI(IdsiCTIjoW~!n>uAquutw@0q#FzcJMuEA}Eavex{zm&&LD<(XcPy zWWRSu?m6Y9N5q9FY<~0W0kW^hZMD*T8nDp%@zqDJT{eC0XCI@`mA%l6d+gjI^Gy2V zwX-&_*n@{PivU{VCM9SkbMIxoQ8a21oN!KMtPfXvt!Ur*{-&${j`WLODYr<-*iqPZ z(9!+Ig+)&{9Cy|OmuLkVmEvZeD;)Fim51SMMoSrA!jdmt+#?*;*wCm>76NwFc~ix- z7FzS6@7yKu(|vVI;XKz*;>v3)BM0`)8K%v-TN5{CN7Ff(_zP-(hU?)}KNX@H^&D z&h=N#pMv|$@pe$0Af7Ohf`pPWT}Y1>%gt14a#H>LwpD%B-8*~#`TG-?MVPtt%)+7{N&9%@1{c1i^4X!MBBWvI-}jjC zYvhvfwk3Eom3gs^)?p~U-KkeF48wQ55$tKYyk-;la=ufhEnu>7^`liN8Mz$lLG!WW ziZ3k1&E?LUXMoi;tbEaEyC&=5%Bx~-`xUQUdb`JfjD=#(=a@QY$uO7h59S1B4Tqhz z-Ha7C|4v%ElZVqevvJKkEN3W=ZeuGXClum-ZiFRSoR#gjkjLA_y<(vdbC`o17P;xV z)Y_wCHY6H@$!!v?BFKhZCQkrZNC)6?nVW)h{qSNJ>|E=UmdYM<>0ygggu08UFWAz) zeu}Kt?;|0P5>P}0+w+EB6hCj4k8KnV!{z0fwWZkCYe5WaOja^-f{Z({;`4Be&h2@- zA|o=x-o^N$Y{TU=rf$1F3 zED6mLWY2qjjMz@3$YVB_lkDNiSk0Y7ben?NF{(XUVc&vgW6jK$WG}TfZ$*aE!`UhfenYg1#5)_ZDt2R&3Uo8LV zxG4vp-IX$Ii9SAP({|@PzVx55S$5%j^g2|`+m_=Di#Z8)gYhKOKEb3ty4=Jx;{rB2 zrws1B00=(Myx<5kpe?3|X>H?xOx!osQxX4r=abyXjM(#wz;4R+NIbT&Pbi?bK^Ba4 za$-klMtIrv^s;N`XPj6jd%>ve%lWjEO1JQ)zpv`b1Gpb+FY3|oYAeI(HkU1*B6~=A{H~I4Yf3th;-gxx`+Oj4V~1->(L?E!o6wU-_v1J5y$aHxgTZ2E67PJBbQ&} zAD?r^Q}<*Jm7ixKB|&&PxXInN9+}RZ$7N@I;$<`cEPV9WZZOwF;-t2^EF(KnMAaU8 zvCn_jjh0DYL1NLO-*Xl(aeZ23lcXrx;opf(;JXy@XG?n*a+DbgF$SIin|!kIrE(<3zitIMD)!uTZ1OP5qzm zbnlGLVj%qG{m}q62^7d*lHnFNit4rF<9BWnE$_Ew!XCN~JDP#)A9Td5`l|@&H7J)_ zE@G5R;+`t9%|>gu!&J*p3&6iSlkDcnLmqT|>&xY?r~RK$0l?bg^5~c}UW{cLL7(+s zFZL34M{0}dvSnnmxYmEu3L{GK*)De0@;FKBIT0oMRc0=17IS?-P?aKO!~A>TH1jo> zy!QS3EHZXDO6X2+ba~{>?lU{39yytl)$A=p_kUh&xG2X37uTNp`)vavdA)42ktWSS-&aGGvq>50^MZ8?Sg`6UxuHw}woZT7f~RZu3)Iy^zBW^T zZ+C&iv%ceFwtFg8<+e#BMGt?+h#HLM;SSJUWm#>H>Z|^8KI)XfM1EQ{un#Q+{U#&=*Q}?@@=~6%gsQ<$>wm)a|V$2CyL9K zEu&Raa6_q}1igD@Ps5lCBx8VuO_%h-Lu2)s3ez~9QhFei|40(di!B84`bqD*L=glsVbpVmZp(UHN6{$a{-Rce4Xe4xn(qov zUlJVXw@+{eQ}mA&lc=Ux!0!Fo5!VfPlDyzY%zR|?lOsjFP!!xYF4~#Fsk->8z+8IL zZ3;1}L$}pSR(GxDUa^P1fTbOf=a9dcw&?dQCWrOX3MM2~aK0CQ&NQKkP*Yc(x|Zvf zwyVvb({iytMH=+96hN3cRw2@{vnm&-yGB9%9$z^G&*qOjl;z?QA5ZCALau=f2ln>i zvw&8`h2H%Sl2WzssT$$wi#+YFJ8PUJ-q7!>n-V>@=j1=nn7~}f6&SJbex7syxkL6Z zk|SoPf8-eK12*m&14Z5JH4_Y^aHS@*U2FBeK~&Jx^%CsxS8~#oTR+J!Cg1&Qw_m|M zj6dH^{JHf`wA!$84Sz7S)D2BFBC5WTwZHyT>K9L>2=fDFxe~W?`SS0NsDGA&4>b$G zpgak#3CGXE!Y7vO+7XC8TKSfZaaw)trKRfk;}(jq6{pjzDH)l$MjHJ&6&7$_s4uOP zkWEeoqj%7%HP8QfA#5!jGY{D{W=miJ&)I>U5JGG4QTy=1k)DE*!K{0)rOCuhD@#{= z3$I7!6$7=1dU#2~LOCOw*cbRi_5FT1nm&$x#z~Vj;$+OE01}dEP!RRzF{B`YlZlI| z!bJWvMPY4+z?}m6*(JO5z}oaNhDNN?AL9~_#W_V+ijm(81TnFjiVAi)FFtMG(E9^8 zZ|QuGCM8QvL$dZpk*Ax^$CM)gw|>j`3{eS#-OE(I^zNUB!F=EbO*$gO&RW0=bhUGl z26MHDOCIgOJ@KN2aFg>oWpY;^Nh!OQ?Wr%k^3&*o*YiqaR~Yo{2mS7UZ}y(DX>7yq zN4Z2p$WbykP}lGJ@o?y3!alP8XhGEfSLeLlI+F-{~t@bByJT{;gr z7yG~sCCkj`X!EO8bf;cj>I-7cgUP65a%(jiV&QjAm63U)1_ssIxpmuWfn8?bezAMTG{U#3}rq4J(eWWqhvX6plKY|3nMBhBM6lhk;meU4v zrofC-NnuOyOJ`Hgii-BB5!yyb?177 z_8liZvWbuz=9K}wST1bqbojLp=auR{)dKi^gF)~Le_uoF?MJvfwdxI%@EbqzfqH%3 zFxZt1T21+0smI^kjfE>0B}6otM3;k$`RGvITUW!<}F{lVtCsKJh{`agkHfbWKQPV23MvZ z`ha^$>1=6K5U$Oxn5o)D0f(&93Glx5=80k~MDNzvzwb?A1E_~Bm!{ymUK-H)S`Q)Y zRuKiZ?|On+6w%E>OjOImfv7mLOD^K1BgGEx8Tt`VW0_D;R zOL)f^2pT$f^cX=Qbwp2P6J&|#5)>dzx`PpgMyNfpoBLSP=pf!Dg%f>48lvc6PB~-K z^aW?@OhDzozoAyKVEtO-FZ^>2)^Cp+J0+IB8p6MPfjf-*M9OMfNrx5~EKSy-Emn%8 zixcWr`(giNtZCWWE$ff0GSbP)ep?+g%eLtQ_ZcrHKoJWG*G-z4-~Tq;3!QKkI|AzF z;q8^!b4x}xB{qO>NhuTY@Abqrh6s^d?I~!>T=V2v)Xnz@H@Jc4+rG9-&~6bM&{TpV zN=_$w*SBC=6bgRFdqvsI{$3b#sd0wA`lfjbuYO&y5c6Cte_niABb5b(*#W+G2Yb~+ zBnF#C0eELeFDJFD`VcP|S?a2WSLTLLR5#X5B?aCQ%BA`{b5|V9G&d_6K6A+U+NXU1T%y5QT^SldSW%iBi5CP)?$s!qTY1sL7zAEo{Nj} zqjsjxRYsAucbij%OV^Wf)CRITBEzAr|6ClJA-=%yB=;34QJN&hKG4#0Lj$p~Fd%no zP6j37g9+1!l}q;vMrpJhy$Gj}%sA!3EFF2fr2~j{!!4Ab^FQKLFD#id_R`cV!{Z9S zuDPG=j7Z^|i>F92IU#7@5wj(lu%TufUQ0XdW#dq=O@ay`s-$+8b`&pAxQn6~QL^VL zh#okhB20G$e5hEBbf(F!*<&0!I;bPwhs1loo=Psf3&vKkCy=)nyx#0~Dhzqw?97B} zB4zgE+Fz6_2;-U-jx*4*Ucj|zgo0^2K|_FbTb?FVC?lt&8&zcj|B5O1G;^HY341<%6eyU=b@qLDiwt0T<)XVaD#jhBufc6J2Aj%S0b! zPRPC{pO@`G_7l%tP4pr0buX0?&%JlfGAcjI;{-dAQpeO)R4Y?gv|15WeylIYR3~Qo zf^(*GXT|@yDbPzh;I2L~eqM-&+}TrxHlP`V@^I?<+)kk<_8U(VOt)uMj5f&)O{L}o z`!5&(m<@BGbv0P#h@tP>|M&z%Fy`Q) z!}qS5;4ICBnM1ex;i8%!D>>2tM9M0Cm*{hbe$%)nQU>{nTHiI<`^6=og4~Fno%A4J zCbA1iT1k^O{$p=458iwll7Db09GdX=#+XmR5X$RbNeqoVp))QX~K6Ey7*n;x**<-y`wNV;YjGo|8|y)**X{1 zx;tu)K$VAM&lC-B*;4$c-6>Bm7uOFDMZ^1(XBHF43;y1ev5}Gz+sFL$0dOqy=k=S%K)`BT@ezP%Jm?!)){ z1kz`*k~_NU%X#j#TLu2H$;<|l)AX)kBN5bkPPmLTbhT+E+_Dx=2Ltp1GE>C5$No58 z=h7kKoRv?UB5Hp2)+`N(i)97jhAGQCZIHBsa@@(!a8T>=>IXPy9-?a>ZVDfx%#&Q8 zYX{@hmGIRnGJNeqKd>z-Cz^9DwJ>?En0d`}=2rn)tR9o#dq_sg`tU|ZqfL4lyJLPC zhW+pZqE~XRRr+eOb(rDl8NMV3^y?B{N`!rWr7%dM<2EibRICH!5%*&%K&m)18|;5M zHF6m*r8dG1h;?Q;(sI`!F5wuNB~H}g8VVYSeR~CGlc)jdx!zCz;aSu|89J53tRN?x zG?9Wzk?9GYodJ{TOuA)p7D0s|cYHjf_!3TM;3wOTFa0yFAx^RiLIrjKG{BU!X#1RR z0%Sl`1y*pX_W~Kb_-|8M7L7Y;+uyaCs9_oS0q#4YR*nVQ6rHNJ)^K->I^ym75J9Os z35czWYSX`hVz)5;kgpc4^$!;SV*=$B$GubP^PMou=+(Bvkz2iVPe!`)krd6Z3(8EV zF3fUxphYB@*~WQbrkWfn$TE(sT68yNsgv~(arsiuhG?m48Xqk?&JH#+Q7{DvT|+ou zo`p-qz`({1U~NbH7kRbh^A*Sj%v+I!e66_m{1X@FW;+qI%VVKjyRCA3m)nEOsnVfv zxl(LM6MmS7iY`F>Y4?|sJnNt1Q$S+D=i8#$;eOE}CJ;8x=97vGgy)hBej zPxyA$k0qde`YcIXOWDBTS=bTIczNe=2Z#rzdSF5p=T3*>K;Ou!a$=UlS~0Ut4B=RQ z`BrT^XXa)2sp2C3kGB#2cLpq_JEwi7CJRqn@$&AG3b3;$Y-Sdw4*>3C=FhWtY)sxv zxi;JR+Wwt5TzZlTb4cnPh#qX*=D*Rp32oH@+^VT<{W(eV@G?YG!kzw|F2NtFd|)ZF z^H)xfh|bvDIXD2C^rPUgX(*R#)92M$*qGr}Lo=yNb8piMLLoaUN05!VmaWs2%U!oK zy^HYw>u`Stjl5rp+FDq)h9{}D9e2oHJ=5>?U0MW|+Z$)Gd8ZXZne3`KL& zOQkK;jcZ&nw~&Wa@dJs=1Oaq-7~19W8U$KY^ty&@YH4^kBwXdZbfd4@MtE>G^%6n6 z_Nz`5Yw|ygX75~d9fTckqGzSNcvx74aYcWxZa>KvZ86X&?y z;AumqkpT<_7;7ogeC*fI{9it3u=~UQHK7Rfn%Gis^TkKsJq1s@hl-2KlxBRMt7*T9 zXm&QPrloMW{7J``gTMJPxQOp zilISLl$uaVOi^;1C}<}^!k_uy7F}I4g?F}Sr`*=DAzL#)k|uRdH6&t=5)OQma=aW> zGJ#oZ)F!ne>jyCv)Pw(*u6dw@YGvhm%Ilh}Q^degOVhNztpy0p89?biKbqasKU$Dy z$6@N2>_cKw#CutL!v-c?~9N-{j&v0vx)66SC_&D z_{sP{FNT3|j~0=@Dm(bxl|24ZWxHXjF)cep2iW-v!qk<6nW54WL#G2;WKqA8;=i(9 zHj?B)E_hviomKFBCThq>Qru&W>)1%*#^BKG)88|pE&tO`n2)4W2(yBh9E79FG{i3XQ-?dt1^KMmZ`K>K**Ne ze8O;657ESHxqgmHlQ%gq8Oz>%$iTtWp}5Au%LQJcZJsZg2q zxznFZjBT@|PuavNp~M(hW=TKZL3Fx1w+_E=^|1@?i`TAR{J_yB)vKh^F|iDB=#0+N zh)2Pg7)b7u;(ggjlIs7x$(W`E;LAd?er6GeHXP%QrqWS==i8<=Nocy{BrZ*)xgvmPQCVK}*P&DVh3s}QXxpD^tQ>_vXQmJ=gJ&*Qfr5{lhlt>Gwi(H!{h5{4u z*wg|)8~&p#_RwRHxb|T4noibl=imx`!+_@N012t{_gjh25XAoogSQ1WxipDop!hIlU z{7;7=cKgKlxi3d%(01dy8|+^o@Z%+Z|!5}5?R02>3l+n@@s=GLtroRZ+K5;IoY(NiYUj%;ddK99dzDh5O(w!bx^hdWLbH z6BAcPCvoSy>tTC;K>e@r-X5i^TVfzu#YIZY7kpbE!Zrz?y%5i4Avb_FoNxv0rYt#= zH-hG*%=cT4M{UvY@|bn(0^+O?4hO8me`TI{B!*LkgKH`jv#k|mqUSLT;%4XvpCQ>a zu*|q{poJ#5*XDql)gT;2ub#B)+Ve#Y-PrafF1<|`g-B)anOapT*ipEJ3E9-l9h=wI z&H5c1tnK9I_(!f#mPXc5WJ`)9Yv1i@*^xycPRY!Ig3T{nqeKrsg?v4}d8eY4-L5M02$_pdTF&xKcbcfBb~y=>;JR1`Xdi94Hl4%d`)soe%` zY2m=rb%(;{uV6`_6dS{Tniw*X;j7QWx><}XCSat&eUm4LAiPS@>~EY{daszAhh-O^1_Cs(zSsO`)N+H4ksCE~d{c5EFw>7%6{G zjP2t*K3n6T7$Ie{tHwt7VhHD7ek#4jC4 z!*d`7a{1fP4kuh~e=tV_fxYIXAuwgSYUDRj; z8Me*R%ILnz3H;F6!fh9JxU)S2fq_l(M(43&r#SJwhPAOxDUk{Dc)HUn6%=9ykk&ii zXclkwd~-%tIqKn}As__55x@wS+CgAP8&CV&1j65|SWqo0NN@(p{WZptqb?+QTiZ8p z#YSFH4P}4!*GP4Qp~FUHMczDX%Fi`~cWb`Wk)faMM@rsIJaa-F?e!mMyzuG49CEE0@P z4=ZL9NX(y?VBjUFr}#REmMELXPB_GT5QImdl)C%FMBQCWNy%MSD8bY68+*Lfa83o! z;!u(ZAUvLxYqvm3EcmzU>^;zwy;Vq>Ag%cJKXBLTjD83`ctLjk6@g2I^fs3upf-fx z7sQLb!%a&DF7K-7BfGzm$N)mJq z<-*d;bPkfQ5iIQ^b!p)_r^jB7Al%WKQ@sQ{!HwT}5pS=2Y&u`^9KW|#Kj#6JMsX&-3=$ zx3HR-*_&uV!?E04M*` z*SepB?@5>x%<~fBl$gR?mChszyV+?V8`zLuE@TqN_fP7loy#aJyRpIErL@4^vNPMG8^4FA_~@5@Vmyz}sQ z$Z7f*@V*KHwCcRtCt5|VY`jJ0U=R%~m+kF>T)DQ@A6Sct zT%(H?)z`WImGsS$_4ziFX0SqY4l4&jT}SHC9n!kr3{!5@ZP@KAY*8G~D6Z)Q zGNPslDk(l*722hT+u^0~6_~ezPO<_jZw^WtCu_S`DjpeS$slSA4m=6puXM(rpXHUm zy7yybX)TKT3W|KTtz_t1nAn8)kcDDjD?KEtd3QMly*l=W!|v$cH}jKmHOpifDJ%V? zZf3VvexYw*&}jA6^1R(Yg4Dw6rJ1idE4@^^qBT|wvW+5{6Om?_8g94nKtbj-A5!i^ zQ@9xFA(n+z9*betF6=1VF}kKjxk7`nu|r(nwHgp_az;M*)?>omaI%{KN-Fn1Tky?6uB*KMBl|h$$=a@g3>i zCo0R)KuSaWA=1b@q7iyZGWSolOtgHxH^aF!A2xSwQ)kGbMq}Z=e%8xVFy+ zk6vYy)3EC!^u^{kD7qZ^j@ee6q>YHN|FiaPyEj}u)KT#gsYT7}kCgk%cGYnaEw|}e z(cs2{K(X$jt?&v25|>x#@1G;FfA@{Y0mTsAuqL~fPNhZ9hi*uX9*p{V923n)EejRD zjS~1DVte_Qb6PbyUan61CihmaX^YTv;~@W*nCdM@S4`3rq=CpQrJX_3XW1kRDLz}F z+v*{^zvP`wQTX^dbXI%6CIT?7g6aNFuW~MInqGQa{nMizm3?Xfz)M`q=@pFV`qFiRdUFSmQq>UPSDv*}73qHE5k$uVKKh9+-zIYbg1AJVGTXRPZ` ze(rYf1pm9DzU*?vQKfAcR?tLV9uJ-{uOzMXf<_^d;1{H$gba$FICz8wdi3yLHXMNq z76xjoX`B(f+5>dt@Co~? zb=!=tQ|1H@EG3)i)QgdpG|LEWdr&UGLu zaq>j__T`~x_DlI%yB3Fd20yFHI=^ngT<^TMD@*CY>vT(AV>ImT@}@$qhpTrnhmzw_ zJ{7rkNDR+9v|87>Y z>5Qf6= zUEaR=N14S-BQUw{{;Bt2-sq2-r8Hg&ake8DHxa)%Xr=8zP2>vhy{R-lRi;AlU(8M= z3Y!sfGz!n|&1e*i%!tdi@ws%yCU?idrYVjV3x|iBNG@_9ZBVkwK$`dbVghhQdTYM-{07aQJY~u*0@4&AfFWzn+GmKz_o8+Nw9v_*; zG~pQTTl151ir6-=+mHTGJ#1zoA(DmO#+LGchy+6;32uSNR;B9h?QJI;a5*rfEDUM& zFx;x5@B3zmU!i=6878PWlGlieS+I)fb*KTW5QgYh86C( zz2gjPPPDb>-xNvj3Qv3~3Uh`CVjHvl=jhikuWHYw$37c_;6==g_SlJUu8sp>3yV0T60jBz2eQUdioO8wXL^vsw|tfUCd+z}VGWydTQ(0MbfX*cZWr`@B6`WhSR+sxUb z`ANM>AH*ms+rP{EFtAmfR2IE+=(|&(P;z_RU&wm~th@ zAFjqu{n7)?Q5jw5;V*gH8O%48j8$X~Z7B{^wEC4_zH<)FhR8;1Q?d&K31xu(a&4(s za>(z=`w^_ahrWxgvu}VHQjM3f`sIk$oX*nH7j6dpt(e+m zFCVRa8d(v11HrhCZlzJkxm?O6B<`~hqQ^hU&Thk|XM%Q2;#JxddN(9&Jd<$Ox`eQv zN3x4g0iq3z09r&o?Vjkrlh;qekgMn!8-_NFG+3HyN)jkfpSz8A>Clf}I3($TrCRf1 zg_&njxVJZ4KAUB;U#Q|l9>`$baMdo_V~*{N3n9 zwaEL1nu0^4UrW#C9Y|q)qdiIdexF*)Lh6^$e9#RUL5vx3I@c7oBj;{qLl}M-C@E0E z1D57lP3`xBXd1a%=^33XLu0fkF8uMzdW&g~c|!*FAyM(7g{!i$A99j4QYI^Q1)f-T z>d8mf1p~a;No@z=u1o9EUzq~7bQ#1X%blEw`%710pAD>--o$k^M`iC$HMe53P$0qg zz>W41B~SR}1ev_bSE?$k^lt_?xN|{{ zmG)a|uqmx1ezbe{s5E3*wGHK?vpeVgXeTA~W;2U$Vd2Q8CtHt7!=usS5R&IBnBy-b z&9{A$53&!5^y0*@U&Xrh%K;sgSC!=@QnV?jZ0JLmezEt_K9NNy>Z3G8BAX-*IL<$f z@%1D@c>RbSpe0+6=Y`e^A|GJooGL;DOk#9Fm4I{Vdqn3!wcHHW8JKld z&{X4=KbO2xMuh@LF|1PuOqZf(;}5`9){Cd)ZZ#i}J*9kHOh);*`db3Sd-awSFJPWZP#TiWJkB@ zOV&arF$15K!)VKkqBT(5`$i8fb{f;}js~c1%S9YL`O)JUcRb%1O^Jz`l4rZvD9A5` zqiBySZhO1g^!|Cmt8=tE-?*l^Juj|sp%mZR!}U{kYx5IPp~)ksWS?Ld#9Ce>o5*|@ zQJkSL0=Ajn@J-WW&aa~w@)MdWfko}XpUBJu34&vgWk{;#Y!9jF*>rYgFvekp?_4M=e?D}2?U#&f$>gP$7}1LZku&wnkFe=vW`M#oil zZ3m#+s+Wjbi5|0IQBck;px1~3EPUL-^< zB*E1Ipq%MWEwz|6x6WN`OPJmd93z#GLrM9i*$TL9AB;2WE+iO0tx1>l8wgZW%s_1( za~o&i%?n$fFJm>w)S|u7={J~SPZ=jWVT}q!=VCYvni?s;g?Z_$q}`>!O0DQ=&fBQ* z>Zs*j>uD(uj;bdatJnURVIvs-b81d{X>f)1AesC7TJ~A4-)S)H*mo~Zpr2tYcKNul z)enhU`WKQI*R57zVd>;JiT-7v+N9uAtC(Hm$G37n=@b$z0n1zjY5+Qf-_4!6*w+_X zmYY%g4e57920G$Cc31ahlkb9|81`&Oy1GjJNgTb7z1YQXlr5IkEEZ8O~H&&tEV8LDo0@6 z{oT_4GvUc;F#&qyLcOgVw2oH5`?^oRw>n_tCH5NmWLQrOw3$$%^gq@4F7q z=Nu9nS$;@(L`ZkQE#iPMHRwRuZgv-B6b69feDOcjZXAuzR$~~(oRv0YTC>ijm$m5S9{A?IFsm85(=D^CU=8U3vHDaOMTeCT&95vfHsw zYtozP+98{2QFX&0G0PUJgW#P6`tafqfLdNEc)TGQyRn59(k)8deUb{LlHMmWfu#{C zt(f4cqo7+1J^ORGn*ge)AMTeFYL%^aX2O&!5lB{}*s}L&)9i9wmkw3T8IAp}9T!J$ z+p!H83Pb7&QL0enoEL?!PkIPUx6jp&&N%ZPYh(3P`U`>e#)(M3*j(j>$0)t;x5i z&8DuW(?;)K>0LBQP?q$D@Yxvwz9lR03v$Zrr4sI-sc56v_O?Z>((AFOt({I^@n_Vy zd8eJRM1GQ?fw}E^ZGH`_cQP?T=Kkg@EqSSKl^8`7N|4cNA1+GP1D&kt6xb04PM$4M3jFuG9TF>?tWZiz3W7bc;8Huk&ZOD7hqbng>zTr&fPsDQY>k zGoTLlL0ee_F>Jb{9}bCo8+Oys7pmokAP8Qyuw~U^m#cOF-Xx`EaI4Yf1-ehTU`et^%4W>`5TQHFV8xI} z4_k4-ODa&g78V#3PTB_0WJ=l{w|g&RwY_Tcu?jH!D@6H8tBMaWHF95Gy4KYi`Vw>< zsm(KXJ`8+Yi|q5|#CM5~45r%UN#FQRwa%EZm;Y)egTZe!tW+h)C412 zQ^ounRx=P!bAQmX@nw(RS0=#Rcy`w!Vp>U*CGWXrdcpN=^DJHO&dT`EVn_O6-D1zB zN0p&IZ)G<)t5gd~@(Xs;3ykvhN{gNtzSGHW(Ls8?0tetdA&~RU+DJ&Z?rFKDE$2u5bG5&(sfa zhC@$z7s&>9&1&Z$swjDnXJ7d??HR8hoSlQ32tR`HQv`aqjZLv8np#;N;v|bPQLPaW z$n3Qvy_>2izQ(WL@BaCN7>B9q?ZJ6_JEE_veBIP`aIwqJZ`(_MIKTGG*se#i@=u}v zE0CsedT)NfEa@WU!=yF3-ya?-M@!r09|I?AW*s2ryH~va((HXRk4#|pQ$K4D6vv3D z^UQznGte~f%NE@SE_+oZ%vUhsFG^!VqB3c|CMiGv1C{lSB9U9RnI))561^AZ@oWV3 zk+d(d-!C)iNP@9MyETp^s??@G-QIW61d&`%Kp z(9|CuIJA!*&wldqBc*L4x8E$NvQ2#!IBZ~FXYGxqH#-P?)soqg^rV$g$`Z*7=bB%n zD+qxy;ewCOMLYVo_4+TM0ylPWM-^Uuh$R29m<#K#m@o@l+>ybZ`r-bQ!2PY<>fWA5 z4sBQ~`MJ)nY&ZI#D+sQx@QZ8kNF6G1$qzZ@ITvnuuI-5n7nbGP)i>W|nuf?*TW5c3 zx~za50ed=L{rPswZh0rJMcGde$`^`Vokn~H#;GPWt1ZriGkBG^EE1(JzTKsyI@^H^ zl2^y}Rl?!*lFUlPTvFafY|E0op)&l>t1B}-3A)f@$%Z4rOcW(TQNliNzEHa-Xi*hi@Sm; zk@rh{_Loi(P2s9McEk5-S={9hKLCEYpiBBsD@>Vej`_kp$;@JC58tm)1E`6bhd z6Y^+^zkvk%TISr6FP(}ZLvDBbltP?8j-aM%BeO8%+hD=VZiATN<3)*gV_H3{y{>jC zmhkBQ9D%4;06w+mX}NmHE`S}%(ehO@qwcyjUk|J+VRYaO8-g=bFt9xX_ay8xK)h+? zkwx?Bm6=0~eT4$wpi~rx6%VcH^4I86)96ZnIkdQXDQye3Dd?2%HvzknJwF>2+HcQA zl7I%kh!TGhm_)GF_K`B{v};n&FuozvG(BgFeq&*I%_BMNG?{QR@a(#Z(*p>)<0U>j_^o_368(vea+wblM zK&%|VbbhGw-JxJ^jIaZ~Z0uu+6e668hJCbf)B9x^k|?Y+XCaD2Tm?^^+juskJ8LA< zXL>=XpOqQSJ0SotfoaiuuV{e4KA&_p_i|rig=PQsE`E0de?{puIuPEeSZYWFYpM>jPKmb&Q*?fTdoW?LSA3qG-Y-mjl_rC zef9SL^<2)OZ)9j3>x{8NwndyZDSAmNAvwe?9BY9(-o9)N0Ou@5JBV z7$NzuU~MwCulQdpok3_h%H|m{|9QuM|3FWJXs1NGk7*VB*N146@EO8TF=iqEdTSYL z+%KyIU|adW*FUyb`TzV_3LLO_xr;jglD)t0#heP^c5@X-`PW;F`R*huh^j?Z{+K3# ze;r@!M0{qd$vL5a?cLv<<~WLct6lnuFJpf7ua{SWhCRVFJWBb0dut1Xy2iqfS;>R{ z;|s?x(fB1AzeNA15RGf1aZNOC&i)$0{}Pn{e>G?H#};Pw#4O4SY$`Eot@o~YYtvA< z?tdBF1{Rj5SEC5b6f5K4D8;$yE3m0ZYaMO?fFun=nH(^`~#-(6f3dW^iTnfgeU|b5u zrC?kN#-(6f3dW^iTnfgeU|b5urC?kN#-(6f3dW^iTnfgeU|b5urC?kN#-(6f3dW^i zTnfgeU|b5urC?kN#-(6f3dW^iTnfgeU|b5urC?kN#--r@7gC@s#&IllIpfJxsVfA{ OZMfWYS+<_*-v0+Ww<&@E literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..21564add --- /dev/null +++ b/docs/index.md @@ -0,0 +1,17 @@ +# AutoCat Documentation + +For full documentation visit [mkdocs.org](https://www.mkdocs.org). + +## Commands + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +## Project layout + + mkdocs.yml # The configuration file. + docs/ + index.md # The documentation homepage. + ... # Other markdown pages, images and other files. diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index c3592594..00000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=src -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/src/conf.py b/docs/src/conf.py deleted file mode 100644 index a182ecc6..00000000 --- a/docs/src/conf.py +++ /dev/null @@ -1,55 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys - -sys.path.insert(0, os.path.abspath("../src/autocat")) - - -# -- Project information ----------------------------------------------------- - -project = "autocat" -copyright = "2020, Lance Kavalsky" -author = "Lance Kavalsky" - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "bizstyle" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] diff --git a/docs/src/index.rst b/docs/src/index.rst deleted file mode 100644 index b5c77c5f..00000000 --- a/docs/src/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. autocat documentation master file, created by - sphinx-quickstart on Tue Nov 24 18:52:19 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to autocat's documentation! -=================================== - -Tools for automated structure generation of catalyst systems. - -.. toctree:: - :maxdepth: 2 - :hidden: - - module_reference/index - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/src/module_reference/adsorption.rst b/docs/src/module_reference/adsorption.rst deleted file mode 100644 index 09813f40..00000000 --- a/docs/src/module_reference/adsorption.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-adsorption: - -autocat.adsorption -++++++++++++++++++ - -Tools for automating adsorption on a given surface. - -.. automodule:: autocat.adsorption - :members: - :undoc-members: diff --git a/docs/src/module_reference/bulk.rst b/docs/src/module_reference/bulk.rst deleted file mode 100644 index dcdf8ef3..00000000 --- a/docs/src/module_reference/bulk.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-bulk: - -autocat.bulk -++++++++++++ - -Tools for automating the generation of bulk mono-elemental systems - -.. automodule:: autocat.bulk - :members: - :undoc-members: diff --git a/docs/src/module_reference/index.rst b/docs/src/module_reference/index.rst deleted file mode 100644 index 7a6ab439..00000000 --- a/docs/src/module_reference/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _sec-module-reference: - -Module reference -++++++++++++++++ - -.. toctree:: - :maxdepth: 3 - :caption: Contents - - bulk - surface - saa - mpea - adsorption diff --git a/docs/src/module_reference/mpea.rst b/docs/src/module_reference/mpea.rst deleted file mode 100644 index 47e571f4..00000000 --- a/docs/src/module_reference/mpea.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-mpea: - -autocat.mpea -++++++++++++ - -Tools for automating the generation of Multi-Principal Element Alloys (a.k.a high-entropy alloys) - -.. automodule:: autocat.mpea - :members: - :undoc-members: diff --git a/docs/src/module_reference/saa.rst b/docs/src/module_reference/saa.rst deleted file mode 100644 index a1b0396f..00000000 --- a/docs/src/module_reference/saa.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-saa: - -autocat.saa -+++++++++++ - -Tools for generating single-atom alloy structures - -.. automodule:: autocat.saa - :members: - :undoc-members: diff --git a/docs/src/module_reference/surface.rst b/docs/src/module_reference/surface.rst deleted file mode 100644 index c5963b52..00000000 --- a/docs/src/module_reference/surface.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-surface: - -autocat.surface -+++++++++++++++ - -Tools for automatically generating mono-elemental slabs - -.. automodule:: autocat.surface - :members: - :undoc-members: diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..843b8d31 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,36 @@ +site_name: AutoCat Documentation +theme: + name: material + features: + - navigation.tabs + - navigation.tabs.sticky + - navigation.sections + - navigation.top + - toc.integrate + logo: img/autocat_logo.png +markdown_extensions: + - pymdownx.highlight + +plugins: + - search + - mkdocstrings: + default_handler: python + handlers: + python: + selection: + docstring_style: "numpy" + rendering: + show_source: true + +nav: + - HOME: + - index.md + - API: + - Structure Generation: + - API/Structure_Generation/surface.md + - API/Structure_Generation/saa.md + - API/Structure_Generation/adsorption.md + - Learning: + - API/Learning/featurizers.md + - API/Learning/predictors.md + - API/Learning/sequential.md From 6e9b00d80b9cdda61f14b7e15afe7eb32d579f17 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 2 Dec 2021 17:20:09 -0500 Subject: [PATCH 130/239] update icon, start adding user guide --- docs/API/Structure_Generation/bulk.md | 1 + docs/README.md | 38 ++++++++++ docs/User_Guide/Learning/featurizers.md | 0 docs/User_Guide/Learning/predictors.md | 0 docs/User_Guide/Learning/sequential.md | 0 .../Structure_Generation/adsorption.md | 0 docs/User_Guide/Structure_Generation/bulk.md | 43 ++++++++++++ docs/User_Guide/Structure_Generation/saa.md | 0 .../Structure_Generation/surface.md | 65 ++++++++++++++++++ docs/img/autocat_figure.png | Bin 0 -> 461463 bytes docs/img/autocat_icon.png | Bin 0 -> 7250 bytes docs/index.md | 17 ----- docs/javascripts/mathjax.js | 17 +++++ mkdocs.yml | 37 +++++++--- 14 files changed, 192 insertions(+), 26 deletions(-) create mode 100644 docs/API/Structure_Generation/bulk.md create mode 100644 docs/README.md create mode 100644 docs/User_Guide/Learning/featurizers.md create mode 100644 docs/User_Guide/Learning/predictors.md create mode 100644 docs/User_Guide/Learning/sequential.md create mode 100644 docs/User_Guide/Structure_Generation/adsorption.md create mode 100644 docs/User_Guide/Structure_Generation/bulk.md create mode 100644 docs/User_Guide/Structure_Generation/saa.md create mode 100644 docs/User_Guide/Structure_Generation/surface.md create mode 100644 docs/img/autocat_figure.png create mode 100644 docs/img/autocat_icon.png delete mode 100644 docs/index.md create mode 100644 docs/javascripts/mathjax.js diff --git a/docs/API/Structure_Generation/bulk.md b/docs/API/Structure_Generation/bulk.md new file mode 100644 index 00000000..80e0ed2b --- /dev/null +++ b/docs/API/Structure_Generation/bulk.md @@ -0,0 +1 @@ +::: autocat.bulk diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..7b781b6a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,38 @@ +# Overview + +![autocatfigure](img/autocat_figure.png) + +AutoCat is a suite of python tools for automated structure generation +and sequential learning with arbitrary candidate metrics for materials +applications. + +Development of this package stems from [ACED](https://www.cmu.edu/aced/), as part of the +ARPA-E DIFFERENTIATE program. + + +## Installation + +There are two options for installation, either via `pip` or from the repo directly. + +### `pip` (recommended) + +If you are planning on strictly using AutoCat rather than contributing to development, + we recommend using `pip` within a virtual environment (e.g. `conda`). This can be done +as follows: + +``` +pip install autocat +``` + +### Github (for developers) + +Alternatively, if you would like to contribute to the development of this software, +AutoCat can be installed via a clone from Github. First, you'll need to clone the +github repo to your local machine (or wherever you'd like to use AutoCat) using +`git clone`. Once the repo has been cloned, you can install AutoCat as an editable +package by changing into the created directory (the one with `setup.py`) and installing +via: +``` +pip install -e . +``` + diff --git a/docs/User_Guide/Learning/featurizers.md b/docs/User_Guide/Learning/featurizers.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Learning/predictors.md b/docs/User_Guide/Learning/predictors.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Learning/sequential.md b/docs/User_Guide/Learning/sequential.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Structure_Generation/adsorption.md b/docs/User_Guide/Structure_Generation/adsorption.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Structure_Generation/bulk.md b/docs/User_Guide/Structure_Generation/bulk.md new file mode 100644 index 00000000..4022a951 --- /dev/null +++ b/docs/User_Guide/Structure_Generation/bulk.md @@ -0,0 +1,43 @@ +`autocat.bulk` provides tools to automatically generate mono-element +bulk structures. These are structures containing only a single +chemical species with no vacuum and 3D periodicity. + +Multiple of these systems can be generated and written to +disk via a single call of `generate_bulk_structures`. + +``` py +>>> from autocat.bulk import generate_bulk_structures +>>> bulk_dict = generate_bulk_structures(["Pt", "Fe", "Ru"], write_to_disk=True) +Pt_bulk_fcc structure written to ./Pt_bulk_fcc/input.traj +Fe_bulk_bcc structure written to ./Fe_bulk_bcc/input.traj +Ru_bulk_hcp structure written to ./Ru_bulk_hcp/input.traj +>>> bulk_dict +{'Pt': {'crystal_structure': Atoms(symbols='Pt', pbc=True, ...), + 'traj_file_path': './Pt_bulk_fcc/input.traj'}, + 'Fe': {'crystal_structure': Atoms(symbols='Fe', pbc=True, initial_magmoms=..., ...), + 'traj_file_path': './Fe_bulk_bcc/input.traj'}, + 'Ru': {'crystal_structure': Atoms(symbols='Ru2', pbc=True, ...), + 'traj_file_path': './Ru_bulk_hcp/input.traj'}} +``` + +In general the following structure of the resulting dict is generated: + +`{SPECIES_bulk_BRAVAISLATTICE: {"crystal_structure": Atoms, "traj_file_path": TRAJFILEPATH}}` + +If writing to disk structures to disk via +`#!python write_to_disk=True`, +then the following directory structure then a similar organization is maintained on the disk: + +``` +. +├── Fe_bulk_bcc +│   └── input.traj +├── Pt_bulk_fcc +│   └── input.traj +├── Ru_bulk_hcp +│   └── input.traj +``` +where each `input.traj` contains the bulk structure. + +**N.B.** by default initial magnetic moments will be set for Fe, Co, and Ni, otherwise no spin +will be given diff --git a/docs/User_Guide/Structure_Generation/saa.md b/docs/User_Guide/Structure_Generation/saa.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Structure_Generation/surface.md b/docs/User_Guide/Structure_Generation/surface.md new file mode 100644 index 00000000..5f5304b5 --- /dev/null +++ b/docs/User_Guide/Structure_Generation/surface.md @@ -0,0 +1,65 @@ +It is crucial for many heterogeneous catalysis studies to be +able to model a catalyst surface where the desired reaction +can take place. `autocat.surface` provides tools for generating +low miller index surfaces for mono-element surfaces with a vacuum +in the $z$-direction. + +The core function of this module is `generate_surface_structures` +where multiple slabs can be generated at once. + +```py +>>> from autocat.surface import generate_surface_structures +>>> surf_dict = generate_surface_structures( +... ["Li", "Cu"], +... facets={"Li": ["110"]}, +... supercell_dim=[5, 5, 4], +... n_fixed_layers=2, +... default_lat_param_lib="beefvdw_fd", +... write_to_disk=True, +) +Li_bcc110 structure written to ./Li/bcc110/substrate/input.traj +Cu_fcc100 structure written to ./Cu/fcc100/substrate/input.traj +Cu_fcc111 structure written to ./Cu/fcc111/substrate/input.traj +Cu_fcc110 structure written to ./Cu/fcc110/substrate/input.traj +>>> surf_dict +{'Li': {'bcc110': {'structure': Atoms(symbols='Li100', pbc=[True, True, False], ...), + 'traj_file_path': './Li/bcc110/substrate/input.traj'}}, + 'Cu': {'fcc100': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'traj_file_path': './Cu/fcc100/substrate/input.traj'}, + 'fcc111': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'traj_file_path': './Cu/fcc111/substrate/input.traj'}, + 'fcc110': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'traj_file_path': './Cu/fcc110/substrate/input.traj'}}} +``` +Here we generated surface slabs for Cu and Li under the following conditions: + +- for Li we only need the 110 facet +- generate all default facets for Cu + * fcc/bcc: ["100", "110", "111"] + * hcp: ["0001"] +- the supercell dimensions of the slabs are 5 $\times$ 5 $\times$ 4 +- the bottom 2 layers are held fixed +- for structures where the lattice parameter is not explicitly specified, +their default values are pulled from the `ase.data.lattice_parameters` +library that used a `BEEF-vdW` XC and finite difference basis set + +When using the `write_to_disk` functionality the structures +will be written into the following directory structure: + +``` +. +├── Cu +│   ├── fcc100 +│   │   └── substrate +│   │   └── input.traj +│   ├── fcc110 +│   │   └── substrate +│   │   └── input.traj +│   └── fcc111 +│   └── substrate +│   └── input.traj +├── Li +│   └── bcc110 +│   └── substrate +│   └── input.traj +``` diff --git a/docs/img/autocat_figure.png b/docs/img/autocat_figure.png new file mode 100644 index 0000000000000000000000000000000000000000..91bb4e90cfa52b49af383d1eda12e84c29a6b445 GIT binary patch literal 461463 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%0A<(7Ta#wCTJJ>w;A`Faf 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�Xq0Gcp@j;oC4b%NH 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 z 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ΠSg75xp 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

ofW%P$!|det&tw( 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ÐAw#c?T! zH4TvJi7Br!C(0(=fXy?+QvsMRAo;)?_`Iig2Zw4AU6{#>BL~%s`BB>@gII#lD&@j~eKZUDK zV|^4~qD(E`zthFb>q^5Tq6vf`PM3xL$7_v$3K;D2DD~HJencxEN5?&>^^e1|dGdBZu*G9Pj)aMjv z0y&{%lp)W5hz%gaj%yKkXh%QESZ99oKSqPZZD)MHis^&tSk8C7VJza&Sp^)Xuq-eq z%dop?R^%COP9;)_jE_qNu*BZM&o6mfiGm zgmWQ;Zko!Hn%iAvgkfmS!nsl@zP6|*rmW!x?eo$qivwC1|El_ZP88 zYqT%b*1O65?;nY9ejk-hUa&^}{4sU8(uWZX;}JEu!^BoZSVT-D6q8gCFOm)%A>i|6wx>NAFS$&+|aAvw4CGW^u>M^T6>o)c;HGO{hoQ--QYt zDe8go(Kv*e&~}j-3Y^;ZaOkxjs^jpBLS!Iy5;90}5$h)$F|>5|e|_l7z*&3h<%+uC zn};;b9cW_5QgVz#5jwzC5riT@kRU;t>=F(k?ExsP0?yr3U@dqcCYzy@uP1r6C?{Br z9lbs3($+6?n3nzzDzD_)8p?EEhv#p}ZX6^rM2B#zSOH8m-CbS)+m0l+Hyz0aMW7ol z2_@AeIUO8@hu}Np5)^Hom-5jb>!OTs3R+*s%~cuxQl3%$CuOM`pe zZH#q`;I8f%6Qr^1HhMmE_+n|@bg1=& z6{t{L9P#~cT>qak@G%JL-LJdUU|>F9ipYv6xgjEo)Y~Jk4MgBYiSRl8js)GF;tIt< zQLbC(grG~mnHMO^b?Hc!3N7M+Yh-J%)7Gn?xNZ;UlyPy zGJcIyW84$e`|phUUu^Ts!|Wan8gJBI>6>AIpCRQy+**h2;pXGBTp||s7P#4E@eGGk zVpkT2H^mWq+%zV3TdDSpZq83pq0ZP5ta|de1;9}07b5FF$p*C-pZp&p{Xbq#=_5>B zv#;Sh6YV|;c_1SCVo0DCw{LIv%@jMJsH&Q*k#9TkBu~U)TG!vOZuQ&>$?#`M z)m-t$sWGW|XaBzx{hcZzGMI*#B!V_>A%jIq|SGVM2vP2DPzKnwAzSXs=F z%3WP%e-!*AcPaqrdd-zE;!{dZE|Epy)1j={QG?` zul`HMzlh&WD(-8l_+I-NK0Pf5nX-jOKOdg5E6dTM(>07wPNp!&!6`#Z9j8y2o;yr( z&f@|p;2B_-gjw60+>-u(bjjDmkm`47DZ?s%NTFaMF#wO|7Ns5Og6W-%_keF&TGg*U zlzxHcpI9GcWwB4QU*o_6p!n+T`}ZMr)*vDiONRe&Mr3`~>0g|oCxlL}8!(<+R!T2a zj79yAt`^SE&6zYOIxMS7%r9i=~j|DI$I*yKYpv%M&)qRjiZ&~=h&fbR$#?EIUE$Hb` z$vJkq72dd8fra0b|1@UpbBJ46y>G&96_}pOs+Fx2gj$5$LPEAn^-WEy*xhSw_xDYQ z${$2~2Uk0nU8D>SlTEWQZS3vX=;As4{Gt1o7`#Vi2+)Ys+^c5U_`v&ciFj7K(eD1* z>NeMl^d{U5oqgQPF9A>if`pk+8V{d&s5N@D$$*-BgGH~B|3-;zCkUQl_^RlQ)tOpxoUooZ2#MP|4(Av z6T`Tb>9#n~q=CWQlLcTf_*VJB1Gt2Z2p88+s`4JC^Z1{=$4S7})>c{a;rnR*_}|`z1`b~$#a(7H8IGlAJeFhsxFTo>ja23^Rlp5Gjwu*V z59=Y?1*HD}60KO&7&76;@d66M%Wqd+P8{L>y7>RO`oH&F>EOEDpK9N%jI7nT3X4W> zY0s{#q!WMr+0DHw)%C91)jiy#Aj1K(n|9fwxC03lDV~4R*D!{%o1Cxnz9f|9Q-oLW z4ym{&%aE_PoJ{Pg`q++;3S{U_dHOR_yDU~FfbqjIxaePIKx7(m%a8rb7ZW0m-K-uY zjlvbOq93^iTT!vB@9hB={tVA+PkcHrGj(YM+mEYYk0JDPy09!wMp8UlRVs#%@wcC>Vy$Ou&dbwwnm)y8$8^ z4j$6r4LY9s5}uE0#JSL&jn^>y$%RD0xDVz)gPrSFE3hJk(nt^D&+YiaAs>O$F4c6 za3?J-&BWT8X^1N99ozrDR46X7kofQ6zXHK6#6&6>Oc`qxv=$|x1h&?$0v8uo(prbd zVzRBXtLy4@g~ME*Ed(|0cB=<=*5=(J#RV1WZv2**vHh{U`)_Svha;DAcXzk;p}r6G z`*O9f*m7AbJ@X!o#G&mwSrWDQzKCTr*+s}TJ2*aA`UHG=-z+6}lFYH?LEW*!z1`ya zPnQD)4GEfkbr9x<7Ie6M&}{QR(*<8TcHcX;fPHjy+Xy~0R@7PcQd%e zN(Ta3lV;xZ_zBkArxZ^Z)_Ed5axLlkYVK~|YYy@AdB)1Q2l`y@LH;M5n2c2m4fRPK^eXfZNZW9m=l4$6uBTM!M`wGj}^ZnpFh_+gGVMc z;BgE6yJx+i5erVO%UteYs8{Eyjan`ps>|3s_E?Gi8fZXe+@!zY1`;;k%A=lw4kmD% z;Pmz3+I`Q|*By*j4-ZRh8pnfYPCvjsZb_(2;Y-ZS%*3B@!o|UDYp_6b1ZoO2pgn@# zsRtG2bg?1W7dHM|M@I*(YK(;SUR4`QMnk5gmW?&mZ7m;JKB!V%?TBxCS+=%z9{kPC zVe#)EjF@8X@YVmC1wer@#9l z59T~yx;@cPBu3|-qX;$;Edr|YRD2lZ+M9E(3;YwxuNjrR8Ozk;R0rt|Mkh~6uT4zj zH<3*#@B2dS)USjS*6~6!1PF!t?edL7i- zP1bOC!R;Z}Dv%O=&uC>CNlALTlcX3t)}-QQO{~zy42y%fk5sjPSFu-D$mTsb`Y#2X z0|#V_J#>_x#7%>J8ye_!Y>to{x4sWyReLv~eN*qO-)8-)3U0z-JTjiPEh+Ded`&tt ztW}*P+$=N+ zk8Ugvr5;t+$eQR5z+@@(-m-~ltkIY*^;P})8xNN=DzTD+8%;5Z5x^)z)MQ-;4U2;@ z83Xks#ya|I3KutJAXi{?DcL(eTZv`|)+@Iuim;Xq0F0+O^0v2FMocQ%KwkcQHD|f* zs-E}Uu->31K0dL zWk<(MsRS-Yw1_sPh-jg3U3>i4L}?7mOl2pK=U~5}?olsowZD=&EGLiNi@jU%&(tDxq0gMRy_X@ovC}5ruL%)yo-7 zTWp>{m%zDTfBeG+7l%1nJ*zgTC1`Iuz}~r0BLS#qXStM-5w0wP&$doLB3HTTZI~s` z=a2bGt`!K5COcH*Tm4pkIa@THcB@1P7m2T8?xVu(diA8Yyivb!Z24P!hY4U1b=NTL z_f}a@RmNozdZJobe~K>H7=dp7`}f_8HOIXP2SG+^ttfmQy;>?NraRv~4IKtLHS=?N z0y36H8~bkPTI3J#aM_RV_CjfC1_1EHSQ;vG)SZl@cn6;Xmkb)I6f#7w^g1XqAA^hz zN40La^LKDTv5kQWbK5+|a6x61U=|Q9m4j5eK%IfRUiQ&^Yfh~yA7HxKkf_j}ert`o zHI{R7^=EdkhOI~d|6Vqmqx-<}2)gAs(bmyee5O^AXdFqn1|z(z9VHB;YH7%O_s8OT zem_HsgN*c8=Dp{q97jiQ-1lbxv|mF_o$m87n{L+)d5g1;ylE*jFUMS2?Wg0PPT6|k zyz%x>*O}jvS%QN6#x4sv9Ew>{HYarNJSK#0C%A0RG|-Cg7^8b{Zjw2VQJ@|rJQayn z8DcIwCpxrM1d>NKoTpVMibydF*QtJ3xCNz-;E@oo05Y<~Mcyh09 zq9Qn)u0>z(Vx(N?8x4ViHCY*@votrKH{&U**jSu1)F0U2xIg=36Vxgs^ZQE=KRb6q z-BP37+|px3X3WZE*=+GoLxf!(gE z7^s9R92}f&2eb*3EE|C2`RxNf32Z5?U(kTgm3*c# zM&RTGO^Vbx&EL0&11`2~_V?{PGA6O!#}LDrTzCW((Vfqi<~PBgg$4h1iX)a(|GHBg z%$BDi^KzkRarc;mZ$`x>70$x8SdM>r39+N8sO@Vd1dMO>B<8aZC{uI@CwW(GwA!_H zxmc-jw;Aps2EYoiZwRnwqPnvPr`x2x=(1-bI6je2FC&!I_t61OLwR|du@kgBKt_g@?240~ zR|rSk@!1mdhg-(le4)Uogg?Y6*9l4R7zE-yZ`PU7fOrxwq4XN7lOlH=i!n6M9&Pc- zE)6%oYZ7OT0vin;SeYkjG;O144H&^g>7J>dYU*?N@Eke|OW6tLw(bR!QoQGv=Vpyc zT_{vZO^30RG88uNi&joNDCscDvWh0ZzwRVmFFzOTeYGX`Wxmt=i+FwTlC(OEM`-SO ze|A?;TsI%O0)7Ku);|$tzz@_>&K$ihCG4fsMz+mo3p;v#EnVn$9Z*@Qy;GpNCRz~p z9S^o!Z#DIRLPJB-{u|R%k0S%kKG<1TS2w)g=8A>`6pkQeI{Dt3QaSq|meOfmQ>{TB zPYLQbKPkiQybIE0e$W3w|6=?dl3vqAJ{e0f-rM246U%2aSzgWN$mZ~n`=mlhUDgwY z`WuKN^9ajFgvkC?F-T!n)hBK_92ZNkgM|63>T-9+#D3pZSb=5~?zmbZ$YW~j_9RmLs&lVkcz9811}6lWNHk1NiFUaX;cu3JkS1WMD|RtY`zaEFn@=2oXIP1O2LWaFY0h7R;DT5(28sK%sC= zH)pHQW>6V?{#eTuo}0k5t?eHXxXLemP?yV@^L9MyDdQFkX{L#>=1evb_k?x_f;vU? zF|RBh%uYV{b&uJ`uUJ)f`YbzHCtX9HW+pmiWP5wAp=zhQ(e1KyMexoFf9VyE>xbML z8C6}t-0`_e=rg~5i+ZJ{gPUB;Men|Vp#&+RtXa`s&Ft~CrycAV#Y;^VjpCoa_ceuv z4HWa|cQ%Dj2Zvd2578?=#W64CY^Qj$c|wLtKR%8mQ|Jt$naVK0V3SM^yI}QnWIe|# zH=NZIxaHLrkcfrRaJDqYet@|`6t^yyxg)ZX3RuBk2|sZq3$0ICEE<8w0WL^~!pD9Z zhHJju?p8ut0~ zO1zgzRF3>Jig+Y;A$k``$y zy#Km7D}f#47k-`wPw13KI&!id4WGfpkO$0MY(UYp(=ipy-Iqqazw%c?$q^0xwlvo) zAkqcvlSKc?q4j#dZGNh>S)G3fnUBB0O&Cq34p6`oLk0+b=Cj^%d+tft(*b!H;*(S; zd_(z_M%%8FjJy8}Od_{ESzt$Lf`o0d_a-*;F`DkclFxv`@%x5X1RA+LrbS}JMl{RE z*`wh&Q|Zo};#fSB>I&cRxR7PK^~KLS2ie|#G&piiHF45wTFcSbQvu}H3kz%17`zpm z>ZToe6Vl#>M=;C%9>LjbqdlsN3ayU7Wy?co{wp(Q17`V5{ z>y?JR zvHQEY)~iPMatB_A2W5wY?dI_k()pS3Y_CfVhuV&TJni`nRZR7Q9+3zK@0>19UhXyX zKy28mmN^xyk-unIgJUBc;y%X^Z>*6%8GcgF8TFcetIlZdSC+N0cYOMhUm_QQONLOk zqj)(do5U-5X1AL>el(*2Dk;B-;yUH$Z!0F6lz*1iJQYNW$P)*Qxho*`jw8uubP~os z0U|HVweLrBFF1-W$fxasF%4*Ew3`#WGUj*s38VARPQO_w0AUA@{IhnuJA*B%|R9*6kUTsK0`DoS zm@T*LSR-J?6Brz&X#1S(l_FTTFZ7^X1$ePc*OL5PgOa>FHJig_9Tg`=#F++6x4at z3oBuT;2F7ZxO$PTkmR`4TNIy#z*BSMI0|6Kmmq+kJ~<(Dv_lhA3(+4Zp<#yc)U%rq)c0^c$Na4bc-kA^No`ms^6_a zmmL$xjpf)as*_{-e55#)C9e1;e==j(da}?q@61B;+2eZ3w;g`fCL@f+DkSZcL3fae zS>@xEgm9qu7>$CPL9+`_%bg*U#bR^d8Qfe6Mf$C}rx)%H`s7mI2Pcz7p6C28v#k>F z=bN^=8HIN+-sY3peIXdD*UX6U`ukGJr?m3#c^wMTZbQ5MKs0B9(d++|I&{0d1uZNs zW%>B|&%AipKNmOez9~Z@D=E-;Y+zm313bb3wIkz<0$zCweQlzjE(4@{Jp5lxFISc) zXF`hHL7|}lmECYx$znii0POm=w|FCF9xK5{C`;=crH|QXaGwP+dhX;iu9Ycv{l0JD zb9Za~O@t4v)Ih`5;re5W(>1ihgFxx0eGcT(I#tFYBo@!lNrV`3-WyVT;`h%gO+(YL z;a6a#X?(~nvdR>Sd$M@_6DkKHp8{#CbdooP#(7fkz*}7%eWS>4NF!jgVCK@hiYa~IvI(Y%WHM|+AN``mdTEh zh;NEsg;^IldbhbIOlk>A4gK|Rus|NUNpb<%?I;tk3A!9z`6&|Ft$6W|y$$gUtRQ?! ztuICvnvyQS2wndd3zg1GOryUT$@){O6t?$B-|Y~Rt=5}wrmT1Ln1>)s&B^{H#Hx)~ zwmZDZsHE$gRt3F@W)HC`9q_3^=3!WM&!_xikG%HP!WJpzsK>4xsm!B7Qy*-Pm)P%d z?qsyPPjbYT2$t(apEPXCbxY(6cfQ-9I2kasLD30xl3K9@ml(*VXX(6~~_JB)-Rwll?i5y)ZVzkkau*HB`i-j(7c(NblDUoUpE z-XwPt&D=}ilWC59$N%E{lCf4^t*R~$>djv)VIFaF&(TJW2@D65+am6vc;QFK*G_ek zC(~V5bnCM(A>8}xLI_SI%G#VOlt4}9cSH}@n52LuX0QtC7MoR>I3yp&{-n$Q?T}z* z7(?{oNwP_Vkrm0x1jRY__(yUdHhWiu1p3wfV0;@2SQ|X3g+1to}#Sh zL9oC`;Bx44{WTo-dv~jpUR<`b>9iemJsTu!RPKk*lprV<%)NfX*;GZe=x8?1CWWyT zGB$z&PS^OYj{b)PaLl>O_TKQBdYt{;{?n z83J5}5}C!rkYu#3FKaG#H>J zj^r7ysV;}&6D&N5;Wz74lv6S8$qs5`krUR*!cmSt#g$jU)|bu_A(V@HbF=J2Wm8KF znY&68ldyV)FO1__F8-*kte?mxLo^{}AVRrlrIaI{^$^_x3oUXHjp{$mekm|EA)psV zeeXK&kUesPp!=a6f^|#i3-jafw)DWBx(`(fve3#@#zFIVqO=|W;Xt*%o0ljK2cH+) z2dyle`%`MW#2U9Ll5Z_n;;Uy8#~h1}ci>+JB6ai5HroR!1D zw1mUepx%kr->tK{VS<*J^MnvU$?c*>);LR%;U51kvY+ z^7mwkbuWIqWN%J|0?AY`JYSmSmd0-dsXiW^)H(BnI49HCwaMR2oPdRTgV&9H&{U?XOJlx3U1@V2sBaKes6ySh3{1 z>FJWHvSg+e-EUce$lOe--i#Wx!0FNCo;i^M_2Ju>cnBtFF;WO!4t=i2jxo7eO+dm_ zA2)uxGq^N2rpmP?OcLA^5KS#J zmlSH=GN-R;Arpo1Bq`C4DkkdPRw-gs%MAWwW{lH1386F6o`u4wmTZen%*yX5v62O> zQ+K@^jP=_OyH zifA9KaDdK1JQMA(z{*hx445!vC&v>DjX0tF9=Kibv#+Qp_$j2LWT`u4_ODx`k&yUa zK^_~339PkIc)<%=j^k1B9}8p~uN>N`JLym@f&|3XYJer+=!K3klabimj74c1Quz!gt3fdxK1sAx=k@Hdd zbj{I~d>F)tgKECQVJ#y;;t{p&gk#;AwtXI5F>y(;f6zq(>ESeHET<9fY$?V4D|l;;^+4bZe4O((6M^APd!7KMQ zeF%kb3Ck4R8gng6q)}K~g;2n13S_nkCKvuXWon{R za&kkefAa~{jF?u*_iz+>)`UR&x{?ascWf1ZSW@s@X)tt$r8`x6OcBjU`M4ihE3&*q z$KOwC1L~|U$8=eu5t@&hXKtkEWeL%I9*vA|-OGdSle5tAb6)T1f1d6|L_(-y6&ULU?w z2P^$mUqnWMcUX8p`&QFj`!}$?I7guYVI+OsX41Y7{ew}h=QsV5fVqv5av^nIW6H?Q zp43i)5UQU!0a%8;Ov1iAd}v*vC0N{UumS8|_@Oia1HkMHW=*sSylMQiRxlu==MI+f zz`s(e{|>O1admA1m2ahO zzXu=N9)B*sU*9bnl#OZ99ZY0#Mvg!ejeO$R4RYG)N1}??8a#YQEpp{T4&00K#(dK|3{< zTF-tuax`J)#&%-DFQ48L%32r}499`-4^NqGH=FYvXJyiX9WQbp`&t{EB^5N18EmPL zgQX;GtuZM$<(n!P58t+04!!HdNa#Jr{fpxvZXNp~g?pXKp1PP~{(plS&M zW?+*sll&+@Ayo4Vf)lSZ);JbgPg}8GmTF9VW`# z_q%l^!v;PReW4uQoyXZrjYw8Z?1Pcy{iKjVc%K>OT-^7sQ@I2}W_QiOXu%v5@)Tva z@L)ITICu$cxwe)4B(>ZLadg541-o@?a$5QAvW>imtnI(x}l02 zuHwm?nNJdUcPJncmOVIreK)cAMQhLwQ%`v{I1{OOL)l!Z5g%$w`|QH3s{-i;&`WzM zd#b6|{RS{YF#9^v*{KXHzC2gq4FIGj+e}8thwi7Nv)bOS&xn@&v{q{=6cUXQzzF8n z!p@camWY=8Sw(2+Q~>*ZX*m;4u!z}~Jn#5~W+q9VkjZ`0rnPXf+E9|avHU(mFg6<)nC;7up#VC@#GCGb~$X|&xGm{~l_y7TgGO(!uCj81_ z`cu|E1uCRmm%fVq5{XqP8M#TrOvB4s5P0LtEJ zK#HT!9|09@;hh2+o0u}mew9ySeD|TLK8$5vJjr_EUXkzl3cjH{rV^gRz+^P++s2wB zIkeJW+D?nInYzd=$N0hF>v~LPO@d+h6$%P_Sv$R z^NDd7h~SPGzoQTXpg1_{FESX&zuf-v`BdX~NWEqMl}`8YuP??c!$~%SKFpb*m&7sU zHJY-hQ2tv9{{=)R)d9FJ#~+1fn`(j>z>w^gtx1Apw#TzjCV!2I%HM%Q2QI6xIu#Br ze}0||S@7x~iq~rl^o62TLa;|>CUuN`Q_;N!BjRolxR=jdgJ(-fUmAdwLGF2vjUqsTv@%D<)b9oi{57 zJ2ktJPP`Dg+s4OxzPAmXai^^=UPr5yTO#}WP9Oj;nUU>hk+>m8SzZ&WnP8~E9&G&K z(XHCGE16Hy%iGCCIlJA01190eY8Y5PDttT=USQ4h5q}lk_!%KYPJ>Su?j~HtywV?7 z$yXff{P)ki>jL78J%@_;!}n^O-Ylz&EZ*do%TpPv)Nyer&oD&}_BMAN_s2hFque5? zsv!v@5%4JM_Ob*~WRuy`LR6^4JHB)yCjK@z7_sqm05m5F;|T^OR5**ND>GvT!X1w8 z!hFbv^wKEN5c&_+@Z5r58d-FD7>K4wMo%icOicJ6ubD=X$$by|kqfwhWxhE#t+Z41CeH1T(sw61gi zgvrh1YAn)KRm#Fkxk`#T3_XD{NVdA6pQyf*SCgk3N~*}HImbRg`vpgfVNCm5b&03( zDyCcG-poCU65Y2fFngh>bV`pdQ(LnUX9OZI)oy}7xFQ*a;~0N|GKu|gj9z;00=*4C z;OM;z6>zZt>Dc@G_~29D8ag2q@FH^l!;brVs6qk@$$YlgU$nCz2MD|zuk_E)Rg6D} z6`U#mUX6}6cIjkhnUPx*q>VuT*}nQ9YGmTemxh^<$N;8OI;Y6>eqNT$vUU}eoCX4kXg@_LWb->~ab8|)DZQBWwq7h>$2;*E%h3qE?ZksJMT3<#!UBLrXe(cq zUN6^%_tq>1C|LV4J-5Hpn6hm>YF%TZkKmwrY&Sbz`wO!B=Dl^l;mcPC-Uz>4T01;7 z^tt;3c_j7&vEosU$iqv_C#_mIuEcMrXr~?pbD*cRN%|s-zu_MO6yxWU|BUeC7`^^| z4N3q3iv2*PA|DYSPF6x9U+rxpN($GoN*?e%#Zg6L zJr&(4(Sc=F@htwX?_qOFlIyX-$nu4n@X+|7&1HpW9_t7__wD(KbV9bzWzn{HC+JLK zJ$y`dB%Htn;33)Z^q%9U#VD{#KFsleNv%l?fj-J^e{MbF5+jjWZn{U=hd5eG1}w&? z(^mKJL}RQ*+-JT9k~;!0ve@a341SUMTqL6TPg_t+Gxr?47~fN^$DGg~y_ZC%g9jh1 zFTO%P9OI2JG+@H2A<~}SM&aakd=*%-a;EFWz1D)L$zw(&xi|Kp6CPU6(=Nvo3Ak0@ zb^H21%WrT>l|)9TG4Cq0DcX1>jI`ntaBHu!f$w%$@A>WoJwW3V4)meGiH`SLr^8!% z9Pbi^GuxwxU93c=lO%z-oqQ$N5vc$%ULe_|S8f6?1^$)3m)zZNt490!5xILLlbL7E z2BK54$G@+?RaeC?4PKTXDo#a&9J>4H7(?bbZB9q&+V(j8Uhggg3rYP;yz?AFmPyD+ zr@-!Y!}dvBCVPCfQ;P*;QqLhQ7RGVjFKU8kOkhktr z{qI;EM*NTx#6utIy7n7nspKc$UNaJda`nFYWj}rI^`t?z$+FPc-cuIozd~aCjp{YZ z(tq+#zGrs*cmMd6$;ada{YLV|#KVW@UQ2%P_KC1pqVB(>C0c*4d%@uNaKCglWcsRB z+lFVTgf<&Rc9utvs1E(hzyG6=a{tB8-cuh-r_c)1oggS6Bq6s}L|W3vrO*1CPcK@V zLBE2!L3bG8f-R8W_ue>aCxJrV(#}zTQC}(3x-;wz`S-v7#`-u_il4TfvLEX&Y{n*p z&@)dcpWsWq%4dkU-+gt(?3x=IB?rV2*sm@ml5I>AMUyse0Ru2Pt-T#-2bq?TwNrFz8M(yTmN{BXt1oXp=e`3p%KD;F!T-CW(O<9cwCZX8D*opmKbo!4aK? zfv%2ZrCklh`u(%wU{+{6~4X~z^2e7|L92z*$@u73cUJ>jou`Ks;bjqv|dPLS!>O8V!#e(8^{vcWO zpL{WZ1y?p?rmQC^I*CVbH2& zE)4q3L(T^K1s_Uh3)lgl87yWh!O@l@2?GX?a=Xz>xT>SE(;=n}nFAh_F*1qgKse64 z*#=B_xb3=P4F*6+q5uGf=EM|oWkQ!kD(0ZQE z??ZKrD7Uy}GpkIQ4IwObpyS0-HS|RM5TKQiM_Nu;!Mo&swm5H{UCImtah8W1P&qSl z2boE4r-I_b<{xM#_MyrVo2gf#Bb8G+_LQ-i*|1?bvyovISF_-f8`25Vgn{LPu%Rqa zws_H5r+hOwRY^5Mb8KSL$`&3tkZ9Bc48ABMGG?<$2*5{ACTxI98G~47a1H@T8O3bT zCyg(5{rE|Z)k8)~%H~Y}z$2U}7Yvra)qwq$2JYw&l8kanEj%yJJv?wAcp#)0bhWhv zd!W&xZjo{fY4pa-cw$pp#? z_*+mOAc7|K9HiQ1b>y#Wz=Iu7Z!+K!Bu02YRvpeVPX=(?qHYQvb9h}!8Wf)rD+ss!zYs3q{Z#b_Cr)0tc3T7H z`ywCbtSyZCo1I<|As0w9h(YWW00uOW{eBq5pW!fvtI-D39or$r8$j*^k;4>P}<~dnHv`d)=ur zYaquqQQy+At4uFwLIoWX$Y)EF>{fPNq5i_ABp8LkG*vS$u}psvFHt)AC?kc)QWsJs|C#f-Y9va-j1r;kBB=*=OFT_jzg{K#BMsGQ`N}#+k$-$)MP1PU7_x_v5 zQgxw-31PFM-|!$IZ(u~<3tI{GnD9|K#HJtw@p>fBuW9m%wvOLo-O4?bXYgY~Rq7t< zAt0ii#e)Qrf_fB^fpQl}a@}A+hIj6#kHI7mWH$8&ak2daOR}-2nYp=SUnR?=s2}Bx zEPa3|fV!hLWL9{*`}#47 zul7@El5|ck^o4qg=fMlxKo1KG)ru>SF`T$)%f$6C?GyUo8SYn2Xt-I#iXS`|6bIg4 zSJb}KC%;D-Q~IxiazIIT{;lh##mJuKejS`&& zIvKzJ`l>vPu2_eiy?7wLXppMaK?m#5!IEc!I%>c9^Zx0$_ z21Ix(;1(WaOm$wtNO3Vc{WsdfiiW9A1FjPX`$SN*tK-vXl^6p8I#+Zwm_;J4Hg$yi zL@HPVM$5kwL6r?-Z(Ox$#DDtU>EtKxoHT;N@-EzH_(`8m=a=8zGMVs#<9cXJsc^IK z=cDY5jxZmY5u#Jd012HjSPlBhdPeuH33N!LKRRr<+9QLJ3h2Xvp5G*jHE|Yufl62} zIxt4SDi7Ehv+HzZ8=1*OHqDF%8jJkW@ui~_HhAolm&}Rb0rQ{$50>hTtJ6gu4$IB| z=x~41D!Y-tg`lEZQ3p=<(CLT#;Qi#`emiBYi@`Fp&x&VJSWc?53LY#MI3+^wfcAk% z>J$%qq}6KKl05LR8%~*{Y_S>U`)7_OXS7!ud&58iJZ~i$wa(r{Y&74Zdgg<-j%nua zkS#m3nRnSc4H^T>yE<(Vo4-xxS@zFt=NWkcv4=%ctAXOeyv+pSHOR*1#Q)~8{W=DC z$m~X6ZNUBc#SsmZ#^vcYZ-Ya0f?ZiXz)jlcYf*dt{Neo7P`kAwy~=o>({aZi^J?X=n}6A24%ncW znHPjQvzQM9*FcbEjFfa4bd6DdWIqs3NA~xaei-<%uMBuWaNJfrbZW>O`bw%pnWx?+ zFw4#$;5F(0Pv1Rl%jzIC*%Rw)&8UJQGph_xUKh^$L^39oA{b@U&s3%=mHxR?2W3ND zHdv%igm56>*$QFyOU?9udHJRYnMdY{fEObAvXU)32aA9HlXKPvA^&&sEckmp!!Y7K z)%oDfXYxvYVM_^>4od(bQL#bWk3;ntvi|0?3r0H8ejSs&LVlXPN+!s&TchLFzClK^ zC)Vi`2bCw49V3*HT+4+uX!_#vi1sPFCE~Or`N{WAhIUT&N!g?QAujgW>(i_|-WfJ? zuV*FB&IRf@>FuHhig+_HDF9J4@Y=B9!T=XLVIckMPcGVk?5poajF`rzTSSsk!9CL; zYgVK&9*B#YV4$7Bju?oYP#tpa0IBgO@0_yw_oFW^8@WUsh)tb4b;xX#GJjs)X0+KX zU$coYd0C;;5fMWS@FA9IfAB1X2&Hb1{ZX4}QlAF$1rmp)RAx5(5jJ?8L&`&JQBIMK ztqML6*~H`s^)P)l2u13$-+p$(pCu^QayJhMbsNm z_JAe-oPR7)!|vbx?rUqy@Q|jB-Y;^HrRJ19^1;$V`Yye~hHV3A-w5e`g6xC9MA|QY z_@>z{6KvmTKS-8G<3UK?n>G1GhQZ8qo{A$tEN=km7d;;t>N5R8a(#a3rU;z-^6KhN ze)j(BHX#!CI3g}4fan{mVOQ0mHiL#&W0pw~KEITA6#XR*5Bk0iCBb7AyXU|cu>aK9HKxV*&Dr$s+8BiEkOU;ZZJs z7)0N{e_#2XPdYlZj8+~Qc&^0$M7iW+x8$U~nUI(5<;#~%=UrV)=7f`ycPq`EikC7B zIoQ+FV=`m2oZRLig8+Dd4*_qpQro^-fjtmtXk8voIhNz>$Ue=%h;*5OLfx_eD-PaE%Z>keSBU-CGDUxJbofHNWwbdiZ zZ$7_dgJwuM>k-k2?o!T?Vd$W91Nypqt}db-Mq&vY*=)Oo(Ejvbv?^np!b6PZ`eQ#$LEetoS_5Mtfg^B_xIUS7f3{bC}pX`Ct2wHfGIelA3P3MD#nsJm9J&l?x;vw zI>gA$ekpiHu^eK*I;`l8GEtNcHbPSnu|k1?IrafIZ)+(PL?PsHjr7FKGv=TIr~%Q# znh1ft+Ll$mMw35kFif3xN=pLXJTa{DaU`kJ(iTN#WrF3PkR)Kl zz=eGiNZUvtvkNWn(r=KplrhMR6FQohLDFFjyy@`E&CQZU7Ys14G4^nxOxs{n`p0e= z1T)yVqd`2LOs7Spd{0Z)D4Vw2P&4+^kDmx@?LDVD&qyNK3mtT1vhqqw*ob6h@K3!0 zfrZ|f06@MYL%lYDs$y^?#8}cYqFHK)N(culPt{=)S7v7ENK-Z;V<4hF5Sd*iSLS4K z!O})242o`Q1{xc)!DrBf4HmFd^hAD$hmLxK2nzjZq)(;F>b&Y&mT53hz_XJ4F+e)7 zD&kLi$9@^)_Glk4RUj%aczRW7`PFx1!P=Q~vIod6y8sS)P^PBut5)y=XfYlM>D|^j#fx$_SG`v>SLw63;=|Pf{lvSq)3Z|9Jkol?&QS z+932~c1S@q>`C=G0u_kjq8_#ZXI+(+7 zc|bt6QRe77Fu_Ba(=WPp3xd;jDSq}tWKSFF1qdAKZL5zWk(49A&U{JhUiBfKj*ulR ztvs%N!doYXZ3!9u0o%h)cE~bJY>~F4Mg!YwkqkC)4VAPJNsuNi313_aOQPwYkdEmT zvGtt|sv(QMxHMwk!VJ`@J0TJuPmZbVhW3*sW_UKUd>{QmbkVkgFy1?qw}YxHsS_b> z={Mn(Z0X647~C_7)TTZH%eKf99#RlvzfJX1=ufhY9nzh0&1B(>+AHcAONc&_;$m6m z7b+KFKcPxn2L9pqUNcW{v8l3Y)rERQpS0BVhLsP9dM1^S6(YGp4G;vwX_cu}d2v!- zjws$QFW)j!+?Fh>e`aM#^^BR3;#}26gZTMb*#JWTklo;21gQl9*(BSw30B>Sryayx zzw*kxc(6b|&<4?`wxx*FFOnO=a!^FfJw-|Tgh#an<==2rTd=BfjGcd@i8<0dd-6^5 zf`w~wBn@u$eM+=e(vJgBfMa;9j-tws* zV?QBc#ZqTplj^IWLp->c>|oP)bT81Xud<2`^8)#&?|oGL@S_KMti7at8o8(TRd$Al zF?D2Rqj32D*}JnYxsD@G@Hdgz_nibl5&%gNq&A9GrBYRO)pXDFnd#+W-sWMx!hE0k z4)Z$a^f}WrJ=0ySqgterx&BSZ#6dWhQ{bd`3abxMCFe%(ZvCscDP zhV!`n4f*ZgL(`fo9Wtm>JtjvU-qd(z*C zq8X2t)~olMcg9JTi3%PmMIsd7sLEUvwQEoDxA2{bnS*3sU{NYsC@{+#U?tJEH#n+6 zn10ki?D06JP51d(vfFDh98N}AveK2S#h>^U4~(PNL)C>h(Tc)mHJc;LDj_AZ@oN}k ziE!~y+Xx>#NEFI|=1gLcfZs};92g8djwXGree!{oF-Xy8n6}eP&@->;du14<){4@z z`c-)V;#k2S@ZlLza<+kd=j{V!q|2`$0Vl^(DFShLaCkC2(Bg~m)Bly+Uz~I`iRefv zgM)~HtkNAh`2AA$cV`S1k1o+9*$-9Y5l%K_jMb@Z>7><3$z(<6;rsQ|!Bq@Jd2?Ja zCgtGbFe)M@+EC<_?Y40E#=c#(lKfU2X~MCqlGM3ApKWJie4hwi@G$vh8<4_K%<+fW zGMmK4Q8b0eyZ2xs17FD&-W=aj_jMZ*;%XaNXtiOz31%dm8ST>+89&M$~+hYKwYn8HT8?* zTZ?D$sj=*|Fyjfm@RL_YWsPm)ma>grcxz*#pYEYoKBF@ku{k`Zk9u7f^JW|v&lMG< zj$Gr!(gwr0a;SNr#%ccKzeuR+555n5K27v$83Vz$1rE;0|30~bUP+`wj*`m5;gP_z zamSkOKw1}RW2}(hjRzZ^8Ev*F*d{_=8n1A%J#%3>T2fO15Ik%Mo~BnfXCk9X|Cg%N zmbSpwwp^U7XblcZ+k-gnI8X6QI#*5lZ0k2iKEs;>P+wYUMz?v7;mJX2)!#{GKZs!~ zvZ6Vh7=EE=__!h>BOB-gjNww6AgX9EsP-A&%2^b28CRRaLmWPU$HTCyl5=x`iavjO1-(kgd!{(dw2bZ&Ucs7H{+15u@V_{IrEH2Df*&_>h&4_7CIcL09FfU^vlC>fRlw}1+er}UO;&i zr#EfsrmxalOiBKRRmqF+JszUZ$)iMp-0Cks}3v-onqE2^%tYwNd?2XW-(X+Yy|}A$2_qhb4PcJ-*C9sKXz~s85a^uZ1TI z4xDl5MIVNnXD^MHY%dgjY4H+l+TvV0lw4(NEIgFH+2)4n*bvjbOofk89FGDNeub-R z=^KYWy^Ld#vz9}#mGOOWRn<{eqBA8=;ms?Fti4h;!@8UdY<`)kIWTRA++r-aKf}W| zw35@I^~E@^MoR))4;?N?SEfnIx@x9FLE3lXcL}I`povLJV5n}}cy9^+~ zkBF5Y{pd%9BYAXQmVy^Kz|Nk{3E5vKS6mj~4j(>TIiFsZK0I$Y<&Qdg^k_9BB2UDb z@lg8F^KN`h{e#(P%r7tSk`ovRF@l#viK9ls5qAIpKmbWZK~#m8p29CNA;Bp`)e7Im ztmG1Y1U+v(<#SKMKkvtApp-yl3FxPUzhqI#Th5q7^Tar?a!RQX1!R?LYZ-c(5o(gm z`&5ny1$#}xWpa&gJ(a6pj+~Z9;zq4{*hk$$Z!_U6APikL7czz@ASMti@szPTi6O93 ziiG1+_L_nIAUI_-IdV1%&7fahj1D2=R?V+ub>0@9A7$&s&L|fm83IIG?M$QEB%~Qr zt_O^ou+mtql3Sg&;>vLF;_SPdN%?k;2BuCj5*eG65a_bagi$n9sMWWG#dHW)+N@ke zIT^1`W8p}Of@md5Unry3tSp9K>ZH8t*jzx0k5bvqTgjOqawLkh=S6|pk|8n$9_lss z(TBeQDvE?iEK0K6A{1ULw2YTjoM#Z|Ih1O<NLq6AY?=P2RNh&pJ(?;?AI$KxUW zsr&|+*lP7WBS4tBt$J3v@eVx7x5Kz&WL!y~(3sL|E6RbuTXKW*i!&IYh3JiIaW;y0)iC5_AE6aT zWPU1{3GXLjfN$9xxs~?d!b=~_J?;5)VkwJ0ll9^EUw(YFB5~v?dNU3r2IHp4uOUyn zHyy;&6RcM9Gm8 z#Ygk+A9?iQj~SDZN9Hp8>qIinq>7eMk4g1;75zxTha55V4Rg09!Ll_pT$M3d<0JPq z`IGrHbO#lj4tX+T#%BwFw$h3YvQ=v6NaC1C;b8hzB3nd!kt-aPB4MX0A z*y4j|JA7LOZ{vD<0y*p&=h&9b7m63b zTFvI2ELxWcOyzMHs+(5Op9VYUY+I-*xw?YjTW(O$+4iT`UVE)Tu`v6x6yp{DUcGv? zJ8|Mf_2a;S11}5sF9c4(HaC;w4xMfDgs;cTx#tJkvO1EQ0M2S(CI*P0T!^Ik=(FP$ zCQK;)NerGN2lp4kKHO*fMnE z*x{_>sN*nFdWP)Zt1DJK(wzb?bm4HYHBTt8d_zrHyVlu0LQ%UHMW^odOg?iKau8CC ztO~YCegsF=^3(kRCnX{s?e3)wil|dvOrTb1;8sLUD^0njj7Wlu&Siwvj{|!3VECGe z8oeaxJrO4i}ai zIAUIo6WcZPPxKZ%G3-SjP>SUQ;dDYPhdl@Ya}!0)uAi5q94~>Z7!0#rWXZ1xuCOJN>^OzSaj@c;px`MvR{_VABBDj4{feC?CB3dW;YT4+WHCO~TQC z{MDxwS}trJjw%u<&&|XTVL&T#76~tBoY+2Q81nLYr+u0ar^7HJ7BYKX3lV=KsJXeZdp5?fLV_O10iL78Ss#nD0hIN5QB-6d- zTmhWz9UM9$Tk44M@J`98Rk<$?v#G#v;nMZmmhh|43r$+|obldT*ALEQTL-2igXNex zynk=uyX5qMk9wJXH8kPqm!Ao5;_c%9v>oIYE>ij2l9gx2+FyyIL9~r=;~56Zualnz zUO6ZGVq1ePp!#pDIJprX3g+OA!@U+!_FOOH+t!>P=Y%2=x#%XEkde!1MgPicVqB$- zJic@#e1WIQ4Y@ne|H#386(z!1)Jhs0v}vnE%foiF=5t#_`Z+0FP}{U5C3doAA6@5! zErxGjM+jY8SZ-#FF`m}1+guT^BD|`ur5yK+^OR#>o$Y>}6U9Vqaw6elG!UJk&tx5$ zgdaG*IY!rorp>8158-*hcro9>n<#^~qc^+}IZ@7l)IINshQ`sqY13U50T@|_C%r_| z&>6~+w`(Eu zrik(Mg^ba^&ofUrLCu^g!tz(yI(sF2!jTlQJM4eWFX_MSMn{vU=s<93UJ+$ib4%`Z zd&!_UbjXT|a!UPDC&--y)}r?iudRB#Hm8tN!3k+oiyfKQS~Pu4U<_il~|1lBFzeoxPxFi#8!!sWrOlpN~nRoHs*pricI1KK{IQXX1KqVgLbS!-Z zuIJ;of7Uxxh&swPeT!la!r;i^Y{%;&@e0=q))9wJVDK`Z{vmS|UD}*s-a#dNCaJmi z>zM~RwIv5%pQtDF0YdJ|JfBF{=#}OJdXeQmKDS z;qWUkCVy5028>->|4yGiUAou4efuh6Wkmq@7$C?Z`XWcztFOLV5lJtTBkVEVTauqe zAoYufwswj}DjRr7YJc)ddh7bhpIm|`$W}&Toz@Zu$HuJM$blq}#Xm)vu<9w~n9^}P zD^-jrN(_ZYo(RfZt&(H~Tlnp|X^L$ojs=DGBMeV4`icgft1G(0pd# z1hGN}M#n<%TWS0ctlr`CMN4z-5SuUue07=3(Xi0`elN*f8Oe4MR=7WHl*&H zM<4nU{JXou34=K!9aY_*G>Aty z2266C03udIFIedn-6B%Js`P{edOlc_@EqdN3-q52|f%^jwA{*L%WRj z+-A&^iRIjhbASSEo6E-Z?LiF3p}|W&r!Yw6q>?j(Y_Ym*i*NxI*v8K63GF!q zj2#Xujxw^yy~YsPfM#G3rQwt)v>I3N92{^I@7TJ#w(oJY)PhlPW~@D`G&z%Tknwai z{K$ah%wo{*j9e7?r;O*tN7cOe-}<^Maxe1D7PC44C63aMa%!JGl2;a1zLk80=2W!a z8~in0p|w@KpFjF!o8%!l};w+Mfihe+Lm=EbJ9GmAO zgUBH|m2J;{6HR5Vu(;{8I*S1uq>><4q}2$ji`w}Er|Kp*=@OgMezgHfBgLZ3iOjR| z*~t;gQCC~(@!Nyus2sU{-bZz1u`m5Y6Z&7}uFEz(+t)In14kRT^B$c28hHFO2GEOB z1#aQ4ke}$w%bC0BMP#nNfB&5~D!&s+XFT`(Gh69XekxenVwOcIdI3iWIm_9`xvrQe zEf8Fcd@_cO&o-YvqTS-X=14ML32F1=tJy-<7WmTNd9;~R+kkAw0^Q2uuDoyiGndn` zszfh>i@6leFX|1Q>EYnT2Xq3Fk8&*y=Owr11ytiopLT{OBJ~^)VDyAZOBR_+$r<@6 zIg%`hd=!0{BMPreEpYMh)kFjCFFxFp`(Ng=WIXmVBVi2e@FIVdh#vFwjvdi=;%s$-eiXRsUvuwzao?83kwl>&B&~WAzCVuvz)2)3f-wE`tCJN; z;v^pq6BC7mlMGywBSqDTWUu79z1@r~D`V@TKsdSJnS{&0n6Roqai~O@5%LU4;lE|b z1{T6w{)js<7|uoUVHDLio%9P)t!B0fcZ8U^o}?O;tA?Xzb=rC*TlC}@sSs6@^vI+I zPhOPWOP8)z?f_fKOb$+~`|A(B)o>O#RtI;SNbqTLH{svPXNnIePJ>%m_?@Z;tc)$j zYbm8xgqvD70`T0AV1ZkPCgt12D7%f z+IKB9V@S1r-RoWxMHFMI94qtaH4f??XVJn+L8T~&G}%fK$b`QuMhRmcUZ06?$21On z__ZQ;C(1EiMmyUV+p62nN|cJ_DL)k3a+<|ZpdgE^;6%`0JgV5zB+(@v=}c`m{;W@O0JH*F|=w7hL1`i3_scq8~<9ZUXz6sV+vs9^~!g0ApaxJvc)$>*LRbD zh{J(1P&5=e*gA7H5ilGT40DFQB&rnpX*q)e=k@Ehp~VOayx^|ryo{F!DBkiR^6*~N z>1;Vn{lSAa^m%&}RR&ma65felOgP zJ~Q$e?#-+BvON=De|_p)IRrRT<=3&bepNop$+gsT#t0ZAb!R(o`YJleI0uvY|4ii1 znHat1lt07~hi-HgCw7?+&<%ddsY1S_U+~Fk?6asM<203aUw(C>`$_V|SiE>8PIWq6 zIj7M)bLQQWducOH3-bk@;b07=azMbPY&*YD@S-6caWLJD3=>&veznCE-d3j{a?*%q z;iR@0DXPgr-nEP^{SY;zZc%>veJ5>`2O=8RWjycLvNNzH*?HJdr0ms19ZEi5eGXjc zge8jpST$c7y+viUr0XI{55+NOeuL9lI(i^Zpy1S4r(|Kv73ced(l_Tp zMfu6REeaQ|$z!^i`KY2$2l8#+Q**yYwpqxr@NDj&Q*zu{T&t~%>KJ)~jMVDAL~q{A zc4zZA*e%%EzR$UCY*?TjmLh!A(LBouD<>dl}BD_63(alS5a5Q^S%Dqrarv$wslPs`*;YW*|O-pERh{0p{fF=ERgi9{pF zO&UD&H*cRVBiy7$ND`)!loF5!_U@|5m~v6tNk5xDEec7~yi7zKG7=MOkFnS$gZw3J z-Iqz{_T8IxEV&grD`N5y5zs|Y_sl82XF|>-K@r;?BUOJGVtsZjO(dG@`J>VFM$zQ3f*M^@^ZSXd)x z5DEpul>%wvr8xfkzxg1^CAW1mH(DfA^0BpD))}3jZlHoKO28_rNTS|mh z=Rz|iMjn~}5$>UuV#t7E%$v9=b~{4*Zf&+|CBMUx;9_3mrLLrXhOEox6Cg@}U^xWki(G*l^0hgcckm@X86r2;da$qi1XlLtFHsG)hvs zZ6?O{6Y%Riw9NThTT??HtI(3i|2E^;SfYAZHf2~ikh4ZRs^qJ;4J`WRD= zwr5N=MJ-PT778~7l_Jdfg?FlhjE6_jC4F)qxqmiFKI6QOkdG6M!%iDh##EKGmGCum zvE9fR=D>I}4)hHfKevK6r??cX_tX)H zZ5itG!Wh4dMLEjW7yVrCek1IQr-;T+WuHPoutFIbE2uS-0ExFW{UQ~ z1$;ImP?BM%NX6I*Z-_qOfR1;x;CkZj%zpqu(m#ChpMUn7;wh&EO8PE|GhCYIj2HP2 zZC7=|p0SVLZ67H|#t1(YjnOmwU?E4J=qwyhyE8YgiEJyJjXX0StoA*yWt?$FaT;>y zi%_XkX@iS394O(HSnT5;U*fAALG&5U9mm=;aMAC}Y+?Rg4n(mCNCt5*qp4hGaI`tH zqZVgI`diUnB`ah5rhm{SYLY|vZ~y6|ipqQM&HcqUWe7oP_*wMx(X?UV0ZtjwwEz}8 zTVV8}JBsY%2%uw#I)Xn%6!cck>O6ZP+uS7d)(89yR*s}|QO-Zjm?9_0ZqXZbIrn`Q zS${P7&+x^e%njOC$QR>XB&d^o7P1p74&~2)(v%KHS}V=k=P?ES}ni|H;u)l@HVZmgvN`jaQ_YXCBcPvI;!{MCm1T6LTziY~IInO|P7b z@1jzkHKye*6x}W2c2gFk{xXTS|LRYURJ-z^EK~}tR5TUcx5Ry5z-;s;?bE5=jvO;* zh}^R9XS*Cc9Xp;pPHFF*EWAi2UpY`i-&Pvk7ixDUU5EeC*GhtICBT9=Me`WDWsj}| zpcV-O*d^S)5qr|-p`NkG=l|Gmvu*L@$&=O1?c2B4IN(fLE;=uZBdLU7P@vORkZfn! z8fEk6S>cxl$;z{65;;fQW19)1ba#$s{_79kji6j#hCQXlgf0|yxRO7|SbGX~GI>Nm zS&^q?QsPXWq5>FnRR}QmO*;9378POVtf}1+U#CEh8uj{R>=Xg-N;&0vls?R@ z6M~#5ih9noUe&ACzdCJ+f)#~he)xAa`~-h$A}`po5|tG)&JRY&Uw`mU_ZQ#)R;>a@ zy9tJztQDBhPr}IHfN;WJR<#(jrG&)*m@|z6JO%B*2M)#UC*Oy zDc^CLs>6yU!<51u>=%wSHK*--f96uSkFpzjSIB;hZ;lGeRUPp=I2}vg7<{rTMlS`F zVrG>OzguN}_wa#Q1q36-$8q(HV|b&u+j9tAz9OgGjLMO)Tr9{$vp2Wk)65Vn=N@w6Gg)=|J@c|YzRyGFiJ~u%rl%a-ifkX_Ms8Hdwx3dw!6R=xQys*7XW3a0jq$FGB5&QcL?jgeA8 z+M`H{UgCf#M^bRxa=;hy>AtC~YN+l)P9Hzq)U90#rKeJydfd*6sgAGU_$9OI7RB{o%|~zjqEt(Fo-r$SG7)aq6_LgegMP z>(_H?Q4DxxmjB5s_Bf{_Aj`J`MWvk(652xe~dRDYRx(v4*66kA=Dx;1J#}7-; zIlN`wxgEJ9VZG$&;N_@wn1+RiIz~JSczPHkF_DV-{bZbxj(rym!0F3@YHa9d;l0q? zV6?4=(QU33X-rPle32P?YE_&+#z`af!bAz2E=-4uO-C7R+A5+g0T8ywH}z#?~&MjWeg9UP%8=E9##dBr##p|T(__vv3w;?>Z9c-gbVPcvXw@^p#p%>-uH2eF%Zv10 z7Mz^6%8B|ATnn|a$m=y9l#V)JCeO`eKGc6W3NKn5qi>vw+&B|m5M7MF`6%j}EL7=Z z=3=MW%J+$v@D4hXu}$3MrU5ScXZv?`kc+mBll|sb+mkK6?2b*+JB~e$?}m8MjeK`M zJ^ zC7OTp+cj&537{RXg#FgQ1U3dM*t< zhf3M~;CAvH8hcI3ECxg&?b)-ZWC}c?voWz;rAeL_+&Y2U1VP*8B*%!YT8_9SIk232C(rgki6!hY;aae_(Q6Nb1+!i}-^WF|36I+ha^ICg5Fe ziDXSIc~Nk(A}EyAK5YigpT2h}`C<;0AzJrDP(NInp+e~rIYcp~gs9Cka8sXsFUD|M z!~G}*r?XmiGRK0xok{yZO;(vLOR1bmzo({RL}qp9t5X;1UXLD%vc=G^{7ZvcDYhm% zia7@ZLk)flZ)v!1yZSn+2o*g33`%b7*|sL)tZ;1GoGlF$9dsD{*x%EChdO0AskX0A z{1@Hz(PXR-PL<)5uqtVb%aJ1oTbGhorxF!ra=m`@M)!3Tpo+{%i;S2)>26J?MblAG zK+o`K$yTjAq7?^bktWKj{`7qseRA}(S|pM06>a!O69!r-m1$srmeI$a{c|B-E}k5v zIau|gJagEr&3ieqqOedV7=2#&$GsF;-}^+)Ym)iNi~T1BFWz z2Icz5;eA<|kMkiod?wtb?+V!?%8?^d{+EMUb*{WUh@Tv66xMF798g!wsr~cceij~> zila!Pd+8K)Q=*g9t$2t~LeGKYMXZK=tJp$z3* zePvJ_Ob})8Ac5cn76=4)2)3|52=2ilxCPzd!6CT2ySoKj+}+(}ad%ss<2$Rm`!_$P zrmAPAWx8L#@j=_*9y&bA?z|t6S>~bhkBBn8yK7~w(8^}%Hts0G8|UV|JT-b5IJqd@ zR$Bkpzo)j!*vM|W_D;glK1^~FE{s8oCLkhjDMWlt)t|wm=&pCp$gXnR>wLHMfncG9 z2X9B9?nj=JxVm__yKavR1iR?rfx#;c4j zFFLNRt#(a%(BIuk2ur9ZUfE#n@w;2Z40QJg+STSmt$~czAT`#9eNfR!@|x>X-qg&< z9hQ(k8OdC23wb%ZR9YsORFB3hbDDY3_+08(YtX~Dc{weT{TBF}ftcRiA3AJ~`)Tqm zFNv|X4qb7(WRi#p^0AT%aW@-FS$7J`D?C@5F&|7Xy7D)5OoSiF@SOw~v8nn-t^lJviRQGwr%pX~0|^MtKlIfdRg{ zI@cIlQ=*IIG9)-IjNlg9CQjOEK58>5q~- zHk&rJ6+a0ZGe~}2F8k!~&RHc!ko73ipy4y$2I!@JsVO}6;ll9RAEc=sqGY}ggo=(o zQE=>8*|Y&|f?KL~Z1vVGZKH6Ff7$+WwIiy3E@7xL{m*^zs$uH~N&)dM=8 zh^Hi9n&stZeuvC#ao;oGCZw>Mr{`_*B(&G1UGmfn0v$K$(+=cw& zFQ6;Re0iwh1y>SgtfRdqv=@DtOeGhbDe_*^{%nkN$JANp7xa3VCIwef=*2m$3VE=BG~cjUt5#VhM4T(jAV9 zUG~j_bPebJ&U~EdD~qcPo~Hbsam^f?Wo(9<=kcs?Q!z%=guyDVTTUQKku@OMp!qGJ z%Ee76n4P|=Ca;Xk&!?kC*BiFEa;FevCz~=;a6KXUGX)h?QQ%xhSGurqVlYeg0^xj~-vu zVbJ^TUtznnxx~E+u4E2>&Vp}muv5&*mVkWO=xlrKG8CHOS>4_lX^0fdE}Lx}wBR}9 zjC+u%$GFI_7H!%O3Hl5HjHq=UwZUPO_`+|+Z){N&J)Q{^RYebrAw0pZ(ym4R*oZEp z!Kv$Fa8jIv9ng4Hld}6;Eq?lheG;ttb6FF-_`vB{9I3dY53g0i08KI8_8!#O`G^C4 z3+CZ0KMi-Dr7zbvZ4Fj!i?U2g?i+>%JPFzns+uBuqz<;KH3JQ2eG35mj)ofgO!>|G zdY{Mdr~jpqE34DDHm-A0R-+u_EVSeLB94-aM!(#dt>537flbX(Gq8GyMsBkp8L0(3HzWXFqX;DHhHW9@S- z)f7H6;dCxct6%-2dJ-BA_LIOoJ-ZDO{mOz+V;1dow?iB6w|Yw^wadscpN5@5ow!qV z&w!~Sp0?r$vB<8kQpwNZ#_{N)cRaKLeml|+CkiY^^2G-)k1E{Pf6QYI<_x*YQP>YB zAJ|~aey>iEecO`Q5Q8Z%n>Ba9qiir=SX_+-55Apta7`4bfXlciEUcQ;TcZB@ek4yKw#{C z5SP5XO}~;>6&Uf{QrLc7Rlae?<~3P%T|Q$THVc8M%8l7@$I?>HH=kmetfY+dN-odU zsgP=?451=)btATz zj`}^Wy7)*6>=#)K+d~r7AI|<_t?~{5OPtPvzE1km@B0t*S*RvdrcI?KnT{<_wKZ*L z_;PlHMP4{X=_;D-0MXX!$Ia3Lx zDKYUB0TD2n1@ZK@GltB${?@(I-S8U8rL+ckjBQM8x%m4&cXb@4TLIXd8yw;Z=N_gw zdpbkf)~&Zp%@3cAijdAS`5a4{C@*g3%AbSBho_rO9I0-^r)SIFyOZuDeJtEhP2!f| zmHcQrdt>N%W?#e$7xG;bB`rRTEdE#_QnXIR(Li_kk6-Ce)e5!~I#HVUA$y~;8kRXA zWscY3alw)=UG!*`w62HY>NO#m(`0I<@D>rlw&y**LVWY%m_T26?&F3yF*W4Gzf9H)Y)wl8y*Y7FpL=zXz+0 z()~C$lL_6Ply`h%P06rzrx9*euf3G*t0znfTe?Mb)N2AMWAo#!9?*4bOhSNQWj@h@ zZKDI!Ze1>c9K6qCB=G66-zy4~J5Pwkzf~kpd@4!9k>RFS`q?Ez**B4rn@8Wp+-QdV z*8rESnmN{|jQkrn_Ccg4cT?zyMS!=eNvTI#fpfv7o7g>)6Hwu7xoaZAwQo-kKxm#c zy_oavQ?r`Wg+KV4^LL%FG4lXdHT%F=@eWhx4uO}#AE8#8D%r-$)zZ@d%Etqt>qh?b z@#HYn0Bnh_UAg)%=)cve{IwbET=oku3@y>!6O=3VM>_RMOk%7H^>CzLJ^BDkbCA+H zO#y}W9eSIfcile95wU5}Z1FGsN@$EUjcyQ4HhEqlvGb<`Umo8;zq=h1|h5V#T}gc5whPLeEVQv_rZ3_ zK5Ns~zdP&Ax!4t!h0GKI09 zj7`v!yU~tv%PZRVvuN>ibNkC1wv7i1QJ717(Q`|yq&#e zxKKGwot&p4KJJ^P{>~SYY_>~U-3=PvV~(c9Y#!)|1;s5sq;d};qc(sQhNM-9$*o93 zx+AW}8uUnAuX}T7C0xPHp+=e0(p1l<69spl$5%(546wVh-x=@fwY`>Kw{N^-8Au4o zZkuaCQQxI{rYUU?`l1YFI#xlH`pr(*@uikn{Dwl0mwP}-%%NR9-}AlvQuxjEm)9su zRd|Gqerspq*W~_Dp4Y>`GinZ|NzG0vVjZQ%+!MjQ*z$_LEHOEcouX71fwO40tUQGz zS~1gV{0W7a*E4E(S5@b2INd2&G{?TqO)$pSzVkXU0AHm`pmwjaCy&~)GkAu$xFnk8 zjsuI%qjqa`9d(Vt5E$jk+BWV{2WCHMC0+txDx}m82wWmFLBmC6ov*@HO7*Knx;Eij z4-~!%wMW}>gPT2Mk*aUVyY(C*u}(=VdJRMI2tXIUI=Ei2VFgPHOfc{t@7L*$w2Brc zy@Aa)3habjZ)Nj=N>&+^j4x?v5&(4PMN_+d6UP0)U@HCGv+?1%z&%vf7T#!u=%5u| zi7PvIq45C1BDY0=RPRbM;*NCHkU|yB3e;;aoWq@{RE;R@Umf2v&9SD}-uep%$bVvP zf(Rpu8k@4%l3`?+-WVx*K4XJPso&2S9i=~HFywn@kx3Mg?D2*OqBiY+zZ989Zv$$g zA1PLL_#1B_o+dan-qnzI9CZGVH2iD0NJtF$2!2c6&`vt>Jt8_FW>iVCECD~F&7>dgE9z!b$7E zx-?lT@@T0HAI1KB6u}6B()=`Q8>z|V<{BV`mkI4|eM1wyDRt#jFb3uwkAYABVHF5D zhvrAOv4yJL$sv#7bsF6cS;P_*RCXuuv(L6Fkb=f>f$s}pgwX*h%#D(c@2D4p?u8@ zvC81pPeETxbM6L07)??8F$K+Rnv35jz#9F_Xvfcx0|6k)I=b#O?NRT>Dgess#hD;e zAs}tR&Vda%S=;8U)iI0mBi+t+He4(4A2~2d^db6doLK;|;BM_71c0XFb7wS<1v-G5 zG(8OV6F&pp@G&4-c_92OPc8x!ol5@JQ?y7bOJv(;(jhYJ$@$ERUwV|w*(-TFNHT~U3_+Y=iUb@qg=oFA< z*%D3gCGZ-Ztc59Qy(VFpX5z3ZW|m@-o4|@FFxALdDz21et86#x&))RwXu}jwEr@7s z6BN(+ll+B(w$l?Jcu3ma=LdF_M_VC!2<|KK&nL#}v+yAD&Nm>7w26XUt~uW?TEG0T zXIvG2sXG%J-&nBf{}-te9cfX86jviM^{Q0#^*1=sgw2<{vh=8xSEK#;mc-Uo^v^F@ zW)pT@UUe4jwjV8$5(YiKp&$_%kOkni*9lq?+iUfVRuz)41ymG`Nn`f)PrU9;O4!<+ z9VnFBaMaogd_oBt5F2yAbcm4V3jtx1ysUEs z;d$DsB_&^Cu=L?*HHSOpxbdtU1%!H0joUiW{b3W3heT7+)t_-j1DkTECMKb@!B5gV?a2v4L0oN1U@#!{J)O~uC7t_fan!;S~Vxpat z2>?uxzc(mbm2#V}E}sLOk-3_XZn7oePL=j1&VVT{Bb_cKS5tEe#o@rK8t&zx z?KS)40P(TmY$U>S1~2;7xczl^33dA+{y<&kU(3om#!jcGjUVK}O1<+S4Zy*Ua=TH>v`+6QxlF`)W5}ggj01t~Uo~ zMM~;3PUZDE>0`66rnaGJEnkTY>nwRdP3{IzMwE&<5v~;{nuZi{N))^nA0|#S6a)otS?}PV_7m?l}&@c@ja{!dD3Iw=y54hnEvQkM6N~ZwdH6jx_xoi!Rx<3yt}cJ-Mq#cn*O=GPi#X?vtKB-RXKu) z+{s(q=y1DJ_<*}u-kl7ym2xVI8bIXj2>887-1{qX&!^!TT0$k|ISWYcx4mA?j-qRR z)0p^LBSlZy5w>rQ+1Ja5*PiFl;HT z@oR`bK4iKlfoU@dvFcoXQf;H~(sH)vP+n1t%H(M6wuUE|X#J>?cQWW4&f4w&Y2D1J zl#BiZTS0oQ!1_O^idSn~EvZoj9R7A6xr=$TZBK6co_F(md%4BMQ}rctRrGb%Em-yj zgjwAGj6wL^5M2}puZULrg73~ePGl0jsf^8~*yJxb?{V5_-yG+N2?R+<#WWi*fYgGM zP@b8ZcE9!qeFWF;@ygIzTImciT=>)Zg)xS-;IHt}@U;u6z_Z?kA#H_HI#Sm5=Gx3j zzrIXNhc-vCqh70jP53kvr=mqjeUmWluP7yD>pI_lKYSMd{U!6|f`{uj5M@0F1HPQP z7P@!VdgV%Qg*Wp0b8AxtI7Vb(lLI|mt~b@PvGqtGLkPMVm987i#FgQU4hm8wuUFon zyIS8qzl$9}suq2#3raQgNBtO}NDK6&47J)=ch<-7#7Q1X_S5{N9)Uy93QM$=1bRqt z(rNOJymEAKr6?{;yF;RlWwh}09hg)0&c5f3{}>#A5*DcE^2nMT-_R^U+;@%>2&Eannxa zvuoOZ--6Ktye7Upes?(_;YKY=^yK2~&2D-W4mOUN&k1Iyz!^_yYh?#aMc)b8F=zq! zw~w5R7S7fnRomxK)6)4jjPCqxi(&O=c)3CS<(m;FYw9U`W&H}j7LX%5l@Jz{+M^a zG_2p3({bOG;z#sstD{v+L}7eSEYCI=Me=Tyrj-93&_B|7npiI>G-{3iA?m&`@zFOV z^X>zb{_>GcRIJeM-MY*q7iJXOZ!6OK^?^?5GAp>wGvm5)_wu6t^cs@^`9|_^V0$<) z8j&EIsc3@!1+;~M1>~}4xS!u07o6=6bF1}aTt=+_t*bYQMnKMc$=g0)a5WmhtFkr> zh?5=Z1oOkY2iyQG2etih--Sh~)gqhn_EP|l@7vYdDeZqM&V_T>e6HQ)rp8k>Q0&v(f(JKAio zwwN6-*mwIeI{SAkoTg01{&xOQ)f;}&-WFx_UkJ}~S_&#k?dD=oW}hKu6Zf)E_UXtA zQ_@8Au2>dZ?G%~1N!BtAckYC9w0mP=|1X7NmA^1^g{JEX>BL}9$WR-Gy=0`%T6@e1 z3pk^}u5_;b2{T~8x;oLRM617K!x|9~!pa+yIt@QHA58Y3`c@7uCpEtO6$5!as1Lk7 z4fsD?0ML=EQQ;t6WmHohJz0}78>W{VtTic?Qs!0Y%chefB~u}L%0Z3ORZ5j%_Uf&I zd<(MVZud&}y&2h#6>gOUJA9_wnouaVwKz&&r9_v|F6+bclVmy4L|qQcQS7}~1d;#B zx<*P|0Nh;^VR1jM$NS87tKNk}lf_}=$E$$(Kiv0~co9^X-8*>~)!}UeKJS%-Jqw4r zUry+H9eu=U5ODLhdjKqF{dA>tA@9fNrKQ#EKSWWpCUL{h8-wwOErfRdzVqbLLcg?^ z!%G9{7xZBF<>*uGt^P8h;o{t~gu5dm_11|XmdD4X%J;9*SE_~=HYeLQ=e!6R@h zz(+&t&5CR45dQnX(jOEnur`cJjB{`!&Zy0uPd6=f__eW2`clAcq#gf@xR)q__Q+R= z9~}+v^VbWFU;~f~1c!c}lxCgkX%w*tYd*(JN1;bFeQwLe28GabOHv7CFY7(oJ<8G< z`nuVV*G=g6q>ue*BlSMYjMfm+kb-Ip-BM}(dUd5yh?ubvxcQ%n49DdZhG>2m{p{Yl zo+wf8pwo}g@6|{l@EwD{;K}*4OS;(OcVf6djp4!dRRyszW``6(9JxMmn;Pl3&k1?A zrnaegxJggq!fFGi!8+9T$Q@S@@g;M({8M>``oWoNiLW_(ZymcrVhpLWY1m zzWj$W^Sg*xB?xoEz!}k#*IGU&%{k#ctvto&#A&|s^|S89f`6g8^=UV@|2~uykz6Gs zgkc+pv9DnU1k`;`&3gjN!mvg>)Ug(`^(axxihf62Nqf=lk{!z5eM)wOptNVt}{E?Gnd0;*|AT3V#X#i4e8=R-4hD6Xdm@> z`#r!OuOq%Fa_t^YZ{Gx|d2og(e`C?Fv+uK+qTiQQ&6G&<>8H-(tG=0x0Y-@!T204o zM)#~>mnrQ_@m*o57)vG;W)rHtwRh^6nv+~d_LYrqUELeSrqqELJKL5r`Ds?KlmYQB zs(Oz=5mgzr@5=`hZ~#CR%Gg;^@0X`SQloJKYOShpd!qzfWWV%iiBDx*Jdysi_&*ML z!^OUyEwvR>j)*cuVWk-z&P;sQVbJwkKQG%iwnHVKz(2IB}M86fI8^03V$+-j6gs{j}>m(-T- z?~{9+mB$ENrv-lGNb3HRe@aec;hR=FFncP)`Q7a9nP@(TkeOx{1OP=xO5mi&Y^cfe zbI??gib=?Wu4@}qc2$?yV6HEL8%Tdv|r&MX5s`@~Uo+Hy*t_ zw73W6BU1tni$rh0uTyrxbCUEjhSO-yU7g3Y{rzw1xY=}@0mrI=l2pJ1Bm0dH>egFD zW3?@^%gLJe9l&W~c-nG$zTkh3l#Ls9%^u zLHrvzDPy_#qxWwr*Gsogsg&4@9|2Lj9`+woCP*!q@LD_8O0YE>__yABO|Fjm=LF#y zp|WaeQ=ntzd%fjZH|0PjSZO&9RD9yG*vx#2uUxrU+pHPzg#$tjq@<;d;9kP#s`8V$ z@@BtQDYz zQ$8C$5QSfvc_z_&J;N4#T^5OI`-{#u{<$$u)JLAcPWz9&JSVkG(BmvsJPu8ZKrFrY zLkl95L0WcbUen}xv`}w$jzCM$s_;?oWmIo2q&L~hG$w6&`x}rAP9vT@J7px!)2@j1 z>V*gcxn9bLgRWq&!T>l+CgQj85-mCksZ{dRhnMPO<$Dc<{-6+z)%I%-uhQpEe=8-84Wmj zAB3Vni6#qagTYkxGA^)^X8X@KG@Mp8Z^OJNZJ94XGHI%0rg?G8* zLZf>*njiC8Lv1_g;^J=1hY4iQk(V~n!CfRPM4{5k6U2H3n0@Gcp~M}9%ebhWK>!|_ z(DAPj>bl<8w(h!@8<1&T9e_#Ehr2hoB5W#q4;LG)zPk5ayN@ENjeo%wBtS(>;V!UR z71!p$lbM%|1}I3Ehem8YOtdu-b*@J4y0)fb1OcPZD7Z&LE;shwfV_59iJvk`XAkM@ z`XW8!R?y|bQi;zRb)N{dkG$}+-Z}i3Q0Pfl-w`8@SbvG&=60_s-xOb0ENtUS@vZ}p zi&$=XPiBulOcFYr>tya&6ko)jotC>BL@RGOXL+r;h35_=Y4w>M!uitB?E|>i5mMW_ z*+rz=h>Zx>&Ig%9-zqNZIyT2HHZYKpUHl&6@5xSQ2?gkd(88lZwcOFrXbUzP6FJk^Atz zjoh|6e4;eagdRGpfE?qsJ!sWFRrOTa&BoEDMK!gOa8qt9vrElM!#J6B=B9wMKdHyD zZ=riy_3cabpJEy2iae#|UZB0Do6^I;xdekfnbC*WWRcTeI77=I=sxAHxZb*f=fR_h z?yBswu}66003zt`X%1rQlhRff(&;*4By?b9e1X%(6~+Iep1L&F*^NlJ!NxK4AwQ73 zd_d=nS*a8_6grQiD7;J5zK^Khx8g3SlBf?bf{J*xiQrMF{q&T$<6fHZYVtirU z=RQ||`b0cvl9wJs>WzYx#mcuSWm8;eMlSn+DR?csS3pt=@G2@<_Rv#PkWde6`G+*i zpFID{ok{nQKemxZsPS0Dc#`}&Kz4RF{d~v(D&(k+keB=Y`-3%+^|STWa?0zf$33~L z$q163I?je2=j5taDj*3H>#j4}clGtb`n4pj^WJ6Q_Q^G2)2$ObgYD12_eQkIqS3;i zX7=zllTo1TtB_iDZo;lJIx7L)bA;9cXMq-k*R<{PP>J&p?uW;{`D|u)pI?U@rg32- z;eCyJZmz|qf>v!uU!J7}TDq5pb{O`@CZ*4m2w%)=rye1#SgUm(^bRfCIYCKA=fdOs ztgZ zdiCOk_TJFu`Qt;WM(A-t_;x2Ibk2z^p468wPxn{($}N{ejQB2B938Ke`rD$d&KHJU z{t(^NoY)v<1r?5$U#B`_N5sO4Mi;>um~G|7J8`V2(_KJZB;Co`(tc+0dUPI0{W9^6oK#u>z_v>+hy#ST{16%%SbURHX-jZTTUyx3TQN&p zY6^Ll@JIm%!o~0(`DBc|a6G#ICYq>TorChYR=T;q3~8i$++{1A&^y&O|9YHn8N zgcL5vo?LZ1)^bnzkX+iA*353l9HtsBXMk^Nq5aRK8I-_^VgrP17gf#*h<2&?>>2}m zLEkE{;<<@C1lNOjZfjmm`EeO$&h$@J)vN!bf^QQDdbm$5+&1dl9N5l4d}T^7@9Y8L zT-kP8Wg~-R+~$w$B}_IGYkPP+-yOEY&dO1q?Qc#af7jMO`8R5p^*LDo9`%ruxnEoV zg>9s^|`Lck$FR$fVpG%iNeuTR@EOA?fR)JpR>@7 z$GL7>Cl)Fmwx~yhqSz`$GWtJnruUiK_v?NxzJG7^fiPV6bC~Wy zW9N-#l2+#KRMztnjEg0Qf-d|a{MJ6xRwh>8_9Xn=$@jC+2b=uUfWq6BJpyIdpeq%< z-t#K5Ddg32JC6xzw_5|}y|qOzMjiGBhC;bXMq0!^+Xr(@lDqANV1n$_f|u*o?$y^j zLg<;AsiN3RQ9twN>l>j*H)!EpC5=b`9VZ+8lsQ-yN_x9iL81ol5_Sffo;0OJCZ*?u}J&0ORag= z`eo|wWBR!a_UYycqDW(cTw*kbHH~*CNad^eJfG6iuEmHg>g?k+WX`_ zmPaqrJnU%)<}RA1vTyuU_IU`-MX|qb709np%gmS!c6LCF%lN{$exP>Yt5A2 zVz%@r?g_Ic`>j()1?2_PBQmV+ZpZ7JI;X<5I*z9aJ?DLopvOS2!@yIIL-dVs3)=M= zH^+BYf?-|zSNpI)tu#`?+;)MdkzCt_rQ}ax7mF9gW1!?r5?%3Sjk_z^Nw8<=7q(f2 zD=+eLR@m**!TM$HDVc}eol6GgiV$?Ib4u@%x8>dW3RcZ{!&+oy^e!G{g9#Q3n zmy0d;OQrFJVky4<(sf!b(@^#)79!+_F(!~eM2>nyI_rbr&5et-Bjin(GxoNRClU@B z6kcvLH(Y)nj!u`8v>tQYZk{n(LJH^uEzEEYB?>BlJn$Nk;EuCIOS>CXFJ+1H)WPVIY&VibxKkvOf zU*Kc*AGo)rcDG~Ae+?a;Q@g3|`*~z7a52E=n$oOHx-pE@XJKiXw^s7Bb0<=v(z6b+ zI(q|Cr%F&w^#pUu2;D`CNL&-_!9Rhjd<`H?Cii|7n8`LHt@~7!Z*8sbJommSslmTi zzL!wCqQpYnFB4@p$;;$SjWs5SmU#)*9_?t(Fbby^_zu~*j6&!)HE7c>F3OzjLpYlb z*`BkG50_PmEVjl|{}5*DY$-i%Xv0kqfbC46EODbhMg^)=xNll;{h66@YHeW1yBM+V z@Iwcyn-25QGSu7_RFTnQEu%VasDFC)X=hpD#s!ZjBi-w4|Ln@^=q)-}s~2#6jh*{S zZkp>jYJH~J#zU53%{fA9RP(PkfxyS4HZs!pVDKnu=gdDN9lE=E|L{Vj$<_Y+ z@|ec=5ZVnZJeW7!zuCTlH>XGwz*I*U;9)o-SM6tt__aaiL0}c>x^_063&NOZa*y;i z#iNaEa;kzctG@nv%734Ofe zy>1dONy!tFxWu9n{_TY>SF`qej#4qnkFAWvbo&@EGMbAovVy*(?^8nzqWpBPqH zFx%e(ktIfulg4vae9s3@CKx{;NTV^iyiM>3))UPsgO5ox2})yK-n3EXP1NN!#YwS? z`Rl=AJ$`$W`ed#`5ay|inO62*V(Po3#{Pr7FkWS0)EdV3jFoa!6j0o@r#xjb;Dc|l zJ-vZzN)y4=h~eph?Hv{Dc09~Yuf(-)wspZWe~#&Uqsvc?%-hEMb5>P>t0RvYSTE$h z;gJmX$V=2G@bz@5!SvBm!PV<^CAN(@<72V6Rb6g=u{u!jL zds0F|;B%xjq=X!vUU^4K}7vZeH4AlO)IyvnHm@T$`ng&*kPx~A>&qg^9N^uqn3mk@u_ ze!auHYqV)QZY#<~6YZzZ;(>kZ*8>ypXR(e!DLT`%uE79EU!NUtMVD;Mb!}#YZfH1q zTwchgD=sz+LXG_|&)?B-f-((noMc4m85cvAjjgi-5#fIiLVCYBP^0?r?Btb@+EM~f zj=!a0OH^|mhUZ~K*)(Ca-?0^`hMs>ZU#NJoeK*H#Qm$9eNBb8nIb~63c{)&>RXJ1a zj(Jc-N2|3yVVQ&eY8&-TeIjNJLh#!BmLem1XQPxlB|*uY=xe}Y9Qr!Pk0F+BoAUlN zDNYl^hH?XgOIL7gf2Poze8w;{W`+50ekb;v|b-G$a5N{P1y1RHrzpWo?J z8+e^=IAE~v*S5oeD>3!%iYxiokK+;SYX16NQ8WZ9+@t-M%^k|Ayw(3D!T%LSKi}vt z3zxlDfmUMfG}b9pheBehBgM{jB3_ zAC_}$hKvsO=Gh!C2J{)!;bFFB6fEIs=6lZ5m=GWUzyQhf4+^@&nhEtcJID9%V0*IJ z?d0}YDSJ37ITnTg=jI?Hpd0+H;^88ppWs$tcDUMpknZT1DFI~b(yDTIgL4on5ldwC z_t`r-DI{XH#uYL^nh}w2(U#+Xn}tIw4jmhQVv(4d%#}b_c-u+l70D)n!)^8o37xNB zVb*`2+(&2~4gPNz@3R`E?!It>ntwA^cWvPk{Vapsg|qfMjWV<}JVc$uX!wQIyX%im5Qx znB|vxUp<*bPMwvxmiElzm9NL9B(d+ZErRt3I9BHWS87BABR00l{Jzg7{(31BR{psA zB$#A{K429}HKG{+QEa(FjVy)4RP$MzRztLew8OY_|5*BIEcCVMai_9DqAwxzOtKQ}@;?^mRJ<^lD41sYadK$%)JueX)cYRa90! zZ|oMrzj~)Sa07S zIQ{u*StC}WMZ*6hgY(t6E;X2+9u;!dI;LE%^Wo7=5I;u?9ng6+sOByQ<3q9+gjDARA&Gm-N~pv=4DvJBZruZ{$0n88-)$}yR zKZ!w-I?-tnXh#w@{(4QMOGV^nzC4-1USIB1oSqIXSHn+Re?JG1+P>dG{5Rl_f_0i- z-8uOlQzl7sb~W9Ti`T1_-~MLnbj&odnUvq!@yzShqTcE3Rx|)y1`+_c?)wPpDx}s2 zVj}<6BH#fpsYG<=#ERD1l!JF%`={)|UP|}>IolQ+j512xue*gg zh^xax0y{H<)V?|ttbhzDUsiJD5iI3hRmIP+Q>U-VS{IYXYi70DNUTzuKQqGCO?HLt zm)hLpbnibVdSiQnTu$v^HIaf?ONCA39#o<F33TBRnT#8f%&S3{v+kANkR%t7wxw#1>qeCB_oLW?z~&Cr+I`qfnHwnb8) zo$;Hk5gXl}4()i?I~TG(d&JXRDX+I*q1}$}YhJQkZR=6*HGca(gG!5}46p6Ivzj3Z ze)7k(v{sw^$L-;1>|Z*tK1TlLbS)l7%R;rpujYp86{16O`YHPBgY4hfcld;yE`Gvx z4z5;jtPpI$dkJ!@B$};yU)m+DDuS~gw;P@d1Z??3IWj#w3Ua>bE;r}$+u7*xkRm+_ zt68)U_BwC!Hrd_$a$@h7%LF-9HRA^^Zy>le@n9Xxt%a+u$^yt^FOP|mD93%q(SbMn z>Y1L49*&3K>y3s3PG@zNafG8(J>>I(;oqo%GiLr-5q(y$a@v&KwAv@d97di%vq;LI zRIDradP~2I5meo*o`yZ66IwfDk~S}q@=rSQoLQZyRj$sKOK%=0Q%xnH9w?d=P{#h+ zyoOGIS6V8YZTEh{jv|FlPoY3u+NsgsuccZ|2q?F-`wyp#h zm$(VR0LwsJ)Y~D(N)^Ag6+OMDdMVMV(Ej^N2wfRSx^Gm=scF28Z#7-ei+V`Rp7LY~ z;_~qi*+3m0J>$Q(+@LVhVv6PA}(nOZzgrQSSvwUz=~>lPDTCWR&4jKhXnFRg-vQ$ih1ZD_P-?T~Yo^ z5F~0q0(6=jikpcfIMPkJ1GrYxwGx}J5>Flb+u9b>LCpotJT>&u+Nl+PuWE4YFMf2R9K^UrfTW_l!_ygH3LHrGQ>AYhkV{>xKX4fwXsR6cApx|VCO z6NrkhC=dB6|JJU*zE?M|TRKn!`N;RUZ0G({O#-JC@Ti6;Nq=>(H01hhK7^FJCdXpH zw2Z~UN1!w$8hOpgaigyWPu@eXVQJb@|iJbaMj@X~`Q{iGz)GCkOM0 z^^Zu}j^2wH)Sp`8r5FZ2v5E_v?*+jq3CVB~;u{(}p1$nTV8QLJ603~{=;{#KVSpd# zXw#yRRBbts*u6ZYI=js|hj7Sf!Y*Vxd1bzmM&7(|on+#}M}gChgz+|4b9;Q}6+MUb zPnlYFGFo68tOplC#ze@+JFs@Kx2nx{gp~rlcB5kMLtZ%?lOxb;Ms*xNVp~iar>09$ z-d}Qgy4X5>LJk;9guJd!jv)N%A%DSLsP`=3LcXE|yYUJD7wpYe&g|T`-DwwA+-3Q= ze(+@Wlz%;hYu|CbFziwZML`z2iQj3>bVpCLS>l9DC_b0&{&H_-26kICbkgtZohj?NWeNG z_k?v=VZ~((BYV-O+X=u1viM;SQjv$Kz2Z+5)+UVaX`7kB&MT1A_kA(xrN|y4-4dh1 z_%}d4ZnuUKR&#-yA0zG_8791X9gIY^C{V#}AtJYj?Dm%(C$zK|7f-rOxTA-xvs|0f z1otw&S#=n)L}O;!Eh&pkM7HK#S)aG5EAg#OgAiL-YPa~ZcFB161yTeV`FSpeKC4D2 zXXLMMw=JIYZe*H9j1DiWt(hy5!q*m8y($pGvQeI!;z!TZ&!!&a^hh!rR6O^^y1o~@ ze%2j(87Bv-|F~Q~9GP{flCRr2w5fo>jbBh!3Nw1ouRjKWwi+3VKQNi+9)7c*x0g~E zCB_f`8Zc+ZJAaDAlM~yaQk`cLCkdUzBhqI9)D`@ss%t_%*KC$HFn4lB#<9$6elDr(17&-FxWVotfc*&RQ1y*qPZb9W2gA}6O9GQ9(TAj=+t zyu?=iL?oF4yx*T$-Ce!0zI!mWquZYo#10>Nz49`N9(vYdOCD#KEpCdfi=0UlI?rNO zq}j1!$0{!shthJFWpyNlAFEJKOUd#r{8|%#K2Ev53IpfPfigfjeOz%*jStn;Ajo@N z%NHY|8qVjzggSOot%(~Dg9`GoDaGPZtmyRh_#rS2;LEq?RtiqtPtvq<*uaaIQt*yf zn);uoD0@n%elMTZ=jl@N^FdA^byr=&%+vMMSohU)^Zpdw`unu6L`g=sZaS|4XtgaQ zb&uk?2YaZ1!e6Ul&fmHQ&H;}7*uy>N#*r_j@TDuw*`ZIQUkVt<(7Xq;>WvUk()Wv!TX-1YZ{(K#)_NbdnQ!&E2WD`W=c?}`->c1h zcWbrLKjY@=U%R=7W$odk;kJ9Ki7) z_!~>q`IY^Ves6Lu{M6&8@B$~eso|-YdvM;9ViX-cJ={lWs=Dgg_I&^Q=rg0At6pO? z#pf#IKxkhwq+Mw+;fF;OIGZchE<@YCNlq)#D!retFAb#Aau03LF(WMaL~%CI3W2ThH>mC@dh ze|7f_E&$KyvrGCJq6;ms)k>pJ&{cC7WDkHFp@ln(*Ye)Wv2l28HMZ_)YB;|Pe(+4y zo;K=tZPcL-_}}OD8qQ^&%Z-m}t8a6u-ut$Sp9{zldTAuQhffNwF^yIq@n_ucn5f%e^3A2y#+yyy>_*M*Wl{!^Ir82`qQ^n zZPYl={q&c*F@MsB{+@Pmq;UkZ)W@8mW@eFoNf6#wr$G2EqZRj{tRoIe3)QhL_&_P4sX-#(D_wk>lXdCG4RcH5e#KRul#+@6$K3|vjV ztCJ^BmXmGl0>|vFFXYp*I+8Ft2glIreX71j5&|SNEEBS4%)|fc7e|NVbdqhv2xtM+Vmy+bsN?{uo zB20E?rDpf`EfsA7<5q?IF4w`;8@Fm7Kjmk`y0vw$`oB(H7qjh&q3nJMs%?GRyK`%| zJ$W^HSk$)^MSWItIWbP3Pl)1`>!l#T5od?NCeP8jcx~nJ*&Q57tO&LyXcJPN+yp1m ze-TKOxC;q;ma9NNFGs*k=eL>MzwOMW;4yXP-=+;~yUoGf)=lech2AyFjymD}OmYaF zzHqs=e7&}JXY|1P$un{_d6#Z>*Q3Dr4jCezwr$?fZOwL(%@Jzwo=UoV@i?Ez);f6J zzGY*#Gl~v66IuGC4K#)Si|OsD-rp`DBod-GTtwEPj}Y}6=?lE6XMNhmqpn{FPVNTJ>Rp}tH|SrsDDK&@rJP_QO#J3$ zmeA(q(4(R%gD-}tm1VqnDTaZu0FB1>hJ*m`*%4ZY2j?e*?&X+q zWBclc^=mq^(0G!3^+K+fEKFr+&)C=({@WJVG%#**s_2xodnJ5#QojNh8AK-F?HcFd z_pKT0+cG9rWlV(Os?8qHWQTsAJbkHnXLooNK5pNM;S*kxv+Y`BKG{)yjNB!EY%3-I zD{31ygcdG>Pp^>yUn`piLb2!yY}LME!$c+?g-1y;rPqHE}N?$XtgKlk&yUnLgS zS7XzB(j53r&UtpJN*n84&0TWgp)-1tnLC09@`9YRbr)auvV&}Wb@#TCjU}HW!;hc6 zQ2j9HqLr;k>M8!x@8He7H`C6EoDPL9Ys2H_4t-rOCrfa1Cv};FB{6>`^CbM?K{AY- zz7w3o5!t4lJv+A6xajBirNT{LPtA*I5L{%KIq>qeo1s^5#Q~n;9yH#ZK5PxV+cRgZ z58V`c3)!hZ`fw@pD_G61=R3ps^ICar^^zBC4z&Gl{!^S;&ZZl3ZcdQK!6^D3S%shNGWBPXFR>`s8#QY4- z`;kRopT3xW9xM4_4l#`Mn;xTYR-z*m^X#4A_NBwrMmoW}DIuPAsfAp8z!3Ei9e(WQrB;2R7>`RUjGDv@`1O~896+g^$7b8pP zL-f`EpS?G0vMV_f^dk3tMFI&V_9|4dSS*smrJGIlERsnr$xJ56Ob_}I`agQrn_gum znaNCQQDdWN&X7$uyU5xLg?&q`xghsl-{*1S=Djyp0LcQt$^dTOd(Mdy;o;%o;qKw# zAzg&A4Ek|C9$?HUZ%99mm3q%(VbYN5JZ)b{i_YnD$i9JN8f*W{d%D1jbMz?VLzsod zzA4GF>^%9-^?Q%fjr&h%cU#D}{m5P*GmLd%*eWj_e;;GuEyO?>$9e3(g$?*KfNV^p z;)!(O!iCr#b{21;&JN_ez7FIoc=Yg8>VDjvR^&r5#j2~Hv-v_1011}|WE4vc9 zySnkb?dC1!jw49&{QiD)CW3a$)7C}rD_1e; zA%q%)(h2xJ1`HJ{Cw-HrdOXG$^aKG)L0CaRMd!--Qz!;Qp;UzNK#2#!W;lcZs32?5 z)K9M64JFLUe*!`0-s34wuDHttjlk#R%R34gA6%LUIhlndVD#|MP3ry-x z+~!f(`|`&95I7VzRG<{B6^@NsufUK;=Jo`CGzi@a;qVRtvajELjIgs9LVE~bRG^@t zKzN0C0}3x;kl`MU;%N@9ef9Qa2%}k<%L25D>ITA#I98Chjh1O!P9dO*bB%Tpq|vfm zE#=yn54uhN;mccL*wF~I3T|#bcoIsAN`gXl3xb$Nqzk7{L_W(GUjUdk5V~%W-@dtW zf&KdkqQY&Gx!JV}WD2gg5%9jc`5=rf8YsHxJ6AjX7mUyj<%bI8BuY{!%OQ|06Id=E z<*g#8q3_DMlhlK?9-&ubknN$OF#LRW{ay&6od{skz#2vp)1Ap@K@TeUPF@P>D$F0g zb2bE42eU$_?Q%u%_T*EJ*nAM}?&>(7Mqs$DFmzrIhOP3dQAhgz?E3wnMQN;uGFzxK zs0Vy2@ZKS&gkwZu-#p?;8orL8c$1F;s74C=%;(>`bS9ms3`ych3NURT;HqS(Jb!)r zVYJ6K3Xo&(1bz6ui)UgSI5xzAw9_zIR~@7BWcxJOE~0dYvKpk3SL4qRxZSa3f433P zkCxLt3h_en>Zw*v?Z>N^d1NKf#&yQReT<2p!G|<}+K7`X}Cpc63TmDfts8 z_6!g929FZ`Hy=Drx1gIjXhDUz4ca_4IuQNtI4@&xSA5V873438CG9d{w!%eEGNrZ|PZDl7DFE zXm{%^G_65R1DUj};`AKeB@GITBq2SUUn7z<*avs+UYne|k1&*!T?Q-V{+HXFUWQ{ZJ zdSY0{uE>75n^~bGpeF4RpTZ-)hM=!jc$96FmpYfIc$;4wX+)MckHGgqe%b^F(%k3F zcN#y8W#fD>25wv6Wn0b#@&i5^Qk)kx8mQ=+Pg?4RS6S8?bIgtVk7J=>>z#9{Wh@^{ zew}+7Cp0pM7yIlzX&TxY243e^73QGnmqW+yZ%LMX98ZqZ2eeZ|n1;7l0O7I5Sq&sE zG#NO%2iUr~w!?A8j4|z;X>e_MbTQgn23cs=T>2FHDhj_m!1>DH(kGb1T;L*DMdr`+ zOBh(0yX38c*Gqj9;8A0p@eJhw(yE3B$Ebbs(UtS*B>e`k%!e2wR#(G7>bxRs(wV%W zi?W=vgU~2X8n}7~c>r0-vE@gY95>c2oc;)!t_)nxX&NUqzPK%3>e- zK+~NVUQFxqF5qzCEf#j*H;!xZpd4k{($_5Xnd{Qdf?*1}xpHni=t#OuI4mvFPq&$a zl`B+$?2|s~>c+@xyTv!aqz&-n+&x6U{A!XaHa6+%<~Y4J<{~U%P6TDBw|LVG>BcoQwUsQRas$2LF&_M_d+pIX=rEA z3@N;kSApO0p&*?V363p9*od3D?IA=c+$!YEP>!o{3dsuHt1vKE-xRj2Q=!DT8X9Gk zze2!>2EN)K>r(hpz?`a-raKRx(yoPA-5wl9V57b;O28B}thsWo0+EeLF~TE{N|}QF z8gMJDEiLC&$t*18)q7WwU6qV`Aio!7P-B7vIhy64y5y>-O0GCi$djQfIJ(-Y=T8rc zmi=pA>SirW8Y@x@UJsNh+^z&arf*K;kzu{@f&v;H0d+*#>$G1*QST1>NM$kliCnft zMP-#bpQ2Ri38Zml27%MOdJ7G1c7<}7VZozBR{z69;_~+Gp-Jaj4>~a4r1@ER<&!f7{l1 zjGrncPMjK8#e?~CEEg4ahjBK3sx*dDCP2VyQZZhKF2W$eN~MCPG;9C%)27G+a_m2q z6}?zg@F!3%Os=37WoVlIwVYch6}Hbl8`k&*toElL`%t5-W6OG^DdcY;6j3(p)e_CY zD5f!wvA41b-ZI(_Q>wVIi+X!d40^L+Pmqry=@qa+o9}@ePefFiafR7cVd+pMUOYX= z2>cCrwy&gD=}SXJp445ProZTC`=OiwB@H>A{D?;J?lu#ZCi&947}(?VBY7CgT4Vy( z!8`Db)6+qtjC8z$k|Zq)m%KvWW?z2QH5q(SLr@sRDMh+19#&7ta~uAnH_Py0Sq- z1rnav$#crp+t6|7B8T|5JeM2lN!j{Tc5h%nk{9X`A)O8PXMU^UR}LV&f5fyyV1 z^>x9++87tWM?H=m=d1JZp9u_iAw<%aqZp^cCq&YWGJx~I*S994UgxP3&|@?r{|UaQ zr;51C{2AJ*JeI_dbRvxpLc`L4bD{HJ%ZeUo(ig@;@`%?WAM`>)F=k4G1d5L|CXS?c zlXW`pDFe*$?fmT=>T8bMY`|xcL_h*w<_i`JEJMSM96V8pgBpaSUIt}(&h^JX;zaJTHL;%0D2z<##J8$h<91l>l@HB#wpsA2ZYZQ94u2I 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

3#%yDkEZtuXUUC1)H2i3QttuE>eG{5> ze$xXd*;0Z;sT^*`d-^2)rrlh*dGLvS__OsAjE1^;^RbUNml3v9FW zsPyqs3`Mkmm~E?>JzdmTngf#^CkOX7HE>p4-sB9-GPs z_P^!oQRLWg;lPCj17`cK&pj2%^5h57qwRNYlV4`9E!U}X5bviL$&~NJpY#>UM${)8 z%SZ3Xlf?4UOHdriE8k^Njl$`-9mHE-!tw8847~9ekdG?2YK%}$Ei$b4zl%wBz0AVh zlgAI!z1!E)qZ?nOwZ~tl)|qSAi0-G3tvO;AwWgsSw{KUcF_-xW=BRGvys!lCT3CWl zEo`P6^Zn`S{F!unaVt%(olWcQ!+0)TPTjr4KSHE|<;rS;U(1HH`5 z(UWw3;wijp6&ZnTaGRX;JN+!+EL#v)R*@AK7t-p?{j@%FC$%h3vB1KiJs6T^kULh< zjbQf7WysP)Zkr!To&9XLBT)Fl)VZ`UeJ(9MzmP`8#?!#yPy}ChPCcrmtRnAT53e^D z*7NT*51L`Sptc2dRSzK1kfSc-_T;we1s<<>?7hCg&*W1$m5v(}_E;Tyl~UzrXDc z3%-b8xVpMXoQ8+#Djx+^nSlz30)>jCLX{pi!guM+X!`K-nRFVDlOSSn=72BBJjxO( zVG0-RxG!qNQCa=}{`sr)yU%Zg5UX%K&ejZ9n_XE~Fi{!WqP$=q;8PcCUH?5b&c3ni zs7hSd)miFp_(s4|XgZC;JkJqa#-ecRgm8Rz?H-H=!x8V0v{TLzlYhV&w+cktR zeU6wS8V&Sd&^sXuv;xD2?S&?Bx`wM~j=6((01RK;940BmN<$j!E(4?Z)==bD!GHhp z*FitF(TUjAa}{pKmaE#HF5^jo8WExpXAat|QS1+2-lES|;{9^6n}#`sX=zcK)N|ny zC$?QYGX@?}z<9TT5D(LUeeK~;sh#{T?@10`ES`OS*EPS$Ir$;WY-X6-#eaFUILyBR48#8urHq-BM&S8 z^UHXjTo2qArP-|~VWdSEp|tQkkZ*)B1!H1tISaUQ)s92G-ZT=+I}TFj90w*9EXSSg z9cMe6^!U5aZo)I}qcCw|TPT?;d5pn3&W;U-QD7QRMGc^n&t`zmptf#fstifA-O@cl z?R{b`iBCNnRlxuLx1WchSol=PY?I?+f^t;pYrP=F@%4L8a0O?@=;3tW!2tKN zEXSAacKpke9AnaohR4vJ_n7x=_sgW<9q?%Dlpgg^+2%0^ln8E|`#yyi|MBZP^w9=E z^GAU91b9H1ryp#T2GjS6Q6*1)0$(xkzWFs^Tsc3UMrb3A)I(vr6uYb2cH(ryM*?- zIGxL_e*<_R>P7o+|LMy-Z_>($RYL*qof}WT{NcN4*f|wk#6T58(g=0eWR7}XFH!E3 zq+birNTspDt*6Qg_u(DC`o}M_VGsB;`f3n!Yv>Gpr4nI#GY^VVWE8}iN~(P$ZbYV5K+=}QA-uq*yJ zCN;82Q$M_NF15+esKdG1vEux7!%$MY04B#(AJ@}$b(i{MTg7T40ey#IaTD4pe< zF1@ILOT0O6%4@FQe;nSB8kRMjnJio5)LPKO2A-;V1kg_4*Sn}8QJ%EqHe_X?&ygYQ zH|zcDkKW-C;}D!2r(+Df%@|PkTf*S+B0$kTNj3RM60!RbPm{6 zN4tl6xV3sAtuH=G>&rLN{L}t4dt)?R?>U|NPraKi|KR88!aLt1ZtFne(BLCUbIa_M zdHO6pp1hZ4pFK=#3o~pem?uu+B6_fkFwT=y27X)Hkv+RuG{+NrVB{3uQfJbc)8oi? zE+ig(8Xjgr@A731IO&f*J^C76tVGD?H6|xR2t}d9En7~$t}MF(*pSF4wYt)x(5)wg zd40DwE%MxJUC|XpPN1l%XvHd`g-Pv)O4gOPPpD+OdZuAZg*sL%D_9OwRKQk7>tS9f zd8nfXE`?-)Ew3ah{LIX?g>gU}sL(5@D0t|RqmtuM#qQVNq#a>&DdqBA6f)mc8B@r5 z%qpG&+5%r=j2F#90l*nV5|aKm%!H=;El!gdlnF^6i$6U4BjUYXcf5hPO=}ucu>J$HQHS4^9vm1tk7h?+aBBE zil>G*+Z(}Bc~7q(8B_i*b?o)EZcB>-^XjLDAiaA`8lY@T&Jz?C_jmgv zo-NM&#_my=otouXO=w6jw5i!VKE$&EZzwvvphA^Tpb<@_j{L3CVVkDs=3`vAYOHIi zIMH?AB)pDO+ovas9$TRhM%#9S^kwg92vB&a-VdA|P*%yi-D3y5@B)u%pM9&*Rc{jw zdD5I8@h1Q1Ve6F2zyL-ISG4U9ah$=T0VrG=o!mC$eU)5k+0|veY~+pNa|L`mMjYSX z)8J~d{z&42Pw)j+@>MW34%^p}qYhVI>tQ3!YS?jHM=@0%Jpkm5-w27G55E>WHvY8$(V0i)tJiGgo@V}`zCywE1ck_H78 zVLc7yk&KRLkNu>_p-Q((p+;d9_-7ar#iL`y{2E9Uf=eIRcj8aXm`c}0x%t*1|BBbT zCfOVc6&LoUJluN4xx!>)=Ef?jr*YF2Hx2jCIq^krkV_ahHT-8KS1%*Id2;Y|^W8%* zl$U8N(sMEJ93|y(p#p3<@&^qXnN}-cBOi1`KWT(H#k?Zi;>xy_w9Svoox^>Xc`xVA zt&~2)o0rqD#LwJ(S5)N!iC$bLjf)<+E-x3C!BFeq$>mjlDK^F~yxKX-g%5G(d}Lqy zZ3QRs=#d|6cQ`lsZ(B8(I3}E%Y=7{2>68EarP99WbLiNye2Rsj_4R4`#P($`ALpB4 z0;P+4w_`>8Ic9B>jAE&eWW)QCEA=P_pA zX?$@GcU(Fr>D zuLQi;5M~+G+Dt&?IM6UG9D#=zdpJiKyyjr-W18H11?RRnO z_>&J*>?ZnON;Ekq%fpYgZ)$+m3sPCjIn@IgtYiBCj9f+(%zf**a^VyU zMssXQyc3?88byN$YL!>-9Df~S;4Q;|wC_ir?Z@BW)g;e-c>hki{`v1QBKMd}Wk@HyDufj`EkBc0*ppVFoL0=hA%oC~b89knLnw zSRf^z<|pFN1>S22&vU6#j5}D`kilBpSy)=_O5d>c>IrhR>2KQ7)49i~qjw>l?mY>wM29DjTlMJWFbwsuH7{-HxruJaiOutK?8MpV zPP1EV!FxIhG#KUa656rW^_%Ei^Xq2)uYYnSgqBfO$epZ% zj4MW>AmVDyIm-K2ZC653JBvW)WUSToP>H-sMhcCW@TO4NQIIkwObC*|co5@KU<$b; ztd&JnI8^O9lQ762g&XBVMdXL9Fr6OB#xaGKU4VmjHe@0vizO3=2y#K+h*S@0G=fvn}(>1r$<6yRgjNZ50s}zgRb-nwI^x+ z05F*sVu?CRUqs*2M=HAt-+FZDU7;YS&@Ma*sS3cBcLEx-U7?tW@46Id;|+!5lf*Xa zh6%)$7lbhdK^11d4GMk+k1gpUD?F4_l_k9xm{g@&8ua*HVUcE4q{c8doTRT*PW9Na zkHbTXJmN+gYyl4%3G|XUjaQb*I#tFpO;`O9PY(D4Ee?RgE9Xx_Bhqwc$_lv}kZh0m z5w{wWG@wdP!X%C(UIkU0V+{W7kKPG|+4dCg!J>@YUfZXlZ?Ivv8`;mLpALZXB_8K7 zaOf8syZX?t9VTP$6O=*PuOg%pCcadxouC|3Dg@&EIrJc1N@MnYtZW2q6@c`YG;|50 zutp+{Ax^q0@B{gzG$`$ML+jS37s3!@S>-5bnmi8L)6m$z`tiFliRl63*zkF-rPPH| zi5JCReRwep2hy}2Mzub(|M{nacaE_tZM*|qDov}nC$CbMinn7>c-;0V9SKi(qX`S` z&veArJ!s@3dCti};O&X=Xd6tK_V>ZIgOj#vGCm*tRLqI3~xSzu=1~ zRvLGPj57s4(dbOJ?evs@{l+cFp?vUEEUK`o z2hMCe0D*&*p z4mINa;GHw+B3t8Pdp3Me<0i$_c&0qGx2GfOJc+TxxGrI|qQA`-Jl49c^97S}tz1E* z;Q@EhFdm~P!Oh&_c+ihVe#ey{IZlHY#@NdJNw`F@rqa$Q#F+$tY%+n_RshIz;8D0qSZcdc%>n}JX8qcQtuA6#IZo(NuT9oFdr zkvJ8XYxI?UDGmB@tkA3AsFa*nu$3uOP6=5RH*4?1B&iLp^i zY51mW>z6JE`#Rxi*_%oJVYSk~v~mh>L+465B>Jnx{r2S0xc1}s&cR;?F+ycyrEQY$ z>4oKdBfh0g=Spd?@D|6bNu!Rqa9$mS?+r4Zjfuq`hIlVB9q`BU#bL-V7#SVU_FeXH z%JJ%yrR1X;{>72~D^Kde;2b!D9@_b>4Dc?rIE(>RURGolUXMETaFP~nhlVm`wn$B5 zxbRNfrW`bWg6$xTd12M-d>v!2Fgtv>b)1ed@K$0#LxgdWE?l_4kly5UFy>r%MaVsG zWs6=8=;wHb-+%BRee=oh(&WegSL&F%mCpCCrom3O4#RIev33K#DQ9)O%TN4n82WJy zFVhf1hG%SRyFEjI?e$A(t@q~~=5ztQKm(FnB~Ma#3wia*UES(R(^Dsi zM|%q0i2&Q=aw}pnBKg)ZhVcuR-bs_6Po&#zY!xQRcZa9Uk$KL=3uH@c>+3|WWbWCwke)8zHf($k;)=jr_){Y{!UbBe|K?A2*qhfPz= zDIfpgpVGbSpQn{4*HY`^LykR|Pnf(oqh%F55hlU-!ELrk=)eK;51bx*Lp$5GJJQPB z7wO@hzI5-iQ|X^aE~WGD{V@IDM?Xs^k*jIKktAbH{pnAC!prM?#7UC=UuSCBSiJW? zgOLB}bpoM)mxVm~L`2gup;$Em75T!aP8Vq1-t5GkJriY_kA7~^luCIoo_Lvokzuo- zx;hUi%!xY&tltOZ-R-eWQozei;{s;6#p>w>Tbxuv6}}d9cSc~6VJKuMq$qg0wZ<(! zp)3l-0m=4Ru62ZhKz;?p5E6xfk5Th??T_%fU|<2LF=zO}+dKPXKM->K4%$as6ad|# zr}Cu%!<97$2#~?Z7`QMp;fOxvUUYC>;VMVso)&~ssaw0aGBBsV74{VT!T^cFm~p%} zyq4uerSeg$i+k1(_0V2V9tiv>h}Na@uB+a9akyfyVarpdRK`>uH^rCmM){Uia9CFm zwT03bdARR8d-O;BTg|`iqkzS}rcRAUDmk;EgmaIi058r%$#HT3H2dFA)N9*;!&4Ph zbi}ROlWdm)s)iF8e)cfY`$anR-DYV1vae&sM!KQzVu}@ST)FBaPHz)8;0k4r zx}>k55$kHKuO7zj-@>2am2a&}i#48TaA&;sP!7W=OWD~3mir;bsyN8KvR!1;FWZ@M z#(N;%zVn_xoOjBYsBc&KEj$~H4XTd(dUD8rG*DT#|=e2qBfUUxDq@JAcGk>9q2Diu1D*YaM-iR9Y{?($~+cjk$dA*J)Ko<+uDTi~Ot zrd&1%9qFCqLPGGMiYFEv#rTW?mbVFtTel) z{_P)_QZ~}x{^b4iJ+=#KVAP--cznUO?p5FM{bLNgGzJ*IFd)2)*%H;^Hs~1T`xck^ z2gX{qwfVc*K(X)3e?bqgU1v-GpZ+;be)6ld_|31_uR#q`&Dge z{WHHyfAgcI^xyrr|4sUvzxg+{0+uN5DPGKf_`mdlg-*@y7g>@ zc`Mh~-;L+z5QqL?P?BXiv&7sLnjxN?i)-oATf`DwA5N!!_+O`Ye*Smq`yc)wUAk~K z77c0@?(xGiUF31G@%q&-(|`PTf1f75{(b74yOD-lrqkJB7Sa0g?qaUX2Fs>`s%gWy zU=`S>7gp1gc}}`rW08t2f#*K>>-5vV{V&tSciv;MWeCsOylC1~$_rio_P4)HzxmB? z*edvL`n$jTJ7Sf-B8NvuF z$tW79ai`b2N`Y{Tay1Yspu6hb$x(})T?Rx(&|pPN1y02z@R&s!z}*khZ8+j+Mam55 zqP;w;+FEj{hV7QZ3QD~ut2<h_i4^S>)`1Id%|pOW|21+?7zhCG4lY+Efpm`4s*Tb+Zsx&=6H;8qH9Y2itF# zcQqd9Wv5Ww!`@>B%1*rkC_7vud4->mcBknqIF`1Xaj)@_OWFOe?{83Njj!L6oX{@c4z0yH*&|xP=zaEr6p61|kRe)}BEKS5)-;F=^ zqiuFllHLNB(y&$q5fmrep|~ctn-IuZ4VY6#z$nO*F_yD{aUZ0^Vp8c_boVF>x>WijDGi-J^J3{c+xvra)TUp&1!R+;@wpF^yER!xg{O zQ(r*+Z|cy{&<sBw?W1*>PkJsWM9ta=T z&ER-#mN0%0cp}7ucu_gT@ui_7$6box&}8tP=C(EiX}y`RRYqpZ+~vK7F-F0{=)1~5 z-96yRws7BhU4u(Jukr-o)jLsk6A;^Umg~-}*AiYVNkf4j5k(R&&as{FXveW|xv&yE z#SG+Y#*yw5a(M?Xv-e6&;x!|-78N&5VgkJHrT z^>nItJq>pedLUGi)-=MlynX{Wkvyf@t;luav0_}nmGG2u(dT;;ed!ci-X7flefo0o zIfsrfMPTp~qa&%C_xDT7Y~{Ok^LqNz?|+@1-uPo`ow}OdZGDsmIjE_xqlfZySOj;E)O5G$$=img&4qinr>@ zvAq52j2bI?)ljSa9Ldi{+ZtKlLl|nX(1A>Rb zs%?{aWuShSmcB@veJ8UCfG-6cqOG=}zTaM?PTy@zB90Ht%vDW=CWUZUv`cEJYa7t= zxb~f!kl#~4GoW#6cg9uV$;l=%)f!uSclRF!uasH6vR%hPWmNCd76*F>&(Tl0tZmZF z@a{!l^=%i%?e}(vc>lZ96}aUKfz<$cogGy>xUb@w%P;M2EG=O1788Xh>UbKF@7sc4 zvf?+|D7p^9Ph;6{`<3lif!C|fxLB@6yX7K0;CSY@%7t4p3`XqsO@p$#N~}Vrb$n;v zmVU_n=J=3~OfmMwb=^2=#$oA8@9)j1lYLfBfkPUu_=#}7XkEGGQZy-F@WXznUBsh?KLE7-o_RBlgeeQS zK2-?e8GI?jPcE+I^Li}hm@k}Sf>(odWQynUYv&(5m{eq?vjMmDVEoXdQ2d)}(AIt& z(1xZeGK>`-nw~XZMqcZ+uByKL{&voS6YmskZ9mPw@~c%jaZ4P<3$d-rcvx=#ajVcl zii30|UNtIqLLcHr15vbyLWQr?r*Wc3ZN5s@DSf+$^WyXwFUPwzT$F+j_zrk7P~?1> zx^o4kT^a(r93Z4HY~{zbcGuu*?ySg1FBMqC1NXdE_UPf%OcUf6c(RiMpS2g_@jicY zdA6|z!#=soqudwSc;nVJtIKpiITVz^EoJVFzs7u)p6`@;4*zYvzMa3FQ`2+PJ?)DPUe2dLRIEo;SQY%JSy`-Yk zGJDyxh~o#x7}k1^{)MtCSz zyu$Ggw+kNNf}wdAJjMIb!}Q?BXKCu%Z_gST^gnp@T@vYImw(csLr@`8(A zOe<_jTWjqj&Jl7&D~B-<;5uz-q%U)0d1Lp&1zS&3=Y#G(VudD7vVAn49p-FG8+)umn?+hfcFfDd?*l!b+Iwf*h=y$E7E*X#S` zATqi7L1s@u-ww= z^%?4Z7kZ5^Qv z9#2_RiH)r>6;j2u_?abuw!+XT`~02c46Ln@iJQQ_lQ8RXswYcZF&srdRD~a5s}G{- zT2-_~by8)!qynqohc2hQ{1V^W_3?5YUS6W817A68n+i-8=RVm%YbYCL+hUv~ z#&5?{$sa#<{K;F&G}5~D%Am{j<(j>|ubgws=!<-HbiJeK18&rp+g#7LF)r%sR0xmj zs`bq{I1D_cJ>^~J)RM}xa(&qE=J)Ea4+E24e-8QbUJDm0WF-~4Dc8;4KCf#=^W&Gk z7M^-qvXa1-mF)vmh9&1MJT<>3DDmr1JT+ILrA^h*V9-3@qbUxryixB z{O#YRa~I#m>$#d64lcGK$HRvY)Aj3ka-BMrKK$^*Fe)`<_?9o;2t!hk%ON}KFam}+ zJZ!%G@SQ>8?;&A}vXBP!G*GZtfQw+ol6aO9_Nz{yW1zSqWx!Y|xD1@V+7qCaO#7F< z=hIgyV=uV&tZc6bO>M}R+lUVVar<(wQsVwzFuiO$_bP8+AgYJW?>4u~Y|VL^%RT6M zbN+*__bd+t1)ldUV4Eg)%eH6PyJh}MyT=%}e>D`{Ar6vWNhY^_i5FRb1|yYPPZTmR zvw@urOziW96_+~P?1|Gu_1D4lRjS&vo|2*Z-Y?&N&+3agY`*$;cKf|+IeDPhvhSLw z_H_3jFY`_*>A-h06Nv0F-Vm5Hav0p+?T{K7^Ej-evQlvKx8*=oDy<5VdM26b>#zUa z{QBXp_aE=|WgJx3LCCMIF6@%R2>@@?EQ{ z`jJVf5sKzK`(5u}B8Et=#6anz6duJA2)0E~`G@e$VRjHUB=U_nOOTzHY8} z`}**-z5UMazxJ(j2R$FYj7*2-KRhiKSe$Fz>Z(_)fzrdk#n!n^y^@a8F$UgJ3`pP3 zks6RRB>CG^c>CMXEW;YiV?dgJ{yaUOok|OQ7UyQt^1}19yf~NE@MN)FdN_7;cBP(P zcBz~>pE`Q_)0YHU{^WPRPG>i-r=OhUpcP{7tg&-t4ZO%F4x?%o9EItf*JfAR(z8|M zhk?_`r5Dl|+i_R79um)UCS70cOA8uQHijwQ?cPN$*_|iZ5ALp6#&fD?V=16MF+E z6cU@gNPewYr%!OI`$lW}a{6AH{OUL9B!(7a6Sa>Fr}`akhM$?5o=JDv9(e5!|8HuW z`YL^YbR~`T_NKN@y}CIR2TRtb)+|ddO^&O*{|K*MRqE~iE;B|%C@`_M!G!Pz_a*Tmv4E)(Jz{KwyYuuwB5t~9`L57gWajw-# z*p*?u6;HBS9Dz@rr9ue!vq5%T@EeK&yIotFJ|;>G_j;X=w(}^~I^Qxje(s@(XEe4R55iCVd`*}Kj1rMIyLA;}g9du_pefITK`tsJ(bZzo3#-)>~zkiVJS!}b5 zgRiWuq{?^#&+*yP{FBtWc86F)Q|ZFcDsVavw{jxeDu$v};s|kaTQ4zM*~Vtl{$dg~ z>_*tEvbPk|e(2TQ$r<>oOkh6cY5v!~yW2aHAzx3-&2tFnWZHWEAk92`LQK704yM^Q z*gR*O+0`%plqRqJG4(FqPW|Ar4+E0hBt1GIwiuS$DF0=W(AtM#3~cF4qr`!FXLyC} zhj-Ffzx#g(Z?Twu@h|_gbmsK<3m2~^(nlYC^hTW6Ch*6Gq!;eOgI|a(EDWZIV{~x# z9XX`i9IIw~;OxkBeB&4c#~65{FtEi+ipuKw@ez(Z?T^_mJST)Q&)flTD8^hHKgW-& zH*xQcYRxe)-xdbs^74C)9WTrAW6qGqNH1GPy!;0Lae<}VfZi{WVot8`^AfKgzjBO$ zw-y5`Xf4%Nw!c)~@YZ(DG2kzZ0gXt?St|X-pkyI$x=~5P&eJDP($%j%OAk1zeqr*f zv^je(b#2V1ZjQ0@-H#S?-{KJm4#1Zpb&zI8>!KBym61qFn z8it_F*7Y<(`8}L$reS4+55?@0loc=}v4w6051xg#k+e4aemehyU#4ID@^5R=Dz4o7 zhK12pynyG@*SGHA`Lo4h&LFV4t(Y>1>q$+yp7`QV+-7Uu$^#5zH&YiUpA8YWdt$5) zgVzWPti%vr!7Eaa9p?1hJ`7(7!4@I>+q?AH(#DObDj=?^%GJb$&wS6%8hBb`LAR%6 zjT6_Nr?r^}>A~HbsiUhW^_@Q(^x{iXk0;Z$Pk*15?|z!b+h_6W%JF&ETQDSrJpN*y zNS3)H(H`5j4uGwcJIHC>-zR?3-PyVH@fZKVR=3r332&*f@o|j(JKo;+#>tZ!^!j5< zn>xhnPOl$=*NuTcNBs~^5VjP4oQ^SYjDhb21{7pWZcQ~lghaS6v5c)b`< zX3{g&&zmkkIqDugnMyalxtbo|`7+Jj|D2P-Zl|H;r);-+o;uq&cm=~pM=Pgb;)#Qe z3`0_#@r1T+5Wsq!wsyCyrk=qqwuG_u3~SLkap9J+$y|H1#A#veY$NMR{Twc&F$OzL z*m-sv^-iP8q3diD-{40_&rljTaV8B8kJXGw_VC2SNw%~trcbX;0@EyBK7()(j4Z)U zD8RzCSW%g$PHqvWiF2mNx6FxW57WT^&)%DUw~=Mpeg`CGkO1dNvr;)!R&`f*y?*8A zWj`-_`GY_CZ}5NVWw)1?pY7MLhIUs~R#v7mQ?o>Io&aJVy?-YVBt=mYC{dy^^Ii!A z5OL!UJK|o%-LcO(<7;*()zhq`~~{Q2w2&t)83GVTsn!pMy%xYHCBT^}Xe4CEGnz0312iF3l{s z3TtEav9;E}woL7*e3kZXG9z=gA*prI*AqC?52DB79J`ch+Cp;Qx>Y#_zxTC`=cerD z^%cX9-KYuEwzai2o0`(GH*fj_=~0h;ANc~2^gd9fOs@El0s;YnpC1A~_VuHxo}c`U zjDXO*5T=Ho-!&0>91sWy1OzSt0sjq(7iyPKf2cAbaM1|xP$5<(U3A$OSsuEUx|p8F z5ANCT|I_~}2ibqM<;0rJrfTjtCp#&&$1=JnRMa`YF?`+4Ve}A^+e?TEx%rWqRL>r7 zH|^0@%PRG*d?jMqmbOVn)l7+z>O%5F6Tl1Iq847LZy;pS^23xMiWscN$uyQx^s;#? zOdN`w+7&U?QWnU&I1?X+Ef<;8Q(-L;HeC|kP~0YWxGj4YqgRa)Vu&yiHjyleC7Me7o6v+_ejO$Gi=^ zAkIvR>b#tRe=^s0)5O(#Us=OYYUR#FB<<|%*xkE#?b@|#{<>^cQXrBp=wb=Q1Ox&C z0f86+e(2tq^y|WpT1WwbfIvVXAP^7;2m}NI0K{DG$y61X* z(HFjVdwa(o-v7a#eDixtY<*>=)?-B|Yg;iruq^(|#F9i28o6JD1bY2_BYPoMPyi*R zV4}~9rL+?`b*XZm@3j>5tZ%m#^(($!mb z=k_h(D{@O|Q_;b`{`y<{?uV+((F!&gn^t)cU)m|ZKdw$vNsu}{5jfp;**djHHkDhq zFK-R(lPjOvWbP9i@5qt(#@BX#{|6B#OLJ+KH--6#yRHgBlP<<@yzZBNR9I3yQ= zr+DEHsVfuR!-gDXSGzI|!$hv<U<_77N+P2B^*VeeVXO%}q z`DhXWwfVr>n>&h7*R`eyq)eKiC8}G;xM>@mzu-iB=p%kGXC`#cI&E9ueq_73IjbH1 zN;_9Zu5o&L+HT*z?LLyY_AfgHBI&ZvK&U?;5D@suA#l<1*qz2o8O(%8A1jH}mfhB~#}DjJ|M{P-z4m8Y8i+)iYFKHaApcHDCx66Y zoML*|FiL_%l9ox%b*{~3Y`&P1DyeCApVvf;^lerSxHEEW9r*@S6(@^@tWg!dNkmRt z49EJ$rp)iYx2v~4vtNAixveZOc~ra~zQ1q({LlZl{pH(fDSZAbOJ}B|GCAJMiAapT zlttaPoP@VGE!+Q#Elup%KmOtq`^~Svv?-aB)v9&-*Wb_BgR)G|st=@C+O;{gu{b&5 z{gCBe)EmAxM%68F?7j@d$wQc*?PI>WK=?xX2#RDQ|rlBlRm4Sh@^|b(fb7^+7A&% zgx*3Sujp;+uBQBx3EaR)Kd~F4+sPV0s;YnfIvVXAP^7;2z)RExcBGPR@ARrO;OJD8ATEbr4OY@ zf`Ey68P+AfL^*{(UFKcuYYIXC;A^Wr`;+CWk8FCTEu{_%|C8#exTI4BjQAC@Bq?*Y zvn^cdk#{om+D@KR;)=LP=%h?aJ-Hli69Z=vm(#Z!ULt1uN_4e^+a+t5;{gc zllrPJEhkJh`H||V)k#I4Tib8TM6T~;Olo^xY!1%8Q8~Ga$XoXAfBa1QAFXHAf#g_E zPS$I3O1^LP@~-V4RBfhI5TSL}74>5MjYh-D<+2m1Gcz+E?Am{^702HKk#vkj`1Fw@ z5KUVjc_Tyf0s;YnfIvVXAP^7;2m}NI0s(;wMW82wqgt)Hv*E6YiIo-k-x)@5d*5Ud zzM+Yh201~ZKB9>dq8?HwPCMJ1cK7SQ*w%wT+hpy5O(iQ<%+FXxPNqBZZPdpr^gAX< zmu!87Th#xyt|&-I4IxT1#Ly{S0z(YV(B0iFEhQa8rzi}a(v6ffNW)OljdaIl-+Rt} z&fUNBAIvk)cdgI6-dM|+T}pFem;ibil^j$)*m`)PI7zAJ^i!XJUwlexM#(t<%MC#55<=JMfA+HtXd_}yZjN>5BApX-8>#V z_IDp`GCVXMqbUEr=J{%+Bch)=xhK)mo=|TX;3YYlmugr2feA>*P*X0!;Ou=lP_7Gv z7aV2=kkm{&E~Fd@C3l}xhqJq_x9n!cT=_9XW#nGe@x=I? zQ#GA5I;>MPW?*7%zdmw%YSv+16?lS^A7<`7vQHEEYRfL|moQPpIatd+WucxDzmvAD zB7En0A0EL-dXA@%>iFuE+-4H}cv=8`0epklRsdGpP;*G5VX-!%*ucxnd$`=@Q(qMB z*lRML*PwE5p8t_X|Ly7bWiG&Hj;r}#&;0gW3`&I_^bCu)seE+3?KPPL)bM}T(!WdV zPhs2(I^nc>KHlH09eW~o=%6vC}J&_<_ z*z7`>eVv5y(dK6X785gI>1}q_do6{%Oe_E=$w(_!fk<6w@5#)e!JOXsTh+e zVPE&Xu*lvhnaNBNcVzw`d8`b`t>DvOn^{*fa?!#A;)IO2ZX?d$yhKNt_)eNEajgc9 z>}*qr;A51X4kfm>077Yc+Y__C;q#@{1XhCj_@OUgIC!$8F!^mW6FNNs;9SNOb#=-3 z(STmXpdh`TT0t%+%+#6Cw*k5Ow}vlw1C>2s*%p5rN`-x z1||Oo(YdnWJQpa6Is7OC-wSNU1BJ>~s&Q`GmX|VV53rn!LT+d;(?Xa0_^C-p zpZV23m&}K#duDcwY|Q_BzwSu^A7jR0Tsacy^4|wPgJlaR|CY&^SdZPlsvCYhswcCn zobUXF50kNV!DYdNSZEa-UzjAGW9nX7w_oBKbFx-b=~pRwcfD?UZ_AJ||Cs1lUVXO6 z*c+>!!BD4C!jLQ~qPML)yFfGe5h(-%y#!TMr0y-%-#e8XL>Po*k2pyPmm18+B%?pM z7u_^rC4FeUU7_86E~nf2_(m6ohn_t8Q6%IiNA@qX!`_)sP;f*%C*D0};5ZazM2vkuSzR9o27!mz;eC1LK$W*UD60XlTf$Ov7vsmM>3uFHD zHOWAdlY&7tN!)6cR(znU%aDZ%CsWZI_BtQ2UAO8#XZ{+3K}ho6POIQE{task#Hbd=bPP!vc+n&|L?ZH;0 zzIH}3vMPR!mIYnu|DAvSqc`TTzPy$U0(+nD!0=@~j~1Onp$DK{d!J=H_rujmcc1H} z*~@Fkr(VRumAyr)Nxs@-LEjx`)E@qhfh}QrZH%NySnf2c-DmkhV$+!-y3KGM?Ms{< z9XI=yl&X$$o_zTGx#{#W-@h_|Ns&twx8-`1c4=@;(v&pI%}pQ}p?+S?a&ukGctAvh z35Ux0XDQQ^A2q-~be)|4w0L70iLH^`y^F%McvGkUbRdd2O(p?C@i>xX z?8DEpFOD{gVw}AI4td6Y)vbw#-l>;a%v`?M|0vEWz#kFf1R!0kGEMK*jN{~01-VS> z5TVcG!4KiaJI#ep$H{eH{mch$U5B<>sz0-9cETltjKqIU9#WS`@}8cw`D(&@(!#R> z--U&`tt46u2+@KTzT9Q6hEekSmof@tYzsI}j=!&1Pc_Crmc z`K&IMzdg98kgL%X0i<%P;)FtRZR-@193mkuNn)qde?derxAA z58OAS z2X9^G{elPmWW9E%*!{t6kb=@ywmgu+U7o-!EQ(xtFF(=!*QNl?qqJ@;i6wADINM#` z_kd|d9s9z^t92Y8oc8X}l9KVFC2St3MMoU<&7WEfZiCdZ2 z_mRCi$NQtjYRe-^dl8v}PO=&MJV(sO`W}-;s_QVCAB;i`Fo@FABiy>;aOI+AX|Do% znk6bzrlgcSGB2&q5{LBI4z1FV3PsrH@u)k$}sGG7yOAqL_1pc zw6tgW;>;`Ww?XUHnc0kx>0}~~aSihHquoD^`fO{10@!&1GjWew+aH^RMYmsb@w$mL zf*59P>f3%e2-hd^6U_`o%{J$EysP4w>O$Ay`WJU!d9TM>M7ACh-6%)SIa`p0Iz z{N-H;uHUiaNluZ1uN>d2zviD>07fxh1gPvtI@qG~6tNf#|7Kw>0lz1Y2w%;ojj7Bu zP#3v6E+f$@wDS71_g19l_2>I}=AoxjU!s_}I!W0-KeY|KHaxf82WbYWjf18q-mFFJ zm@yE=I+G;5#}%B(62lnGi}Rf?;jUNhg5E`tu4%r8RM`vV{-}zH|X9 zwi=aoL%mb&1iIJhdopavRSc^L8PbeJUhPs)6ah@;+I@|zL-9P@WeGDzpi`|hKk7! zJ$|I>LWU*UlTMCfzB!S2*Xyg{QIj#(JEf)69&_xj zW?OqdHxQAPD4wC;cKB-KbCQ_8!bNDmo!7Lqp};LR68J(sY3}w?5}Tm~XW0#7+Z*P~ zbwt*a5a*QDR4h!G-gl_EhoLfm=^-vlIQ``&Jt5`|h45?T&T$chv%n3{MR3wLeeZ-S z-HK-X;}nGSMq_Cd7Y|jP8eB1#mJQD=X?3&z@~xR*iDTc;Gv=gcJY8I~Q%Ov|OiUdN z@$qH7CWihe(Eqa?`vQ5Qvor8cret>iC}4XqnaOd}?USufU;Evqm#G z*B0+Oqx=JSOjK4$9d(xpWpB7h8^C+)J9kV9tKw~yS!TTH&RB|z;-wIRE?jx+$bM=w zM9{o}$n|u9>&Q!Gp+^odXe`Xm3Jtz^;U7<_p^*@H^k}>~%dc$|T38-o&eubh*%sbP|Ux{a5Fxwwr z@@__ryo%WRB90DpGJGufwLyg85=C&e(vfwB<4)!9jg`REUb1n>@-zjuQsg}Io0=xK z&UqWlwri_sc?vfuw zN2s)~?y$`6DZV2FkCrjZ>q7f@z{>}uq=>zl_SDYL>3B6`Q+$HlRKJKcG^2TK$m`mG z6V^{vTiA6!)C&yo;1geOXUBQyWY~=q^H|;kB2$u04e6;^=j|(}v*PR>Qy$f;T zKV2Xof_oAuRhT6lI!c&-yg%GkpXE5OfewATqcbl-MNft4V3hd<@jFaJ;C1)?<0&1( zL7HLkcPuX)j+ZSmHn7w`W(?8dwJ#U03tPD#FNu2`p(9bUXPvC-ooEfK2I-_pi%%jL zb^KaO;6x35`vlC~GTOyj1)D$9&0j0#@@^84K*O=p^76y*h?c*Efge-V*!C;yXD~Ka zN^0#53+&=qYVdVh6wK|!;L0ldyeX!3VJ{+*1r1X6jB*|Y$B|c)#+#Kd0T{e1HGaZQ zj!C>_9#zKb{NBG3zzT#M$+cZQ@Hfc5G03K_jDancscJ~JbryLJo55rYOTIQOO6@-e zZ#`$00*ulx>t|&2GnuT&ioZe1+_ko={7Fcn1`ngYSzk>?I?Q_Fe+(z-XFa~E!((Iy zSQ(+g5>qgu0!)GMIS&=|EG?@Noxi#qM1T(!I|)E*C~Tc>`3I+C1>;15hB|H8GFc+J z*)Pt}1ckSSS?|d5BR`QIkM38iKC1$En5_l=ImkT-PGj*G_J_%b?eEN%RZUn`5Z7SF z-qEu|KZ@TC8e4}$x|(*1aE02O&Y#HCPZB`guMP)uvsao}r-ku%i*$7Gk_ZsS6c{Kx z(|}{Au*nfVa5XTE5`27IYDg26Mh0@8sW;QTZ$PbQ7RSg%H`!J4%$YN2+OiYCHhenRdxGo;Wu4Z?`njj(J+NW_a%fWAnhAv4Y|k)9@Sfgq~{7Xa3~v| zj}GUg%L^IYmWH$VevN9po%B@7n#&b2ps#qAB{tsDEMDhvXEMG$_C|)}Y;nVlbos|pU4PB9eepRrI%n<^dx(mJ&Zgg2GTRaeA~VdXb@OEgqd5MJ(-aC%3=mmhf}%;6~7fE#{Qb z*c-$)G?1Jc*Pi(-B&3tbR+cuj$FFUc%rLIcICAH%O%;dN#+S(K(TyVEJSWFZZEPBW56SO^r=+u!6C;THGBpe% z1i~07kag5c%8GsaDu&JM9DuY?RQHLr^{9Y6szv99m0Pk!ZET72jeNRS%PD(d$Qyb= ze-~zYi~smK2?a43+SVtQD47IJWQ~lB>GI&C^DIvVW@nSNc>ev$!pe#`Kd+4o<|R85 zxk3f*`sLn@n(rq}{@LgFeZ_bw6N0=UtG$<&KcAog$qs&^M9Y!km~=a1O5($PAJ3py zHvA!;q{v#iS6LB)3{~BuTd8C|iq`Q;j~_HC;q@3&^pD3el>Cab7P|V^To#3PZD$-& z&tzLMH1$T302w{;&1iM{Mhj5?c)OOZhBdt0!^h&2?+1qQ(SidT<8~_TMY!UjGmn8h zFFw(<*cLmkin?STOzKRPqcyAhC+#5ReaAxNZhj=Q7+klDw*;qOFWp zOR?&gvHhCY#ZMoh^dITPWyw=NYsq)wto57PxGgddL;9P_MuvxX+Wy$$fBuGf9A}8j z7ZygiKoC)Tv%@6lds~@OAzW?p-8w3<8a$5R$V}$4vM%!m?pU$y3)Jy3#_%fbME!nC z175Z&rvzqm4=#9?|{IXPR%K=$|Jg#|LIh9l{He2ip@LR$Zkq;X?jm zAw1dAkV*m6UZ!K}EJd(*vWi8$dZwsJhhJafsAW=0&m=)k%Ge4Dkw)TO{mJ{()EUSo zGZ_O!LA1m;(s}bFr1}Tle!f&UzjoG73}4sC0P0Ym#0(#fhpEdRZbu1I*|xwNe`>L4 z0UNl9eIaz9t9A z*A_Y!s_sFLAG_|i&TKUtS+9}5h%-_HvDmKp&PDj_=6G>t^^h*)k(D&4=pC8IB59cB z)7UNQ|3)4M{h24Q4ueLX4=pftd3DL6C`1AiWMudq_q6D)2+86mcB}+c{r=pW+4ntq zKcy!+&%Q0NQ(-JKhiNVckVy;Ax?}TtGS&Mj*SOy#-G=V1`AS$wT1F$pJ@F#G{r4^a zjDJuhzJNal+W)LTF0CwAQ9RzF+LkXT^Xm>3fOV?ipLdQ4Dt%aA zSj=dZRZ&torC{-&MEFu0pI$Cygp+g)D|zaNb`y(_P53bclEi`p6@1!fU3&4xhmP?> z#>kz;+QCNIV!5#@83$f#(E*BIL zmf~e)1-t4z=Isp4#LhtC+8g=|A1HX{+H7Yz?|2&0s>B7L2DAk4BFu>yDRlPsmD+qt z^9rPsYa|aRIAl{f7S6g|e0;S|6Wv06(=`g?jm)g%G zSn5ts^q019TzZ9;_uNwZ!D+)&lG#qKt-8(7`Q@wAS17-8!_kM~xmG5*Hc4ZW{#7GD z-mGu|^ej4ZRtq#r1GYnotxz);{IaifT3B>sobugBJ;()5GK5s0*;^z!dqU27%gsTJ z{cD^1uMVZLLS4B@)mt8__69fI}Eh0~`XuE7RZK!?eY3(Knp>kbNl*VEM z1(B2;)WL!hUz(9(eZzia>@pRA3eH2&yI>Uy6F9j+&~OCnFmfi;tUM~*ySkmpwLQVl z!*slYWq7Xju0mF_Zy)uCLC%j3;ZF`qyf}I&o9~Jrs8IfYrT1SCHQ=R?yi`o!4dLq8 zq&KAyH*Hh|0N*zJ;gDN5;5d1cDQ=U_)F?li8Nc>zColHZmGn?HV8~aMXHcRo7>D}) zjyYw*f$qWxzoQ&%vabyh`9uf*MKwY4m>#b8+PTX=%Vs5Qi0=pBPCPdoepY|M*AWhw z;iOMeQfdC-tlIpg}IohtljxsKJ09)@i7SG*g3du=V--h{yBuFmYc7hI6hb=4}^t&6*rLmp8{`)F{`?=qt%tSGNNO%w%WZxN5pC4ib z&T}_iLn7?PcjB^YRyBxa^@GV=f2=cXu~d3D|FrT}F!{+sNZ3R2k^?9IQhap?mltrtuYF&f zWf6%Vd7Tnq94ou`E=g-S!11LL-ZKu-H#a2?gOJ)A00Pg4#wv6n7WR9>$-6lR-!2$5 z6YvF!bNa*5SHGh*Ip|yX-(vG5QT#8nbM%X=UgobSW8DuNIdK%k9pvw|rRM|5f29&e zwm01UPH5AI;kxir;TL;|HU|Xwq5o}974$}XoAe>-GzK{WnInkP+9fb-8r`Rv)v*3F zD#S22KeL*US7^}ce`Ktye(}%^24VKy~x(HDK4Akk2EonQ|24%gXTMXk#sum zPrGB2m<2gQ5gJoGGO=ay9Z9S7n8LHyjauvB*(cpfxZ>NSNCu+ze1Z`aF6!8HhHtnM zs~fVN)}%SO;(afl&FwCmol<X*58xGGl zv+yB&Ia`faV!+;q7&48TqX7$o*5Yc@X%=B8!*(jJt`!~V>TaKz7gCce{Ys-xS%62~V zb}yn#E`v-rq1m>Xawy))>ph_pEFhoqR6Xah%E33Y=N4O@eW+|msA>YD>O`QK(7_OENgQqf@OLgPxQ`5$~G zP~_ld#V%SY?KQMWNkf zf25>bG{CGbTe?0C55}>K_nz*l{uMKXe(jLsd@~L4V&JtixPyVEw8$|WaE&RF))Gi| z*NJgYxxgMuJ@#fuz%QTyE}Nuy8OZ|b@cDutu;y1r%|Dilq{2V<_5NIB!CH&kXT;%Y zOB7LO_|OnJRsU(Ne_V9A(YYIrQata2A&27uPLZY-EnXBQm+xBvIi!jR_9Z0 zV%r-z)r>#2tv)x0)7uYX#?pPq>B5g&7g_h6$6t#$>x~D)o{Bn_eJqrCJ;>N(Sb%*B zk;c-(8|Dmb$an+B{I9Stb9K5HcvSwzYhW{7tzY9OJDs~@yKaQ5KL6lEEx&xM-3kw^M{4YN(n{+Whtd8 z)hJ;ZRr<2+?WamacYU*<+esp-5R`s39%D_q@OKU7N;!))YGNV=n~4oeHghv)s563} zOf)fE)D(lyZ9cIddUnV2P0;oI?VbN}Lp=TYb6L0Vof}dr2>KD7H}_AzBoLx1 zhTdMVHYrFu-EF>_`eiVL;ItxEnST2({cgxrE#2SZ?7NW=;o_|_cArWAyM?Fmqx-!E z9){A~Gnxu7gR`@<`o_j09x;tJHvX9%mN(#uooDedi^I9AHz7I6Rngz$NN<10QfX$S zk{H?y#vb{QW)>@1%aLWprL7NRwALuFFa=6{Noqkpyvg`VTO5bFh3fB;r;!o8N`O;qm$ zW;k6q>6C<#kHw^c&@kxnQzF;zt{@0%ki80B0F$fgBUx9JtM_SN9OQ-NpTwK2(OEdt z_n!%gpPC&8dqNaZcY>?lo`fhd->dY_$}YwplmD07_1|30buraXzYmovs(BSM+JYo0 z_G$#$c6vCG)VFBWM&}o!MRjfbISzN(d_h7swWtz61{Zey1Y!m@ya;YI#iA9@NEL#W z)&2AcU&LE=&oY#B51ZOg^0GXYShnrkF7T z+jK{d4owNfdCJgfNR~`Q@SLjm7$uWLZk307M1;&p5}_r}viwnXQwBxtESAm2f^nol zD7g@t)W@o~7B?xlUQ-SYi_?%M2E1Zg)XiPm%%{^gx{vn_9uJq6JW4%|o5D(6oxV+a zZ^^400U%|1f_wLQvuEXW)b%YJm7_ea(|3Z6{D&>s!3xGKAvp3L;cy-M{xcEwg(;bw z=|Ct1qa=-i|EHp}x~;fTyL)lAQu|(bJ4H;>b#VQUB@i3OALpg^lVrbb%H;A_VPPGd z+f1Hd$t(mJ@7~nbH4_{y~R{%g| zK35yk*P)EEWr5C1ZD;keo3xyjLaqWkmlbg>{@6%0Zb3(cE3?b+N+j<((A1>K&wTkY zv*W49k9_=fU()H?hpckGj`f6aZ)83{5xHcr2JkF~V?koXHP+1`ZcJ6r>eD}*o%bJb z!w2$q{P`o(gc#GOtY^v%V7=lk4(S6NONr7~tIaWwe;5p)I~pqkG{m2)e^JXv5v0Q) zB#a~kyo*;kdYr9ezwlrgA(F3iL+Z#R`xd$*Mh6)#o8sOL#itM zx5A-~D;l4C?vJkjkdJ;OjoN+x=xHK3`cAPdec;`@=wJ>7^4-|i=1RttgzoZv)m~aX zO!0A>(I@milJ##^NTlfn1J*S~{ePOaH>P7y_D?6Q^#9`%`J~Jgp+M88& zBR2EDbnkS+W@_W|qtsN1!l^Fu_{3vEtw|a`7X84Cn&l~zh%V;xAx4M!|{i4IRf>^~2ICxHn{47$}<5C9_KFBwxmE zmZ!GatTW9XZmn6?JMC+F$k9yPPek@ppggY`3&AMXVrEQJ?W=t76`|0Va8#L*B*!Cw z5JhbH(C>#I7=>^Q9j|qCF^i++OF{{_(>Q!|(~7;PIHx|)J132taRO+$-x;U7a?+R| zt74YW#(czxLc=l#f$3zkGQw$+xCJ_zU*~jU&~*g`Nm0@1rnp5_;$j2 zi&+$y+gJGrY*kAVImPWKl*kjIXBZ zg-k&&Trp9-GX8{c#MYeXhpr#K;@4WxNNUqo2(7>l=F1&#%@U?aK_S{)2wMqbS&wmf z2$Qn!oPWMK;;lp_?s-VmpmDI`=QTOfS zEp))pX2S-D8oT7vKzAF|lmktcyhBrTW;R@)p8T8j$rpiY{m}zn9wAYsfm#bi`eiV! zhWt7X;qzxIF6<}9@G`fG$o_y-i)EOCaBJ$#;>Xcy+ep0Ggoup%V$?(t1VNLSwQYId z?deC1;*pYj{kn}zPn*$#l*eTU-xHF%wZBgTDH;{pD!?#L=h*BK3;X9l0YvQ=(uy=K zbcHZ=mW(1CKKtEu;4W>45|v%u=iaOv63-whoyoYdq!;z3sS@&oapt^9zNP*WyRG+n zGaaDXg=m#(KFmp|BbLuL?l~h*3 z5BL$!aqOx${q~;YJMJ2(-I)BR>?e<_2$mtGi8VV87nusqqL$z;neD)8zOCD}=sWi{ z4sf(!VSIYxrI^zp87blm;!+-*D!6;;LA%u1%#?W#__dQ3mi6za{$Jsuf3=3V_eh2m zGGr6Uj=w!x4ofhTAbD^rVcLRUe+%<`9>clE$DfZ3z0XIo+=!SF(itpzGcJHZL;_IB zHSY>7D;+JmjLBT9vxHihGuUcZ>erM^$;=e#xctWJ1iRUyWBCT=uCd~ZX65h_^u~sC zXz9gXC%9^%=FCeW^R5(Pk-h@(o;N&b9De_lq=c)f^GVk=zFtGt_HZ+kC%#URf3iNT zJcV|zQ7EF$PA7A~@X(0a{NYD5mIdLT-N9PlB~~5dqYQ=0#rk~P2KyU?0@Fa3q~?lX zzD12SDk|_CE)xW1NtRWlb8ctldJdd$jt59A`P=tzE7mO5clch-ku&Hllqd7wKr--m zVUv^zHP*#p?A^^zc5)kifhQ8;p+TGP9+gwkw}@^Nod*_4LE-5WZK=FVZH1oD(cz;UAf~Qtt`aH8cFUa*v4$K;?{Q z^@k57nTe|;7|D3;Fm^VdbKeE@g4QW5%?(40Np7?P>iV4O^C0HIg2E-Wqus6c!^n@f zysL^deC!}ba#?CkW(Xied#Rks)k7T_cfm{YUEu*@trqPg%-y!d;8!(ish@`*#KIHy zA>s1-txmT})Ra2%2m8c-&&gNlCI zPsr!J%_W;J2hj-*d;_0pEMs9u?a}tCR&Rg<^s&`PoytAL?BOEEltD}1hr4&4z}RO3 zms#>8V zOu?@|87)@u$qXBWjrbr8^Hj%%;~yNW5O49{J~rcg<<0ngc8bEy{T(-g6F{Px)7pdI z`>U6azu5Wq8BIDE(v_>HOP=-5Up4H5Cg-sk6xcd!g`3VRY z;_&%G@Q=>|o$6;0f4Ps_(UXcm{=x|pCseoWlNB9Mfr}S^!m%d=b@dD_?!3X%I4IZ zJ64=feq#G0v|G)F)O`Pec`knYFTM<#whmwGgf!hI*~0Ni1-ZT{UnsX1HnO*!;Urch z-y~#)@UPP3M*S!n-|3Ne`yC!I&4b;geHlr>!zBqErIHZ#F$sU~D?=(nqG?$CLZ&`a ze$vA%SK8H?6`K{nBMGo#$bl>45Q5Wg%D4`Qz{D|WDvv9^{!10I(-&Ymv%6DQWV!`l z0%buF6X=xzsq2^`WRTvKAC1KkpRe>ca4U@XJQj8vocPzJeAB{JAL7dI2)6pnu1f&; z0z@}aBrHO&rIvk!97gsRqi-tBXCXWl#&;i&N2qx_gK;tcsg|mOUjWg=x_{Kzcf?7Z zaEK3Xfb3kn#R?#aksoIgtEJkZu0TK!5OZT2WS|J zHg4W2p}xPub;nEy>+Ck_X5hh~=}$-?5cdKItv7!&&D5n_<9dZ{LQiL){0H|W8VLK(KeV6jfX9CN^PBq_Dwbi?MG|^=&0wb^+X%5uAnQTavnd z@leG3P|mbn30X5JL^h8QWD11>7&tT4bk5(nSiJ3$zAWa07sOmM1zo30)k)4v0O=m7 z0yhRlUJH62`MzSYs>eTR%M5YmSr zL?1F0K1z%rTt^Om%yvMb!b=Ne=xb9f5zb;CEc@V zn;%C$^x*bJa!+JVm{}v&N|W4ACh6()l3k04_AK#9@7w zLfCrB?k(T3{#6=0&QxRD2jJ-oYh}F~7m3B+&|#8B#WQYU4sv#{v?t*%_Y;H+T@B8N zEV|ng^t?v_@TR6~C5q=(`)^!u#LN5vgu{ng8JCqCa4ZzE^mb@rZ?CV#M!L)}Isu@5 z8Umg<6Dj!X#PJBs?r6L9wP5=6w?AGst-=;+bKY=Li=*YDpgwyn#6duz4Vht4X%rRg ze5Hu<0r*?tve*Uo6BIyUiT9TPL{Z9 z6f6(l*Q>}`r#nPxzb!!p=YEQCJ(buxu7rM01xZK);2F;VW45=)%T zoZbDqG4s8YdP7-#sercMt2h%879nx}z_-P(dNo1gd->S;B{OQ=}vo3aeR0MDb1Y%B4#Q7GHD?YS&pzIeuqe*~Y zX|b%eA@iP|#s9||KNrKRQu@axE%Co)&vqXqNu)|4K1Pl&_*V>#(ShKSdhdFb-MQ|i z`Vcg~Oom$f`iFjvjyuhL(1gc!6q~)j!{soYUr|h%gpUrBL~eEc=3QU>bHbh@D|EIt zuoE-lGvuv>&ZrB0M;!j+^*58?_MTS^h_Vhg&|naAP;GzY244YplIB58>UthCYnsA% zQ8Xm;T6hRqS-am_t>t8dW>d?{im#fk4r3cviu#Gx>e5Iq<7 zOU6&BO(&QY9u``B3v)#gI$;V}Y`D&DimZ+u3BG^wOs7=D=CgDPwcD>Fzs5jD%e{LE-Q5iW5Rjnw@ylSOpb#{kWdi`rt=?|FGFkcVysYdi&neTy*h^;#wK#F zzj0;2%UDN0e(P*5rhB>jaoa>yto+hV#HpB*m*0X3|J6{;0M6l_{(kgOi6jkP<+w(^ zrJ65NX5(YivNdmxmmekrE`*|6`&~YkMDA)nAXf~4wZ8M0UQ*Ij{>B|kD$&w+stQwz zI3K&?myc41;5-+N>2li5d1vJ{wpY?`%0M{3r$FfV^RkSb4==OtouxM_i>QIFU+Bf+ zvpJX~X}{86xyJ}kvyPQW#Knm`x;4K8oq%V!H;49c0@@r6S9I$N%-Z;93;{4VbK_sx zPLqhDr9wHhl~?~W8U3#bO4I-g@-vq|)eJTdJi`1kOLysooFZ`r4AozqZ> zZ@}of*Xw-jA`1zoN!2T9(!4v1d8U%dimR`zDMf|I7ABbXBhv!y_iHkvi=WQ|Me=Ji z%5YFC_9`qDtxq$B^41%{F=5H6drA9A8Bt9M{?&o*)bGPLW3^Y|&+C~C8PGsqv(I7^m{`jCx3m?{JwAm~C;^!7 z_)y=Isac=fJ$$ntyCM|RWvl;Jina^B7z-!BEC#JKE?kxK%jj;N8ETb!Nz?iFbp_#1 z`^LxVJ*BiS8b4d7_!Q&|QGd&MDcbYt&n(~qoZmaT z9u#kp`i07Sr=G6y^rC!KpYht`(_h{njJe9bcpTld3fc@ahmJN?_5-fh4w4l5!Ab;m zSRYPurpIa!fck$5Ow4FbaZz{-JSP!^vU&9@4+E@vKblB7Y9GCa24liZtPwmM3{Rra zH)8Tdk#7_T!kmz&G0ipM{__?6IB^8*MaC&_tGYf0CON4#e5tp$UO6RbcaTiev$7p! zjph-rR;Dls?QWj;r~|Ehw3oXoohdCtj81mUf4iRxejOth`XU{l%-th>dxaP&ZgM{> zH*%6dB$$L7Mj6;YJ+6c1`XQY6RID|S3Lcrc%GH zY|a7;X(c#Ed!{xxuRR(ND5Bx~;(9aqiMe~L= zw=9Z&{hZ;`ET=TTf0B@M%V;96$X3}^14Ec8UiId$j>(k(k7Zk>U+kYgxtS z8)FPQd&}=+|M@506X5=ohBoQdcXvzJeju)p($Oe#Cv}L+upoMFhXL1>_NJ{4f}S~A zE#uX`Vi@lL^zl5cCTk{BoQBD9cdq?!fFCT)3q0tP$0nBw9;(Kxwp&;<$^3vQ!{wo6 ziSNg*bS_gt>LA`ZZ0DCM;y(1-ZL}DgwDH=lsk-R8EJHoz`gKgEO63h6MmQdicG((B z-WT?A$Xxj zg2nh~EF09-ZI`B`Xq$UT86eoGoF8~INAjLU^J4lWR^4QRaJXFKJMmZ3ll%kZZGiI`(jxnsnG~CMn=33;L*IpXZ2wa znB9+xBuS2jGu*f=9XUe~{{ed2KYnG<=9roMRl)4mqkJx&qVUV%%uM+Nad=NWuqbs# zE@%2{l%c;-^V@WmTd#K>X*}X3y#FTLc>q0^mkUE{#*ZIM=K9QvSL?MfNxvl92lSQXF?xG?FJ7P9Xjrxl`&cH*7oC9T^u)qFC+a==nO98cs4g ztis9P--S{AoOCbTIsnrXUur+w4KFhA<(ZP9Km8O2?h>7QUgz4Y={Ab@>>p74DSr9+r#aQ1bCvryYl%zd zbu?2X|Cq_y(W^tY{9AIf*jThY zjtaPkNa|Y}EXZsTlrS~5?F|?|;ieB@oC1_=Fs-^7-H-h)`g@zErWsg{26-=4q1cEq zK}5{sV?1*NJJ4R{JMsVrOATXq4DF3nm+zZRyhk5d=zx(o;f;1=ILocKN(8I;+Ifho zJaDxSo%uv5>Fq8j7Zqsx##vK}8;qzu59(Rfni7iV#bv9#J50pD?<$#@Ry0^vKItJJ zMfn@=KwozWc2g0xVRfn)bmpDaby+qP|-yyyK6p7Z^JJ;okut*V+;bxlLO=;ag%a?My6GmX@FSuqYM zp=zPalOJCN`L18k9r@2oyH$(L+2i6KkC*K)(V}y>RX#dUupe)p!wj^0H$UJ8u<(D8 zzCDn9J;x_}056~d!C(K+Va0<5c?s?1MG1!^P&F2YnWUw1y(ij3MxLv6uPh@QW){Y^ z(Cl8eQ-F5Xiv)M2utF^~8$T-e8-J?MMW+uLF4UxRx)wcF(O`U}q?DF0M%su}_Gg6< zDm0X=S4?{J+qH3u^RL&n(LhTgV@d2t&?&mnxQcK=NN9{8G7g#U*Oj|Wp#0e9TMgbJ zBAA#mk)pg)m6%tcLYP&o_&Nt}H@Abr5BcaofF3nNSN?jP$Z0t2_ggUuXB-%NLw;NK zs0(TV>}PdToAa~VX4SNbrToCS&~MWPFBi0~GS5eq?IR&y{t3Q;2YiDq27Nw}JgNnO zhbZrTFr2#(Mc@_Y#rwbIr!AYQ<7L`rq%CYJ2Ki#rMR{rz@({FdrFk1UC}V_YBj-Ws zFp4}fC6sX`hN&M?=bT?=^8`shzo)Ux;|^;vvflINd302qH1-dqjQ!#L*Jl9hfl`p4 z7zeo63+DfOfd5;S#)AYYi8Ki5hezgZbQiRaDo7Wn;#qPM0C|i}5bwX@ zq`!qB1DL)M5G&;;$fU|6|APDG4!Ikj|2@h;yktU=Ge=1fc;g1P8_2$F(z;KCHoJDm zaO^+QTL4=JjbcF;8xLb}2v)j>2IVuAMlfF;n)&adYy?9|aY2|f!W_@5*oqA@WS}?A zNumM)q12)ehWOX;MXAq=Ehai!1H1qP)l`-vT!_4kI7OGylO@B!_8qHyMV>EDjxBlg(bQtN=muG!n-*l9es1jWx}qF5!%i z9#8(Otg2kkZ8BCI&HhzlhPgCS112~bB+f0w4Bk{*7 zveH9eK1Y~VW;wvPwA&NQ8R>7mM>FM_#pSV2r^m-ikR>~B7M5yqgj;JK7q5*W8c|vc z5`ac|tQ#%(d3&ZFZBHAsaasF!9jlzRg)olG=!_7cJ}i-DPB^%GL{2$fF#ofW_d<@p z0~Vdg5dO2lx>6ry8e{o6t6gr%)lj>ZUdZ{LEZIOssVpz?ZyoYZ84V+ypI{^=MyHZr z0)OXFPt!=)s@sJ9a54prpPCYtJdVnVTmwJ`2u>%E1c=<=Eu8MYxJ@iFAPEtviiJh? zaKRXWqd6lMZvgQK$qk)b)>K?1- zup{oY=Q~@6{1pE4BlkWG6xW638{)84I*)UQ>jczS^c-ewdkBZ}?4dYZ;c2_5Z_u@Y&B{zipK#!CA zjaD8v{mstTwBM^`7Tc>Au7lP@=gDI%Mr}*UW{HV>8j*aD9k#NiSVW;fvnT|p?oGKf zPtD=tk%0kScm{4dV@x=>cvx5_luoc}h|j zf|r6yEYbZ;6O-jaj=A;%s{;s2TCt=^I2!`>LV~lP{2+0lc71jS!pKH!9MKJ>L zia|RmqACt;PzJ3gNET$JZc1PV7jp1ag(aMnWtOsn140s5DRP+o>N@d-EvEC<>>YTH zQ{`Dnd`j~Evc6lBhi@`BDpUQN+N>4V(Q(~H!mbd@l#E|K?-|hby#MRd6W^p5S|GiV zlhaWoLV2)fk6el5z{0wW*5&!cyfUbKt3v$K^Q+W*zr`8gyLu<+Muj(zf`b6t4}eBg z*H>uD6-O4jP37E_BoS07CVbb2*NdLlrx3j6xkv*eeqV@W8On`73`KzT@$rXp90+xw zwyhd=y0pdVyc8OLJh*?9MJkSK8+akQq6EFM8b)|=u>Ipn$dAkLe?7N1w}ds#?H147 z-4-t2cbpz~#ozM^Ns!LkQ{~mPN1NpK1o*;{!ey!XKR^Y=(e^7k`Hx&JjQbAqQY{W= zAHlNpf3Ls6<|@!=gj6I+4{uAb(=_$nmyi<@;;2y#XUr$V-~duZBI@t^a^8QQ!D9kM%^rk~s37kax0q`L7-6eUal3YD8hF7$53#F?@0(#snr<#H zwI{tli3(!xl8!D*oDo@EHf!5-dQs}|F#M294DS-w_7V~yOdGeFvI5Giu2nx4|BlvX z$a1A?wbglLO{`a6Y1J17nF&`d?94XJn@Kg5MV1Vow>w{twiTpW;k{64;Un9_B&em2 ztZys?Qcp^&%P7o-PI@f^d$keoz8e$rfx*qKJ^6WkyKa&;B)yVnP)na+&dg(LQ{Eaq z7d1uO;hcr!llsS7ATuzO-o5U=s%x>Ic?j@Ad9iz0UR!BaJD-#hD0vm2dy~Bs65mRm zo(OG2S~pW~gwH2utJl;@vAiiiP3+44$HXNb9>@kdkM4HE`}Wsh*Z1bP31m!nUD7Re zhPIU3LJbkD`R9JNA}fzZf9A>lP4cJ>SxClc2(0zj{>xkaVMl^!>~HE)zb?)4@!f}( zcbGS!Ga^q2eQWT4duRA@XgUd3!+=77B0yaZg&moQk!l1tG+Xv}y$skdAsYR+AOjNn zbR2Hi7@O00g_T?miE&wEy8Q^$=WHal)VflR=?DTC6i7zKP{IXO zSO$xH>!plx59#M-ChGVQ4nXgUs7L8}1)4I(Yq|h=P&jOS#FIT74Dp9lW$1FJxqAypdn*kbZoM}ecQ zt={_`Y_J(AkwU3b-Ri1(Wq1DL3NiEe4_;4w#HYkPBtJCFwOgUyG{fV`)@hugu??ch zl4|fxrOZk5x1Xc2-B7a1QE+-sN*EDNt_q@MwL;^y5H2?MlGiMv!$c4D@46-`hFuw( zUg#h#or}ufEyMG3A!m*y$DM?9MEEWTH#gJUymri3yc$AIsWWb}x8?bGHS!9&%d)c7 z8uf*ECDGnw2Pd+!mM_Q=)^H-^hr=0=C}E)lpG|#LRlJQ$M-J!Bp=@&)UohDpk5Tm8 zty6jlpXHn1_um$rF5d}- z=CYSZix`?|^8t*w8fV^$dpXEg==tv?*3$c&O)EujkOjyxPZey-eE&m>aQ_&1S zHzt9%9Ao37|6dIyA3#2!0+5_tzL`1~S4U6>@S)R=$cjLBltth{5_>1eVi6{&ylTXu zz1aot4M@1XUOty`rDmF6Qs6FWhcIW9AiT-brX^A0H81b+9SF#M)6chlC)gi}!B|JP zKY$MdTOwNfXHtGaSRv?u)B(>?psEKGU?xJ)R<&pVX2sn#WXosNy5?`jbgpoqS@xNR(d_ zRoFpNjqyCBTgqIym~7Z$KPmXPsSqNpz_-n4zx>IuJnfUo{a6D0{cb6qq_7IHNm`iK zmBttbr6(JTQq0da{;4jyE0c!K^ShQ1X_fCegcm8UbUz{{Kxb1>Nm`3`ogm-&2~pTO zoNJc$VeF%!N}k*tP=3&SPMQNni7y-ZsWOi#d#(AowOye}O}SCh7x`9wRSr=Qi*a?v zibWyFszi`er)snMB4kI_ODy=um-p3z%#rSY16O$Q!88~ix3@N%MP0h z#4%)w>frTaCO_ODZ zZv8GTA{hL2I6Rymt??U|_4wdK-AS)1L;*$^sQ`_{uOxAf0@46#&a54!v@)+L8<3y` zWC10HFGd?(OywyyNa7o7l3K$*5SL<1M*9X19Xc1ma$atTm?pe=QS8<5-Vyd?fH5nm zuVSNKnbUBw&~cb-tyU$+Wn)KsxLjyVtq$JkIK7>8f06x>F7g}7yn+I6UU1j05;{J7 zBze^612Z?aw3HktpUj6K(RCzydNc(ss`KE#)J7iQ>vnetT=InE0~?dMBVm%qs!7et zS$mYz+qLMVLNNhx6z$lt?{GiXnY^3^Ce6Cju!@YK-X!nmpE0+>qR`UFYy_m)os&6l zVRaq3m-~I;p6YVHoa;0Lqnv0I^78Y@Vkn#RPOq!^YMAAPCb5EdSyrvxA*a-7lZ&_b z++EdUQ+^_PjfV&EuOf~lFN;e{HP!3Srk`OB268`CqBzPVM^eUY%9T_do*97cIkO~@ z;*va3LKhU7H7S`>pyKXf2=9NWCp^Ewet@Q%@4Frz507}4J0ai0_EDx!qmAmw;>2mO z{~C68jSqZIl+Tk@ABlDH`3RgGbHc(Ql_%o~TKEa{j+)5(iU|6^73rJe02Uu#$~p9+ zf~NuZ=fm!`TmC|=EE!KD20|Kw-1ADD`;BT^)#yVxyO?3DI$n*Uy9z>mLg&9V$sywhx4d^Egm4$OmQlOhJcsLhwiE8^`fMJP!l}O!ylS+^fs4 zDGgYlUjCVP8e|78P7lorOC!!`cb~sYxE3M8?_rB|n%Uj`xA@r8vRID z1^B@i_=JuhDDWnEDoNZ8`_5M-u&06z55;2^g!7l;q&6-x-`pDa`x5yu34Qjb`Rs{< zpAy56KBpCAvQvK$PTx`xQ=ClfZ$FEWaO7O1R_>*JITq>>dCJMoE}+xT*lut&-=nD3;bSll(ba%kN zJG2pyhLM40CMKIkJjLYb&KI(qL^u#r9@M>6eCj>Jd~A@S{ON5e&ilF(oPz!Md)xv= z{uJ|%?5WN*riSypqQjPF>I^+bk4@(#+lk8y-;F(z*=cvMJm6ZwuKU4T|Et^aY7jH* z&zR;@g6=AS)r|X9^6ta}vzRQw9kP9CRi}9=0naiQFZ*F~n(FzG7rK0;8O6kI0=ad) zJ9I&e9+;U6GIffW&Sl;uQT7cft=mk7O~z5f%CBZmG|D+HuvoNwCME-Jz{%=w_n8Bg zK}D(PJ4!kh*B$nHYmu=%>x`S%X&w)S;$!zraESyn;Eea$jx)HuPxJpu`gpa$h&pO3 zACI4Is=p1AhJEUS9HqSj0OXF-x$hZ)dG&+M_0^rFSil5V&`!;Hn#m)iGm)N`e03KR z`e?7*@u-%_Fh8%Bj_o_FdH@XM9RwqzwHsZWAaP(Fw87Xl^W*Vf>R!{seVKFVFincF z1MP3^I6b7*F2xp zZCf8o$HO;S*2{^j9PVd$^ZXN3$u4)ha}Dj5EdP##M_8z+*8-@#h%Q$i_EcSPbFJvF zmGcKN;5gFR?D5fa zZcDDIwDK`dCM2Tovew3*0>yBz!5K#^emVnG&>WR$ro1$QSTQnvvms=JYPIX@&OgrI}#Kp?b}g1?8&dOsGNL-mZ67n99LbW%jXFX4|W&X*Di1#G0Q^4aaH>tL(P1mTA6 zB2BgGFQP5mPod**lkPI+>hj$~FPT4KbQsgJ8oSHiy$rfv?Svn47?0;6OYToSn7p3G zonzULj$bf7-*}d9K3}?-EvHG(1a-oO9Mqam1=v=2aqRspZH(3zBw4$h64^OJ$ zkpF9duxA>?ujq>TX-s)t&Fk+EjeBYi^2gMu90M7Zz1#^!CY(L=KT%&MtUrM>?&PPr>NCHG!hv2}5GIr&l~_Uf&%$t7F}}dz!DhrZ z&1=pqCe}6!f3&TQP!^I}qU=JQjjF{J8n!_wnO*8EKhiG-hS52CC2;yc0q8*_?~I$E zfy&p)SHo@*Hj3iHoz+3g=g_dH+Zk72Ua_)v@IQ<6Nhnl0L1QqHo$)%- zzMyVoa`4mOQ(UjW%=Josym1bt=C6(@>-UF^?p9MB^F6tft1KSF{fWACp64eS?0ZL|ii*ZUy=li!`=yqBQ z90%Ci?&KCfoq7L*{nZ^96!_faynJMwh_GM9t0R|r_Q57?DK*yeuyq*E zAnZ|w0G#Z;BjXq6#^?3doN4+4S03zi#SMWBwB*8<=y8SOIK|r`kHMAKiHCezU(O38 zByE2vN8qJCszz$r*xU-J;vj0%%^jKzrkkI%lz&VDm+EkxWE82u;ZQ%X&QgP?puLNl znD}>2u9g&Quyj2Zh zJlA+jA6Z?GLcTj{?YYjfzetUmv*+jiBENm_+c#_BvvRn_Id2OWmA+Y@ z5V-R66yM_wR<3#1&LK7Q#40tk1)$?Kt0_S`l72WE4d>th`XQSm{L@@Dw+E`;YcailY#(LSQO9TuQW{ zKQk0d_FzASa<4bfD*p?dNS`x!j#hbLTGVk;V2zLH(cB#dAEcBrt(+3Ik(YRmTvua` z^wagMh3xd>7L~}-Q|pX&neDiy^=qKa<(sNbLjLjVwEO-N(&wYQg4~}16Hul|ghb*{ zl5YQ^PH{2{8|YAPf)l6wVq-pjLF00Xn(^_&kFK@smA>o=c`edpw>8~8AO_J^xVzt| zq@`l!F1{V5?V3uPs@6p(sHNMs;Ch=%Z&Sw}sgNbz2qRliJUu*5Zl@XERSXyV$MR|? zb2F%_w6K4}`oGJEA9E7~kj4IRZjX%{kJ*ziE7xjA ze(hu2n0Ne5m9{r1V-)B*2-0;VsX?sOx{KE zsk2MYtrn8&yCqqtr>AFUG+`9Exp92vPKu;C$1hO>lLOKKV4^y?`CYy(EY`lk+x;u# zE1Qnv7NDrR#jTK#)Z~=oz}x$im~_3fvb0tkR6ufWTy*oXz8Q6H$|R0&{L&O1vm$L3 zp}`k=&_RXaCO_`f^Q5Glr3dM`c0Eru*23W~l9w+m2ROPA>V;;$&tKJ~A&d!N;QH4U6KRdtD^%||6O6dDv3dv3#wQg60rbqMZob_hTo$)5l?elLN%&Ag2 zJG`ZgQPDk0Ylw{^tKVK6kb^J>juMoTZp82dAn>Sl(!bTWcLa~d*Ae->nx7yI962B) z;F!E0>RLVno@pWE{N@R{M%*abnclF~zK%J+5>Mn3_n|OY%M4@x5-HlxEt*Ywbe8~$9f=SU7v3{>@*J@y?*Vh4FLt; z!+ncm0^RYqjA?5K-#Orf>_W}!sRcVLFUn2#8>UVEtJBs& zTLROp3tpZFPb`44LQT;f^3Lxb$&J-n>R+TTxaq!fM|Rh@S_k?*3lnxA^-UJXBy;S} zU#K&djo)ciORQuB51sHYj?eU*wi|AVzf;Oj#A_IrncvWEf13^4TuTqTB{%^st>9ko zvY8>#mBMpNa6YEX@~=Dnj$QQZZu^ANGoPiDucpSO;Lf@{C4Be-_Tsmd%B2$M3+Y2SueV4V<0Y>twxn9tI~M#WQ-Gh zU{z`XJ#^9>LXmyx@KKDtu&)o#~p& *FS)1_7IcOxRg@I%5E!cs*`^>YBYS`=kVaUv!Ie<9%Uo zf-^t96Sq*gV8BL6qbxo=oX&v6)WzqZxDtlf8?Fvu8Q{ zRF#Yy8=~a0s8t@R&7(~Ad9O2roP*00E3w4bSpQ@CO z3;RO!n(^FmI3K*F{9A&oHvp6ZG)E~$(Z{<5BvHiq@Moxd>_F=`!_!{}3u!ZNRIkh1 zxrQ_wM~@9U-tN?{x1)@Q%VM9jE3Y>j#pSmEM=La5Rqe?*S;Yie zIx%}c_8*1FI&VD>frqyssuo-OWBfF7yhNY2qdXg(g!nJC3XEmdSt4Qtx! zpz(?rn*=Bn{j~p~0oLe(Jnp`e!L*mPC18@a5XUm!pR}5EmUudHK(y=ShH+F`cevAQfjygnezpPnrARJ!~KeBaU(aT%FZX2I0 zs}BhI(Jy0=g-1O+yj*gSRS||@BR1}!Ur)?@=MNVItV+&xKr{Dkr{Fgq*~X?VwAQ?0 zs#DS6_v2jfO|>v;A%jfQFUUI%*lrp#;>X)_-uv)>?%p*&q2w-qSKH0>vm}JGTi-~I zFLfu|B-%cN+*as{iaMNK59(yuY_kMOkqBM^C75r2(FpL|F@pNpbvbiC=n6e#+;i+~ z^-ae|=B@g?`xGqg(r^0}G*RL~KoRYWc1tDcI1WeK)Fm)+PDlZq9gW&&D_e`r_HP69 zP!2DEV+lNX+;%cdJdDY$rlt!jS4m3?T>u%=uek6S0yI6jm&QQF(_bu=p0|`211XA! zeUE=srRgS(XxJgwA>=eZhPz?Dz!OzK|9Ht5RBY{MB%` zXT7ABExua1T$ubAXw9LDk@h9Z60xW|?9ikO;`TPg^*|{Lb;H15N1X4LWf|_XV=6LF zH)XG`R(PW5iN9+A#|oA<(0d7GJkcEG+EU3KNri#YXk=0C>zG1MJ2R?0`X0fVa&-Iz z<1@u9-kpjFDT&~T*Z`k;0kId>zW2O)B#JIuGP1Y6pt1dYk(t|&-jO6pj2;=L_d}Jl z$1TAl6_iqEe@Gb~Bcvl{JI<_~>w~^K_}(QJE|@lD)H1@E82*?Xw%u^Yn07gQqCcGJ zvvGH0<<`&;t!6AWKIPYNeFJd2-JzK-2h@^;fna+6MZUe6b!_{bw3)k{cB_5csb{LY z2f#N}7knj0FNkL}o`6CFM!RUGU30!8jK%S}b++|=-*kjYvtK;dYK=P8VV!PWiVJ+eNQP;0N-RC4VF92B?ZA{ zgr%$@P zdnssw(#a5!;KNM*p)mvsWcIBP^#b1DN>#hcz%A#Voiw1R(TA*(zt-!c`+K?+^u95h z_v?P)-Spdv?e&Qq6QSKr%!~snFG%O&{)oRlubMw*> zu&Gp9SDKj{is_$VZ`tFISLuiD=Xt}&VR7kk%okPEhw8VW`DpZ1Mruz1>aW4? ztmM%8VU=R-l&u(?Gp0|W-v1MAe=av(BSa?0pUlE^AP>t`J2c)k!``)tp)Pi_NDWOv?4h{wjj;ZO?t42N2?ZnZseJ@K5a zW+I1fI5HN|haFKlHr#5W-F`Fg>N`XHT`g#J05`# z{$vU)7dnOFk<`cWRcyoAuRs&BZv0b)1IJ3OJ z@IKw%s=p(?xjdJaNO>#wH_Bzo8|pGu=X`N~0Q?++8Gp{;iC*^>#M1j3hU5?XCbZ+7 zfq0tOlD%$me1js+-FySoK5~Y#E|bii?Bn)_{NtVJFK0!gBT>4UecBvN(3Ye{A#nfX zfS{Du1RB9PIdsifiR~kPSv)y=dm<%N60cB8_FlBvJ99Z{#u{>cIT|%%y(M)zLeCh& z@QIMby*-hbK{4UX2=a2&^{d<@6mY!>#cLl)`=FuLsaPX03@7W;r{y^P0XeDh+J5OR z={PXhOyM8xr$vFo#o*s3dWDghC%t7ZZE+Xgg!cI^m`gb|7D3HAzvs+E@g zL&+`ST~x~%nu|bM4lU4GNP0cD5z~g6fSOSErsPNfXW`*OBC+>(hnP2WMz_Qn=l8KV z$J#ZO=vLw33f6PY8Z^577!Z>JNt9I-nnbeMb1}29;7Qd_uYT2=lXvC%9G!Bu+o;GR z?d9q%Vl)&iMbDMfHEe3N!s4l1;T|wSXwq~fa`Nl^fU$uwaTQ`%wY$5m+rh%04{NNb zZeH9%nnhs=cB+64@m{K8;?YdGn8ClD)ki@(8;1PT72Q_tjpQ4H+<(g2Ucp>jo(e8mR;}rIv1BCvX1z%Lmk;Z_!C9P8$MoX=Q%iMnEO4?T5PEwy>C29e*s3{ z)g3G#_2w1UT3*#PN*ju$)#S=MR^`Qao1tUPRZ&~L(w<<+5&!clWWjr+YJf*b%@)XT5xcMi6z2xbVdi`Rb#y;znC5r@P>A)tk z`FLUVT2AsVvC_pajB-z0F;y$-rh}}R$Pj|ZA%r7i!tsgGm8L;)rg~$L?j4TfH+5&} zCn!saKE_a@Qe`^#Bh|;O{Ek{991~%fA975l0)r~2YcaAj>udRAHQt7GdAYa)haWS5pPMeT+5m9lBV50Evchs)(oM?mnDr}rDdwxdNp$!;lRCGP8{o4 z_r^85VQ@Q@rQALN%Vv;#I5$qfc}g3Iyz{5drEv%H@Z&%%)zVDk;qaNw#hS@qMyU;t zrQQ=1sWixXEO*WSa*zEJ6M747j3Z8cmrMvf78*F#wcXcyVHx<-7WKAyrx!AXgfNHJ z+7JXxlGry=AmwOqQ^0}c^973H0i^6iQCx3;s}C$X8JQ?fSVbNJ!aa!P{L-3~*Hg<* zyPJ1xHdsBA3h%OWk`-IcL$uDW@pGzKu1l9x2obtQ0LSefh3FJ)=NBtJ*o8)=U9Tx3oX@U{n*Cc5oQN1sI0LNVTxuo^1ia-M{OKAvmZw=<2ka!huO% z8~xEJJUdXjz0-{`=Fh8!U<7-ScR=uBo)mcj>oxaK@2f@|8#CI@KOiMZ4->_pd~V-T z9_x<$7msw{2L;4*^sLWI%5vV)okmov zOP3FgY^L3EPk+_%e6j19tuj6N-!W`r>IA)bly#9(B5cr@Fi?>!a4@C0zb|wPwq(DE zoU*z>ioh(yfU!ZBU^#8CX)}TK_SBT9FsB%Yq26bo5XBDYKTF#{P*1bWaQ&?f%*u4qCG2)fz(=!XssRP6OOMSV8F#Fj=Mydj#T{{1o8* zOh?YDn_&FFlgiX0e< z8nPwj_^D!bi4W%mZaUJNq%>6&NHfS(C>shQ>cX%<8k849ECHoiCN{k*$LT;*4t|QY zJP~2H4yxxeP!O#MOS(yb85v)%GXY-tBdu*BVdo|7r&NaY>@=G-K3V^O`3TL^U4O6W z&D&tkc_kmy^pi_i#b(`&SBB00h0Bk_=Q(gs7w6rD4jw7q!RK>U-9iuDuaE|ZYa0dC zq^wnUeDIp+*v)|_hSH2Ki`HFNaBFd!nrOn+FDIC|dEoJcxL4{xK_sFlsu@M?VzP$VQ0)gc2MQx*tND)ki`_ zonczLo?9&hW2b!Uwd39zRQ(si1WZp9ApQaigS7eP7ctI~aCdGrLM}KI2YstNF9>81 z!hX?U>|&;5)3V`;n9j0G3`fi$)7TAub8y;10y}ojKhw$LjZTZ2ADbP#O!)pPa$FLE zk>0I;p1tU4Ea`A&Jf^KW_1fIkw4~~$Z-3`Z<)-?vhUu@EXe|pebsXsZXuR0fRb3k1 zzjo(m7HK|*T*&=(&Pq`A@SW+_muLAg5-BEofw_7uD;4m^ZXd$6P{vc5fV*FCe}^MgdarI27P}qzZdF{`zzwMz0VcJL-tyQ zB$#o%gzASV-gNzNOnAH={`u*odTF7%&Z)O~jk-b<-leC;vA)$JW5esrO~+hnE|BE0 zeAp@o+%eGsl`O7HZJmHOx?T-{8jr#*`#Ga?mO6pXH6X$PPXMjp*Z8Ugu3-VN_E%-h zwAh=Zrmwg{m}??1_Kzm=8*Xo8%&#wCg6AMOn|iPPo5ox8NLjPY+XL1irh1=QZpH)W zudL=8q9RR)&^)v*ToO@L71H$oPiaTSY}PmHtrYSLPb;HqT;3r18*=|g_gLWp5 zu_~!DB79Z#wPZ+_Gpmqhan^UK4OYv{5SKm8(e=4iFN4#TP*iN`vQ;EGTF7Y8bM^dw zf4DjK5#yBYmO-_GH8b_CTjOwJUl-_1o4?C;H%I!een2;_XI(da@iEeb%GB^VoTFV% z7@T}au1kAcQ-8PNl3;A+mBzUk^W6PtU0C$uaZG8vf8vz{oL-*4G)jho zvqo5DZJApae5Y66?8oCs+>2!6CZkWF^`6+qw$wP!K*=q{vFm6CyD}Tc`dD}Wg2eW-J5K#tm`Wp*AzYNc|puJujwc80%0zjG- z$4J-z5jlxAO_mu}xj7!LSZOYjd|M`ql9zeZ_NeLEkgdj*{vZo zfyeDnVHk8iiAtm45?qk^nH|6(^ns6D?I--TI4=k41rsw8lGgWT#WzM}>@-;sTWJr9 zp6JI~^+S3bb=td+@;`OJb&J>b;PzkLa8vWk_Z*h7 z@^z2~QhysnVY44!q)b_xW4}nr-#AWu(t;07Hnt@15jv=o5pK^C!gE!jC>#ZVid|x3 z?AvfX3#UnltkFKCX=E23p4j5C+1?8Ch6l1;{~nohLGe990GeGp{*Gn2_RRXN4-f{X zQ1C@jZ?bJFb-1zG+a1M*+;+g{1-+CGbc@S9Z2VuU$A3kVod#yP43a=R;q2#j&WS~! zxYW+uV^12|65|h>x?CO8s*632k6lOOA689C#!!bHd?tFBLuxgNe%)U%fah*GTrzR! z{M3guyC=PCncrTf2~5+=o_$7Rpoy%%R(c%tz{!p~7%C$_kvgXoFkzIFyC#2HX0s;l z{615(4~eENCeQ(XdrK=}hP|=XAQhXV)(BxJAyCl=E_!ttmf3RSJk7$)9i#z053nGm*^yw4LF_QDhC02tb;12rndH0+NxNyc>5TFH6vwB znRD`dwa;tB1CYoJB=4(PoBN~0*%=nFn1<|g_q&xw$E&KD?YTFHi=8j}M5mcqeGrFD zQTs&&>B?g7xWOTEYJ+rBZ0QNhqm_a7Ck~1GUkOyJ^lTNhk2-<+znI1dy>Hbd2+5hQh-Y=WKQ6k3{V-|I9y*Ci69Rdmx2PS#jISwzwtKgx?QtN@-1!2h~ zHXwX;QcM;c{m(!w1t5Z3H()AHpsYD09jCBp55R0@EXDW6X)?#R(!_G4SZ2RfQsHvs zd(tjUNolPoOGhgI#t7hfNAP=W$$6I7?5%t-Dwd59=XE`aKHw>gi_Ou-6?vGh^2>xQ zZ>=or-6@^Y)~$S?hN?qY*6MKA*m&~&Mhx1Al3#dav&)INr%% z_g>nnyvuDM!YB_Usaf(c*D7x>R46ahd(}O zu~M%K4sQP%H96XzqS+zuv#pa@pU~%Eti5j+4o76+N{4twY#d1Ie8jZ;w>{c+$RPcc zyx*D7%+EN*ST_Yi9Vj3#a&vR?0gaL45yfuQGABw13#MTbgdk{}L<|#HEPkKG@?W1_ z+nob>4=x??(FaZ`j_lszS$~7~H{QH}Dl_+*d*;&`q~RnxJeJw**5tx+cifvuySiab zuz_?g`#;WQXgzK%-=yj1G!Bz^hdl#oxs$=KO8*O+C24hI2l51j`H)+;!qU7Wr=in| zs=}$uw1#?DT5dO6%Em{y+UQQ8*g(om#s50nFcRKb1X^gVWWSMSo5!oWyHaq0%`sB&I0K&FZZb%8~L z!DjaWy&t`c(#w(R=QAPuh~I$gnVT2zI#17UgmI$DrV9X18^Lj7%ZVTt&+gx8@p zkgxu6`hJB(r@!tq5@YOqtZV?H%L}-^#+#~B%qr4~nNxQL|9DwrcZRez1u+7bW4A`4 z;?yB?l=n<1ySia>bG=WpT7b@VNYlO{0rLO2Y+0QyC;6GTCtMOOS{va*ME&KR2l+0C zbri(G{!HCVC&lQcY-KBLD{Z=2&$j2h%;}{mJ@<4xGn-UzX!a+IqkOez_Q|GeMJmUq zUgY(NROxvS=m^7Zv+}b4T-wYWMD$?uZF$V_?bqV%USA6)$ByQx(qQd2)d_|zW5H2CvXH=!C#^k_7nu$Lfi%34^1KRp z^lzyL&7YW~Q>8k+q;Y7CqME7$_wg}6uBLiow8dWuQSZCOBdf^kNdFatF`2ep%2qsh z8ubs)X6i>4jyYnD8UH;SPzy}hLI%Q^$0~A7mgnOxbWA9RJfxaE=j5~E`2^RIb+^=f zIrS-hk%eSx7jrdj0Y8;yGH@)FC`5d2oZCj4wkbNdmIV-u+1fJlSA1+lW~D=|QL}kb z)Z1vfKlCFMkOZB%c`mp15x50j1r3&BKa7)ridhLNAPPPxd*hR&(HXID^j7`@B!y3b z?9S!3Kl&gCc8Cu`Xqeh}{4DxYu^ zgBj8#vy8gv{Yu-bu5ET5g=_`%_-^O* zd~9Pb6*^3xWMbQcKQk4#k?k1vRj$5^>5O#~pIq+dFTQ6WKo9QD8yD7yTzY?9)YZD& z)F7#}3h`Rs^pH?KID$`qPa>d9DN;^CdU>bvsa=&C*eKu^mQz__Af;DIryF9 zNUW*pqSlIOik80q-NZEHxRCWYHg;zvF!`0kK4J9=ex|MBQO!AAg}P#! zsA;Pl{zoBj1NyfIJ-hZ*BB6@?W#3F^f?5#U8;vj8fuTA-!bckHyY#5DI=}qSoC+psmLZmk7WliEttj8{!-)1l9oImpCG>FiEe+8W$F;$ce z`--L8KkYVbyb#h&B+Adre2Lz;_=Iq_RThxOSQke7jW*PN*~M*Ye$!xx+h`9~D*|;U zMIYwxTG1c1x^6Nol7?{9$&oas4b;~Fxj0}5V4*pqQf(f3`TNd(x9c1&pugF!; z6Qb^xxJ&WzJkwON4R0oNjTn%?@tntoUPCv_3FFDyD?P*+%TXX~DrIGT<-R@Jx(<+Q z9Pv`VEQvGQe1XJ}5Ls3Ot-|tn<6YhY{+fmN3X6aAYHj0oQj<24opZdQ9}SR8nD(?I z!`Eu_vmq(KSGu|C&Tr}J_{UdNgtb8 zR7hDJny2^)PkdMt87d_GscI{1J&O>)F>kpeXu$IMeSXN}c{F<9x2c}*X6*G<< z>{@U%0p7chL_Xmp+qyH?U9fM+^teC=KEc%p(vC8s@jacAkkhMkza8wzZMp`tG0TMHE*1FnB)Ok&bnxUVsidm<*HwCK6v**UxY`lVLnR&)Ae1O&$E8 zCyjT5(X} z7#}TtkRfI<_Slb)GsI94PtX<&H$j1AeR8%Zf6oXDSyBAb>}&S1@#luS{~$Eq+yI|u z8_!}A1dz`Ks=p8;ooiQ?$ri#N2e%4BFIxynQ0EQQ13OZaUTWjCfUE+a0tX=ZfSOqM zl}Bo$T)E<=WNjOP(hchW!PQ%^#T9j1x)e^Z;1FB_1a~PsxNC4Jf))e|PH+Mw!CeY> zg1Z#%?(V_e3J4UEOZV;5=Y0JS_VcW@=bAF+J5(LUu2`F~mMxx<4@3MHy~x_ot8Z-0 zS)Z>T_qaZ^z4@l_R*~jp%~?KBDY?q{@4mpyB*%?&hnS@YyQ4SCQlao_Q_Ld5s8@}| zOkAbqaVead!Q+c*ZGdfe| zVDv>e)tCV@T8FQ=c2?1^`oMgq=;w?d1@dGaQ7h)-=V@P^8XUXv?$#l70^w=DG?tF5 zm^P%}{QNEbG)=2iZ(M#+R_W8UZYASY2eI*ZAnIt?UOO^61q`-G8?+s%TO~woVRt_v z(I6qe)fuutI#P}LvMt>r7;#VjIA*nzhq@vY1b+EbG&`bNpCI6R!Q%%o zkgr;A6B!>*HUUf>jfSlH{#s~4rk|jkzEQU_tHm|gEV8I?f1VV#G#ZK2+L$8p?(3D4 zYW8Tx#mKv>ou&FN=9pujnq3i=oj>8&_=7V`l<|E98y$=tPE$;`>($nuyT(o-YMA|I zMwFl(6L}}AIkifAMC3yRTG=TkZ)?^h?L&_boj1zNY&kVP5qWyGb zA41-*D==yEzKtzKui0Oc0n%=wJ~RDEVxjE=N!6%zx(W zXzu}ZK*HzW*z-uWW8KChki?Ro01$9kd*Nxo#VKC6Ds}F`ewR3_Q5iUY?lB&o zAXx!zHOIH<92ps|mFIPxsPX+f&9>he;bNXsjH$i&a}J4_qWhZ_*?(qIDoEbq_)xYT z$>yZ>N)L$0j2eNmPGdj2m*RvHin4%hN^9xzdyMc74U0@vYeMwfn6~^Ue1sphF1{eR=dYRbQM`l(wm;FXZLxp7 zr9>&U?d^APXAF2#5?;L7pz^wLpg74X+W_l3+vt4`A4GO(E>GW5R-+1%98DAb%5rx1 z++lL9zNS<9?h@|!7H3lF%*G28>$M;KD>2;q&fUy%T&QN7S;XV0 zECh;OUH161VF*&si7r%J!>!=`#@c9aN}9Nw`M~Io8Wra^Y?8kpAP8yYjH-8*bgFdO zGzLU(8%#ZqrR6@MToSF~o6b!Xj2AM2d%b}qiZ2V&7!2+1M4kQsj;|(ctBN-P1v|E? zOP{&tu~L!CzSE`%aBH?oh^2^!tMW9}<0HgHxH<)gOHTt#nnxGNzQ@tm{^lf=Dv^DG zUr{F*^8xMLyawr3Gk5GP;0DQAH0b463D(#MpR-Wf{eK4k2{Hi-S-KpckZ}x|CjMhf zSQPn{?rj0u4XrzT_oJ{1GX2M2u4Uu2-8=7Z8+;NcJ+S2?0?gRk#hvr(4 zmKf1piDpOpRM{V+wu`Wt-!fVrivK0OhSA(aOVG}^(ZtYXz7z|9Dh^{<_?HLGYh`k9 zT_^!CZB!^2&&zZBTi`{W)46J~!STI3wJ9JG(C2*6a_DOwl+l=+e6(7oDY5B;A$g0) zfm(+uoV>7bS=jLXfm&_VygG<6kIaaE@-0hb=li9gbfsf2D1RWM`<7YJN;K9(Sf1Bm z+FgGxDP~1AwurY^11kN`o8uzQYIeH-&dFyD)Pe{&#e7zH-r%HHI+8ddT|<;g(ZUAc zc;wl7-{rUe-%oZ&#|IZ!nu4nAva!?>uD(dkNKU1PlAW0=<9@@Q#G9cx|Y zUD9_(TlEXG&Zq51(A+X238VZB4c3IBuZ^r3Yfyn4yKxrl|CZ(b?}t#L9M#U1%0$Pl z7EhrtpXqFy*=x{Ujldtz;6Uh`o6>R5DmXF{-|Pp}TZKCXSfezz>r%&lwTg?T)sa-3yD1C0fB$4~b~>Ov;bSA+ zIM4WhHJ9-uT9%l0x)?`AIN~R>?m*A#37CG=UI);*BkdACTm*G7M|cyLC4U|PQ;P0u z>DQp2HY>%xFML>w8g>jUQ)kWG04A~9in|r z9<6=nwQ{FC@L0h%qssC^zS#9p#I^Kx+r}%WwVK0dZEW2^Swd?=L7?=XB=tn1u&x9- zg0cDmF@V_v5q%~mUOXDP>vOKXSQh3G7HRIRgXg6WPbcBkWUXx5OG2w{LT|V6$M=WE z1R25atwgIHR>R}}e%gzM`6@R55F|IP%8ZF{f@{vkZN7E%a>N`uTT$FWijwl%vahm1q)*s)0Yn z+kBmJ>+Zm$c084Ai`@ASI*ylXgI2`uo*y7dI~P{nUZ{g<G zzheH13vNO19~2%KB$ILCqLnt{lsOYs+>%PV+~ zi>tkSxc|+}#l3gsiqpMsAGw93CJ2WnHWc{Z6c+4wJkt<$B(X`Gw7O03`0Rs9N_pMv zL(BCP^b9rg#S_#6uzM;!?jAoNfROu;lYc>0JJ_nc--e)x4Tr&rb$@}h zn7%@xx&kRP=E9iTr0=0@k(1Hb1-uASe*853pCib7JJ8>RueI;GhGveIAE=IPv)Q{1 zH6G$l(Vr_BM|AOr%JCP-KWw?_&NoF`m@0)uY9)vDo+4dZn4nplFL{}v2D<|~3$?c4jBXp^ zLh*MTrpnNOrvq@P+dP`*Oz*$j044VI_F-QvDA$rlGRf?@HGe~$k~?#KB$T4H^N}|(!}pE;t_l1Ha>-YG1b!mP>px-9pS&7(NXn?^N%?n z`|^Ewk!LGnu}T$W(Bp0(foX4~c>(1;IhwJRe|KDoTsLg5YfLTs=~|MVE0ZNHRK^gd z6F{PvzjMN_SunL0(NNfYN3`dx%8F0~UX>of3?szz$AJyOfJ+NH6=>m0!PyRdP0`eu zXQ-Au<8ttGMSgglWU7CH+&5aD@7`4Q73nz7;yg5w?)LTrgoJ{W^PhafK7(tJ$b$Pf z;)d0>dsfGv85o`@$9qE?nVZz|O{%j1C0DACVk%3Prp?Yy;h&E_ub)kFM&cHnupOct zALM`5m9#@uu6}31a7ZV~ALGyGin*>4?apP>pF+eZz?aTpfm6`yLh?_(L3Qn> z<#(ysw^h(RwV|;=%nNvR^lbvho|@gkp#(v7^4%RMaDJ~uR9D>(yEqF$d0p|G*5C2m zoRP^ZpT)8b8>#3HK#V+OO^4j5`UpUFuwWuZ3^&{GCxpVh$GK~4hAVvS~8GlFIUH^WT*U;1OsMZ_Yr3WxH%!a?=;y);)V50a!`rTB1q%Ct{H$FIEZ@kgPc;&vL1@SKUk0E)=mW2b-O zXKS6A=@)*O+D9tK%{QjMRZLY(4k|blcgXvc+>ZG@VdGuiV{=lQo~1giCiPCp4-HvVN*k9?J9VY;^J|68g1U zb?G`!QBvf$A7TEdPHDpgo9mOZNt@0wd1oMaqPJeuHQeUO<6Yq4sqYk787I0)j>zAM zSyF9ES8>?PZzzqY@}0@Zp!XFqsGq9{enwClLNcs6E>h~}K;zCYp(0YD97tj`c916V zhT+4&2b6<)j(bZL4BMJrOpoY(68O)|_GYTtA?^4w<|2(y$z+HAM2`JG=`k1`LI=8P zAujnq-w~M^GY#uc(>Q&2LK$XpEg=w`u6K`8B!N4m)}PZOUIjhz>Rdz#d$R++lZ(-? zdv(L@rjsg{Cu(rg_B&8pCuOH>=AW3u?lqsGX|(DD5I$KV``@zrB!)7KI*vdc5V*ep1jmZ2n!9&uN;lvdJMM7ARq6S7N$B-vD zbQ@mh1(v$h7QOq0&(!X?afie>*DSK30I7x91I2-R5VH?K1%iuzjK;b*dUGuPlnH-g z0(H$fo3YKD>2o-uP#fE3qhQTsoV3FT07_Wf@Ac(W-8{ZQF>ug;sSyr4Z}bnjj2c6+ zhytJpt_S%bQ!f(RSlNkqa?S^=*vW6F9sM>QzKOAHLsZe^hF@G+7p0{*DbyPzFBhJ-x!m>yx1d7ga(cLaw2{gD zMp%p7_AF(hGJYKd5!7l{u>~*)-^nL9gtp;nB<1{s&YFmcydj-M)u!JR!hA^T^#`pF z9GM*}7J^UrWDOQQKhW@>Z_f93 z4Hr_e>Mj>$YDP9?Hz{PBh%VxT1NdE#nScmxYX+*sO4QMf{1x?Qr!0sC$5Wy`okTRB zsePCIXfSeye>?}O+^yrmE05S$9pA1;wVhf72f_)-$*H=0acJVNJVhxf3!y0)#m;^S z;LnS1a%LU~;Im!mFXcI}81&>uP+Y_A3t^AINc2tE9p*)0?!T7+EXm(I(Ps2%A0x+i zzPD+UKa)u)`ei@4*ilj`XHq};3#~hb?XRy0b~XIeIigk?U-v^$B`@7M4lgAFDR^kD zZ2YMCB|sJjWOV9{&V&hyD~{hw9};HtaOb$Iif|QCG$Giq@6L033%F-NKazzkKTQe= zD6xAj$%wV-4QJf=Umh*~oz+gY3O+*2gR2s7arl_UH9m3tccEN}y1|Z(RgE#2^g3e1 z?p8?nFBZVXVpajP$v@6VKtO-fEgT~rrc1`2>CmRJ6U52o5)Z3R&5w0TYRP4bpC17M zzdbX7x3(_|B=^D1&Hi7SsoB$k4!!6o+coUKvj?C5)!ITRUPE1v4 zl)8kJWC|@3op_PSRtQD8j%JKVYc!D7GKhHlCn<{#b@P#8YFZ|nOs+uWFK!$azjXz( z#z)E{q-J@7idl}X9c`CwW{7Vwb5OFS((}M{`=Vyy^U3Dx)~{}lx`ApoVJq5#`<6Tj zIe)^n9VwkLFC)9|rn?(+X1_KN6)%V!MTCD@Kw7$?Vq;R>7cJKI+^hVVj8g9RTl*x( z_@&+3Zxe{6-!{5+^QFhMY-bcC4?#23m*=;^ z$?z?i%a?FZnHG(U60Abr)IVUkeQ%h(T&2JhHij-KCr}79PqlGIKi7d{wR&&kpn>@s z$;>2GyNWvPSv+W5jXcmS0~L))eBTQFSRa{st#BelhZujJ)~Xe_<4N=KWwM?O>Cxn( zb&3Rwsva6~(Z77HZPO@ZBU6#)YN2Qgrr;4SuiED~NtIsCLVcEhn3`5ka%f~S8s*!# z1mJi8c%FE(Q^MFB*$3$o~w*DgnrsGa`E|B&@8_smBR zy>4S$8fcWl^(%4B<42gxJF`kcZ`46RL5AdU3JlF^MKZnpaH6pAb9ydm)6(oD!a)47 zSPy%|_VGl>D96UGKD#FFgaq`~rL8i{Do*zKFlOZQRB=rA-BHFn)L2S%k0j_|n)tOR z*1f#XN!;LyePE8}Oqt*5@NTY{A)37QL6@yv`?dKT1$*~KkZUG8L(CZ8jBA6#weh zy@kB?sixn!Oshh|UwVT)P78vh&)VOmTKYkjZYx?aQHdP}ZHGkV@C~i~s{QV~xa1{@ z{E+8SqAdx0B<%7fWP@(V9<~0Tl2{K(tX=G=r;rtVb#->*JxVIsE!u^Mb?>pnsM3DN z=ZUDm)2tz}Y$I|)ypIW&FkJdvG|!v90lk)Ly`Gg&3z0tM88x0V$u2gx2x#PnoQpkh zyq2`oQ0%%xo5puv+beLDnTvQ3vCdB$5MYc*)bOQg8#c~Gmec+5hg?U~eT!?xt;Tjt z%tOnh`ReqEE93Jsjg5Eq*Z_j=tW+W%K|&R7OI{v(%cm6_?>Tt_?vwrxzF23-2=oz( zn;)M86?%n=Je~{87*6cHEB~_f)*^dYg2CGY81}<6mUk*Our%+X&tId;807)JR2-*a zis?s8_99*0^S&`}Otq5`CA?Gkt(Mh#Pp688Gp)Tx7`y~9ysV5=IV)v7?%4mSG$AA? zYK)mI>&C5mXuaM*SoMZ?d%U|xqNwR7Dx2O)F^I+ZT;g8}2gY>xm8qMnQMgJ%w+%&KLC^U9y$6l|5u8?|m5Wt~i_82nA~;CwR*0};+Q@C_GQ zW8?{g&-HI?k@!9vrzDkcBHYFlErD>hQrd75*G4Pb2n#a zU!E1ItJ!Y1@45WWC8A(LsXN0jwNa;fBcQ_cD|$i|+K8HXPV|Lk|0zI@%^t*@o)Xp4 zG}iZDyidvDB|cY0utEA?YUSu~V)(G1NRw(Ah|;RXVv~w*wKpnTifQ0GyvWak6#9)a zlAU}ooW>fOzQvEk!b3V7;Z#M5cQft|JKu3b`)3&Je_@t^&O7(R->sJZE310S|Jls` z|69P{QEBWiKjRfW%!Tq@Z)6uFT|j2rTdHPw$a~zj)oXepSc{P+BC!11K|apqEJ6V; z>1UX04N-Q6C^9SO!Vutq<0bz2=XYe}7C1=qX!RTm&D^{wbG(;CV}=jyMZpAI458+$7lFY6$3 z11rQap$e{os<(UqC~rh%sZ>oEIG-)Xih4O3SScWCIyc4^qMGF@SJnUHYFzoBvQ4;o zC;cxgOo8XH%!MqPUhgNuVc&QntCf%c?)cqp#2i8%Q)}047KLh67MY-*i}^i&)v;ai z1kpazvU}A2!TRZt4Ng_$<=Y+~oCn#fVt>X)zOM(g(0NqkuNS2V%zWE1K`_(Th?OS@ zI4;p&jDHTw5l8}KURL%Pc4P|spt;%&(>RIDRe&}G_r9INJSB;Vr)p9qsNIi*XphQ zFn=@sA;8f{qiuN}thf=n_N=_c8s^F7CLHen<}DqS0KfNf;RmJb`1yH*`iaUU z^_H6kzTS|CGOkakv$CYDJXD|gs7tf4D2Til0}ebiujwqocXc0QrvUC+(U12StuR}G z8-j`42upe26R%8)9v!u?JPKG13}fWS)qUQ#Dq)54QQQRFtT`WEZ$9y|no>W0l_p~vMS!#1Ew}PagzuY#634CHQkJT`m zX-g)Qf6rpAuOk>N84(x(O&RtPMk2lHPCExZ9?m`dd9uFWy+XM|L0~M7;I_JXEcvK* z*tREhumCj2(Ve6BCI3)`#Snw?<+GA`oa*-4AV59f2IDrKMEqe-*veS>e^2Iky-J_#*FFh z)EH^pUcG`cN496K8fb5HA#%T=SUxpUxHQY!snWrpQV@JAEoO>(9e={^=pVH){pyi& z)Tfm8<3xrq#K9fK7Uzz~g<~nU^f13jf42@l+)dT>K-1oPhZ!uV&g^g*Y;oo?_C3mhPUR^>gsOD1J3>8`b>s11c&0 zMTca3%y_BQ{aMeG>JP|CY-UvEIxOEH7iaLcH-9)XzkLXJuyV+2cqF|1u0PT>H1e0q zK2H_n@-UktmGNlc)GOaJcp|iv%|F@)>GI7pg;H&mg*#yluAFT_b_loWlfA^hR@bFel>iV){aeug)K*O(B{~QsLQsCgo+?2VE?jKuYQ4f zpOzDUh)ilsICZn>GH>|d-vTiL3OUkvm0c!*0vziupH{ zjLx3uE>`f}XwuF7hVeVFkut%TeIu|Y@hcC6y3!=0vb~4zM$$mmFbKb!?BoOK6?(PQ zlr+->3cbAZN7Tu^8V*Za)W~Iw@R?wtGLsJUv7*3G6|^_NAnN@KW#g0I;Pl83LCGv# z6Z2OSd^jMw50W&@6gHDKb(NYb^Q`~_~M+xtWpA>E!bZL*3h=);68JzI`fj z1tUPckB>I4xZQg7i9`77tZ?<6c=r=IdjW2?$kXGeUjYgAw|f*x>KQhKKz;u9nGc>B z77VAjKj4wa(Gj93=zK5&+p9l+wmwu6ZMqL%=jNZwcH@nAW!pUdt?CH)F1)NH1X-Yt zKP`ss^xnrDk78@z9}eii^anW6WQKv>k2LjjYd3Y|RdlhvK%Uga$Xv zM@#%x%g{bC8*sa|TA+rQS$r_lota+5JqU0c%dcF@k~jHw^&n@pcyiZY3Y!n}-c^ZC zc&Zo#Gi#Rvu~GM zlXD%`l4$dvA`xXIc%Yb|Fd`2rQw#VAu=l;R0puf<{l(CNNZ2%O?qZKQ{IO0nQ zbIHypNW&=h7ru?Ds~-S@ibdEP%~Yfo<)MktGuR*juKv6vp5bYhr8tUtI+)|ZDtYG zcK(pqa%M=7Z<_QW8>1qyt(kM2wNaWWj{N?a@)CICu)A%XlFCrKXZ-Q(Z zl76e?w2^16j>dG!9EYL2cO52|c@g4i66^qFB9GM)xTzeW_hawKrjY=7Dk2Vd0@XQ3 zs@inzw$|qZEp(Omc96Rk!=I@x*jLkxZ@8_E?EMGd3C{(1p6Dp){?Ao~IeUf6FD0jiDyf#c-49<3>z#+C2eLKFfigN~7dfUYCmL}v7@!u_H0X^7&t0qcR zu@V6!V@$_I^m(y%9D#0)NEOtZIf^WbOuxo|x&srdc3HuP^q_Uc-D>OIkyBWwnWXTe zaM4*Db96)|158AjLQvZnJebJ4jZgf#;sJrt?<=9M%k;Z$RK;dYCm%3NNC?C{4P6Bf zsN94L(nzrBkVfIH`@;HYle@ZivtQru4xL~9VhEG!k`F39G89hUtF#N&;Ynab_=eT- zop37+z{XR9(=mQQ&z@&T8@}(=7!?@%^+;WD zR-{{Cw8qR^%~?6^o@^`8q2wX6_6UOrnLz(8zh5H-lU-R$dzxx+5)RS zH_%vs!|x#oiswU5ZYT$XpHwR0a$5fmCuf$THr1(puVH)`t3xE(oS1&8Q=T4mCtn{W z)6H2Q3dvN?9s%stSQ=E_!TQSIHBeIB-6~^E>4qHfSeeI``3MO5V)y7TznT|_`Cg-! ze6OM+6@=sRL>Wzv29g1u-ZHm5cwq+?2?xHPBqJPj##ep^V%W7~g{CfQIkM7fE$ft>`nac|<-`R~0aTuNIs(*3N zLM5}HL&JAkF#uG`_z`d^P5Lph4xtRb?;CAdHNzAx?K(4jrXf5EeNR85QqIh;qw%$o zYTscQ{xyagzNz)y@rD(SMQ6kU2JfJ$@PSDr2Ef-WE#{yCZ7ny0F)3_xH=*k5!ESrb zr+&$=pe)NDLCRr{7nNX@L=bIq|4@`sAku=S?=LAzZzNI2r4+}1Tg@Lvy$VrZ4FjAb zMO|gG(Ug~;12P;8l>W!cQuIt$R~HEAdBhT7^R%I&TCl!%G}p=v*%v&R?HTd4vv#F0 zX7BYe*M8fD;GFUNJ`4fb{%&dBj)2AvS{kzcIb7=~7Um@@`0^C{iONLPf`D58ZpSHU^kN z-$lP+P7sOB?4?kKaap~U!3ZqUE%{2pM7rLLNOf#Lz&}1u1*&A^Rq{I_P{{}n^*diZ-oj_I-Gsy%;Gy_@oPokZ8=Zq5S8WDEWdpBo`FrKOq!t#+PrnkR_!bh+1 zc4r=%#M3`=SWX>GufREg1Cddiweda(;f$C3mK159(oxlrJeI|ILMwOBmC=^{`iQC{ z8rgP-wax;Fzy4EJ`l9CVigL|~57a=Slr>(5Q z-nk^%V}9z=G_Ju?HBqG3Ceap&ESP=6N&TVty`sUyMF#5UHNucX$`U`SOB}O*Sb+-B z_tad3g1OWh-)j%GkS^*`O<$rfGKo70EC1&V-0LT*et2=64dbljz2sAGePeAR)%v5j zSSw65z2kYcp2wB7!GW`AqUd&Rxd+Ft+}XY8jK>1AQ<7YS9qiB)FOa^fZ3Pm&#qCU) zFnMM+dAuwJ`yLw~pPdcLRKoOeexaD{r<5IC^$wN-zX{$th$}olpMaa|$PtRuJJL3C z)}oe(PRBq-ruRr#=)cjAQ#T&`OcAXlyY|9Zak`^a#n)O?F8j}3C59N;G{KCYfKCDx z-d?A2^gKDp$~Wuu`q$(6c7eEv!2ODV9Pn7J%TzhgG*sIC1l9vG<(C>a%alObf}?sY;7b|_t;jV7E53(}3Z%X3 zX%Rq(Q(*@1_yNV_vVrZyC6AmEWJ;X@7}ObW#|1ziFfIxADBj0>X0F;|{D(BQy5IDW zw$KTO6Gr;Xf_tq0X%=x(mxrd&r5fFo;Yfb4THCN!!!$Z8t?*-r%7n_95i78eDatp< zdw0xr8O=$$*j$UmoC^6Yn{2Zy=~(}dKlr~Q_m-ytl0#}lRAgj9PItFBZ=NI;gCYKr zAF+K8zx$0ghCEy8eN7R4W-T@HOj@OF2MS@K^2d(@OBHv*tn*{#xM3}H>XdO1UE;(u z=5Hq_^7h~w60{A!L(k&)1(7iA#i@S2H$V7*0#rpw@&~@PdP;IL!^d@FXYv(6zPvx6 zhcT^Gg_r*m@%VqXNngOWU8;OOtuV()RiU}_qf>>D=k=qNJxm&!fD8da^XBUORYd>9 z

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 a-*y>1LXL87C}xQXe;AY#@`aW6t^Wlz4L8^T literal 0 HcmV?d00001 diff --git a/docs/img/autocat_icon.png b/docs/img/autocat_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..df146db31a777072a76c97d4b91b87e4300756df GIT binary patch literal 7250 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 A)&Kwi literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 21564add..00000000 --- a/docs/index.md +++ /dev/null @@ -1,17 +0,0 @@ -# AutoCat Documentation - -For full documentation visit [mkdocs.org](https://www.mkdocs.org). - -## Commands - -* `mkdocs new [dir-name]` - Create a new project. -* `mkdocs serve` - Start the live-reloading docs server. -* `mkdocs build` - Build the documentation site. -* `mkdocs -h` - Print help message and exit. - -## Project layout - - mkdocs.yml # The configuration file. - docs/ - index.md # The documentation homepage. - ... # Other markdown pages, images and other files. diff --git a/docs/javascripts/mathjax.js b/docs/javascripts/mathjax.js new file mode 100644 index 00000000..5bf8e9aa --- /dev/null +++ b/docs/javascripts/mathjax.js @@ -0,0 +1,17 @@ +window.MathJax = { + tex: { + inlineMath: [["\\(", "\\)"]], + displayMath: [["\\[", "\\]"]], + processEscapes: true, + processEnvironments: true + }, + options: { + ignoreHtmlClass: ".*|", + processHtmlClass: "arithmatex" + } +}; + +document$.subscribe(() => { // + + MathJax.typesetPromise() +}) diff --git a/mkdocs.yml b/mkdocs.yml index 843b8d31..8fabab2c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,9 +7,17 @@ theme: - navigation.sections - navigation.top - toc.integrate - logo: img/autocat_logo.png + logo: img/autocat_icon.png markdown_extensions: - pymdownx.highlight + - pymdownx.superfences + - pymdownx.inlinehilite + - pymdownx.arithmatex: + generic: true +extra_javascript: + - javascripts/mathjax.js + - https://polyfill.io/v3/polyfill.min.js?features=es6 + - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js plugins: - search @@ -24,13 +32,24 @@ plugins: nav: - HOME: - - index.md + - README.md + - User Guide: + - Structure Generation: + - Bulk: User_Guide/Structure_Generation/bulk.md + - Surfaces: User_Guide/Structure_Generation/surface.md + - Single Atom Alloys: User_Guide/Structure_Generation/saa.md + - Adsorption: User_Guide/Structure_Generation/adsorption.md + - Sequential Learning: + - Featurizers: User_Guide/Learning/featurizers.md + - Predictors: User_Guide/Learning/predictors.md + - Sequential: User_Guide/Learning/sequential.md - API: - Structure Generation: - - API/Structure_Generation/surface.md - - API/Structure_Generation/saa.md - - API/Structure_Generation/adsorption.md - - Learning: - - API/Learning/featurizers.md - - API/Learning/predictors.md - - API/Learning/sequential.md + - autocat.bulk: API/Structure_Generation/bulk.md + - autocat.surface: API/Structure_Generation/surface.md + - autocat.saa: API/Structure_Generation/saa.md + - autocat.adsorption: API/Structure_Generation/adsorption.md + - Sequential Learning: + - autocat.learning.featurizers: API/Learning/featurizers.md + - autocat.learning.predictors: API/Learning/predictors.md + - autocat.learning.sequential: API/Learning/sequential.md From 3f05e8207f8bb5be85176b6ef2cab99675e50dcd Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 3 Dec 2021 18:40:41 -0500 Subject: [PATCH 131/239] add data section, start writing for struct gen --- docs/User_Guide/Data/hhi.md | 6 ++ docs/User_Guide/Data/intermediates.md | 0 docs/User_Guide/Data/lattice_parameters.md | 0 docs/User_Guide/Data/segregation_energies.md | 13 ++++ docs/User_Guide/Structure_Generation/bulk.md | 8 ++- docs/User_Guide/Structure_Generation/saa.md | 64 +++++++++++++++++++ .../Structure_Generation/surface.md | 14 ++-- mkdocs.yml | 5 ++ 8 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 docs/User_Guide/Data/hhi.md create mode 100644 docs/User_Guide/Data/intermediates.md create mode 100644 docs/User_Guide/Data/lattice_parameters.md create mode 100644 docs/User_Guide/Data/segregation_energies.md diff --git a/docs/User_Guide/Data/hhi.md b/docs/User_Guide/Data/hhi.md new file mode 100644 index 00000000..6f08cc05 --- /dev/null +++ b/docs/User_Guide/Data/hhi.md @@ -0,0 +1,6 @@ +The Herfindahl-Hirschman Index (HHI) is an index that measures market concentration. +Thus, in the context of different elements, it can be used as a proxy for cost, +as proposed by [M. Gaultois, et. al.](https://pubs.acs.org/doi/10.1021/cm400893e). + +From the tabulated values in the reference above, we provide HHI values for both +reserves as well as production. diff --git a/docs/User_Guide/Data/intermediates.md b/docs/User_Guide/Data/intermediates.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Data/lattice_parameters.md b/docs/User_Guide/Data/lattice_parameters.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Data/segregation_energies.md b/docs/User_Guide/Data/segregation_energies.md new file mode 100644 index 00000000..d0d92d3c --- /dev/null +++ b/docs/User_Guide/Data/segregation_energies.md @@ -0,0 +1,13 @@ +When determining the stability of dopants within a host, one +important factor to consider is the segregation energy. This +predicts the thermodynamic preference towards pinning the +dopant at the surface of the host versus burying itself in +the bulk. + +Segregation energy values are tabulated as reported by +[A.V. Ruban, et. al.](https://journals.aps.org/prb/abstract/10.1103/PhysRevB.59.15990) +for multiple combinations of transition metal hosts and dopants. +By definition more negative values indicate more stability towards +keeping the dopant at the surface. +Values where the host is the same as the dopant is the surface energy for +that species. diff --git a/docs/User_Guide/Structure_Generation/bulk.md b/docs/User_Guide/Structure_Generation/bulk.md index 4022a951..6e303a77 100644 --- a/docs/User_Guide/Structure_Generation/bulk.md +++ b/docs/User_Guide/Structure_Generation/bulk.md @@ -1,9 +1,11 @@ -`autocat.bulk` provides tools to automatically generate mono-element +[`autocat.bulk`](../../API/Structure_Generation/bulk.md) +provides tools to automatically generate mono-element bulk structures. These are structures containing only a single chemical species with no vacuum and 3D periodicity. Multiple of these systems can be generated and written to -disk via a single call of `generate_bulk_structures`. +disk via a single call of +[`generate_bulk_structures`](../../API/Structure_Generation/bulk.md#autocat.bulk.generate_bulk_structures). ``` py >>> from autocat.bulk import generate_bulk_structures @@ -22,7 +24,7 @@ Ru_bulk_hcp structure written to ./Ru_bulk_hcp/input.traj In general the following structure of the resulting dict is generated: -`{SPECIES_bulk_BRAVAISLATTICE: {"crystal_structure": Atoms, "traj_file_path": TRAJFILEPATH}}` +`{SPECIES: {"crystal_structure": Atoms, "traj_file_path": TRAJFILEPATH}}` If writing to disk structures to disk via `#!python write_to_disk=True`, diff --git a/docs/User_Guide/Structure_Generation/saa.md b/docs/User_Guide/Structure_Generation/saa.md index e69de29b..656f4c77 100644 --- a/docs/User_Guide/Structure_Generation/saa.md +++ b/docs/User_Guide/Structure_Generation/saa.md @@ -0,0 +1,64 @@ +Single atom alloys (SAA) consist of a transition-metal host +with lone dopant atoms embedded at the surface. This +dispersion leads to unique electronic properties. + +With the [`autocat.saa`](../../API/Structure_Generation/saa.md) +module, we can generate structures of these +systems to study them further. The main function for this purpose +is [`generate_saa_structures`](../../API/Structure_Generation/saa.md#autocat.saa.generate_saa_structures) +where multiple SAA structures can +be generated simultaneously. + +```py +>>> from autocat.saa import generate_saa_structures +>>> saa_dict = generate_saa_structures( +... ["Fe", "Cu"], +... ["Pt", "Au"], +... facets={"Fe": ["110"], "Cu": ["111"]}, +... n_fixed_layers=2, +... write_to_disk=True, +... ) +Pt1/Fe(bcc110) structure written to ./Fe/Pt/bcc110/substrate/input.traj +Au1/Fe(bcc110) structure written to ./Fe/Au/bcc110/substrate/input.traj +Pt1/Cu(fcc111) structure written to ./Cu/Pt/fcc111/substrate/input.traj +Au1/Cu(fcc111) structure written to ./Cu/Au/fcc111/substrate/input.traj +>>> saa_dict +{'Fe': {'Pt': {'bcc110': {'structure': Atoms(symbols='Fe27PtFe8', initial_magmoms=..., ...), + 'traj_file_path': './Fe/Pt/bcc110/substrate/input.traj'}}, + 'Au': {'bcc110': {'structure': Atoms(symbols='Fe27AuFe8', initial_magmoms=..., ...), + 'traj_file_path': './Fe/Au/bcc110/substrate/input.traj'}}}, + 'Cu': {'Pt': {'fcc111': {'structure': Atoms(symbols='Cu27PtCu8', initial_magmoms=..., ...), + 'traj_file_path': './Cu/Pt/fcc111/substrate/input.traj'}}, + 'Au': {'fcc111': {'structure': Atoms(symbols='Cu27AuCu8', initial_magmoms=..., ...), + 'traj_file_path': './Cu/Au/fcc111/substrate/input.traj'}}}} +``` +Here we generated SAA slabs with Fe and Cu as hosts and Pt and Au dopants under the following conditions: + +- for Fe (Cu) we only need the 110 (111) facet +- the bottom 2 layers are held fixed + +When writing to disk the following directory structure is used: +``` +. +├── Cu +│   ├── Au +│   │   └── fcc111 +│   │   └── substrate +│   │   └── input.traj +│   └── Pt +│   └── fcc111 +│   └── substrate +│   └── input.traj +├── Fe +│   ├── Au +│   │   └── bcc110 +│   │   └── substrate +│   │   └── input.traj +│   └── Pt +│   └── bcc110 +│   └── substrate +│   └── input.traj +``` + +**N.B.** by default, initial magnetic moments are given to the dopant species based upon +the ground state magnetic moment of the species diff --git a/docs/User_Guide/Structure_Generation/surface.md b/docs/User_Guide/Structure_Generation/surface.md index 5f5304b5..4a161678 100644 --- a/docs/User_Guide/Structure_Generation/surface.md +++ b/docs/User_Guide/Structure_Generation/surface.md @@ -1,10 +1,13 @@ It is crucial for many heterogeneous catalysis studies to be able to model a catalyst surface where the desired reaction -can take place. `autocat.surface` provides tools for generating +can take place. +[`autocat.surface`](../../API/Structure_Generation/surface.md) +provides tools for generating low miller index surfaces for mono-element surfaces with a vacuum in the $z$-direction. -The core function of this module is `generate_surface_structures` +The core function of this module is +[`generate_surface_structures`](../../API/Structure_Generation/surface.md#autocat.surface.generate_surface_structures) where multiple slabs can be generated at once. ```py @@ -16,7 +19,7 @@ where multiple slabs can be generated at once. ... n_fixed_layers=2, ... default_lat_param_lib="beefvdw_fd", ... write_to_disk=True, -) +... ) Li_bcc110 structure written to ./Li/bcc110/substrate/input.traj Cu_fcc100 structure written to ./Cu/fcc100/substrate/input.traj Cu_fcc111 structure written to ./Cu/fcc111/substrate/input.traj @@ -40,8 +43,9 @@ Here we generated surface slabs for Cu and Li under the following conditions: - the supercell dimensions of the slabs are 5 $\times$ 5 $\times$ 4 - the bottom 2 layers are held fixed - for structures where the lattice parameter is not explicitly specified, -their default values are pulled from the `ase.data.lattice_parameters` -library that used a `BEEF-vdW` XC and finite difference basis set +their default values are pulled from the +[`autocat.data.lattice_parameters`](../Data/lattice_parameters.md) +library that used a BEEF-vdW XC and finite difference basis set When using the `write_to_disk` functionality the structures will be written into the following directory structure: diff --git a/mkdocs.yml b/mkdocs.yml index 8fabab2c..cbea655e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -43,6 +43,11 @@ nav: - Featurizers: User_Guide/Learning/featurizers.md - Predictors: User_Guide/Learning/predictors.md - Sequential: User_Guide/Learning/sequential.md + - Data: + - HHI: User_Guide/Data/hhi.md + - Segregation Energies: User_Guide/Data/segregation_energies.md + - Lattice Parameters: User_Guide/Data/lattice_parameters.md + - Intermediates: User_Guide/Data/intermediates.md - API: - Structure Generation: - autocat.bulk: API/Structure_Generation/bulk.md From d0e9d74f7ae1184b4f89247cfe4fc1bb5b70d387 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 6 Dec 2021 12:10:01 -0500 Subject: [PATCH 132/239] start adsorption doc --- .../Structure_Generation/adsorption.md | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/docs/User_Guide/Structure_Generation/adsorption.md b/docs/User_Guide/Structure_Generation/adsorption.md index e69de29b..e670963b 100644 --- a/docs/User_Guide/Structure_Generation/adsorption.md +++ b/docs/User_Guide/Structure_Generation/adsorption.md @@ -0,0 +1,175 @@ +Tools within +[`autocat.adsorption`](../../API/Structure_Generation/adsorption.md) +are geared towards generating structures with adsorbates placed on +a candidate catalyst surface. + +The core function of this module is +[`generate_rxn_structures`](../../API/Structure_Generation/adsorption.md#autocat.adsorption.generate_rxn_structures) + for generating multiple adsorbed structures with a single function call. + +For the oxygen reduction (ORR) and nitrogen reduction (NRR) reactions, +AutoCat has default starting geometries for all of these intermediates +which can be found in [`autocat.data.intermediates`](../Data/intermediates.md). + +In addition, by default initial heights of the adsorbates are guessed based +upon the vdW radii of the nearest neighbors to the anchoring atom. + +```py +>>> from autocat.surface import generate_surface_structures +>>> from autocat.data.intermediates import ORR_INTERMEDIATE_NAMES +>>> from autocat.adsorption import generate_rxn_structures +>>> surface_dict = generate_surface_structures( +... ["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 +... ) +>>> surface = surface_dict["Pt"]["fcc111"]["structure"] +>>> ads_dict = generate_rxn_structures( +... surface, +... all_sym_sites=True, +... ads=ORR_INTERMEDIATE_NAMES, +... refs=["H2", "H2O"], +... write_to_disk=True, +... ) +OOH at (0.0,0.0) written to ./adsorbates/OOH/ontop/0.0_0.0/input.traj +OOH at (7.623,6.001) written to ./adsorbates/OOH/bridge/7.623_6.001/input.traj +OOH at (6.93,5.601) written to ./adsorbates/OOH/hollow/6.93_5.601/input.traj +OOH at (9.702,4.001) written to ./adsorbates/OOH/hollow/9.702_4.001/input.traj +O at (0.0,0.0) written to ./adsorbates/O/ontop/0.0_0.0/input.traj +O at (7.623,6.001) written to ./adsorbates/O/bridge/7.623_6.001/input.traj +O at (6.93,5.601) written to ./adsorbates/O/hollow/6.93_5.601/input.traj +O at (9.702,4.001) written to ./adsorbates/O/hollow/9.702_4.001/input.traj +OH at (0.0,0.0) written to ./adsorbates/OH/ontop/0.0_0.0/input.traj +OH at (7.623,6.001) written to ./adsorbates/OH/bridge/7.623_6.001/input.traj +OH at (6.93,5.601) written to ./adsorbates/OH/hollow/6.93_5.601/input.traj +OH at (9.702,4.001) written to ./adsorbates/OH/hollow/9.702_4.001/input.traj +H2 molecule structure written to ./references/H2/input.traj +H2O molecule structure written to ./references/H2O/input.traj +>>> ads_dict +{'OOH': {'ontop': {'0.0_0.0': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/ontop/0.0_0.0/input.traj'}}, + 'bridge': {'7.623_6.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/bridge/7.623_6.001/input.traj'}}, + 'hollow': {'6.93_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/hollow/6.93_5.601/input.traj'}, + '9.702_4.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/hollow/9.702_4.001/input.traj'}}}, + 'O': {'ontop': {'0.0_0.0': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/ontop/0.0_0.0/input.traj'}}, + 'bridge': {'7.623_6.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/bridge/7.623_6.001/input.traj'}}, + 'hollow': {'6.93_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/hollow/6.93_5.601/input.traj'}, + '9.702_4.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/hollow/9.702_4.001/input.traj'}}}, + 'OH': {'ontop': {'0.0_0.0': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/ontop/0.0_0.0/input.traj'}}, + 'bridge': {'7.623_6.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/bridge/7.623_6.001/input.traj'}}, + 'hollow': {'6.93_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/hollow/6.93_5.601/input.traj'}, + '9.702_4.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/hollow/9.702_4.001/input.traj'}}}, + 'references': {'H2': {'structure': Atoms(...), + 'traj_file_path': './references/H2/input.traj'}, + 'H2O': {'structure': Atoms(...), + 'traj_file_path': './references/H2O/input.traj'}}} +``` +In the example above we are generating adsorption structures for all ORR intermediates +on all of the identified unique symmetry sites on a Pt111 slab. The unique sites are +identified using the Delaunay triangulation, as implemented in `pymatgen`. +Additionally, by default initial heights of the adsorbates are guessed based +upon the vdW radii of the nearest neighbors to the anchoring atom. + +One such use of these structures is generating free energy landscapes. +With this in mind, there is also the `refs` parameter +which allows specifying the reference states that will be used. This generates a separate +directory containing the isolated molecule structures so that their energies may also +be calculated. + +In general the dictionary generated has the following organization: + +``` +{ADSORBATE_SPECIES: + {SITE_LABEL: + {XY: {"structure": Atoms, "traj_file_path": TRAJFILEPATH}}}, + "references": + {REFERENCE_SPECIES: {"structure": Atoms, "traj_file_path": TRAJFILEPATH}}} +``` +When writing these adsorbed structures to disk it is done with the following subdirectory +format (mimicing the organization of the dictionary). + +``` +. +├── adsorbates +│   ├── O +│   │   ├── bridge +│   │   │   └── 7.623_6.001 +│   │   │   └── input.traj +│   │   ├── hollow +│   │   │   ├── 6.93_5.601 +│   │   │   │   └── input.traj +│   │   │   └── 9.702_4.001 +│   │   │   └── input.traj +│   │   └── ontop +│   │   └── 0.0_0.0 +│   │   └── input.traj +│   ├── OH +│   │   ├── bridge +│   │   │   └── 7.623_6.001 +│   │   │   └── input.traj +│   │   ├── hollow +│   │   │   ├── 6.93_5.601 +│   │   │   │   └── input.traj +│   │   │   └── 9.702_4.001 +│   │   │   └── input.traj +│   │   └── ontop +│   │   └── 0.0_0.0 +│   │   └── input.traj +│   └── OOH +│   ├── bridge +│   │   └── 7.623_6.001 +│   │   └── input.traj +│   ├── hollow +│   │   ├── 6.93_5.601 +│   │   │   └── input.traj +│   │   └── 9.702_4.001 +│   │   └── input.traj +│   └── ontop +│   └── 0.0_0.0 +│   └── input.traj +├── references +│   ├── H2 +│   │   └── input.traj +│   └── H2O +│   └── input.traj +``` + +Instead of generating the adsorption structures for all unique sites, +the xy coordinates of individual sites may be specified using the `sites` + parameter. + +```py +>>> from autocat.surface import generate_surface_structures +>>> from autocat.adsorption import generate_rxn_structures +>>> surface_dict = generate_surface_structures( +... ["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 +... ) +>>> surface = surface_dict["Pt"]["fcc111"]["structure"] +>>> x = surface[15].x +>>> x +4.1577878733769 +>>> y = surface[15].y +>>> y +5.6011665451642 +>>> site = {"custom": [(x,y)]} +>>> ads_dict = generate_rxn_structures( +... surface, +... ads=["Li"], +... all_sym_sites=False, +... sites=site, +... write_to_disk=True, +... ) +Li at (4.158,5.601) written to ./adsorbates/Li/custom/4.158_5.601/input.traj +>>> ads_dict +{'Li': {'custom': {'4.158_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/Li/custom/4.158_5.601/input.traj'}}}} +``` From b0ccbc165b3737c5e879019fe8438da9b65d3270 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 6 Dec 2021 15:24:14 -0500 Subject: [PATCH 133/239] refine example txt, add struct gen figs --- .../Structure_Generation/adsorption.md | 1 + docs/User_Guide/Structure_Generation/bulk.md | 6 +++--- docs/User_Guide/Structure_Generation/saa.md | 9 +++++---- docs/User_Guide/Structure_Generation/surface.md | 10 ++++++---- docs/img/struct_gen_figs/adsorption.png | Bin 0 -> 37223 bytes docs/img/struct_gen_figs/saa.png | Bin 0 -> 35358 bytes docs/img/struct_gen_figs/surface.png | Bin 0 -> 35477 bytes mkdocs.yml | 1 + 8 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 docs/img/struct_gen_figs/adsorption.png create mode 100644 docs/img/struct_gen_figs/saa.png create mode 100644 docs/img/struct_gen_figs/surface.png diff --git a/docs/User_Guide/Structure_Generation/adsorption.md b/docs/User_Guide/Structure_Generation/adsorption.md index e670963b..a6bc4b67 100644 --- a/docs/User_Guide/Structure_Generation/adsorption.md +++ b/docs/User_Guide/Structure_Generation/adsorption.md @@ -1,3 +1,4 @@ +![Adsorption Figure](../../img/struct_gen_figs/adsorption.png){ align=right } Tools within [`autocat.adsorption`](../../API/Structure_Generation/adsorption.md) are geared towards generating structures with adsorbates placed on diff --git a/docs/User_Guide/Structure_Generation/bulk.md b/docs/User_Guide/Structure_Generation/bulk.md index 6e303a77..ad95cce1 100644 --- a/docs/User_Guide/Structure_Generation/bulk.md +++ b/docs/User_Guide/Structure_Generation/bulk.md @@ -14,11 +14,11 @@ Pt_bulk_fcc structure written to ./Pt_bulk_fcc/input.traj Fe_bulk_bcc structure written to ./Fe_bulk_bcc/input.traj Ru_bulk_hcp structure written to ./Ru_bulk_hcp/input.traj >>> bulk_dict -{'Pt': {'crystal_structure': Atoms(symbols='Pt', pbc=True, ...), +{'Pt': {'crystal_structure': Atoms(...), 'traj_file_path': './Pt_bulk_fcc/input.traj'}, - 'Fe': {'crystal_structure': Atoms(symbols='Fe', pbc=True, initial_magmoms=..., ...), + 'Fe': {'crystal_structure': Atoms(...), 'traj_file_path': './Fe_bulk_bcc/input.traj'}, - 'Ru': {'crystal_structure': Atoms(symbols='Ru2', pbc=True, ...), + 'Ru': {'crystal_structure': Atoms(...), 'traj_file_path': './Ru_bulk_hcp/input.traj'}} ``` diff --git a/docs/User_Guide/Structure_Generation/saa.md b/docs/User_Guide/Structure_Generation/saa.md index 656f4c77..dedbd658 100644 --- a/docs/User_Guide/Structure_Generation/saa.md +++ b/docs/User_Guide/Structure_Generation/saa.md @@ -1,3 +1,4 @@ +![SAA Figure](../../img/struct_gen_figs/saa.png){ align=right } Single atom alloys (SAA) consist of a transition-metal host with lone dopant atoms embedded at the surface. This dispersion leads to unique electronic properties. @@ -23,13 +24,13 @@ Au1/Fe(bcc110) structure written to ./Fe/Au/bcc110/substrate/input.traj Pt1/Cu(fcc111) structure written to ./Cu/Pt/fcc111/substrate/input.traj Au1/Cu(fcc111) structure written to ./Cu/Au/fcc111/substrate/input.traj >>> saa_dict -{'Fe': {'Pt': {'bcc110': {'structure': Atoms(symbols='Fe27PtFe8', initial_magmoms=..., ...), +{'Fe': {'Pt': {'bcc110': {'structure': Atoms(...), 'traj_file_path': './Fe/Pt/bcc110/substrate/input.traj'}}, - 'Au': {'bcc110': {'structure': Atoms(symbols='Fe27AuFe8', initial_magmoms=..., ...), + 'Au': {'bcc110': {'structure': Atoms(...), 'traj_file_path': './Fe/Au/bcc110/substrate/input.traj'}}}, - 'Cu': {'Pt': {'fcc111': {'structure': Atoms(symbols='Cu27PtCu8', initial_magmoms=..., ...), + 'Cu': {'Pt': {'fcc111': {'structure': Atoms(...), 'traj_file_path': './Cu/Pt/fcc111/substrate/input.traj'}}, - 'Au': {'fcc111': {'structure': Atoms(symbols='Cu27AuCu8', initial_magmoms=..., ...), + 'Au': {'fcc111': {'structure': Atoms(...), 'traj_file_path': './Cu/Au/fcc111/substrate/input.traj'}}}} ``` Here we generated SAA slabs with Fe and Cu as hosts and Pt and Au dopants under the following conditions: diff --git a/docs/User_Guide/Structure_Generation/surface.md b/docs/User_Guide/Structure_Generation/surface.md index 4a161678..6788082c 100644 --- a/docs/User_Guide/Structure_Generation/surface.md +++ b/docs/User_Guide/Structure_Generation/surface.md @@ -1,3 +1,4 @@ +![Surface Figure](../../img/struct_gen_figs/surface.png){ align=right } It is crucial for many heterogeneous catalysis studies to be able to model a catalyst surface where the desired reaction can take place. @@ -25,13 +26,13 @@ Cu_fcc100 structure written to ./Cu/fcc100/substrate/input.traj Cu_fcc111 structure written to ./Cu/fcc111/substrate/input.traj Cu_fcc110 structure written to ./Cu/fcc110/substrate/input.traj >>> surf_dict -{'Li': {'bcc110': {'structure': Atoms(symbols='Li100', pbc=[True, True, False], ...), +{'Li': {'bcc110': {'structure': Atoms(...), 'traj_file_path': './Li/bcc110/substrate/input.traj'}}, - 'Cu': {'fcc100': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'Cu': {'fcc100': {'structure': Atoms(...), 'traj_file_path': './Cu/fcc100/substrate/input.traj'}, - 'fcc111': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'fcc111': {'structure': Atoms(...), 'traj_file_path': './Cu/fcc111/substrate/input.traj'}, - 'fcc110': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'fcc110': {'structure': Atoms(...), 'traj_file_path': './Cu/fcc110/substrate/input.traj'}}} ``` Here we generated surface slabs for Cu and Li under the following conditions: @@ -67,3 +68,4 @@ will be written into the following directory structure: │   └── substrate │   └── input.traj ``` +**N.B.** by default, initial magnetic moments are given to Fe, Ni and Co diff --git a/docs/img/struct_gen_figs/adsorption.png b/docs/img/struct_gen_figs/adsorption.png new file mode 100644 index 0000000000000000000000000000000000000000..5d2de92863edfccd6ec54b406f7b5b8df2bc0bef GIT binary patch literal 37223 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 literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index cbea655e..203bad85 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,6 +14,7 @@ markdown_extensions: - pymdownx.inlinehilite - pymdownx.arithmatex: generic: true + - attr_list extra_javascript: - javascripts/mathjax.js - https://polyfill.io/v3/polyfill.min.js?features=es6 From 62b449699cdaf50832e7c54023451991f6bb0677 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Tue, 7 Dec 2021 06:23:41 -0800 Subject: [PATCH 134/239] propagate sl info through kw; constructor only constructs --- src/autocat/learning/sequential.py | 211 +++++++++++++---------------- 1 file changed, 96 insertions(+), 115 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 98eb8af7..f1d15f07 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -176,143 +176,117 @@ def __init__( design_space: AutoCatDesignSpace, predictor_kwargs: Dict[str, Union[str, float]] = None, candidate_selection_kwargs: Dict[str, Union[str, float]] = None, + sl_kwargs: Dict[str, int] = None, ): + # TODO: move predefined attributes (train_idx, candidate_idxs) to a + # different container (not kwargs) + + self._design_space = None + self.design_space = design_space + + # predictor arguments to use throughout the SL process + if not predictor_kwargs: + predictor_kwargs = {"structure_featurizer": "sine_matrix"} + self._predictor_kwargs = None + self.predictor_kwargs = predictor_kwargs + self._predictor = AutoCatPredictor(**predictor_kwargs) + + # acquisition function arguments to use for candidate selection + if not candidate_selection_kwargs: + candidate_selection_kwargs = {"aq": "random"} + self._candidate_selection_kwargs = None + self.candidate_selection_kwargs = candidate_selection_kwargs + + # other miscellaneous kw arguments + self.sl_kwargs = sl_kwargs if sl_kwargs else {} + + # variables that need to be propagated through the SL process + if "iteration_count" not in self.sl_kwargs: + self.sl_kwargs.update({"iteration_count": 0}) + if "train_idx" not in self.sl_kwargs: + self.sl_kwargs.update({"train_idx": None}) + if "predictions" not in self.sl_kwargs: + self.sl_kwargs.update({"predictions": None}) + if "uncertainties" not in self.sl_kwargs: + self.sl_kwargs.update({"uncertainties": None}) + if "candidate_indices" not in self.sl_kwargs: + self.sl_kwargs.update({"candidate_indices": None}) + if "aq_scores" not in self.sl_kwargs: + self.sl_kwargs.update({"aq_scores": None}) - self._predictor_kwargs = predictor_kwargs or { - "structure_featurizer": "sine_matrix" - } - self._candidate_selection_kwargs = candidate_selection_kwargs or { - "aq": "Random" - } + @property + def design_space(self): + return self._design_space + @design_space.setter + def design_space(self, design_space): self._design_space = design_space - dstructs = self.design_space.design_space_structures - dlabels = self.design_space.design_space_labels - mask_nans = ~np.isnan(dlabels) - train_idx = np.zeros(len(dlabels), dtype=bool) - train_idx[np.where(mask_nans)] = 1 - self._train_idx = train_idx - - masked_structs = [struct for i, struct in enumerate(dstructs) if mask_nans[i]] - masked_labels = dlabels[np.where(mask_nans)] + @property + def predictor_kwargs(self): + return self._predictor_kwargs - self.predictor = AutoCatPredictor(**self.predictor_kwargs) - self.predictor.fit(masked_structs, masked_labels) + @predictor_kwargs.setter + def predictor_kwargs(self, predictor_kwargs): + if not predictor_kwargs: + predictor_kwargs = {} + self._predictor_kwargs = predictor_kwargs + self._predictor = AutoCatPredictor(**predictor_kwargs) - self._iteration_count = 0 - preds, unc = self.predictor.predict(dstructs) - self._predictions = preds - self._uncertainties = unc + @property + def predictor(self): + return self._predictor - if False in mask_nans: - candidate_idx, _, aq_scores = choose_next_candidate( - dstructs, - dlabels, - train_idx, - preds, - unc, - **self.candidate_selection_kwargs, - ) - else: - candidate_idx = None - aq_scores = None + @property + def candidate_selection_kwargs(self): + return self._candidate_selection_kwargs - self._candidate_indices = candidate_idx - self._acquisition_scores = aq_scores + @candidate_selection_kwargs.setter + def candidate_selection_kwargs(self, candidate_selection_kwargs): + if not candidate_selection_kwargs: + candidate_selection_kwargs = {} + self._candidate_selection_kwargs = candidate_selection_kwargs @property def iteration_count(self): - return self._iteration_count + return self.sl_kwargs.get("iteration_count", 0) @property - def design_space(self): - return self._design_space + def train_idx(self): + return self.sl_kwargs.get("train_idx") @property def predictions(self): - return self._predictions + return self.sl_kwargs.get("predictions") @property def uncertainties(self): - return self._uncertainties + return self.sl_kwargs.get("uncertainties") @property def candidate_indices(self): - return self._candidate_indices + return self.sl_kwargs.get("candidate_indices") @property def acquisition_scores(self): - if self.candidate_indices is not None: - return self._acquisition_scores - else: - print("Design space fully explored, no more candidates") + return self.sl_kwargs.get("acquisition_scores", None) @property def candidate_structures(self): idxs = self.candidate_indices if idxs is not None: - structs = self.design_space.design_space_structures - return [structs[i] for i in idxs] - else: - print("Design space fully explored, no more candidates") - - @property - def predictor_kwargs(self): - return self._predictor_kwargs - - @property - def candidate_selection_kwargs(self): - return self._candidate_selection_kwargs - - @property - def train_idx(self): - return self._train_idx - - def check_design_space_different(self, other_design_space): - """ - Compare contained `AutoCatDesignSpace` object to another. - The other design space must be of the same size to the - one that is contained. - Returns True if they are different, False otherwise. - - Parameters - ---------- - - other_design_space: - `AutoCatDesignSpace` object to be compared to - """ - ds = self.design_space - assert len(ds.design_space_structures) == len( - other_design_space.design_space_structures - ) - for idx, struct in enumerate(ds.design_space_structures): - # check same structures - if struct not in other_design_space.design_space_structures: - return True - o_idx = other_design_space.design_space_structures.index(struct) - # check if both nans - if np.isnan(ds.design_space_labels[idx]) and np.isnan( - other_design_space.design_space_labels[o_idx] - ): - continue - # check same labels - if ( - ds.design_space_labels[idx] - != other_design_space.design_space_labels[o_idx] - ): - return True - return False + return [self.design_space.design_space_structures[i] for i in idxs] def iterate(self): - """ - Iterates the SL loop. - This consists of: + """Runs the next iteration of sequential learning. + + This process consists of: - retraining the predictor - - obtaining new acquisition scores (if fully explored returns None) - - selecting next batch of candidates (if fully explored returns None) + - predicting candidate properties and calculating candidate scores (if + fully explored returns None) + - selecting the next batch of candidates for objective evaluation (if + fully explored returns None) """ - self._iteration_count += 1 dstructs = self.design_space.design_space_structures dlabels = self.design_space.design_space_labels @@ -322,32 +296,35 @@ def iterate(self): masked_labels = dlabels[np.where(mask_nans)] self.predictor.fit(masked_structs, masked_labels) + train_idx = np.zeros(len(dlabels), dtype=bool) train_idx[np.where(mask_nans)] = 1 - self._train_idx = train_idx + self.sl_kwargs.update({"train_idx": train_idx}) preds, unc = self.predictor.predict(dstructs) - self._predictions = preds - self._uncertainties = unc + self.sl_kwargs.update({"predictions": preds}) + self.sl_kwargs.update({"uncertainties": unc}) # make sure haven't fully searched design space - if True in [np.isnan(l) for l in dlabels]: + if any([np.isnan(label) for label in dlabels]): candidate_idx, _, aq_scores = choose_next_candidate( dstructs, dlabels, train_idx, preds, unc, - **self.candidate_selection_kwargs or {}, + **self.candidate_selection_kwargs, ) - # if fully searched, no more candidate structures else: candidate_idx = None aq_scores = None + self.sl_kwargs.update({"candidate_indices": candidate_idx}) + self.sl_kwargs.update({"acquisition_scores": aq_scores}) - self._candidate_indices = candidate_idx - self._acquisition_scores = aq_scores + # update the SL iteration count + itc = self.sl_kwargs.get("iteration_count", 0) + self.sl_kwargs.update({"iteration_count": itc + 1}) def write_json(self, write_location: str = ".", json_name: str = None): """ @@ -361,6 +338,8 @@ def write_json(self, write_location: str = ".", json_name: str = None): jsonified_list.append(self.predictor_kwargs) # append kwargs for candidate selection jsonified_list.append(self.candidate_selection_kwargs) + # append the acsl kwargs + jsonified_list.append(self.sl_kwargs) if json_name is None: json_name = "acsl.json" @@ -376,7 +355,7 @@ def from_json(json_name: str): all_data = json.load(f) structures = [] with tempfile.TemporaryDirectory() as _tmp_dir: - for i in range(len(all_data) - 3): + for i in range(len(all_data) - 4): # write temp json for each individual structure _tmp_json = os.path.join(_tmp_dir, "tmp.json") with open(_tmp_json, "w") as tmp: @@ -384,16 +363,18 @@ def from_json(json_name: str): # read individual tmp json using ase atoms = ase_read(_tmp_json, format="json") structures.append(atoms) - labels = np.array(all_data[-3]) + labels = np.array(all_data[-4]) acds = AutoCatDesignSpace( design_space_structures=structures, design_space_labels=labels, ) - predictor_kwargs = all_data[-2] - candidate_selection_kwargs = all_data[-1] + predictor_kwargs = all_data[-3] + candidate_selection_kwargs = all_data[-2] + sl_kwargs = all_data[-1] return AutoCatSequentialLearner( design_space=acds, predictor_kwargs=predictor_kwargs, candidate_selection_kwargs=candidate_selection_kwargs, + sl_kwargs=sl_kwargs, ) @@ -629,7 +610,7 @@ def _collect_pred_stats( # get scores on test perturbations if testing_structures is not None: if testing_y is None: - msg = f"Labels for the test structures must be provided" + msg = "Labels for the test structures must be provided" raise AutoCatSequentialLearningError(msg) mae_test_score, test_preds, test_unc = predictor.score( From 67a2051890c13dac68e7386a2237bb41affa65bd Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 8 Dec 2021 16:26:48 -0500 Subject: [PATCH 135/239] fix typo in candidate_selection_kwargs --- src/autocat/learning/sequential.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index f1d15f07..4cb39849 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -193,7 +193,7 @@ def __init__( # acquisition function arguments to use for candidate selection if not candidate_selection_kwargs: - candidate_selection_kwargs = {"aq": "random"} + candidate_selection_kwargs = {"aq": "Random"} self._candidate_selection_kwargs = None self.candidate_selection_kwargs = candidate_selection_kwargs From f935a818444c7c9b810f246ca7c32661dbcd08e0 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 8 Dec 2021 16:28:49 -0500 Subject: [PATCH 136/239] update acsl tests --- tests/learning/test_sequential.py | 101 ++++++++++-------------------- 1 file changed, 33 insertions(+), 68 deletions(-) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index c20dd7eb..54a72fc3 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -66,7 +66,13 @@ def test_sequential_learner_from_json(): acsl.write_json(_tmp_dir, "testing_acsl.json") json_path = os.path.join(_tmp_dir, "testing_acsl.json") written_acsl = AutoCatSequentialLearner.from_json(json_path) - assert not written_acsl.check_design_space_different(acds) + assert np.array_equal( + written_acsl.design_space.design_space_labels, acds.design_space_labels + ) + assert ( + written_acsl.design_space.design_space_structures + == acds.design_space_structures + ) assert written_acsl.predictor_kwargs == predictor_kwargs assert written_acsl.candidate_selection_kwargs == candidate_selection_kwargs @@ -119,7 +125,15 @@ def test_sequential_learner_write_json(): # check predictor kwargs kept assert predictor_kwargs == sl[4] # check candidate selection kwargs kept - assert candidate_selection_kwargs == sl[-1] + assert candidate_selection_kwargs == sl[-2] + assert sl[-1] == { + "iteration_count": 0, + "train_idx": None, + "predictions": None, + "uncertainties": None, + "candidate_indices": None, + "aq_scores": None, + } # test writing when no kwargs given acsl = AutoCatSequentialLearner(acds) @@ -140,7 +154,7 @@ def test_sequential_learner_write_json(): # check default predictor kwargs kept assert sl[4] == {"structure_featurizer": "sine_matrix"} # check default candidate selection kwargs kept - assert sl[-1] == {"aq": "Random"} + assert sl[-2] == {"aq": "Random"} def test_sequential_learner_iterate(): @@ -166,76 +180,29 @@ def test_sequential_learner_iterate(): acds = AutoCatDesignSpace(structs, labels) acsl = AutoCatSequentialLearner(acds) - old_candidate_structures = acsl.candidate_structures - assert acsl.iteration_count == 0 - - # make sure doesn't iterate if no changes to design space - acsl.iterate(acds) assert acsl.iteration_count == 0 - assert acsl.design_space is acds - assert acsl.candidate_structures == old_candidate_structures - - # check updates for first iteration - new_labels = np.array([11.0, 25.0, 13.0, np.nan]) - new_acds = AutoCatDesignSpace(structs, new_labels) - - acsl.iterate(new_acds) + acsl.iterate() assert acsl.iteration_count == 1 - assert acsl.design_space is new_acds - assert acsl.candidate_structures[0] == sub4 + assert acsl.predictions is not None + assert acsl.uncertainties is not None + assert acsl.candidate_indices is not None - new_labels2 = np.array([11.0, 25.0, 13.0, 30.0]) - new_acds2 = AutoCatDesignSpace(structs, new_labels2) + cand_ind1 = acsl.candidate_indices[0] + acsl.design_space.update([structs[cand_ind1]], np.array([13.0])) + + acsl.iterate() + assert acsl.iteration_count == 2 # checks being iterated a second time to fully explore the design space - acsl.iterate(new_acds2) + cand_ind2 = acsl.candidate_indices[0] + assert cand_ind1 != cand_ind2 + acsl.design_space.update([structs[cand_ind2]], np.array([17.0])) + acsl.iterate() - assert acsl.iteration_count == 2 - assert acsl.design_space is new_acds2 + assert acsl.iteration_count == 3 assert acsl.candidate_structures is None assert acsl.candidate_indices is None - assert acsl.acquisition_scores is None - - -def test_sequential_learner_compare(): - # Tests comparison method - sub1 = generate_surface_structures(["Ca"], facets={"Ca": ["111"]})["Ca"]["fcc111"][ - "structure" - ] - sub1 = place_adsorbate(sub1, "Li")["custom"]["structure"] - sub2 = generate_surface_structures(["Nb"], facets={"Nb": ["110"]})["Nb"]["bcc110"][ - "structure" - ] - sub2 = place_adsorbate(sub2, "P")["custom"]["structure"] - sub3 = generate_surface_structures(["Ta"], facets={"Ta": ["110"]})["Ta"]["bcc110"][ - "structure" - ] - sub3 = place_adsorbate(sub3, "F")["custom"]["structure"] - structs = [sub1, sub2, sub3] - labels = np.array([np.nan, 6.0, 15.0]) - acds1 = AutoCatDesignSpace(structs, labels) - acsl = AutoCatSequentialLearner(acds1) - - assert not acsl.check_design_space_different(acds1) - - structs_reorder = [sub2, sub3, sub1] - labels_reorder = np.array([6.0, 15.0, np.nan]) - acds2 = AutoCatDesignSpace(structs_reorder, labels_reorder) - - assert not acsl.check_design_space_different(acds2) - - structs_new_label = [sub1, sub2, sub3] - labels_new_label = np.array([3.0, 6.0, 15.0]) - acds3 = AutoCatDesignSpace(structs_new_label, labels_new_label) - - assert acsl.check_design_space_different(acds3) - - structs_new_label_reorder = [sub1, sub3, sub2] - labels_new_label_reorder = np.array([3.0, 15.0, 6.0]) - acds4 = AutoCatDesignSpace(structs_new_label_reorder, labels_new_label_reorder) - - assert acsl.check_design_space_different(acds4) def test_sequential_learner_setup(): @@ -263,9 +230,9 @@ def test_sequential_learner_setup(): assert acsl.design_space == acds assert acsl.iteration_count == 0 - assert (acsl.train_idx == np.array([True, False, True, False])).all() + assert acsl.predictions == None + assert acsl.candidate_indices == None assert acsl.candidate_selection_kwargs == {"aq": "Random"} - assert acsl.candidate_structures[0] == structs[acsl.candidate_indices[0]] # test default kwargs assert acsl.predictor_kwargs == {"structure_featurizer": "sine_matrix"} # test specifying kwargs @@ -301,8 +268,6 @@ def test_sequential_learner_setup(): # test passing candidate selection kwargs assert acsl.candidate_selection_kwargs == {"aq": "MU", "num_candidates_to_pick": 2} - assert len(acsl.candidate_indices) == 2 - assert len(acsl.candidate_structures) == 2 def test_design_space_setup(): From 0ae6b4b4ef9907dcc4d8b13c7afb9b64bd04d192 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 20 Dec 2021 13:54:12 -0500 Subject: [PATCH 137/239] start predictors doc --- docs/User_Guide/Learning/predictors.md | 71 ++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/User_Guide/Learning/predictors.md b/docs/User_Guide/Learning/predictors.md index e69de29b..18426f85 100644 --- a/docs/User_Guide/Learning/predictors.md +++ b/docs/User_Guide/Learning/predictors.md @@ -0,0 +1,71 @@ +In order to iterate a sequential learning pipeline, +a regressor is needed to select subsequent candidate systems. +For this purpose, there is the +[`AutoCatPredictor`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor) +object class. This contains two key attributes: + +- a regressor that can be fit to data and used for predictions +(the class provided must have `fit` and `predict` methods) +- specified featurization methods that are available in +[`autocat.learning.featurizers`](../../API/Learning/featurizers.md). + In particular there are two currently implemented approaches, +structure methods that featurize the entire structure (e.g. `sine matrix`) + and adsorbate methods that featurize locally (e.g. `SOAP`). + +Generally, this predictor object behaves similarly to regressors found in +[`sklearn`](https://scikit-learn.org/stable/) +with its own +[`fit`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.fit), +[`predict`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.predict), +and +[`score`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.score) +methods. + +As an example, let's train a random forest regressor on some +single atom alloys. + +```py +>>> import numpy as np +>>> from autocat.learning.predictors import AutoCatPredictor +>>> from autocat.saa import generate_saa_structures +>>> from autocat.utils import extract_structures +>>> from sklearn.ensemble import RandomForestRegressor +>>> saa_dict = generate_saa_structures(["Cu", "Au", "Fe"], ["Pt", "Ru", "Ni"]) +>>> saa_structs = extract_structures(saa_dict) +>>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) +>>> acp = AutoCatPredictor( +... structure_featurizer="sine_matrix", model_class=RandomForestRegressor +... ) +>>> acp.fit(saa_structs[:-1], labels) +>>> pred, _ = acp.predict([saa_structs[-1]]) +>>> pred +array([0.]) +``` +Here we have chosen to featurize the structures as a `sine matrix`. +Note as well that the `predict` method will return uncertainty estimates +if available. To see this, let's train a gaussian process regressor with an RBF + kernel. + +```py +>>> import numpy as np +>>> from autocat.learning.predictors import AutoCatPredictor +>>> from autocat.saa import generate_saa_structures +>>> from autocat.utils import extract_structures +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from sklearn.gaussian_process.kernels import RBF +>>> saa_dict = generate_saa_structures(["Cu", "Au", "Fe"], ["Pt", "Ru", "Ni"]) +>>> saa_structs = extract_structures(saa_dict) +>>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) +>>> kernel = RBF() +>>> acp = AutoCatPredictor( +... structure_featurizer="sine_matrix", +... model_class=GaussianProcessRegressor, +... model_kwargs={"kernel": kernel}, +... ) +>>> acp.fit(saa_structs[:-1], labels) +>>> pred, unc = acp.predict([saa_structs[-1]]) +>>> pred +array([0.]) +>>> unc +array([1.]) +``` From 9ee8452dc9dc0fc168b3c0b24366ed3f6891ce07 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 20 Dec 2021 20:24:42 -0500 Subject: [PATCH 138/239] add latt param docs --- docs/User_Guide/Data/lattice_parameters.md | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/User_Guide/Data/lattice_parameters.md b/docs/User_Guide/Data/lattice_parameters.md index e69de29b..136a6cce 100644 --- a/docs/User_Guide/Data/lattice_parameters.md +++ b/docs/User_Guide/Data/lattice_parameters.md @@ -0,0 +1,44 @@ +In some codes, optimizing cell parameters on the fly +during geometry relaxations is not available. +For this reason we have compiled +calculated lattice parameters +using multiple different +calculation schemes as a convenience for high-throughput +studies. Every calculation was conducted with +[`GPAW`](https://wiki.fysik.dtu.dk/gpaw/index.html). + +There are two axes to the settings applied here: + +- exchange-correlation functional +- basis set mode (finite difference or plane-wave). + +Available sets are as follows: + +- `BULK_PBE_FD`/`BULK_BEEFVDW_FD`: +``` +These are parameters using the finite difference scheme +and PBE / BEEF-vdW XC functionals. Obtained via fits to an +equation of state (https://wiki.fysik.dtu.dk/ase/ase/eos.html) + +FCC/BCC +h = 0.16, kpts = (12,12,12) +fit to an SJ EOS + +HCP +h=0.16, kpts = (12,12,6) +fit to a Birch-Murnaghan EO +``` +- `BULK_PBE_PW`/`BULK_BEEFVDW_PW`: +``` +These are parameters are obatined with a plane-wave basis set and +using the Exponential Cell Filter to minimize the stress tensor and atomic forces +(https://wiki.fysik.dtu.dk/ase/ase/constraints.html#the-expcellfilter-class) + +FCC/BCC +mode=PW(550), kpts = (12,12,12), fmax = 0.05 eV/A + +HCP +mode=PW(550), kpts = (12,12,6), fmax = 0.05 eV/A +``` + +All of these lattice parameters are available within `autocat.data.lattice_parameters` From 0fcd0bc609d9be4d90743cedb013c2e16cfedd0a Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 20 Dec 2021 21:54:45 -0500 Subject: [PATCH 139/239] start doc for intermediates --- docs/User_Guide/Data/intermediates.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/User_Guide/Data/intermediates.md b/docs/User_Guide/Data/intermediates.md index e69de29b..90ed656e 100644 --- a/docs/User_Guide/Data/intermediates.md +++ b/docs/User_Guide/Data/intermediates.md @@ -0,0 +1,40 @@ +When characterizing a surface in the context of a +specific reaction, calculating adsorption energies +for all of the intermediates is often important. + +Here, AutoCat has default structures for adsorbates +of both the oxygen reduction reaction (ORR) and +nitrogen reduction reaction (NRR) intermediates. + +The names of all of the reaction intermediates can +be imported and fed directly into +AutoCat functions: +```py +>>> from autocat.data.intermediates import ORR_INTERMEDIATE_NAMES +>>> from autocat.data.intermediates import NRR_INTERMEDIATE_NAMES +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.adsorption import generate_rxn_structures +>>> pt_dict = generate_surface_structures(["Pt"]) +>>> pt_struct = extract_structures(pt_dict)[0] +>>> orr_dict = generate_rxn_structures(pt_struct, ads=ORR_INTERMEDIATE_NAMES) +>>> nrr_dict = generate_rxn_structures(pt_struct, ads=NRR_INTERMEDIATE_NAMES) +``` +In the above example, `orr_dict` and `nrr_dict` have all of the corresponding +intermediates at every identified unique surface site. + +Alternatively, if you would like to access the +`ase.Atoms` objects for the intermediates directly, +they can be imported as a `dict`: +```py +>>> from autocat.data.intermediates import ORR_MOLS +>>> from autocat.data.intermediates import NRR_MOLS +``` + +**ORR Intermediates**: + +OOH\*, O\*, OH\* + +**NRR Intermediates**: + +NNH\*, NNH$_2$\*, N\*, NH\*, NH$_2$\*, NHNH\*, NHNH$_2$\*, NH$_2$NH$_2$\* From 89cb01449a1856c9496a9a46ffa7c0f36c87817e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 26 Dec 2021 16:08:47 -0500 Subject: [PATCH 140/239] start sequential doc --- docs/User_Guide/Learning/sequential.md | 105 +++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/docs/User_Guide/Learning/sequential.md b/docs/User_Guide/Learning/sequential.md index e69de29b..5ac30512 100644 --- a/docs/User_Guide/Learning/sequential.md +++ b/docs/User_Guide/Learning/sequential.md @@ -0,0 +1,105 @@ +## AutoCatDesignSpace + +The +[`AutoCatDesignSpace`](../../API/Learning/sequential.md#autocat.learning.sequential.AutoCatDesignSpace) +class object is intended to store the +*entire* design space. As the sequential learning +loop is iterated, this can be continuously updated +with the newly found labels. + +There are two key components required for this object: + +1. `design_space_structures`: *all* systems to be considered as `ase.Atoms` objects +in a `list` +2. `design_space_labels`: `numpy array` of the same length as the above list +with the corresponding labels. If the label is not yet +known, set it to `numpy.nan` + +**NB:** The order of the list of design space structures must +be in the same order as the labels given in the +design space labels. + +```py +>>> import numpy as np +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.learning.sequential import AutoCatDesignSpace +>>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) +>>> surf_structs = extract_structures(surf_dict) +>>> labels = np.array([0.95395024, 0.63504885, np.nan, 0.08320879, np.nan, +... 0.32423194, 0.55570785, np.nan, np.nan, np.nan, +... 0.18884186, np.nan]) +>>> acds = AutoCatDesignSpace(surf_structs, labels) +>>> len(acds) +12 +>>> acds.design_space_structures +[Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...)] +>>> acds.design_space_labels +array([0.95395024, 0.63504885, nan, 0.08320879, nan, + 0.32423194, 0.55570785, nan, nan, nan, + 0.18884186, nan]) +``` + + +## AutoCatSequentialLearner + +The +[`AutoCatSequentialLearner`](../../API/Learning/sequential.md#autocat.learning.sequential.AutoCatSequentialLearner) +object stores information regarding the latest +iteration of the sequential learning loop including: + +1. An [`AutoCatPredictor`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor) +2. Candidate selection kwargs for score calculation (e.g. acquisition functions) +3. Iteration number +4. Latest `AutoCatDesignSpace` +5. Candidate system that is identified for the next loop. + +This object can be thought of as a central hub for the +sequential learning workflow, with an external driver +(either automated or manual) triggering iteration. + +```py +>>> import numpy as np +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.learning.sequential import AutoCatDesignSpace +>>> from autocat.learning.sequential import AutoCatSequentialLearner +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from sklearn.gaussian_process.kernels import RBF +>>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) +>>> surf_structs = extract_structures(surf_dict) +>>> labels = np.array([0.95395024, 0.63504885, np.nan, 0.08320879, np.nan, +... 0.32423194, 0.55570785, np.nan, np.nan, np.nan, +... 0.18884186, np.nan]) +>>> acds = AutoCatDesignSpace(surf_structs, labels) +>>> kernel = RBF() +>>> acsl = AutoCatSequentialLearner( +... acds, +... predictor_kwargs={ +... "structure_featurizer": "sine_matrix", +... "model_class": GaussianProcessRegressor, +... "model_kwargs": {"kernel": kernel}, +... }, +... candidate_selection_kwargs={ +... "aq": "MLI", +... "target_min": -2.25, +... "target_max": -1.5, +... "include_hhi": True, +... "hhi_type": "reserves", +... "include_seg_ener": False, +... }, +... ) +>>> acsl.iteration_count +0 +``` From be6cce008c38e13ebd5056e35d2c94ed6876abe8 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 28 Dec 2021 21:16:59 -0500 Subject: [PATCH 141/239] update simulated sl to take acds as input --- src/autocat/learning/sequential.py | 22 +++--- tests/learning/test_sequential.py | 106 +++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 37 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 4cb39849..4cbe4034 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -443,8 +443,7 @@ def multiple_simulated_sequential_learning_runs( def simulated_sequential_learning( predictor: AutoCatPredictor, - all_training_structures: List[Atoms], - all_training_y: np.ndarray, + design_space: AutoCatDesignSpace, init_training_size: int = 10, testing_structures: List[Atoms] = None, testing_y: np.ndarray = None, @@ -470,14 +469,9 @@ def simulated_sequential_learning( predictor: AutoCatPredictor object to be used for fitting and prediction - all_training_structures: - List of all Atoms objects that make up the design space - to be considered - - all_training_y: - Labels corresponding to each structure in the design - space to be explored (ie. correspond to - `all_training_structures`) + design_space: + AutoCatDesignSpace of the design space to be explored. + Must have labels for the entire space. init_training_size: Size of the initial training set to be selected from @@ -552,6 +546,14 @@ def simulated_sequential_learning( - test_uncertainty_history: uncertainties on predicting on test set """ + all_training_structures = design_space.design_space_structures + all_training_y = design_space.design_space_labels + + if True in np.isnan(all_training_y): + missing_label_idx = np.where(np.isnan(all_training_y))[0] + msg = f"Design space must be fully explored. Missing labels at indices: {missing_label_idx}" + raise AutoCatSequentialLearningError(msg) + if init_training_size > len(all_training_structures): msg = f"Initial training size ({init_training_size}) larger than design space ({len(all_training_structures)})" raise AutoCatSequentialLearningError(msg) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 54a72fc3..ee1a5a5c 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -454,10 +454,18 @@ def test_simulated_sequential_outputs(): base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [ + base_struct1, + base_struct2, + base_struct3, + sub1, + sub2, + ] + ds_labels = np.array([0.0, 1.0, 2.0, 3.0, 4.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2, base_struct3, sub1, sub2,], - np.array([0.0, 1.0, 2.0, 3.0, 4.0]), + predictor=acsc, + design_space=acds, init_training_size=1, batch_size_to_add=2, number_of_sl_loops=2, @@ -498,10 +506,12 @@ def test_simulated_sequential_batch_added(): acsc = AutoCatPredictor(structure_featurizer="elemental_property") bsta = 2 num_loops = 2 + ds_structs = [base_struct1, base_struct2, sub1, sub2] + ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2, sub1, sub2], - np.array([5.0, 6.0, 7.0, 8.0]), + predictor=acsc, + design_space=acds, batch_size_to_add=bsta, number_of_sl_loops=num_loops, acquisition_function="Random", @@ -528,12 +538,14 @@ def test_simulated_sequential_num_loops(): base_struct1 = place_adsorbate(sub1, "H")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "N")["custom"]["structure"] acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [base_struct1, base_struct2, sub1, sub2] + ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) # Test default number of loops bsta = 3 sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2, sub1, sub2], - np.array([5.0, 6.0, 7.0, 8.0]), + predictor=acsc, + design_space=acds, batch_size_to_add=bsta, acquisition_function="Random", init_training_size=1, @@ -543,19 +555,21 @@ def test_simulated_sequential_num_loops(): # Test catches maximum number of loops with pytest.raises(AutoCatSequentialLearningError): sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2, sub1, sub2], - np.array([5.0, 6.0, 7.0, 8.0]), + predictor=acsc, + design_space=acds, batch_size_to_add=bsta, acquisition_function="Random", init_training_size=1, number_of_sl_loops=3, ) + ds_structs = [base_struct1, base_struct2, sub2] + ds_labels = np.array([5.0, 6.0, 7.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) + sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2, sub2], - np.array([5.0, 6.0, 7.0]), + predictor=acsc, + design_space=acds, acquisition_function="Random", init_training_size=1, ) @@ -574,10 +588,12 @@ def test_simulated_sequential_outputs_testing(): base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [base_struct1, base_struct2, sub1] + ds_labels = np.array([0.0, 2.0, 4.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2, sub1], - np.array([0.0, 2.0, 4.0]), + predictor=acsc, + design_space=acds, init_training_size=1, testing_structures=[base_struct3, sub2], testing_y=np.array([8.0, 10.0]), @@ -612,10 +628,12 @@ def test_simulated_sequential_write_to_disk(): base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] base_struct3 = place_adsorbate(sub2, "N")["custom"]["structure"] acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [base_struct1, base_struct2, base_struct3] + ds_labels = np.array([0, 1, 2]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) sl_dict = simulated_sequential_learning( - acsc, - [base_struct1, base_struct2, base_struct3], - np.array([0, 1, 2]), + predictor=acsc, + design_space=acds, init_training_size=2, testing_structures=[base_struct3], testing_y=np.array([2]), @@ -631,6 +649,34 @@ def test_simulated_sequential_write_to_disk(): assert sl_dict == sl_written +def test_simulated_sequential_learning_fully_explored(): + # Checks that catches if ds not fully explored + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ + "structure" + ] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] + acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [base_struct1, base_struct2, sub2] + ds_labels = np.array([0.0, np.nan, 4.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) + with pytest.raises(AutoCatSequentialLearningError): + sl_dict = simulated_sequential_learning( + predictor=acsc, + design_space=acds, + init_training_size=1, + testing_structures=[base_struct3, sub2], + testing_y=np.array([8.0, 10.0]), + batch_size_to_add=1, + number_of_sl_loops=2, + acquisition_function="MU", + ) + + def test_multiple_sequential_learning_serial(): # Tests serial implementation sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ @@ -638,12 +684,14 @@ def test_multiple_sequential_learning_serial(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [base_struct1, sub1] + ds_labels = np.array([0.0, 0.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) runs_history = multiple_simulated_sequential_learning_runs( number_of_runs=3, sl_kwargs={ "predictor": acsc, - "all_training_structures": [base_struct1, sub1], - "all_training_y": np.array([0.0, 0.0]), + "design_space": acds, "number_of_sl_loops": 1, "acquisition_function": "MU", "init_training_size": 1, @@ -661,13 +709,15 @@ def test_multiple_sequential_learning_parallel(): ] base_struct1 = place_adsorbate(sub1, "Li")["custom"]["structure"] acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [base_struct1, sub1] + ds_labels = np.array([0.0, 0.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) runs_history = multiple_simulated_sequential_learning_runs( number_of_runs=3, number_parallel_jobs=2, sl_kwargs={ "predictor": acsc, - "all_training_structures": [base_struct1, sub1], - "all_training_y": np.array([0.0, 0.0]), + "design_space": acds, "number_of_sl_loops": 1, "acquisition_function": "Random", "init_training_size": 1, @@ -686,6 +736,9 @@ def test_multiple_sequential_learning_write_to_disk(): ] base_struct1 = place_adsorbate(sub1, "N")["custom"]["structure"] acsc = AutoCatPredictor(structure_featurizer="elemental_property") + ds_structs = [base_struct1, sub1] + ds_labels = np.array([0.0, 0.0]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) runs_history = multiple_simulated_sequential_learning_runs( number_of_runs=3, number_parallel_jobs=2, @@ -693,8 +746,7 @@ def test_multiple_sequential_learning_write_to_disk(): write_location=_tmp_dir, sl_kwargs={ "predictor": acsc, - "all_training_structures": [base_struct1, sub1], - "all_training_y": np.array([0.0, 0.0]), + "design_space": acds, "number_of_sl_loops": 1, "acquisition_function": "Random", "init_training_size": 1, From 6502d4e9a9514f4507698990341ee957d0b0fb49 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 29 Dec 2021 12:54:07 -0500 Subject: [PATCH 142/239] keep candidate idx history in acsl --- src/autocat/learning/sequential.py | 14 ++++++++++++++ tests/learning/test_sequential.py | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 4cbe4034..651ccf46 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -211,6 +211,8 @@ def __init__( self.sl_kwargs.update({"uncertainties": None}) if "candidate_indices" not in self.sl_kwargs: self.sl_kwargs.update({"candidate_indices": None}) + if "candidate_index_history" not in self.sl_kwargs: + self.sl_kwargs.update({"candidate_index_history": None}) if "aq_scores" not in self.sl_kwargs: self.sl_kwargs.update({"aq_scores": None}) @@ -277,6 +279,10 @@ def candidate_structures(self): if idxs is not None: return [self.design_space.design_space_structures[i] for i in idxs] + @property + def candidate_index_history(self): + return self.sl_kwargs.get("candidate_index_history", None) + def iterate(self): """Runs the next iteration of sequential learning. @@ -322,6 +328,14 @@ def iterate(self): self.sl_kwargs.update({"candidate_indices": candidate_idx}) self.sl_kwargs.update({"acquisition_scores": aq_scores}) + # update the candidate index history if new candidate + if candidate_idx is not None: + cand_idx_hist = self.sl_kwargs.get("candidate_index_history") + if cand_idx_hist is None: + cand_idx_hist = [] + cand_idx_hist.append(candidate_idx) + self.sl_kwargs.update({"candidate_index_history": cand_idx_hist}) + # update the SL iteration count itc = self.sl_kwargs.get("iteration_count", 0) self.sl_kwargs.update({"iteration_count": itc + 1}) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index ee1a5a5c..20127705 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -187,6 +187,8 @@ def test_sequential_learner_iterate(): assert acsl.predictions is not None assert acsl.uncertainties is not None assert acsl.candidate_indices is not None + assert acsl.candidate_index_history is not None + assert acsl.candidate_index_history == [acsl.candidate_indices] cand_ind1 = acsl.candidate_indices[0] acsl.design_space.update([structs[cand_ind1]], np.array([13.0])) @@ -197,12 +199,15 @@ def test_sequential_learner_iterate(): # checks being iterated a second time to fully explore the design space cand_ind2 = acsl.candidate_indices[0] assert cand_ind1 != cand_ind2 + assert acsl.candidate_index_history == [[cand_ind1], [cand_ind2]] + acsl.design_space.update([structs[cand_ind2]], np.array([17.0])) acsl.iterate() assert acsl.iteration_count == 3 assert acsl.candidate_structures is None assert acsl.candidate_indices is None + assert acsl.candidate_index_history == [[cand_ind1], [cand_ind2]] def test_sequential_learner_setup(): From fe0bb1c0df1dd5d4c94b1ee72251707918b498a5 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 29 Dec 2021 21:44:26 -0500 Subject: [PATCH 143/239] keep pred and unc histories --- src/autocat/learning/sequential.py | 26 ++++++++++++++++++++++++++ tests/learning/test_sequential.py | 11 +++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 651ccf46..5e0724b0 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -207,8 +207,12 @@ def __init__( self.sl_kwargs.update({"train_idx": None}) if "predictions" not in self.sl_kwargs: self.sl_kwargs.update({"predictions": None}) + if "predictions_history" not in self.sl_kwargs: + self.sl_kwargs.update({"predictions_history": None}) if "uncertainties" not in self.sl_kwargs: self.sl_kwargs.update({"uncertainties": None}) + if "uncertainties_history" not in self.sl_kwargs: + self.sl_kwargs.update({"uncertainties_history": None}) if "candidate_indices" not in self.sl_kwargs: self.sl_kwargs.update({"candidate_indices": None}) if "candidate_index_history" not in self.sl_kwargs: @@ -283,6 +287,14 @@ def candidate_structures(self): def candidate_index_history(self): return self.sl_kwargs.get("candidate_index_history", None) + @property + def predictions_history(self): + return self.sl_kwargs.get("predictions_history", None) + + @property + def uncertainties_history(self): + return self.sl_kwargs.get("uncertainties_history", None) + def iterate(self): """Runs the next iteration of sequential learning. @@ -308,8 +320,22 @@ def iterate(self): self.sl_kwargs.update({"train_idx": train_idx}) preds, unc = self.predictor.predict(dstructs) + + # update predictions and store in history self.sl_kwargs.update({"predictions": preds}) + pred_hist = self.sl_kwargs.get("predictions_history") + if pred_hist is None: + pred_hist = [] + pred_hist.append(preds) + self.sl_kwargs.update({"predictions_history": pred_hist}) + + # update uncertainties and store in history self.sl_kwargs.update({"uncertainties": unc}) + unc_hist = self.sl_kwargs.get("uncertainties_history") + if unc_hist is None: + unc_hist = [] + unc_hist.append(unc) + self.sl_kwargs.update({"uncertainties_history": unc_hist}) # make sure haven't fully searched design space if any([np.isnan(label) for label in dlabels]): diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 20127705..2616462c 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -130,8 +130,11 @@ def test_sequential_learner_write_json(): "iteration_count": 0, "train_idx": None, "predictions": None, + "predictions_history": None, "uncertainties": None, + "uncertainties_history": None, "candidate_indices": None, + "candidate_index_history": None, "aq_scores": None, } @@ -185,7 +188,11 @@ def test_sequential_learner_iterate(): acsl.iterate() assert acsl.iteration_count == 1 assert acsl.predictions is not None + assert len(acsl.predictions_history) == 1 + assert len(acsl.predictions_history[0]) == len(acds) assert acsl.uncertainties is not None + assert len(acsl.uncertainties_history) == 1 + assert len(acsl.uncertainties_history[0]) == len(acds) assert acsl.candidate_indices is not None assert acsl.candidate_index_history is not None assert acsl.candidate_index_history == [acsl.candidate_indices] @@ -200,6 +207,8 @@ def test_sequential_learner_iterate(): cand_ind2 = acsl.candidate_indices[0] assert cand_ind1 != cand_ind2 assert acsl.candidate_index_history == [[cand_ind1], [cand_ind2]] + assert len(acsl.uncertainties_history) == 2 + assert len(acsl.predictions_history) == 2 acsl.design_space.update([structs[cand_ind2]], np.array([17.0])) acsl.iterate() @@ -208,6 +217,8 @@ def test_sequential_learner_iterate(): assert acsl.candidate_structures is None assert acsl.candidate_indices is None assert acsl.candidate_index_history == [[cand_ind1], [cand_ind2]] + assert len(acsl.uncertainties_history) == 3 + assert len(acsl.predictions_history) == 3 def test_sequential_learner_setup(): From 873990c8c262866f7cb1da5d1bae3eee8f392b64 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 29 Dec 2021 22:22:18 -0500 Subject: [PATCH 144/239] keep train idx history --- src/autocat/learning/sequential.py | 11 +++++++++++ tests/learning/test_sequential.py | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 5e0724b0..a2418e3c 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -205,6 +205,8 @@ def __init__( self.sl_kwargs.update({"iteration_count": 0}) if "train_idx" not in self.sl_kwargs: self.sl_kwargs.update({"train_idx": None}) + if "train_idx_history" not in self.sl_kwargs: + self.sl_kwargs.update({"train_idx_history": None}) if "predictions" not in self.sl_kwargs: self.sl_kwargs.update({"predictions": None}) if "predictions_history" not in self.sl_kwargs: @@ -261,6 +263,10 @@ def iteration_count(self): def train_idx(self): return self.sl_kwargs.get("train_idx") + @property + def train_idx_history(self): + return self.sl_kwargs.get("train_idx_history", None) + @property def predictions(self): return self.sl_kwargs.get("predictions") @@ -318,6 +324,11 @@ def iterate(self): train_idx = np.zeros(len(dlabels), dtype=bool) train_idx[np.where(mask_nans)] = 1 self.sl_kwargs.update({"train_idx": train_idx}) + train_idx_hist = self.sl_kwargs.get("train_idx_history") + if train_idx_hist is None: + train_idx_hist = [] + train_idx_hist.append(train_idx) + self.sl_kwargs.update({"train_idx_history": train_idx_hist}) preds, unc = self.predictor.predict(dstructs) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 2616462c..8e7d1371 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -129,6 +129,7 @@ def test_sequential_learner_write_json(): assert sl[-1] == { "iteration_count": 0, "train_idx": None, + "train_idx_history": None, "predictions": None, "predictions_history": None, "uncertainties": None, @@ -196,6 +197,8 @@ def test_sequential_learner_iterate(): assert acsl.candidate_indices is not None assert acsl.candidate_index_history is not None assert acsl.candidate_index_history == [acsl.candidate_indices] + assert len(acsl.train_idx_history) == 1 + assert np.count_nonzero(acsl.train_idx_history[-1]) == 2 cand_ind1 = acsl.candidate_indices[0] acsl.design_space.update([structs[cand_ind1]], np.array([13.0])) @@ -209,6 +212,8 @@ def test_sequential_learner_iterate(): assert acsl.candidate_index_history == [[cand_ind1], [cand_ind2]] assert len(acsl.uncertainties_history) == 2 assert len(acsl.predictions_history) == 2 + assert len(acsl.train_idx_history) == 2 + assert np.count_nonzero(acsl.train_idx_history[-1]) == 3 acsl.design_space.update([structs[cand_ind2]], np.array([17.0])) acsl.iterate() @@ -219,6 +224,8 @@ def test_sequential_learner_iterate(): assert acsl.candidate_index_history == [[cand_ind1], [cand_ind2]] assert len(acsl.uncertainties_history) == 3 assert len(acsl.predictions_history) == 3 + assert len(acsl.train_idx_history) == 3 + assert np.count_nonzero(acsl.train_idx_history[-1]) == 4 def test_sequential_learner_setup(): From 00f72a4cb8e8638b3772b67f62fc5c921a4f0874 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 30 Dec 2021 00:43:09 -0500 Subject: [PATCH 145/239] ensure deep copy of Atoms in acds, add copy method --- src/autocat/learning/sequential.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index a2418e3c..efddfba5 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -45,7 +45,9 @@ def __init__( msg = f"Number of structures ({len(design_space_structures)}) and labels ({design_space_labels.shape[0]}) must match" raise AutoCatDesignSpaceError(msg) - self._design_space_structures = design_space_structures.copy() + self._design_space_structures = [ + struct.copy() for struct in design_space_structures + ] self._design_space_labels = design_space_labels.copy() def __len__(self): @@ -66,6 +68,16 @@ def __delitem__(self, i): masked_structs = [structs[j] for j in range(len(self)) if mask[j]] self._design_space_structures = masked_structs + def copy(self): + """ + Returns a copy of the design space + """ + acds = self.__class__( + design_space_structures=self.design_space_structures, + design_space_labels=self.design_space_labels, + ) + return acds + @property def design_space_structures(self): return self._design_space_structures From 9f80791f76f1d05002362d7ad79bb98e7a559c98 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 30 Dec 2021 01:26:08 -0500 Subject: [PATCH 146/239] add copy method for predictor --- src/autocat/learning/predictors.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 6d801b48..b3ae45c0 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -1,3 +1,4 @@ +import copy import numpy as np from typing import List @@ -269,6 +270,34 @@ def species_list(self, species_list): self.X_ = None self.y_ = None + def copy(self): + """ + Returns a copy + """ + acp = self.__class__( + model_class=self.model_class, + multiple_separate_models=self.multiple_separate_models, + structure_featurizer=self.structure_featurizer, + adsorbate_featurizer=self.adsorbate_featurizer, + maximum_structure_size=self.maximum_structure_size, + maximum_adsorbate_size=self.maximum_adsorbate_size, + elementalproperty_preset=self.elementalproperty_preset, + refine_structures=self.refine_structures, + ) + acp.regressor = copy.deepcopy(self.regressor) + acp.is_fit = self.is_fit + acp.structure_featurization_kwargs = copy.deepcopy( + self.structure_featurization_kwargs + ) + acp.adsorbate_featurization_kwargs = copy.deepcopy( + self.adsorbate_featurization_kwargs + ) + acp.model_kwargs = copy.deepcopy(self.model_kwargs) + if self.species_list is not None: + acp.species_list = self.species_list.copy() + + return acp + def get_total_number_of_features(self): # get specified kwargs for featurizers str_kwargs = self.structure_featurization_kwargs From a815063eed903c3fa7aed1bc1fdacc9178a3c782 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 30 Dec 2021 01:28:27 -0500 Subject: [PATCH 147/239] ensure acds is deepcopy in acsl, add copy method --- src/autocat/learning/sequential.py | 12 +++++++++++- tests/learning/test_sequential.py | 5 ++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index efddfba5..555cbf66 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -1,3 +1,4 @@ +import copy import numpy as np import os import json @@ -194,7 +195,7 @@ def __init__( # different container (not kwargs) self._design_space = None - self.design_space = design_space + self.design_space = design_space.copy() # predictor arguments to use throughout the SL process if not predictor_kwargs: @@ -313,6 +314,15 @@ def predictions_history(self): def uncertainties_history(self): return self.sl_kwargs.get("uncertainties_history", None) + def copy(self): + """ + Returns a copy + """ + acsl = self.__class__(design_space=self.design_space,) + acsl.predictor_kwargs = copy.deepcopy(self.predictor_kwargs) + acsl.sl_kwargs = copy.deepcopy(self.sl_kwargs) + return acsl + def iterate(self): """Runs the next iteration of sequential learning. diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 8e7d1371..1e76cb31 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -251,7 +251,10 @@ def test_sequential_learner_setup(): acds = AutoCatDesignSpace(structs, labels) acsl = AutoCatSequentialLearner(acds) - assert acsl.design_space == acds + assert acsl.design_space.design_space_structures == acds.design_space_structures + assert np.array_equal( + acsl.design_space.design_space_labels, acds.design_space_labels, equal_nan=True + ) assert acsl.iteration_count == 0 assert acsl.predictions == None assert acsl.candidate_indices == None From b6de6e223427691a0ec74583506ebe9ccd29fd50 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 30 Dec 2021 21:12:03 -0500 Subject: [PATCH 148/239] fix acsl io, refactor simulated sl for acsl - jsonifies sl_kwargs for reading and writing - refactors simulated sl so that it now iterates an acsl internally --- src/autocat/learning/sequential.py | 319 ++++++++++------------------- 1 file changed, 111 insertions(+), 208 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 555cbf66..19b562e5 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -412,7 +412,17 @@ def write_json(self, write_location: str = ".", json_name: str = None): # append kwargs for candidate selection jsonified_list.append(self.candidate_selection_kwargs) # append the acsl kwargs - jsonified_list.append(self.sl_kwargs) + jsonified_sl_kwargs = {} + for k in self.sl_kwargs: + if k != "iteration_count" and self.sl_kwargs[k] is not None: + jsonified_sl_kwargs[k] = [arr.tolist() for arr in self.sl_kwargs[k]] + elif k == "iteration_count": + jsonified_sl_kwargs["iteration_count"] = self.sl_kwargs[ + "iteration_count" + ] + elif self.sl_kwargs[k] is None: + jsonified_sl_kwargs[k] = None + jsonified_list.append(jsonified_sl_kwargs) if json_name is None: json_name = "acsl.json" @@ -442,7 +452,29 @@ def from_json(json_name: str): ) predictor_kwargs = all_data[-3] candidate_selection_kwargs = all_data[-2] - sl_kwargs = all_data[-1] + raw_sl_kwargs = all_data[-1] + sl_kwargs = {} + for k in raw_sl_kwargs: + if k in [ + "predictions", + "uncertainties", + "acquisition_scores", + "candidate_indices", + ]: + sl_kwargs[k] = np.array(raw_sl_kwargs[k]) + elif k in [ + "predictions_history", + "uncertainties_history", + "candidate_index_history", + ]: + sl_kwargs[k] = [np.array(i) for i in raw_sl_kwargs[k]] + elif k == "iteration_count": + sl_kwargs[k] = raw_sl_kwargs[k] + elif k == "train_idx": + sl_kwargs[k] = np.array(raw_sl_kwargs[k], dtype=bool) + elif k == "train_idx_history": + sl_kwargs[k] = [np.array(i, dtype=bool) for i in raw_sl_kwargs[k]] + return AutoCatSequentialLearner( design_space=acds, predictor_kwargs=predictor_kwargs, @@ -515,264 +547,135 @@ def multiple_simulated_sequential_learning_runs( def simulated_sequential_learning( - predictor: AutoCatPredictor, - design_space: AutoCatDesignSpace, + full_design_space: AutoCatDesignSpace, + predictor_kwargs: Dict[str, Union[str, float]] = None, + candidate_selection_kwargs: Dict[str, Union[str, float]] = None, init_training_size: int = 10, - testing_structures: List[Atoms] = None, - testing_y: np.ndarray = None, - acquisition_function: str = "MLI", - batch_size_to_add: int = 1, number_of_sl_loops: int = None, - target_min: float = None, - target_max: float = None, - include_hhi: bool = False, - hhi_type: str = "production", write_to_disk: bool = False, write_location: str = ".", -): + json_name: str = None, +) -> AutoCatSequentialLearner: """ - Conducts a simulated sequential learning loop given - a data set to explore. Can optionally provide a holdout - test set that is never added to the training set - to measure transferability at each iteration + Conducts a simulated sequential learning loop for a + fully labelled design space to explore. Parameters ---------- - predictor: - AutoCatPredictor object to be used for fitting and prediction + full_design_space: + Fully labelled AutoCatDesignSpace to simulate + being searched over + + predictor_kwargs: + Kwargs to be used in setting up the predictor. + This is where model class, model hyperparameters, etc. + are specified. - design_space: - AutoCatDesignSpace of the design space to be explored. - Must have labels for the entire space. + candidate_selection_kwargs: + Kwargs that specify that settings for candidate selection. + This is where acquisition function, targets, etc. are + specified. init_training_size: Size of the initial training set to be selected from the full space. Default: 10 - testing_structures: - List of all Atoms objects to be excluded from the - SL search (ie. structures to never be added to the set). - Used for testing transferability of the model at each iteration - - testing_y: - Labels for corresponding to `testing_structures` - - acquisition_function: - Acquisition function to be used to determine candidates to be - added to the training set at each iteration of the sl loop - - batch_size_to_add: - Number of candidates to be added to the training set on each loop. - (ie. N candidates with the highest uncertainties added - for each iteration) - Default: 1 (ie. adds candidate with max unc on each loop) - number_of_sl_loops: Integer specifying the number of sequential learning loops to be conducted. This value cannot be greater than - `(len(all_training_structures) - init_training_size)/batch_size_to_add` + `(DESIGN_SPACE_SIZE - init_training_size)/batch_size_to_add` Default: maximum number of sl loops calculated above - target_min: - Label value that ideal candidates should be greater than - Default: -inf - - target_max: - Label value that ideal candidates should be less than - Default: +inf - - include_hhi: - Whether HHI scores should be used to weight aq scores - - hhi_type: - Type of HHI index to be used for weighting - Options - - production (default) - - reserves - write_to_disk: - Boolean specifying whether the sl dictionary should be written to disk as a json. + Boolean specifying whether the resulting sequential learner should be + written to disk as a json. Defaults to False. write_location: - String with the location where sl_dict should be written to disk. + String with the location where the resulting sequential learner + should be written to disk. + Defaults to current directory. Returns ------- - sl_dict: - Dictionary containing histories of different quantities throughout - the calculation: - - training_history: indices that are included in the training set - - uncertainty_history: prediction uncertainties at each iteration - - predicted_history: all predictions at each iteration - - mae_train_history: mae scores on predicting training set - - rmse_train_history: rmse scores on predicting training set - - max_scores_history: max aq scores for candidate selection - - aq_scores_history: all aq scores at each iteration - If testing structures and labels given: - - mae_test_history: mae scores on testing set - - rmse_test_history: rmse scores on testing set - - test_prediction_history: predictions on testing set - - test_uncertainty_history: uncertainties on predicting on test set + sl: + Sequential Learner after having been iterated as specified + by the input settings. Contains candidate, prediction, + and uncertainty histories for further analysis as desired. """ - all_training_structures = design_space.design_space_structures - all_training_y = design_space.design_space_labels + ds_size = len(full_design_space) - if True in np.isnan(all_training_y): - missing_label_idx = np.where(np.isnan(all_training_y))[0] + # check fully explored + if True in np.isnan(full_design_space.design_space_labels): + missing_label_idx = np.where(np.isnan(full_design_space.design_space_labels))[0] msg = f"Design space must be fully explored. Missing labels at indices: {missing_label_idx}" raise AutoCatSequentialLearningError(msg) - if init_training_size > len(all_training_structures): - msg = f"Initial training size ({init_training_size}) larger than design space ({len(all_training_structures)})" + # check that specified initial training size makes sense + if init_training_size > ds_size: + msg = f"Initial training size ({init_training_size})\ + larger than design space ({ds_size})" raise AutoCatSequentialLearningError(msg) - max_num_sl_loops = int( - np.ceil((len(all_training_structures) - init_training_size) / batch_size_to_add) - ) + batch_size_to_add = candidate_selection_kwargs.get("num_candidates_to_pick", 1) + max_num_sl_loops = int(np.ceil((ds_size - init_training_size) / batch_size_to_add)) if number_of_sl_loops is None: number_of_sl_loops = max_num_sl_loops + # check that specified number of loops is feasible if number_of_sl_loops > max_num_sl_loops: msg = f"Number of SL loops ({number_of_sl_loops}) cannot be greater than ({max_num_sl_loops})" raise AutoCatSequentialLearningError(msg) # generate initial training set - train_idx = np.zeros(len(all_training_structures), dtype=bool) - train_idx[ - np.random.choice( - len(all_training_structures), init_training_size, replace=False - ) - ] = 1 - train_history = [train_idx.copy()] - - # fit on initial training set - X = [s for s, i in zip(all_training_structures, train_idx) if i] - y = all_training_y[train_idx] - predictor.fit(X, y) - - pred_history = [] - unc_history = [] - max_scores_history = [] - aq_scores_history = [] - mae_train_history = [] - rmse_train_history = [] - mae_test_history = [] - rmse_test_history = [] - test_pred_history = [] - test_unc_history = [] - - def _collect_pred_stats( - all_training_structures, all_training_y, testing_structures, testing_y - ): - _preds, _uncs = predictor.predict(all_training_structures) - - # get scores on full training set - mae_train_history.append( - predictor.score(all_training_structures, all_training_y) - ) - rmse_train_history.append( - predictor.score( - all_training_structures, all_training_y, metric="mse", squared=False - ) - ) - - # get scores on test perturbations - if testing_structures is not None: - if testing_y is None: - msg = "Labels for the test structures must be provided" - raise AutoCatSequentialLearningError(msg) - - mae_test_score, test_preds, test_unc = predictor.score( - testing_structures, testing_y, return_predictions=True - ) - mae_test_history.append(mae_test_score) - test_pred_history.append([p.tolist() for p in test_preds]) - test_unc_history.append([u.tolist() for u in test_unc]) - rmse_test_history.append( - predictor.score( - testing_structures, testing_y, metric="mse", squared=False - ) - ) - - unc_history.append(_uncs) - pred_history.append(_preds) - return _preds, _uncs + init_idx = np.zeros(ds_size, dtype=bool) + init_idx[np.random.choice(ds_size, init_training_size, replace=False)] = 1 + + init_structs = [ + full_design_space.design_space_structures[idx] + for idx, b in enumerate(init_idx) + if b + ] + init_labels = full_design_space.design_space_labels.copy() + init_labels = init_labels[np.where(init_idx)] + + # set up learner that is used for iteration + dummy_labels = np.empty(len(full_design_space)) + dummy_labels[:] = np.nan + ds = AutoCatDesignSpace(full_design_space.design_space_structures, dummy_labels) + ds.update(init_structs, init_labels) + sl = AutoCatSequentialLearner( + design_space=ds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + ) + # first iteration on initial dataset + sl.iterate() + # start simulated sequential learning loop for i in range(number_of_sl_loops): print(f"Sequential Learning Iteration #{i+1}") - # make predictions - _preds, _uncs = _collect_pred_stats( - all_training_structures, all_training_y, testing_structures, testing_y - ) - - # check that enough data pts left to adhere to batch_size_to_add - # otherwise just add the leftover data - if batch_size_to_add < len(all_training_y[~train_idx]): - bsa = batch_size_to_add - else: - bsa = len(all_training_y[~train_idx]) - - # select next candidate(s) - next_candidate_idx, max_scores, aq_scores = choose_next_candidate( - labels=all_training_y, - train_idx=train_idx, - pred=_preds, - unc=_uncs, - aq=acquisition_function, - num_candidates_to_pick=bsa, - target_min=target_min, - target_max=target_max, - include_hhi=include_hhi, - hhi_type=hhi_type, - ) - max_scores_history.append([int(i) for i in max_scores]) - aq_scores_history.append(aq_scores.tolist()) - - # add next candidates to training set - train_idx[next_candidate_idx] = True - train_history.append(train_idx.copy()) - - # retrain on training set with new additions - X = [s for s, i in zip(all_training_structures, train_idx) if i] - y = all_training_y[train_idx] - predictor.fit(X, y) - - # make preds on final model - _, _ = _collect_pred_stats( - all_training_structures, all_training_y, testing_structures, testing_y - ) - - sl_dict = { - "training_history": [th.tolist() for th in train_history], - "uncertainty_history": [mu.tolist() for mu in unc_history], - "prediction_history": [p.tolist() for p in pred_history], - "mae_train_history": mae_train_history, - "rmse_train_history": rmse_train_history, - "max_scores_history": max_scores_history, - "aq_scores_history": aq_scores_history, - } - - if testing_structures is not None: - sl_dict["mae_test_history"] = mae_test_history - sl_dict["rmse_test_history"] = rmse_test_history - sl_dict["test_prediction_history"] = test_pred_history - sl_dict["test_uncertainty_history"] = test_unc_history + if sl.candidate_indices is not None: + next_structs = sl.candidate_structures + next_labels = full_design_space.design_space_labels.take( + sl.candidate_indices + ) + sl.design_space.update(next_structs, next_labels) + sl.iterate() if write_to_disk: - if not os.path.isdir(write_location): - os.makedirs(write_location) - json_write_path = os.path.join(write_location, "sl_dict.json") - with open(json_write_path, "w") as f: - json.dump(sl_dict, f) - print(f"SL dictionary written to {json_write_path}") + # for now does not write out model class + # serialization needs to be implemented + sl.predictor_kwargs.update({"model_class": None}) + sl.write_json(write_location=write_location, json_name=json_name) + print(f"SL dictionary written to {write_location}") - return sl_dict + return sl def choose_next_candidate( From c88ccbee82780a9c11e876cdda8cb102eaecae33 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 30 Dec 2021 21:15:05 -0500 Subject: [PATCH 149/239] updates tests for acsl io and new simulated sl --- tests/learning/test_sequential.py | 317 +++++++++++++++++------------- 1 file changed, 175 insertions(+), 142 deletions(-) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 1e76cb31..cbd67d5a 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -46,7 +46,7 @@ def test_sequential_learner_from_json(): ]["structure"] sub3 = place_adsorbate(sub3, "N")["custom"]["structure"] structs = [sub1, sub2, sub3] - labels = np.array([0.1, 0.2, 0.3]) + labels = np.array([0.1, np.nan, 0.3]) predictor_kwargs = { "structure_featurizer": "coulomb_matrix", "elementalproperty_preset": "megnet_el", @@ -62,19 +62,41 @@ def test_sequential_learner_from_json(): predictor_kwargs=predictor_kwargs, candidate_selection_kwargs=candidate_selection_kwargs, ) + acsl.iterate() with tempfile.TemporaryDirectory() as _tmp_dir: acsl.write_json(_tmp_dir, "testing_acsl.json") json_path = os.path.join(_tmp_dir, "testing_acsl.json") written_acsl = AutoCatSequentialLearner.from_json(json_path) assert np.array_equal( - written_acsl.design_space.design_space_labels, acds.design_space_labels - ) - assert ( - written_acsl.design_space.design_space_structures - == acds.design_space_structures + written_acsl.design_space.design_space_labels, + acds.design_space_labels, + equal_nan=True, ) + assert False not in [ + written_acsl.design_space.design_space_structures[j] + == acds.design_space_structures[j] + for j in range(len(acds)) + ] assert written_acsl.predictor_kwargs == predictor_kwargs assert written_acsl.candidate_selection_kwargs == candidate_selection_kwargs + assert written_acsl.iteration_count == 1 + assert np.array_equal(written_acsl.train_idx, acsl.train_idx) + assert written_acsl.train_idx[0] in [True, False] + assert np.array_equal(written_acsl.train_idx_history, acsl.train_idx_history) + assert written_acsl.train_idx_history[0][0] in [True, False] + assert np.array_equal(written_acsl.predictions, acsl.predictions) + assert np.array_equal( + written_acsl.predictions_history, acsl.predictions_history + ) + assert np.array_equal(written_acsl.uncertainties, acsl.uncertainties) + assert np.array_equal( + written_acsl.uncertainties_history, acsl.uncertainties_history + ) + assert np.array_equal(written_acsl.candidate_indices, acsl.candidate_indices) + assert np.array_equal( + written_acsl.candidate_index_history, acsl.candidate_index_history + ) + assert np.array_equal(written_acsl.acquisition_scores, acsl.acquisition_scores) def test_sequential_learner_write_json(): @@ -92,7 +114,7 @@ def test_sequential_learner_write_json(): ]["structure"] sub3 = place_adsorbate(sub3, "H")["custom"]["structure"] structs = [sub1, sub2, sub3] - labels = np.array([0.1, 0.2, 0.3]) + labels = np.array([0.1, 0.2, np.nan]) predictor_kwargs = { "structure_featurizer": "sine_matrix", "elementalproperty_preset": "deml", @@ -121,7 +143,7 @@ def test_sequential_learner_write_json(): json.dump(sl[i], tmp) written_structs.append(ase_read(_tmp_json)) assert structs == written_structs - assert (labels == sl[3]).all() + assert np.array_equal(labels, sl[3], equal_nan=True) # check predictor kwargs kept assert predictor_kwargs == sl[4] # check candidate selection kwargs kept @@ -154,12 +176,52 @@ def test_sequential_learner_write_json(): json.dump(sl[i], tmp) written_structs.append(ase_read(_tmp_json)) assert structs == written_structs - assert (labels == sl[3]).all() + assert np.array_equal(labels, sl[3], equal_nan=True) # check default predictor kwargs kept assert sl[4] == {"structure_featurizer": "sine_matrix"} # check default candidate selection kwargs kept assert sl[-2] == {"aq": "Random"} + # test after iteration + acsl.iterate() + with tempfile.TemporaryDirectory() as _tmp_dir: + acsl.write_json(_tmp_dir, "testing_acsl.json") + with open(os.path.join(_tmp_dir, "testing_acsl.json"), "r") as f: + sl = json.load(f) + # collects structs by writing each json individually + # and reading with ase + written_structs = [] + for i in range(3): + _tmp_json = os.path.join(_tmp_dir, "tmp.json") + with open(_tmp_json, "w") as tmp: + json.dump(sl[i], tmp) + written_structs.append(ase_read(_tmp_json)) + assert structs == written_structs + assert np.array_equal(labels, sl[3], equal_nan=True) + # check predictor kwargs kept + assert sl[4] == {"structure_featurizer": "sine_matrix"} + # check candidate selection kwargs kept + assert sl[-2] == {"aq": "Random"} + assert sl[-1].get("iteration_count") == 1 + assert sl[-1].get("train_idx") == acsl.train_idx.tolist() + assert sl[-1].get("train_idx_history") == [ + ti.tolist() for ti in acsl.train_idx_history + ] + assert isinstance(sl[-1].get("train_idx_history")[0][0], bool) + assert sl[-1].get("predictions") == acsl.predictions.tolist() + assert sl[-1].get("predictions_history") == [ + p.tolist() for p in acsl.predictions_history + ] + assert sl[-1].get("uncertainties") == acsl.uncertainties.tolist() + assert sl[-1].get("uncertainties_history") == [ + u.tolist() for u in acsl.uncertainties_history + ] + assert sl[-1].get("candidate_indices") == acsl.candidate_indices.tolist() + assert sl[-1].get("candidate_index_history") == [ + c.tolist() for c in acsl.candidate_index_history + ] + assert sl[-1].get("acquisition_scores") == acsl.acquisition_scores.tolist() + def test_sequential_learner_iterate(): # Tests iterate method @@ -468,8 +530,8 @@ def test_get_design_space_from_json(): ) -def test_simulated_sequential_outputs(): - # Test outputs without any testing structures +def test_simulated_sequential_histories(): + # Test output sl has appropriate histories sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] @@ -479,7 +541,6 @@ def test_simulated_sequential_outputs(): base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") ds_structs = [ base_struct1, base_struct2, @@ -489,34 +550,33 @@ def test_simulated_sequential_outputs(): ] ds_labels = np.array([0.0, 1.0, 2.0, 3.0, 4.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, + candidate_selection_kwargs = { + "target_min": 0.9, + "target_max": 2.1, + "aq": "MLI", + "num_candidates_to_pick": 2, + } + predictor_kwargs = {"structure_featurizer": "elemental_property"} + sl = simulated_sequential_learning( + full_design_space=acds, init_training_size=1, - batch_size_to_add=2, number_of_sl_loops=2, - acquisition_function="MLI", - target_min=0.9, - target_max=2.1, + candidate_selection_kwargs=candidate_selection_kwargs, + predictor_kwargs=predictor_kwargs, ) - # Test number of train histories equals number of sl loops + 1 - assert len(sl_dict["training_history"]) == 3 + # Test number of sl loops + assert sl.iteration_count == 3 # Test initial training size - assert len([a for a in sl_dict["training_history"][0] if a]) == 1 + assert sl.train_idx_history[0].sum() == 1 - # Test keeping track of history - assert sl_dict["training_history"][0] != sl_dict["training_history"][1] - - # Test that number of max uncertainties equals number of sl loops + 1 - assert len(sl_dict["uncertainty_history"]) == 3 - # Test the number of total uncertainties collected - assert len(sl_dict["uncertainty_history"][-1]) == 5 - - # check all mae and rmse training scores collected - assert len(sl_dict["mae_train_history"]) == 3 - assert len(sl_dict["rmse_train_history"]) == 3 + # Test keeping track of pred and unc history + assert len(sl.uncertainties_history) == 3 + assert len(sl.uncertainties_history[0]) == len(acds) + assert len(sl.predictions_history) == 3 + assert len(sl.predictions_history[-1]) == len(acds) + assert len(sl.candidate_index_history) == 2 def test_simulated_sequential_batch_added(): @@ -529,28 +589,23 @@ def test_simulated_sequential_batch_added(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") - bsta = 2 + candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} + predictor_kwargs = {"structure_featurizer": "elemental_property"} num_loops = 2 ds_structs = [base_struct1, base_struct2, sub1, sub2] ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, - batch_size_to_add=bsta, + sl = simulated_sequential_learning( + full_design_space=acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, number_of_sl_loops=num_loops, - acquisition_function="Random", init_training_size=1, ) - assert len(sl_dict["training_history"]) == num_loops + 1 - num_in_tr_set0 = len([a for a in sl_dict["training_history"][0] if a]) - num_in_tr_set1 = len([a for a in sl_dict["training_history"][1] if a]) - num_in_tr_set2 = len([a for a in sl_dict["training_history"][2] if a]) # should add 2 candidates on first loop - assert num_in_tr_set1 - num_in_tr_set0 == bsta + assert len(sl.candidate_index_history[0]) == 2 # since only 1 left, should add it on the next - assert num_in_tr_set2 - num_in_tr_set1 == 1 + assert len(sl.candidate_index_history[1]) == 1 def test_simulated_sequential_num_loops(): @@ -563,116 +618,97 @@ def test_simulated_sequential_num_loops(): ] base_struct1 = place_adsorbate(sub1, "H")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "N")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") + predictor_kwargs = {"structure_featurizer": "elemental_property"} + candidate_selection_kwargs = {"num_candidates_to_pick": 3, "aq": "Random"} ds_structs = [base_struct1, base_struct2, sub1, sub2] ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) # Test default number of loops - bsta = 3 - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, - batch_size_to_add=bsta, - acquisition_function="Random", + sl = simulated_sequential_learning( + full_design_space=acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, init_training_size=1, ) - assert len(sl_dict["training_history"]) == 2 + assert len(sl.predictions_history) == 2 + assert sl.iteration_count == 2 # Test catches maximum number of loops with pytest.raises(AutoCatSequentialLearningError): - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, - batch_size_to_add=bsta, - acquisition_function="Random", + sl = simulated_sequential_learning( + full_design_space=acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, init_training_size=1, number_of_sl_loops=3, ) + # Test with default num loops and default num candidates ds_structs = [base_struct1, base_struct2, sub2] ds_labels = np.array([5.0, 6.0, 7.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) + candidate_selection_kwargs.update({"num_candidates_to_pick": 1}) - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, - acquisition_function="Random", - init_training_size=1, - ) - assert len(sl_dict["training_history"]) == 3 - - -def test_simulated_sequential_outputs_testing(): - # Test with testing structures given - sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ - "structure" - ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") - ds_structs = [base_struct1, base_struct2, sub1] - ds_labels = np.array([0.0, 2.0, 4.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, + sl = simulated_sequential_learning( + full_design_space=acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, init_training_size=1, - testing_structures=[base_struct3, sub2], - testing_y=np.array([8.0, 10.0]), - batch_size_to_add=1, - number_of_sl_loops=2, - acquisition_function="MU", ) - # Check length of testing scores - assert len(sl_dict["training_history"]) == 3 - assert len(sl_dict["aq_scores_history"]) == 2 - assert len(sl_dict["max_scores_history"]) == 2 - assert len(sl_dict["mae_test_history"]) == 3 - assert len(sl_dict["rmse_train_history"]) == 3 - assert len(sl_dict["mae_train_history"]) == 3 - assert len(sl_dict["test_prediction_history"]) == 3 - assert len(sl_dict["test_prediction_history"][0]) == 2 - assert len(sl_dict["test_uncertainty_history"]) == 3 - assert len(sl_dict["test_uncertainty_history"][0]) == 2 - assert sl_dict["mae_test_history"] != sl_dict["mae_train_history"] + assert len(sl.uncertainties_history) == 3 + assert sl.iteration_count == 3 def test_simulated_sequential_write_to_disk(): # Test writing out sl dict - _tmp_dir = tempfile.TemporaryDirectory().name - sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ - "structure" - ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - base_struct3 = place_adsorbate(sub2, "N")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") - ds_structs = [base_struct1, base_struct2, base_struct3] - ds_labels = np.array([0, 1, 2]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, - init_training_size=2, - testing_structures=[base_struct3], - testing_y=np.array([2]), - batch_size_to_add=2, - number_of_sl_loops=1, - write_to_disk=True, - write_location=_tmp_dir, - acquisition_function="Random", - ) - # check data written as json - with open(os.path.join(_tmp_dir, "sl_dict.json"), "r") as f: - sl_written = json.load(f) - assert sl_dict == sl_written + with tempfile.TemporaryDirectory() as _tmp_dir: + sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"][ + "fcc111" + ]["structure"] + sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"][ + "fcc100" + ]["structure"] + base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + base_struct3 = place_adsorbate(sub2, "N")["custom"]["structure"] + predictor_kwargs = {"structure_featurizer": "elemental_property"} + candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} + ds_structs = [base_struct1, base_struct2, base_struct3] + ds_labels = np.array([0, 1, 2]) + acds = AutoCatDesignSpace(ds_structs, ds_labels) + sl = simulated_sequential_learning( + full_design_space=acds, + init_training_size=2, + number_of_sl_loops=1, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + write_to_disk=True, + write_location=_tmp_dir, + ) + # check data written as json + json_path = os.path.join(_tmp_dir, "acsl.json") + sl_written = AutoCatSequentialLearner.from_json(json_path) + assert sl.iteration_count == sl_written.iteration_count + assert np.array_equal(sl.predictions_history, sl_written.predictions_history) + assert np.array_equal( + sl.uncertainties_history, sl_written.uncertainties_history + ) + assert np.array_equal( + sl.candidate_index_history, sl_written.candidate_index_history + ) + assert np.array_equal(sl.candidate_indices, sl_written.candidate_indices) + assert np.array_equal(sl.predictions, sl_written.predictions) + assert np.array_equal(sl.uncertainties, sl_written.uncertainties) + assert np.array_equal(sl.predictor_kwargs, sl_written.predictor_kwargs) + assert sl.candidate_selection_kwargs == sl_written.candidate_selection_kwargs + assert ( + sl.design_space.design_space_structures + == sl_written.design_space.design_space_structures + ) + assert np.array_equal( + sl.design_space.design_space_labels, + sl_written.design_space.design_space_labels, + ) def test_simulated_sequential_learning_fully_explored(): @@ -685,21 +721,18 @@ def test_simulated_sequential_learning_fully_explored(): ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") + predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, base_struct2, sub2] ds_labels = np.array([0.0, np.nan, 4.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) + candidate_selection_kwargs = {"aq": "MU"} with pytest.raises(AutoCatSequentialLearningError): - sl_dict = simulated_sequential_learning( - predictor=acsc, - design_space=acds, + sl = simulated_sequential_learning( + full_design_space=acds, init_training_size=1, - testing_structures=[base_struct3, sub2], - testing_y=np.array([8.0, 10.0]), - batch_size_to_add=1, number_of_sl_loops=2, - acquisition_function="MU", + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, ) From c72947591ebec8756b40c1735e7a5b8eed5ad55d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 31 Dec 2021 13:52:27 -0500 Subject: [PATCH 150/239] update multi simulated sl --- src/autocat/learning/sequential.py | 86 +++++++++++++++++++++------- tests/learning/test_sequential.py | 91 +++++++++++++++++++----------- 2 files changed, 124 insertions(+), 53 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 19b562e5..05e354e5 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -155,7 +155,7 @@ def write_json( json_path = os.path.join(write_location, json_name) with open(json_path, "w") as f: json.dump(collected_jsons, f) - # write to jsonified list to memory + # write jsonified list to memory if return_jsonified_list: return collected_jsons @@ -484,64 +484,110 @@ def from_json(json_name: str): def multiple_simulated_sequential_learning_runs( + full_design_space: AutoCatDesignSpace, number_of_runs: int = 5, number_parallel_jobs: int = None, + predictor_kwargs: Dict[str, Union[str, float]] = None, + candidate_selection_kwargs: Dict[str, Union[str, float]] = None, + init_training_size: int = 10, + number_of_sl_loops: int = None, write_to_disk: bool = False, write_location: str = ".", - sl_kwargs=None, -): + json_name_prefix: str = None, +) -> List[AutoCatSequentialLearner]: """ Conducts multiple simulated sequential learning runs Parameters ---------- + full_design_space: + Fully labelled AutoCatDesignSpace to simulate + being searched over + + predictor_kwargs: + Kwargs to be used in setting up the predictor. + This is where model class, model hyperparameters, etc. + are specified. + + candidate_selection_kwargs: + Kwargs that specify that settings for candidate selection. + This is where acquisition function, targets, etc. are + specified. + + init_training_size: + Size of the initial training set to be selected from + the full space. + Default: 10 + + number_of_sl_loops: + Integer specifying the number of sequential learning loops to be conducted. + This value cannot be greater than + `(DESIGN_SPACE_SIZE - init_training_size)/batch_size_to_add` + Default: maximum number of sl loops calculated above + number_of_runs: Integer of number of runs to be done + Default: 5 number_parallel_jobs: Integer giving the number of cores to be paralellized across using `joblib` + Default: None (ie. will run in serial) write_to_disk: - Boolean specifying whether runs history should be written to disk as a json. - Defaults to False. + Boolean specifying whether runs history should be written to disk as jsons. + Default: False write_location: - String with the location where runs history should be written to disk. + String with the location where runs history jsons should be written to disk. + Default: current directory - sl_kwargs: - Mapping of keywords for `simulated_sequential_learning`. - Note: Do not use the `write_to_disk` keyword here + json_name_prefix: + Prefix used when writing out each simulated run as a json + The naming convention is `{json_name_prefix}_{run #}.json` + Default: acsl_run Returns ------- - run_history: - List of dictionaries generated for each run containing info - about that run such as mae history, rmse history, etc.. + runs_history: + List of AutoCatSequentialLearner objects for each simulated run """ - if sl_kwargs is None: - sl_kwargs = {} if number_parallel_jobs is not None: runs_history = Parallel(n_jobs=number_parallel_jobs)( - delayed(simulated_sequential_learning)(**sl_kwargs,) + delayed(simulated_sequential_learning)( + full_design_space=full_design_space, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + number_of_sl_loops=number_of_sl_loops, + init_training_size=init_training_size, + ) for i in range(number_of_runs) ) else: runs_history = [ - simulated_sequential_learning(**sl_kwargs,) for i in range(number_of_runs) + simulated_sequential_learning( + full_design_space=full_design_space, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + number_of_sl_loops=number_of_sl_loops, + init_training_size=init_training_size, + ) + for i in range(number_of_runs) ] if write_to_disk: if not os.path.isdir(write_location): os.makedirs(write_location) - json_write_path = os.path.join(write_location, "sl_runs_history.json") - with open(json_write_path, "w") as f: - json.dump(runs_history, f) - print(f"SL histories written to {json_write_path}") + if json_name_prefix is None: + json_name_prefix = "acsl_run" + for i, run in enumerate(runs_history): + name = json_name_prefix + "_" + str(i) + ".json" + run.write_json(write_location=write_location, json_name=name) + print(f"SL histories written to {write_location}") return runs_history diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index cbd67d5a..77e64421 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -742,23 +742,22 @@ def test_multiple_sequential_learning_serial(): "structure" ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") + predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) + candidate_selection_kwargs = {"aq": "MU"} runs_history = multiple_simulated_sequential_learning_runs( + full_design_space=acds, number_of_runs=3, - sl_kwargs={ - "predictor": acsc, - "design_space": acds, - "number_of_sl_loops": 1, - "acquisition_function": "MU", - "init_training_size": 1, - }, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + number_of_sl_loops=1, + init_training_size=1, ) assert len(runs_history) == 3 - assert isinstance(runs_history[0], dict) - assert "mae_train_history" in runs_history[1] + assert isinstance(runs_history[0], AutoCatSequentialLearner) + assert len(runs_history[1].predictions_history) == 2 def test_multiple_sequential_learning_parallel(): @@ -767,24 +766,23 @@ def test_multiple_sequential_learning_parallel(): "structure" ] base_struct1 = place_adsorbate(sub1, "Li")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") + predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) + candidate_selection_kwargs = {"aq": "Random"} runs_history = multiple_simulated_sequential_learning_runs( + full_design_space=acds, number_of_runs=3, number_parallel_jobs=2, - sl_kwargs={ - "predictor": acsc, - "design_space": acds, - "number_of_sl_loops": 1, - "acquisition_function": "Random", - "init_training_size": 1, - }, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + number_of_sl_loops=1, + init_training_size=1, ) assert len(runs_history) == 3 - assert isinstance(runs_history[2], dict) - assert "rmse_train_history" in runs_history[0] + assert isinstance(runs_history[2], AutoCatSequentialLearner) + assert len(runs_history[1].uncertainties_history) == 2 def test_multiple_sequential_learning_write_to_disk(): @@ -794,29 +792,56 @@ def test_multiple_sequential_learning_write_to_disk(): "structure" ] base_struct1 = place_adsorbate(sub1, "N")["custom"]["structure"] - acsc = AutoCatPredictor(structure_featurizer="elemental_property") + predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) acds = AutoCatDesignSpace(ds_structs, ds_labels) + candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} runs_history = multiple_simulated_sequential_learning_runs( + full_design_space=acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, number_of_runs=3, number_parallel_jobs=2, + init_training_size=1, + number_of_sl_loops=1, write_to_disk=True, write_location=_tmp_dir, - sl_kwargs={ - "predictor": acsc, - "design_space": acds, - "number_of_sl_loops": 1, - "acquisition_function": "Random", - "init_training_size": 1, - "batch_size_to_add": 2, - }, + json_name_prefix="test_multi", ) - # check data history - with open(os.path.join(_tmp_dir, "sl_runs_history.json"), "r") as f: - runs_history_written = json.load(f) - assert runs_history == runs_history_written + # check data history in each run + for i in range(3): + written_run = AutoCatSequentialLearner.from_json( + os.path.join(_tmp_dir, f"test_multi_{i}.json") + ) + written_ds = written_run.design_space + assert False not in [written_ds.design_space_structures == ds_structs] + assert np.array_equal(written_ds.design_space_labels, ds_labels) + assert written_run.iteration_count == runs_history[i].iteration_count + assert np.array_equal(written_run.predictions, runs_history[i].predictions) + assert np.array_equal( + written_run.predictions_history, runs_history[i].predictions_history + ) + assert np.array_equal(written_run.uncertainties, runs_history[i].uncertainties) + assert np.array_equal( + written_run.uncertainties_history, runs_history[i].uncertainties_history + ) + assert np.array_equal( + written_run.train_idx_history, runs_history[i].train_idx_history + ) + assert np.array_equal(written_run.train_idx, runs_history[i].train_idx) + assert np.array_equal( + written_run.candidate_indices, runs_history[i].candidate_indices + ) + assert np.array_equal( + written_run.candidate_index_history, runs_history[i].candidate_index_history + ) + assert written_run.predictor_kwargs == runs_history[i].predictor_kwargs + assert ( + written_run.candidate_selection_kwargs + == runs_history[i].candidate_selection_kwargs + ) def test_choose_next_candidate_input_minimums(): From c379035edfeece0131849c21441f7b0ff803b310 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 2 Jan 2022 14:03:16 -0500 Subject: [PATCH 151/239] bugfix: reading of None sl_kwargs from_json --- src/autocat/learning/sequential.py | 41 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 05e354e5..f960212d 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -455,25 +455,28 @@ def from_json(json_name: str): raw_sl_kwargs = all_data[-1] sl_kwargs = {} for k in raw_sl_kwargs: - if k in [ - "predictions", - "uncertainties", - "acquisition_scores", - "candidate_indices", - ]: - sl_kwargs[k] = np.array(raw_sl_kwargs[k]) - elif k in [ - "predictions_history", - "uncertainties_history", - "candidate_index_history", - ]: - sl_kwargs[k] = [np.array(i) for i in raw_sl_kwargs[k]] - elif k == "iteration_count": - sl_kwargs[k] = raw_sl_kwargs[k] - elif k == "train_idx": - sl_kwargs[k] = np.array(raw_sl_kwargs[k], dtype=bool) - elif k == "train_idx_history": - sl_kwargs[k] = [np.array(i, dtype=bool) for i in raw_sl_kwargs[k]] + if raw_sl_kwargs[k] is not None: + if k in [ + "predictions", + "uncertainties", + "acquisition_scores", + "candidate_indices", + ]: + sl_kwargs[k] = np.array(raw_sl_kwargs[k]) + elif k in [ + "predictions_history", + "uncertainties_history", + "candidate_index_history", + ]: + sl_kwargs[k] = [np.array(i) for i in raw_sl_kwargs[k]] + elif k == "iteration_count": + sl_kwargs[k] = raw_sl_kwargs[k] + elif k == "train_idx": + sl_kwargs[k] = np.array(raw_sl_kwargs[k], dtype=bool) + elif k == "train_idx_history": + sl_kwargs[k] = [np.array(i, dtype=bool) for i in raw_sl_kwargs[k]] + else: + sl_kwargs[k] = None return AutoCatSequentialLearner( design_space=acds, From 5ea18edeb2912d0b44cf03a29994335b41f0c97e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 3 Jan 2022 21:57:50 -0500 Subject: [PATCH 152/239] add acds equality interface --- src/autocat/learning/sequential.py | 16 +++++++++ tests/learning/test_sequential.py | 52 ++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index f960212d..2dbd8bae 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -69,6 +69,22 @@ def __delitem__(self, i): masked_structs = [structs[j] for j in range(len(self)) if mask[j]] self._design_space_structures = masked_structs + def __eq__(self, other: object) -> bool: + if isinstance(other, AutoCatDesignSpace): + # check that they are the same length + if len(self) == len(other): + # check all their structures are equal + self_structs = self.design_space_structures + o_structs = other.design_space_structures + if not self_structs == o_structs: + return False + + # check their labels are equal + self_labels = self.design_space_labels + o_labels = other.design_space_labels + return np.array_equal(self_labels, o_labels, equal_nan=True) + return False + def copy(self): """ Returns a copy of the design space diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 77e64421..0f900e37 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -72,11 +72,10 @@ def test_sequential_learner_from_json(): acds.design_space_labels, equal_nan=True, ) - assert False not in [ - written_acsl.design_space.design_space_structures[j] - == acds.design_space_structures[j] - for j in range(len(acds)) - ] + assert ( + written_acsl.design_space.design_space_structures + == acds.design_space_structures + ) assert written_acsl.predictor_kwargs == predictor_kwargs assert written_acsl.candidate_selection_kwargs == candidate_selection_kwargs assert written_acsl.iteration_count == 1 @@ -437,6 +436,47 @@ def test_delitem_design_space(): assert acds.design_space_structures == [sub1, sub2, sub3] +def test_eq_design_space(): + # test comparing design spaces + sub0 = generate_surface_structures(["Pd"], facets={"Pd": ["100"]})["Pd"]["fcc100"][ + "structure" + ] + sub0 = place_adsorbate(sub0, "O")["custom"]["structure"] + sub1 = generate_surface_structures(["V"], facets={"V": ["110"]})["V"]["bcc110"][ + "structure" + ] + sub1 = place_adsorbate(sub1, "H")["custom"]["structure"] + sub2 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ + "structure" + ] + sub2 = place_adsorbate(sub2, "S")["custom"]["structure"] + sub3 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ + "hcp0001" + ]["structure"] + sub3 = place_adsorbate(sub3, "P")["custom"]["structure"] + structs = [sub0, sub1, sub2] + labels = np.array([-2.5, np.nan, 600.0]) + + # test trivial case + acds = AutoCatDesignSpace(structs, labels) + acds0 = AutoCatDesignSpace(structs, labels) + assert acds == acds0 + + # test comparing when different length + acds1 = AutoCatDesignSpace(structs[:-1], labels[:-1]) + assert acds != acds1 + + # test same structures, different labels + acds2 = AutoCatDesignSpace(structs, labels) + acds2.update([structs[1]], labels=np.array([0.2])) + assert acds != acds2 + + # test diff structures, same labels + structs[0][0].symbol = "Ni" + acds3 = AutoCatDesignSpace(structs, labels) + assert acds != acds3 + + def test_updating_design_space(): sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["100"]})["Ag"]["fcc100"][ "structure" @@ -816,7 +856,7 @@ def test_multiple_sequential_learning_write_to_disk(): os.path.join(_tmp_dir, f"test_multi_{i}.json") ) written_ds = written_run.design_space - assert False not in [written_ds.design_space_structures == ds_structs] + assert written_ds.design_space_structures == ds_structs assert np.array_equal(written_ds.design_space_labels, ds_labels) assert written_run.iteration_count == runs_history[i].iteration_count assert np.array_equal(written_run.predictions, runs_history[i].predictions) From d190a44fdbb8f782f818c117060de27b1664b125 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 6 Jan 2022 19:00:56 -0500 Subject: [PATCH 153/239] refactor AutoCatX -> X --- src/autocat/learning/predictors.py | 6 +- src/autocat/learning/sequential.py | 70 ++++++++-------- tests/learning/test_predictors.py | 18 ++--- tests/learning/test_sequential.py | 124 ++++++++++++++--------------- 4 files changed, 107 insertions(+), 111 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index b3ae45c0..2461e48d 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -17,11 +17,11 @@ from autocat.learning.featurizers import _get_number_of_features -class AutoCatPredictorError(Exception): +class PredictorError(Exception): pass -class AutoCatPredictor: +class Predictor: def __init__( self, model_class=None, @@ -483,7 +483,7 @@ def score( if metric not in score_func: msg = f"Metric: {metric} is not supported" - raise AutoCatPredictorError(msg) + raise PredictorError(msg) score = score_func[metric](y, pred_label, **kwargs) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 2dbd8bae..390e5729 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -12,7 +12,7 @@ from ase.io import read as ase_read from scipy import stats -from autocat.learning.predictors import AutoCatPredictor +from autocat.learning.predictors import Predictor from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES from autocat.data.segregation_energies import SEGREGATION_ENERGIES @@ -20,11 +20,11 @@ Array = List[float] -class AutoCatDesignSpaceError(Exception): +class DesignSpaceError(Exception): pass -class AutoCatDesignSpace: +class DesignSpace: def __init__( self, design_space_structures: List[Atoms], design_space_labels: Array, ): @@ -44,7 +44,7 @@ def __init__( """ if len(design_space_structures) != design_space_labels.shape[0]: msg = f"Number of structures ({len(design_space_structures)}) and labels ({design_space_labels.shape[0]}) must match" - raise AutoCatDesignSpaceError(msg) + raise DesignSpaceError(msg) self._design_space_structures = [ struct.copy() for struct in design_space_structures @@ -70,7 +70,7 @@ def __delitem__(self, i): self._design_space_structures = masked_structs def __eq__(self, other: object) -> bool: - if isinstance(other, AutoCatDesignSpace): + if isinstance(other, DesignSpace): # check that they are the same length if len(self) == len(other): # check all their structures are equal @@ -102,7 +102,7 @@ def design_space_structures(self): @design_space_structures.setter def design_space_structures(self, design_space_structures): msg = "Please use `update` method to update the design space." - raise AutoCatDesignSpaceError(msg) + raise DesignSpaceError(msg) @property def design_space_labels(self): @@ -111,7 +111,7 @@ def design_space_labels(self): @design_space_labels.setter def design_space_labels(self, design_space_labels): msg = "Please use `update` method to update the design space." - raise AutoCatDesignSpaceError(msg) + raise DesignSpaceError(msg) def update(self, structures: List[Atoms], labels: Array): """ @@ -190,19 +190,19 @@ def from_json(json_name: str): atoms = ase_read(_tmp_json, format="json") structures.append(atoms) labels = np.array(all_data[-1]) - return AutoCatDesignSpace( + return DesignSpace( design_space_structures=structures, design_space_labels=labels, ) -class AutoCatSequentialLearningError(Exception): +class SequentialLearnerError(Exception): pass -class AutoCatSequentialLearner: +class SequentialLearner: def __init__( self, - design_space: AutoCatDesignSpace, + design_space: DesignSpace, predictor_kwargs: Dict[str, Union[str, float]] = None, candidate_selection_kwargs: Dict[str, Union[str, float]] = None, sl_kwargs: Dict[str, int] = None, @@ -218,7 +218,7 @@ def __init__( predictor_kwargs = {"structure_featurizer": "sine_matrix"} self._predictor_kwargs = None self.predictor_kwargs = predictor_kwargs - self._predictor = AutoCatPredictor(**predictor_kwargs) + self._predictor = Predictor(**predictor_kwargs) # acquisition function arguments to use for candidate selection if not candidate_selection_kwargs: @@ -268,7 +268,7 @@ def predictor_kwargs(self, predictor_kwargs): if not predictor_kwargs: predictor_kwargs = {} self._predictor_kwargs = predictor_kwargs - self._predictor = AutoCatPredictor(**predictor_kwargs) + self._predictor = Predictor(**predictor_kwargs) @property def predictor(self): @@ -463,7 +463,7 @@ def from_json(json_name: str): atoms = ase_read(_tmp_json, format="json") structures.append(atoms) labels = np.array(all_data[-4]) - acds = AutoCatDesignSpace( + acds = DesignSpace( design_space_structures=structures, design_space_labels=labels, ) predictor_kwargs = all_data[-3] @@ -494,7 +494,7 @@ def from_json(json_name: str): else: sl_kwargs[k] = None - return AutoCatSequentialLearner( + return SequentialLearner( design_space=acds, predictor_kwargs=predictor_kwargs, candidate_selection_kwargs=candidate_selection_kwargs, @@ -503,7 +503,7 @@ def from_json(json_name: str): def multiple_simulated_sequential_learning_runs( - full_design_space: AutoCatDesignSpace, + full_design_space: DesignSpace, number_of_runs: int = 5, number_parallel_jobs: int = None, predictor_kwargs: Dict[str, Union[str, float]] = None, @@ -513,7 +513,7 @@ def multiple_simulated_sequential_learning_runs( write_to_disk: bool = False, write_location: str = ".", json_name_prefix: str = None, -) -> List[AutoCatSequentialLearner]: +) -> List[SequentialLearner]: """ Conducts multiple simulated sequential learning runs @@ -521,7 +521,7 @@ def multiple_simulated_sequential_learning_runs( ---------- full_design_space: - Fully labelled AutoCatDesignSpace to simulate + Fully labelled DesignSpace to simulate being searched over predictor_kwargs: @@ -571,7 +571,7 @@ def multiple_simulated_sequential_learning_runs( ------- runs_history: - List of AutoCatSequentialLearner objects for each simulated run + List of SequentialLearner objects for each simulated run """ if number_parallel_jobs is not None: @@ -612,7 +612,7 @@ def multiple_simulated_sequential_learning_runs( def simulated_sequential_learning( - full_design_space: AutoCatDesignSpace, + full_design_space: DesignSpace, predictor_kwargs: Dict[str, Union[str, float]] = None, candidate_selection_kwargs: Dict[str, Union[str, float]] = None, init_training_size: int = 10, @@ -620,7 +620,7 @@ def simulated_sequential_learning( write_to_disk: bool = False, write_location: str = ".", json_name: str = None, -) -> AutoCatSequentialLearner: +) -> SequentialLearner: """ Conducts a simulated sequential learning loop for a fully labelled design space to explore. @@ -629,7 +629,7 @@ def simulated_sequential_learning( ---------- full_design_space: - Fully labelled AutoCatDesignSpace to simulate + Fully labelled DesignSpace to simulate being searched over predictor_kwargs: @@ -678,13 +678,13 @@ def simulated_sequential_learning( if True in np.isnan(full_design_space.design_space_labels): missing_label_idx = np.where(np.isnan(full_design_space.design_space_labels))[0] msg = f"Design space must be fully explored. Missing labels at indices: {missing_label_idx}" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) # check that specified initial training size makes sense if init_training_size > ds_size: msg = f"Initial training size ({init_training_size})\ larger than design space ({ds_size})" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) batch_size_to_add = candidate_selection_kwargs.get("num_candidates_to_pick", 1) max_num_sl_loops = int(np.ceil((ds_size - init_training_size) / batch_size_to_add)) @@ -695,7 +695,7 @@ def simulated_sequential_learning( # check that specified number of loops is feasible if number_of_sl_loops > max_num_sl_loops: msg = f"Number of SL loops ({number_of_sl_loops}) cannot be greater than ({max_num_sl_loops})" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) # generate initial training set init_idx = np.zeros(ds_size, dtype=bool) @@ -712,9 +712,9 @@ def simulated_sequential_learning( # set up learner that is used for iteration dummy_labels = np.empty(len(full_design_space)) dummy_labels[:] = np.nan - ds = AutoCatDesignSpace(full_design_space.design_space_structures, dummy_labels) + ds = DesignSpace(full_design_space.design_space_structures, dummy_labels) ds.update(init_structs, init_labels) - sl = AutoCatSequentialLearner( + sl = SequentialLearner( design_space=ds, predictor_kwargs=predictor_kwargs, candidate_selection_kwargs=candidate_selection_kwargs, @@ -823,20 +823,20 @@ def choose_next_candidate( if include_hhi: if structures is None: msg = "Structures must be provided to include HHI scores" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) hhi_scores = calculate_hhi_scores(structures, hhi_type) segreg_energy_scores = None if include_seg_ener: if structures is None: msg = "Structures must be provided to include segregation energy scores" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) segreg_energy_scores = calculate_segregation_energy_scores(structures) if aq == "Random": if labels is None: msg = "For aq = 'Random', the labels must be supplied" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) if train_idx is None: train_idx = np.zeros(len(labels), dtype=bool) @@ -856,7 +856,7 @@ def choose_next_candidate( elif aq == "MU": if unc is None: msg = "For aq = 'MU', the uncertainties must be supplied" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) if train_idx is None: train_idx = np.zeros(len(unc), dtype=bool) @@ -872,7 +872,7 @@ def choose_next_candidate( elif aq == "MLI": if unc is None or pred is None: msg = "For aq = 'MLI', both uncertainties and predictions must be supplied" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) if train_idx is None: train_idx = np.zeros(len(unc), dtype=bool) @@ -915,7 +915,7 @@ def get_overlap_score(mean: float, std: float, x2: float = None, x1: float = Non """Calculate overlap score given targets x2 (max) and x1 (min)""" if x1 is None and x2 is None: msg = "Please specify at least either a minimum or maximum target for MLI" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) if x1 is None: x1 = -np.inf @@ -954,7 +954,7 @@ def calculate_hhi_scores(structures: List[Atoms], hhi_type: str = "production"): """ if structures is None: msg = "To include HHI, the structures must be provided" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) raw_hhi_data = {"production": HHI_PRODUCTION, "reserves": HHI_RESERVES} max_hhi = np.max([raw_hhi_data[hhi_type][r] for r in raw_hhi_data[hhi_type]]) @@ -1004,7 +1004,7 @@ def calculate_segregation_energy_scores(structures: List[Atoms]): """ if structures is None: msg = "To include segregation energies, the structures must be provided" - raise AutoCatSequentialLearningError(msg) + raise SequentialLearnerError(msg) # won't consider surface energies (ie. dop == host) for normalization max_seg_ener = SEGREGATION_ENERGIES["Pd"]["W"] diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 4e016328..0a410bf0 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -15,8 +15,8 @@ from autocat.perturbations import generate_perturbed_dataset from autocat.learning.featurizers import get_X from autocat.learning.featurizers import catalyst_featurization -from autocat.learning.predictors import AutoCatPredictor -from autocat.learning.predictors import AutoCatPredictorError +from autocat.learning.predictors import Predictor +from autocat.learning.predictors import PredictorError def test_fit_model_on_perturbed_systems(): @@ -28,7 +28,7 @@ def test_fit_model_on_perturbed_systems(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatPredictor( + acsc = Predictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, @@ -63,7 +63,7 @@ def test_predict_initial_configuration_formats(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatPredictor( + acsc = Predictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, @@ -86,7 +86,7 @@ def test_score_on_perturbed_systems(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"][:, 0] - acsc = AutoCatPredictor( + acsc = Predictor( structure_featurizer="sine_matrix", adsorbate_featurizer="soap", adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, @@ -106,7 +106,7 @@ def test_score_on_perturbed_systems(): assert len(pred_corr) == 5 assert len(unc) == 5 assert mae != mse - with pytest.raises(AutoCatPredictorError): + with pytest.raises(PredictorError): acsc.score(p_structures[15:], correction_matrix, metric="msd") # Test with single target @@ -120,7 +120,7 @@ def test_score_on_perturbed_systems(): def test_model_class_and_kwargs(): # Tests providing regression model class and kwargs - acsc = AutoCatPredictor(KernelRidge, model_kwargs={"gamma": 0.5}) + acsc = Predictor(KernelRidge, model_kwargs={"gamma": 0.5}) assert isinstance(acsc.regressor, KernelRidge) # check that regressor created with correct kwarg assert acsc.regressor.gamma == 0.5 @@ -128,7 +128,7 @@ def test_model_class_and_kwargs(): acsc.model_class = GaussianProcessRegressor # check that kwargs are removed when class is changed assert acsc.model_kwargs is None - acsc = AutoCatPredictor() + acsc = Predictor() acsc.model_kwargs = {"alpha": 2.5} assert acsc.model_kwargs == {"alpha": 2.5} assert acsc.regressor.alpha == 2.5 @@ -144,7 +144,7 @@ def test_model_without_unc(): p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) p_structures = p_set["collected_structures"] correction_matrix = p_set["correction_matrix"] - acsc = AutoCatPredictor( + acsc = Predictor( model_class=KernelRidge, structure_featurizer="sine_matrix", adsorbate_featurizer=None, diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 0f900e37..7e8b401b 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -12,12 +12,12 @@ from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES from autocat.data.segregation_energies import SEGREGATION_ENERGIES -from autocat.learning.predictors import AutoCatPredictor +from autocat.learning.predictors import Predictor from autocat.learning.sequential import ( - AutoCatDesignSpace, - AutoCatDesignSpaceError, - AutoCatSequentialLearningError, - AutoCatSequentialLearner, + DesignSpace, + DesignSpaceError, + SequentialLearnerError, + SequentialLearner, calculate_segregation_energy_scores, choose_next_candidate, get_overlap_score, @@ -32,7 +32,7 @@ def test_sequential_learner_from_json(): - # Tests generation of an AutoCatSequentialLearner from a json + # Tests generation of an SequentialLearner from a json sub1 = generate_surface_structures(["Au"], facets={"Au": ["110"]})["Au"]["fcc110"][ "structure" ] @@ -56,8 +56,8 @@ def test_sequential_learner_from_json(): } candidate_selection_kwargs = {"aq": "Random", "num_candidates_to_pick": 3} - acds = AutoCatDesignSpace(structs, labels) - acsl = AutoCatSequentialLearner( + acds = DesignSpace(structs, labels) + acsl = SequentialLearner( acds, predictor_kwargs=predictor_kwargs, candidate_selection_kwargs=candidate_selection_kwargs, @@ -66,7 +66,7 @@ def test_sequential_learner_from_json(): with tempfile.TemporaryDirectory() as _tmp_dir: acsl.write_json(_tmp_dir, "testing_acsl.json") json_path = os.path.join(_tmp_dir, "testing_acsl.json") - written_acsl = AutoCatSequentialLearner.from_json(json_path) + written_acsl = SequentialLearner.from_json(json_path) assert np.array_equal( written_acsl.design_space.design_space_labels, acds.design_space_labels, @@ -99,7 +99,7 @@ def test_sequential_learner_from_json(): def test_sequential_learner_write_json(): - # Tests writing a AutoCatSequentialLearner to disk as a json + # Tests writing a SequentialLearner to disk as a json sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["110"]})["Ag"]["fcc110"][ "structure" ] @@ -123,8 +123,8 @@ def test_sequential_learner_write_json(): } candidate_selection_kwargs = {"aq": "MU", "num_candidates_to_pick": 2} - acds = AutoCatDesignSpace(structs, labels) - acsl = AutoCatSequentialLearner( + acds = DesignSpace(structs, labels) + acsl = SequentialLearner( acds, predictor_kwargs=predictor_kwargs, candidate_selection_kwargs=candidate_selection_kwargs, @@ -161,7 +161,7 @@ def test_sequential_learner_write_json(): } # test writing when no kwargs given - acsl = AutoCatSequentialLearner(acds) + acsl = SequentialLearner(acds) with tempfile.TemporaryDirectory() as _tmp_dir: acsl.write_json(_tmp_dir) with open(os.path.join(_tmp_dir, "acsl.json"), "r") as f: @@ -242,8 +242,8 @@ def test_sequential_learner_iterate(): sub4 = place_adsorbate(sub4, "Fe")["custom"]["structure"] structs = [sub1, sub2, sub3, sub4] labels = np.array([11.0, 25.0, np.nan, np.nan]) - acds = AutoCatDesignSpace(structs, labels) - acsl = AutoCatSequentialLearner(acds) + acds = DesignSpace(structs, labels) + acsl = SequentialLearner(acds) assert acsl.iteration_count == 0 @@ -309,8 +309,8 @@ def test_sequential_learner_setup(): sub4 = place_adsorbate(sub4, "N")["custom"]["structure"] structs = [sub1, sub2, sub3, sub4] labels = np.array([4.0, np.nan, 6.0, np.nan]) - acds = AutoCatDesignSpace(structs, labels) - acsl = AutoCatSequentialLearner(acds) + acds = DesignSpace(structs, labels) + acsl = SequentialLearner(acds) assert acsl.design_space.design_space_structures == acds.design_space_structures assert np.array_equal( @@ -323,7 +323,7 @@ def test_sequential_learner_setup(): # test default kwargs assert acsl.predictor_kwargs == {"structure_featurizer": "sine_matrix"} # test specifying kwargs - acsl = AutoCatSequentialLearner( + acsl = SequentialLearner( acds, predictor_kwargs={ "structure_featurizer": "elemental_property", @@ -358,7 +358,7 @@ def test_sequential_learner_setup(): def test_design_space_setup(): - # test setting up an AutoCatDesignSpace + # test setting up an DesignSpace sub1 = generate_surface_structures( ["Pt"], supercell_dim=[2, 2, 5], facets={"Pt": ["100"]} )["Pt"]["fcc100"]["structure"] @@ -369,15 +369,15 @@ def test_design_space_setup(): sub2 = place_adsorbate(sub2, "F")["custom"]["structure"] structs = [sub1, sub2] labels = np.array([3.0, 7.0]) - acds = AutoCatDesignSpace(structs, labels) + acds = DesignSpace(structs, labels) assert acds.design_space_structures == [sub1, sub2] assert acds.design_space_structures is not structs assert np.array_equal(acds.design_space_labels, labels) assert acds.design_space_labels is not labels assert len(acds) == 2 # test different number of structures and labels - with pytest.raises(AutoCatDesignSpaceError): - acds = AutoCatDesignSpace([sub1], labels) + with pytest.raises(DesignSpaceError): + acds = DesignSpace([sub1], labels) def test_delitem_design_space(): @@ -401,13 +401,13 @@ def test_delitem_design_space(): structs = [sub0, sub1, sub2] labels = np.array([-2.5, np.nan, 600.0]) # test deleting by single idx - acds = AutoCatDesignSpace(structs, labels) + acds = DesignSpace(structs, labels) del acds[1] assert len(acds) == 2 assert np.array_equal(acds.design_space_labels, np.array([-2.5, 600.0])) assert acds.design_space_structures == [sub0, sub2] # test deleting using a mask - acds = AutoCatDesignSpace(structs, labels) + acds = DesignSpace(structs, labels) mask = np.zeros(len(acds), bool) mask[0] = 1 mask[1] = 1 @@ -419,7 +419,7 @@ def test_delitem_design_space(): # test deleting by providing list of idx structs = [sub0, sub1, sub2, sub3] labels = np.array([-20, 8, np.nan, 0.3]) - acds = AutoCatDesignSpace(structs, labels) + acds = DesignSpace(structs, labels) del acds[[1, 3]] assert len(acds) == 2 assert np.array_equal( @@ -427,7 +427,7 @@ def test_delitem_design_space(): ) assert acds.design_space_structures == [sub0, sub2] # test deleting by providing list with a single idx - acds = AutoCatDesignSpace(structs, labels) + acds = DesignSpace(structs, labels) del acds[[0]] assert len(acds) == 3 assert np.array_equal( @@ -458,22 +458,22 @@ def test_eq_design_space(): labels = np.array([-2.5, np.nan, 600.0]) # test trivial case - acds = AutoCatDesignSpace(structs, labels) - acds0 = AutoCatDesignSpace(structs, labels) + acds = DesignSpace(structs, labels) + acds0 = DesignSpace(structs, labels) assert acds == acds0 # test comparing when different length - acds1 = AutoCatDesignSpace(structs[:-1], labels[:-1]) + acds1 = DesignSpace(structs[:-1], labels[:-1]) assert acds != acds1 # test same structures, different labels - acds2 = AutoCatDesignSpace(structs, labels) + acds2 = DesignSpace(structs, labels) acds2.update([structs[1]], labels=np.array([0.2])) assert acds != acds2 # test diff structures, same labels structs[0][0].symbol = "Ni" - acds3 = AutoCatDesignSpace(structs, labels) + acds3 = DesignSpace(structs, labels) assert acds != acds3 @@ -492,14 +492,14 @@ def test_updating_design_space(): ]["structure"] structs = [sub1, sub2, sub3] labels = np.array([4.0, 5.0, 6.0]) - acds = AutoCatDesignSpace(structs, labels) + acds = DesignSpace(structs, labels) # Test trying to update just structures - with pytest.raises(AutoCatDesignSpaceError): + with pytest.raises(DesignSpaceError): acds.design_space_structures = [sub4] # Test trying to update just labels - with pytest.raises(AutoCatDesignSpaceError): + with pytest.raises(DesignSpaceError): acds.design_space_structures = np.array([4.0]) # Test updating label already in DS and extending @@ -514,7 +514,7 @@ def test_updating_design_space(): def test_write_design_space_as_json(): - # Tests writing out the AutoCatDesignSpace to disk + # Tests writing out the DesignSpace to disk sub1 = generate_surface_structures(["Pd"], facets={"Pd": ["111"]})["Pd"]["fcc111"][ "structure" ] @@ -524,9 +524,7 @@ def test_write_design_space_as_json(): structs = [sub1, sub2] labels = np.array([0.3, 0.8]) with tempfile.TemporaryDirectory() as _tmp_dir: - acds = AutoCatDesignSpace( - design_space_structures=structs, design_space_labels=labels, - ) + acds = DesignSpace(design_space_structures=structs, design_space_labels=labels,) acds.write_json(write_location=_tmp_dir) # loads back written json with open(os.path.join(_tmp_dir, "acds.json"), "r") as f: @@ -544,7 +542,7 @@ def test_write_design_space_as_json(): def test_get_design_space_from_json(): - # Tests generating AutoCatDesignSpace from a json + # Tests generating DesignSpace from a json sub1 = generate_surface_structures(["Au"], facets={"Au": ["100"]})["Au"]["fcc100"][ "structure" ] @@ -557,13 +555,11 @@ def test_get_design_space_from_json(): structs = [sub1, sub2, sub3] labels = np.array([30.0, 900.0, np.nan]) with tempfile.TemporaryDirectory() as _tmp_dir: - acds = AutoCatDesignSpace( - design_space_structures=structs, design_space_labels=labels, - ) + acds = DesignSpace(design_space_structures=structs, design_space_labels=labels,) acds.write_json("testing.json", write_location=_tmp_dir) tmp_json_dir = os.path.join(_tmp_dir, "testing.json") - acds_from_json = AutoCatDesignSpace.from_json(tmp_json_dir) + acds_from_json = DesignSpace.from_json(tmp_json_dir) assert acds_from_json.design_space_structures == structs assert np.array_equal( acds_from_json.design_space_labels, labels, equal_nan=True @@ -589,7 +585,7 @@ def test_simulated_sequential_histories(): sub2, ] ds_labels = np.array([0.0, 1.0, 2.0, 3.0, 4.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) candidate_selection_kwargs = { "target_min": 0.9, "target_max": 2.1, @@ -634,7 +630,7 @@ def test_simulated_sequential_batch_added(): num_loops = 2 ds_structs = [base_struct1, base_struct2, sub1, sub2] ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) sl = simulated_sequential_learning( full_design_space=acds, predictor_kwargs=predictor_kwargs, @@ -662,7 +658,7 @@ def test_simulated_sequential_num_loops(): candidate_selection_kwargs = {"num_candidates_to_pick": 3, "aq": "Random"} ds_structs = [base_struct1, base_struct2, sub1, sub2] ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) # Test default number of loops sl = simulated_sequential_learning( full_design_space=acds, @@ -674,7 +670,7 @@ def test_simulated_sequential_num_loops(): assert sl.iteration_count == 2 # Test catches maximum number of loops - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): sl = simulated_sequential_learning( full_design_space=acds, predictor_kwargs=predictor_kwargs, @@ -686,7 +682,7 @@ def test_simulated_sequential_num_loops(): # Test with default num loops and default num candidates ds_structs = [base_struct1, base_struct2, sub2] ds_labels = np.array([5.0, 6.0, 7.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) candidate_selection_kwargs.update({"num_candidates_to_pick": 1}) sl = simulated_sequential_learning( @@ -715,7 +711,7 @@ def test_simulated_sequential_write_to_disk(): candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} ds_structs = [base_struct1, base_struct2, base_struct3] ds_labels = np.array([0, 1, 2]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) sl = simulated_sequential_learning( full_design_space=acds, init_training_size=2, @@ -727,7 +723,7 @@ def test_simulated_sequential_write_to_disk(): ) # check data written as json json_path = os.path.join(_tmp_dir, "acsl.json") - sl_written = AutoCatSequentialLearner.from_json(json_path) + sl_written = SequentialLearner.from_json(json_path) assert sl.iteration_count == sl_written.iteration_count assert np.array_equal(sl.predictions_history, sl_written.predictions_history) assert np.array_equal( @@ -764,9 +760,9 @@ def test_simulated_sequential_learning_fully_explored(): predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, base_struct2, sub2] ds_labels = np.array([0.0, np.nan, 4.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) candidate_selection_kwargs = {"aq": "MU"} - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): sl = simulated_sequential_learning( full_design_space=acds, init_training_size=1, @@ -785,7 +781,7 @@ def test_multiple_sequential_learning_serial(): predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) candidate_selection_kwargs = {"aq": "MU"} runs_history = multiple_simulated_sequential_learning_runs( full_design_space=acds, @@ -796,7 +792,7 @@ def test_multiple_sequential_learning_serial(): init_training_size=1, ) assert len(runs_history) == 3 - assert isinstance(runs_history[0], AutoCatSequentialLearner) + assert isinstance(runs_history[0], SequentialLearner) assert len(runs_history[1].predictions_history) == 2 @@ -809,7 +805,7 @@ def test_multiple_sequential_learning_parallel(): predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) candidate_selection_kwargs = {"aq": "Random"} runs_history = multiple_simulated_sequential_learning_runs( full_design_space=acds, @@ -821,7 +817,7 @@ def test_multiple_sequential_learning_parallel(): init_training_size=1, ) assert len(runs_history) == 3 - assert isinstance(runs_history[2], AutoCatSequentialLearner) + assert isinstance(runs_history[2], SequentialLearner) assert len(runs_history[1].uncertainties_history) == 2 @@ -835,7 +831,7 @@ def test_multiple_sequential_learning_write_to_disk(): predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) - acds = AutoCatDesignSpace(ds_structs, ds_labels) + acds = DesignSpace(ds_structs, ds_labels) candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} runs_history = multiple_simulated_sequential_learning_runs( full_design_space=acds, @@ -852,7 +848,7 @@ def test_multiple_sequential_learning_write_to_disk(): # check data history in each run for i in range(3): - written_run = AutoCatSequentialLearner.from_json( + written_run = SequentialLearner.from_json( os.path.join(_tmp_dir, f"test_multi_{i}.json") ) written_ds = written_run.design_space @@ -892,21 +888,21 @@ def test_choose_next_candidate_input_minimums(): unc = np.random.rand(5) pred = np.random.rand(5) - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): choose_next_candidate() - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): choose_next_candidate(unc=unc, pred=pred, num_candidates_to_pick=2, aq="Random") - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): choose_next_candidate( labels=labels, pred=pred, num_candidates_to_pick=2, aq="MU" ) - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): choose_next_candidate(pred=pred, num_candidates_to_pick=2, aq="MLI") - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): choose_next_candidate(unc=unc, num_candidates_to_pick=2, aq="MLI") @@ -986,7 +982,7 @@ def test_get_overlap_score(): norm = stats.norm(loc=mean, scale=std) # checks that at least target min or max is provided - with pytest.raises(AutoCatSequentialLearningError): + with pytest.raises(SequentialLearnerError): get_overlap_score(mean, std) # test default min From f27a43deecfe505c8bc216b3b4d696ec8546e340 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 1 Dec 2021 18:55:25 -0500 Subject: [PATCH 154/239] sphinx to mkdocs --- docs/API/Learning/featurizers.md | 1 + docs/API/Learning/predictors.md | 1 + docs/API/Learning/sequential.md | 1 + docs/API/Structure_Generation/adsorption.md | 1 + docs/API/Structure_Generation/saa.md | 3 ++ docs/API/Structure_Generation/surface.md | 1 + docs/Makefile | 20 ------- docs/img/autocat_logo.png | Bin 0 -> 111373 bytes docs/index.md | 17 ++++++ docs/make.bat | 35 ------------- docs/src/conf.py | 55 -------------------- docs/src/index.rst | 22 -------- docs/src/module_reference/adsorption.rst | 10 ---- docs/src/module_reference/bulk.rst | 10 ---- docs/src/module_reference/index.rst | 14 ----- docs/src/module_reference/mpea.rst | 10 ---- docs/src/module_reference/saa.rst | 10 ---- docs/src/module_reference/surface.rst | 10 ---- mkdocs.yml | 36 +++++++++++++ 19 files changed, 61 insertions(+), 196 deletions(-) create mode 100644 docs/API/Learning/featurizers.md create mode 100644 docs/API/Learning/predictors.md create mode 100644 docs/API/Learning/sequential.md create mode 100644 docs/API/Structure_Generation/adsorption.md create mode 100644 docs/API/Structure_Generation/saa.md create mode 100644 docs/API/Structure_Generation/surface.md delete mode 100644 docs/Makefile create mode 100644 docs/img/autocat_logo.png create mode 100644 docs/index.md delete mode 100644 docs/make.bat delete mode 100644 docs/src/conf.py delete mode 100644 docs/src/index.rst delete mode 100644 docs/src/module_reference/adsorption.rst delete mode 100644 docs/src/module_reference/bulk.rst delete mode 100644 docs/src/module_reference/index.rst delete mode 100644 docs/src/module_reference/mpea.rst delete mode 100644 docs/src/module_reference/saa.rst delete mode 100644 docs/src/module_reference/surface.rst create mode 100644 mkdocs.yml diff --git a/docs/API/Learning/featurizers.md b/docs/API/Learning/featurizers.md new file mode 100644 index 00000000..e0cf1037 --- /dev/null +++ b/docs/API/Learning/featurizers.md @@ -0,0 +1 @@ +::: autocat.learning.featurizers diff --git a/docs/API/Learning/predictors.md b/docs/API/Learning/predictors.md new file mode 100644 index 00000000..2ce437cc --- /dev/null +++ b/docs/API/Learning/predictors.md @@ -0,0 +1 @@ +::: autocat.learning.predictors diff --git a/docs/API/Learning/sequential.md b/docs/API/Learning/sequential.md new file mode 100644 index 00000000..ca81144c --- /dev/null +++ b/docs/API/Learning/sequential.md @@ -0,0 +1 @@ +::: autocat.learning.sequential diff --git a/docs/API/Structure_Generation/adsorption.md b/docs/API/Structure_Generation/adsorption.md new file mode 100644 index 00000000..daca8e34 --- /dev/null +++ b/docs/API/Structure_Generation/adsorption.md @@ -0,0 +1 @@ +::: autocat.adsorption diff --git a/docs/API/Structure_Generation/saa.md b/docs/API/Structure_Generation/saa.md new file mode 100644 index 00000000..f67a49c6 --- /dev/null +++ b/docs/API/Structure_Generation/saa.md @@ -0,0 +1,3 @@ +# Single Atom Alloys + +::: autocat.saa diff --git a/docs/API/Structure_Generation/surface.md b/docs/API/Structure_Generation/surface.md new file mode 100644 index 00000000..d826fc1f --- /dev/null +++ b/docs/API/Structure_Generation/surface.md @@ -0,0 +1 @@ +::: autocat.surface diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index c7c06965..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = src -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/img/autocat_logo.png b/docs/img/autocat_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..131058a8b51c9ac80c406745cc7a4030fbb03dea GIT binary patch literal 111373 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 OZMfWYS+<_*-v0+Ww<&@E literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..21564add --- /dev/null +++ b/docs/index.md @@ -0,0 +1,17 @@ +# AutoCat Documentation + +For full documentation visit [mkdocs.org](https://www.mkdocs.org). + +## Commands + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +## Project layout + + mkdocs.yml # The configuration file. + docs/ + index.md # The documentation homepage. + ... # Other markdown pages, images and other files. diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index c3592594..00000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=src -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/src/conf.py b/docs/src/conf.py deleted file mode 100644 index a182ecc6..00000000 --- a/docs/src/conf.py +++ /dev/null @@ -1,55 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys - -sys.path.insert(0, os.path.abspath("../src/autocat")) - - -# -- Project information ----------------------------------------------------- - -project = "autocat" -copyright = "2020, Lance Kavalsky" -author = "Lance Kavalsky" - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "bizstyle" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] diff --git a/docs/src/index.rst b/docs/src/index.rst deleted file mode 100644 index b5c77c5f..00000000 --- a/docs/src/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. autocat documentation master file, created by - sphinx-quickstart on Tue Nov 24 18:52:19 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to autocat's documentation! -=================================== - -Tools for automated structure generation of catalyst systems. - -.. toctree:: - :maxdepth: 2 - :hidden: - - module_reference/index - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/src/module_reference/adsorption.rst b/docs/src/module_reference/adsorption.rst deleted file mode 100644 index 09813f40..00000000 --- a/docs/src/module_reference/adsorption.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-adsorption: - -autocat.adsorption -++++++++++++++++++ - -Tools for automating adsorption on a given surface. - -.. automodule:: autocat.adsorption - :members: - :undoc-members: diff --git a/docs/src/module_reference/bulk.rst b/docs/src/module_reference/bulk.rst deleted file mode 100644 index dcdf8ef3..00000000 --- a/docs/src/module_reference/bulk.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-bulk: - -autocat.bulk -++++++++++++ - -Tools for automating the generation of bulk mono-elemental systems - -.. automodule:: autocat.bulk - :members: - :undoc-members: diff --git a/docs/src/module_reference/index.rst b/docs/src/module_reference/index.rst deleted file mode 100644 index 7a6ab439..00000000 --- a/docs/src/module_reference/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _sec-module-reference: - -Module reference -++++++++++++++++ - -.. toctree:: - :maxdepth: 3 - :caption: Contents - - bulk - surface - saa - mpea - adsorption diff --git a/docs/src/module_reference/mpea.rst b/docs/src/module_reference/mpea.rst deleted file mode 100644 index 47e571f4..00000000 --- a/docs/src/module_reference/mpea.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-mpea: - -autocat.mpea -++++++++++++ - -Tools for automating the generation of Multi-Principal Element Alloys (a.k.a high-entropy alloys) - -.. automodule:: autocat.mpea - :members: - :undoc-members: diff --git a/docs/src/module_reference/saa.rst b/docs/src/module_reference/saa.rst deleted file mode 100644 index a1b0396f..00000000 --- a/docs/src/module_reference/saa.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-saa: - -autocat.saa -+++++++++++ - -Tools for generating single-atom alloy structures - -.. automodule:: autocat.saa - :members: - :undoc-members: diff --git a/docs/src/module_reference/surface.rst b/docs/src/module_reference/surface.rst deleted file mode 100644 index c5963b52..00000000 --- a/docs/src/module_reference/surface.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _sec-surface: - -autocat.surface -+++++++++++++++ - -Tools for automatically generating mono-elemental slabs - -.. automodule:: autocat.surface - :members: - :undoc-members: diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..843b8d31 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,36 @@ +site_name: AutoCat Documentation +theme: + name: material + features: + - navigation.tabs + - navigation.tabs.sticky + - navigation.sections + - navigation.top + - toc.integrate + logo: img/autocat_logo.png +markdown_extensions: + - pymdownx.highlight + +plugins: + - search + - mkdocstrings: + default_handler: python + handlers: + python: + selection: + docstring_style: "numpy" + rendering: + show_source: true + +nav: + - HOME: + - index.md + - API: + - Structure Generation: + - API/Structure_Generation/surface.md + - API/Structure_Generation/saa.md + - API/Structure_Generation/adsorption.md + - Learning: + - API/Learning/featurizers.md + - API/Learning/predictors.md + - API/Learning/sequential.md From 8536ae34270dfc23971ace75de20e0134b266ce8 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 2 Dec 2021 17:20:09 -0500 Subject: [PATCH 155/239] update icon, start adding user guide --- docs/API/Structure_Generation/bulk.md | 1 + docs/README.md | 38 ++++++++++ docs/User_Guide/Learning/featurizers.md | 0 docs/User_Guide/Learning/predictors.md | 0 docs/User_Guide/Learning/sequential.md | 0 .../Structure_Generation/adsorption.md | 0 docs/User_Guide/Structure_Generation/bulk.md | 43 ++++++++++++ docs/User_Guide/Structure_Generation/saa.md | 0 .../Structure_Generation/surface.md | 65 ++++++++++++++++++ docs/img/autocat_figure.png | Bin 0 -> 461463 bytes docs/img/autocat_icon.png | Bin 0 -> 7250 bytes docs/index.md | 17 ----- docs/javascripts/mathjax.js | 17 +++++ mkdocs.yml | 37 +++++++--- 14 files changed, 192 insertions(+), 26 deletions(-) create mode 100644 docs/API/Structure_Generation/bulk.md create mode 100644 docs/README.md create mode 100644 docs/User_Guide/Learning/featurizers.md create mode 100644 docs/User_Guide/Learning/predictors.md create mode 100644 docs/User_Guide/Learning/sequential.md create mode 100644 docs/User_Guide/Structure_Generation/adsorption.md create mode 100644 docs/User_Guide/Structure_Generation/bulk.md create mode 100644 docs/User_Guide/Structure_Generation/saa.md create mode 100644 docs/User_Guide/Structure_Generation/surface.md create mode 100644 docs/img/autocat_figure.png create mode 100644 docs/img/autocat_icon.png delete mode 100644 docs/index.md create mode 100644 docs/javascripts/mathjax.js diff --git a/docs/API/Structure_Generation/bulk.md b/docs/API/Structure_Generation/bulk.md new file mode 100644 index 00000000..80e0ed2b --- /dev/null +++ b/docs/API/Structure_Generation/bulk.md @@ -0,0 +1 @@ +::: autocat.bulk diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..7b781b6a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,38 @@ +# Overview + +![autocatfigure](img/autocat_figure.png) + +AutoCat is a suite of python tools for automated structure generation +and sequential learning with arbitrary candidate metrics for materials +applications. + +Development of this package stems from [ACED](https://www.cmu.edu/aced/), as part of the +ARPA-E DIFFERENTIATE program. + + +## Installation + +There are two options for installation, either via `pip` or from the repo directly. + +### `pip` (recommended) + +If you are planning on strictly using AutoCat rather than contributing to development, + we recommend using `pip` within a virtual environment (e.g. `conda`). This can be done +as follows: + +``` +pip install autocat +``` + +### Github (for developers) + +Alternatively, if you would like to contribute to the development of this software, +AutoCat can be installed via a clone from Github. First, you'll need to clone the +github repo to your local machine (or wherever you'd like to use AutoCat) using +`git clone`. Once the repo has been cloned, you can install AutoCat as an editable +package by changing into the created directory (the one with `setup.py`) and installing +via: +``` +pip install -e . +``` + diff --git a/docs/User_Guide/Learning/featurizers.md b/docs/User_Guide/Learning/featurizers.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Learning/predictors.md b/docs/User_Guide/Learning/predictors.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Learning/sequential.md b/docs/User_Guide/Learning/sequential.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Structure_Generation/adsorption.md b/docs/User_Guide/Structure_Generation/adsorption.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Structure_Generation/bulk.md b/docs/User_Guide/Structure_Generation/bulk.md new file mode 100644 index 00000000..4022a951 --- /dev/null +++ b/docs/User_Guide/Structure_Generation/bulk.md @@ -0,0 +1,43 @@ +`autocat.bulk` provides tools to automatically generate mono-element +bulk structures. These are structures containing only a single +chemical species with no vacuum and 3D periodicity. + +Multiple of these systems can be generated and written to +disk via a single call of `generate_bulk_structures`. + +``` py +>>> from autocat.bulk import generate_bulk_structures +>>> bulk_dict = generate_bulk_structures(["Pt", "Fe", "Ru"], write_to_disk=True) +Pt_bulk_fcc structure written to ./Pt_bulk_fcc/input.traj +Fe_bulk_bcc structure written to ./Fe_bulk_bcc/input.traj +Ru_bulk_hcp structure written to ./Ru_bulk_hcp/input.traj +>>> bulk_dict +{'Pt': {'crystal_structure': Atoms(symbols='Pt', pbc=True, ...), + 'traj_file_path': './Pt_bulk_fcc/input.traj'}, + 'Fe': {'crystal_structure': Atoms(symbols='Fe', pbc=True, initial_magmoms=..., ...), + 'traj_file_path': './Fe_bulk_bcc/input.traj'}, + 'Ru': {'crystal_structure': Atoms(symbols='Ru2', pbc=True, ...), + 'traj_file_path': './Ru_bulk_hcp/input.traj'}} +``` + +In general the following structure of the resulting dict is generated: + +`{SPECIES_bulk_BRAVAISLATTICE: {"crystal_structure": Atoms, "traj_file_path": TRAJFILEPATH}}` + +If writing to disk structures to disk via +`#!python write_to_disk=True`, +then the following directory structure then a similar organization is maintained on the disk: + +``` +. +├── Fe_bulk_bcc +│   └── input.traj +├── Pt_bulk_fcc +│   └── input.traj +├── Ru_bulk_hcp +│   └── input.traj +``` +where each `input.traj` contains the bulk structure. + +**N.B.** by default initial magnetic moments will be set for Fe, Co, and Ni, otherwise no spin +will be given diff --git a/docs/User_Guide/Structure_Generation/saa.md b/docs/User_Guide/Structure_Generation/saa.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Structure_Generation/surface.md b/docs/User_Guide/Structure_Generation/surface.md new file mode 100644 index 00000000..5f5304b5 --- /dev/null +++ b/docs/User_Guide/Structure_Generation/surface.md @@ -0,0 +1,65 @@ +It is crucial for many heterogeneous catalysis studies to be +able to model a catalyst surface where the desired reaction +can take place. `autocat.surface` provides tools for generating +low miller index surfaces for mono-element surfaces with a vacuum +in the $z$-direction. + +The core function of this module is `generate_surface_structures` +where multiple slabs can be generated at once. + +```py +>>> from autocat.surface import generate_surface_structures +>>> surf_dict = generate_surface_structures( +... ["Li", "Cu"], +... facets={"Li": ["110"]}, +... supercell_dim=[5, 5, 4], +... n_fixed_layers=2, +... default_lat_param_lib="beefvdw_fd", +... write_to_disk=True, +) +Li_bcc110 structure written to ./Li/bcc110/substrate/input.traj +Cu_fcc100 structure written to ./Cu/fcc100/substrate/input.traj +Cu_fcc111 structure written to ./Cu/fcc111/substrate/input.traj +Cu_fcc110 structure written to ./Cu/fcc110/substrate/input.traj +>>> surf_dict +{'Li': {'bcc110': {'structure': Atoms(symbols='Li100', pbc=[True, True, False], ...), + 'traj_file_path': './Li/bcc110/substrate/input.traj'}}, + 'Cu': {'fcc100': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'traj_file_path': './Cu/fcc100/substrate/input.traj'}, + 'fcc111': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'traj_file_path': './Cu/fcc111/substrate/input.traj'}, + 'fcc110': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'traj_file_path': './Cu/fcc110/substrate/input.traj'}}} +``` +Here we generated surface slabs for Cu and Li under the following conditions: + +- for Li we only need the 110 facet +- generate all default facets for Cu + * fcc/bcc: ["100", "110", "111"] + * hcp: ["0001"] +- the supercell dimensions of the slabs are 5 $\times$ 5 $\times$ 4 +- the bottom 2 layers are held fixed +- for structures where the lattice parameter is not explicitly specified, +their default values are pulled from the `ase.data.lattice_parameters` +library that used a `BEEF-vdW` XC and finite difference basis set + +When using the `write_to_disk` functionality the structures +will be written into the following directory structure: + +``` +. +├── Cu +│   ├── fcc100 +│   │   └── substrate +│   │   └── input.traj +│   ├── fcc110 +│   │   └── substrate +│   │   └── input.traj +│   └── fcc111 +│   └── substrate +│   └── input.traj +├── Li +│   └── bcc110 +│   └── substrate +│   └── input.traj +``` diff --git a/docs/img/autocat_figure.png b/docs/img/autocat_figure.png new file mode 100644 index 0000000000000000000000000000000000000000..91bb4e90cfa52b49af383d1eda12e84c29a6b445 GIT binary patch literal 461463 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%0A<(7Ta#wCTJJ>w;A`Faf zxcy*?l>}Ebml$;+CER)pk(vH#76Wb8fp~bKxS(IF=ocFYaf*M5GUSMs6vmifz6OLI z7^)bnd-|@_Xn$sL1-hLnr-@LSdzHA?qp$-32*x7FGT7f;f5|X0MxLz>B50+X(-Xf! zJ{jsu`HaHyOt|b{%y*}|QwmSHwE}4B?kP9fq4Lmz$ z6tmjgc?HTP)%5bip8(Sc50MYN7&B6DLka!`uzcw5Ea{acin7p&54LI25Tq9F{AQ#o zk)%JKrQ%%wXBNiO_gh);#$nCo4QUHSam?;)VFq?`A3+Oo!4N3CU{pqL^2fo#G;%Vm zw(`RP3I;Ke!ZjmbK3_kJCB_h2j_?oGd@!4%?Z7+H+v!L6fS}=_TEoVionu^FoV*9$ z0dch`?3i-DWj&q3_0^P(F5mp^N9ttxoXP(99|Lq)s3GSnX%w_no+ZcTdlTNrPmu2vVvA@&|ssR zWkzD+Q^~b%L?~&+L4hfaA8!xyLYPex#3906)cAld*|!I6`QtPw0+yF^`+9HcbKG&|b!{cuGBWN7 zyEMNk2$LM8veq2q%Ugq^gS(1(UTUCizIhHUs0|nQ+9S4npu=r4x#uYfpr|Tyo~1D{ zyO3b?KYK`IJ4I|eg&H)wD1!%i(xeU3zFBXbm!GQY(!y6D+mddV{_SZQ>$5`y&hIO# z&6-ByCHI2$oDm(TdSv7}bOEq{`vF}q{6Vw$^o);}duCieZpJ@$@)MSlAV^9!jgR4K zAvp{7Mg$vJcrxXE96*yLtj}`R;bU#V_O$QKh3`G6s~Q_2R$^$evHI?*LoABoVlSc> zXVl8|M=9H9$6tokeR#@X#!+?yWs`-6mBpG5li^C&{+bhaF!BO$>!qVeva3$UzYj(8DxHJD2Txum!RKe>iI0HKdfeCSU{cD z^N%&BM~>pii5;HY0~#Q*zBL3oKfdY(y!}c$BPTOIQbKTRrR`I}gj0-Iobt-)z32b@ zM}1YX_-y^5yz{$0r8}Y70`v=O{YOof@J~L4HzQX)!$p7?<(=6#5R#N3xWpxPKX!!C zgN(Ea7wTJjwd)&wIAEc0SnQECZf_RkY(JaWqOZ}h*{6ANV+Y`0mdth8V#XqjqaHC< ztaMn#@vFL|h+8*M+aq`Ful{YYi6`H*LpH3DO+6FY9)||I`9rld3zfI5^-VXOSN06f(;7#o4)a!YPRy< zUI0&1L0qAZ*m{+sAzLde821W0xAh;1U(4Uigds7h`QGw^7fFdZ;UCSB@R0`-4OgmN z%TL$s6;?x>CEH;Y$4T8@9;CPfcg*_5Y}9??b076#+Szp1w7SmuVRj6UVa`a)5i4@_ z=6<>~U9WKYJ^x4@i!V5E$SP}ZTGNG8(rH@R#22SRl!3_Kx3+EZ@!7K1!cf<~E_T63IqoA9RCGh6`Ofm{ znSBL=p+9{#MO|R6^tBpn#R}pd$p)%Hl^ZDa+NOuxuqWi}GrMrOW@!VHFQD}$qeW893C-{Co0iu);T!}H7I-BpEL zMSB|b&&91_R6Rt4>ccUYVD&&#g2*bAa9eD;6uwn6tSj=@UmEaPFOaHmzHl1mhxkz| zP5#SKv2;PH{YrkGE36W?oNBPE>eZK4sUr!-?Q(kl?g{u)OPDHnCKnpz4-}~*xU@e~ z4GM|}ri?3ujecgFDs@%HSp=G$jWMDsW(#L&ai5I|7j%}i<+o~YoIlA zyVHK;yO z>RUDX=>qj~zIFkc8l~6fXEfRSG6a}fYTD8ecJVT(p4_Q~F5PhCR#LgJgx#>D{H)R_`(7aC-bbD^5l$K6$3FvK$$MVuv5#Fbwaxxdc9s>sRAs4 zpJ@ZQj_}o|PPF+oJq#qT>D)flFV#u(zT#3LpNL6Y)=<9ZR7o*3v|dIg;kPIj7vAK< z{SI$Jpusz2m+~PYwMV?&C{Z?i2`e4~verBANt4d$Tgr4-=y%(vu0~Wh9=<)$tTDCBIA_7``bT=ti?Ri6|j2b)Z!_0GB5a1 zEZR>((IEtNiB3==cs(2JF@t}=Ix7CWlNec9IDz9)eE>B0=Pn!k+D67>D=k6h`j_~^ zV0BTbJa#CqYL%k8jrJORC&fbW*&wcwCby*=NO9jKQz_D zE*_I0`*QF>Ww)L_vIWtb7qi$)D2(`Q;5@OBJ?1nOyvXPiwM~Ri$t!C4jWKg=)@}EH zc?^(wz>&#N*>uhLFO<|?%N0Iu;rY)}#icQ#&^f~Duq-q4)Iw3kTub?>>UBX>A65!C z@>z46>Kldy4`dCMa#S#0?%g|^3ZkR#G&ss+N#eR4HXP0uhvwvwa{-E-hA5?()gu$Z z+ifTW)$pcjD5M@0i*e$PIwf}N7RkW9D!fNDfze-n)AOC3xhQ=B{b3E0EMHoc34VaZ zs1in*VDS0WJ8X15%O3#&2|f|hae8A{kjmxb4^pW{H@l(W=%3w8QDvL+84>k}FNV(N zS4OmPBe{4Bh^E%6kMXj-y?xe~V21jHZ!JJn=a%N#)_&E-XIu%72CdLGnmsfZ|DiPU zSUzEwz|4qt9F$7s(ULKpjvGHdlvbF+9Zj#}`%@h4_bX7kwmj>w2n&)5ry$23Or3>d zu^J^L98*)g&q@|<_Mzmf>i1f=>9ngy{o5F`K0itRf&>Er2O#{q2sT5sz~!2WNxtI*X~(j8z;;@J5|OMjBA_z#TvhI}oFKw#ZXLD&(Nd-$of+J6|bM%7#vyHv^n zbECIMv}>O}u)0AN3_CPED;HG+ADr)xZ?N{Ja|Rdhc7K&qwKy`dZqblNJc>P*h!c8a zKL+axdyD1i7F0%QojJf;xWt7QAih2W8`6D#6`6sKTEo;Ri^yZdfUiZ^J z&R%0mY;ot|sSZWaz@<8f7l)cER=*xpbRrYir!uJA2C$>aL%24_7FhR)fM+wc z>6H`GPdXAnRrj=KJU*gtpWKH4nHk?>15ZxqDIBlnTeo9@s-IA;AY*@yzR_SgqR{C7 zM(Szrv`Tg8lSL5&9zKn>1!|+(Zz0Qi{86q#8!4m_iZVJ6(jLKT%*s9Tj!n!IhM1ns z7goo$-|P`tMEL$V1!OX4!Shrq`nhjp_L^WyTL!2aJ4WNU;xn;arRKifd_2@TS)gk5 zEoQh!%8(rO^|W3N^f=1uoSw7sD-M^@tBXg9s2sb#UvhekFgHQ1l`T5O50f#uehHF2~xC(_5#p2pBXadSEGvu4POY-OL2iaWlIZfDo zkAI?dz1t<-gPbmAMpu8^?liA^POjDM^r|B`>bX3VG2fm4gD7>96;&Z6`Wbfud5w>Q zUmee8peMnZL$&ssH1FTR_YK^xS8Bw9ubIayhwW{+EwY3eeHX|}Z4ov;_4s+OgBOVn zk#S!pM7o%0@G*h28I^i5riMA z6MhP-DgD=Ek5jbNb;>#8I356;mx`Lobv|iDxV7*^SdiGIio;s!(1FzNlCRC5WkbGe zt@ zJN0_w>Tap$M~jSl&xvvcTeiuFodPE?J<9p2WCslr=2MJt_20W$^3fhgM<^r((dEr~ zYNu$~+&VgIlk zgxoX8^bI0+TmDI)H<14zcTutOMX@Mg&X3DsP%%KEEy~5o@>lS08$legAqDNA@-vT+ z_O7pYNPiM;iB3?B)Vymh|Cd4Czx(#znZOyH9|Di4~b&(b=HW$bNx>Qi0QW!a-JoO|Q{Ww}rw#`qYKJ%VLo984F7Wb>m+uHmZw0 zB2q`p3jZa;a^PD`XD8jNND)@PdEX=h_bl=tTPqW6T2A)NgFu23Hd&i9{UX-rQxIU2{NLyj9W*#vW%b@0~NuhM{otiA|u6ho#Ad>`29$rFb(9ZJml z0Ui|oX&_>yf;8%`;r$@PCc?u4ykoF z8>=JCqm$QF@8l-5`(KxeD5@t|923-AeKdMQx4fttA1|$%u+^PZ?eY3b-@@1&YI8<# zz7W6d84S0dD819$1q4Qo{nE35ZdJ2H3O|NfaM5KOzLav2_?I4A%Ib5pwl#X`%(>OB zG=d*y?zb9ET5FmF@7q==n;dEkCOAyP$lo@f0tE)mgVPx*~vtao&;oGX)k!{YnO z)RhTKHU$;Al?;=aXPe?sas-Sc6fq|&ew5`P8PMn7PVgJLLf?WeI!Wh@r=eVFagcNj zK8b0IAv{gl)b^HDn9ljan^|q3PX?@#>yOnY)!v5J&0RQ~#S40#CcU+P@||1%i=bvInD$)hA_1L% zcr-oWeQCYYrpwYa7Wx-o^+dhRpJ>Nvob;Gqr@ZFX@_u!b=XcP|xGlnDiufT<+GTZb z`oFvJvsLwG8uVBjJ;7YM`M)i$>tGhT=!&+llwa2y=s6@sdn#0nMY(Z0*^R7|kt}uE z^jO;+*n}#oS*@>9sMJZURT^wCQL$4XF+RHuzM75s%fHsmu-LkmOWpo z>?tIZPQ0WWUtQ79DDI4F806?w658I4e~0rw)_>JX>#ni|w&BSUh4q2#wjTGuJ79xb zm%%WKk!6)8bcu9y%#DoJmfb+bKn&mj_m57V-+?u^u&#CK&;pMhE=*tT4^LsaelW(k zf0F?JafUQL!_c;pP=%mSro*+b8aMT+>3MaUi25u6QRr?366UcdTn3GP$FSsk{N7+y z2z9n)FNP0<;gAVYCC>L5q+`ohkft3C!BsWX3st(cTl%U4xEx4}@Soe2s&aWxjY$i` z3t~X1DZu(D>H5p|cQ)O-0?I{vd%HAcsiE|tt6dqV`Jx9hp9@dGZn zE~HB7d2UPvz7H>iqQM=9dy~y*Y0kN`g(;Z(lBwn%?a|alV%Wc=mWvm;Y8lf~I&Kz* zg|7a=2G1wdMu?`j`i+vG8Hkzc1y&BdHf6m)eW(zaPl95clJHcCY@ePFVeeh9E+Cu6 zlr=K`?wM1?!8H{ab#k1H7fLyp>@JYmJf%R42`B0FjZNSucgfKNF=T+HY-Yv$l;j#n zQ=0mL3-{LfNS|-Z9V(n!qKmjwWX^Qmy^cCG^X5^;B}d=YrLqmSW-TtZj$2TviUQqk zBe{;&6?6G%UbfnNH>%UJikAi0LXY4(vm&I5_GtENB^Ld@N!?y;MpDJ0mLVlX4(B)M z=ygY)` z&c>dF2E5P2_%64om``i4UcQ8jj!Hf9XH#enOJ29VVm8yg1yk)c#CSW=P8!J$N5As ziv;u&8>aa5kjP~MGfvJpuib>vfS93Ng)XN*Hpr4hk`W7kG#K({;zxHw+DAMD;5&8{`n z(DKmoS~B*vboA`@z2C_8xynhUzs?cx;kfwZaHjW5Y* zmwINS-2Ilfm|=uKsll%3l1G@vIYlU)>hxlvn@d!d^na*&%c!`bW?K{|IE3KtE&&1! zH16&e+#$F$E+M!F4;~zXTNB*f-QB%u+;6^f?|J9E-+Qb*)~K=9+_h#^&3S}C**#`S zop3N2i0!ZhjJ@za7A*5VIaW-YxN1)%Tag;iW!`iN+_RkXHN=nwj5Qa2^1_LOS=u0={yG!B$~#!lef zekmZE=Rb+Y$x1XwzAxe?ov8|_a|m=xxSsCn9wN2V7$l1PQy^jZ>73p8p|j~6t4$V; zo*3hChDn3^7}Y_m*rD^xyd$NmTi5s3QM(&pWNo7|;1zwHE;x>Qw8K_On8bFkSqthP zkl;eF(cr^HAh4B_-@Z3~K770t3o*?4T79Zl&lIm&6 zc|J}G7w6mTNiS@limh069+EsI45wDCh{%Fj3yI~YR#fL|^A zfpo_Yf3oCU*rSE17?!l4%fw-8Pe4o>oAM8663Y%$<%&NRIkCq}VBh|RIVmDhjdMr( zt;Vs8Deo9mzApRb7WEARmG`8F%o$(*3IF%%T_-gOn?gCsFllJ~TXe4)$lRevzdjDo zO&mI(M#eD|D1;PIRjT>_N;xISB~~Z_p!J_K3KaNVlmy#JZ2Q~ILPX(ZroANPA5o80 zs!T*@4RXSF{!|Q<3A5^y8m8QFI?)7%^yWs@`*m|MkV}ioRqulmAHxKiBp+Iieiqzu zkreS6qGj=BH^l}< z25lLf5DSsw?sw}15-?0%mw9hFr+$8ld^t6P&U>C|8t4y_?lT0u?ee@GzK1d5JW;#* zPzE5EUj>Uim=eFe1|}j+0x{0*WqOF_>G_Gm>hP7%Mm}rvceqP9O+0_6H+@M^Es#SZ zZX*{abpp6nWvTGJ<-UKRRtuTh`?(i%*qI(?^yES8^LV?YO|mOvH|8TM)au2$5fFoU zHI|tIxVIueT&-7KsMN_x;8uLU#xSHX?8x4D-D6I`1>g_a8P`^?+=GZ$?+2Z!!|S{J z^Gr;Wdle2NeiS-&NV$(nQRBxl`kd~LZtp?Qcm{)=ggm+OWwT^`C#Ef-MEowqgB1=Z zu=02NIyv~-+^>mEt%fusP~KI4cU~_e1rhW9^jbC2p%S=`!(+e&V%?*PU?^4~2Dvw> zIT}R~ve9$E*M2pg*If~%+0Hi+J2t&{WLJ`;_Ya22WV-4f?S1$MN9n{6F~NoEg~z?c^y0# z=*4;;UCLtZebxz(v`EDfeFa2pn66$S6F5Tr`3?V0;468Z{k0cdwIVx)Q64y6%CWjh zOyK0m5=vcV`6)KYnR>G4eV;Tc`PbmXVT6Eadc(NcDs`P{_GAwA4AP;D2d(8{$?KFu za=4zN*K2?M&?MmGQ~(;Gk%RbQ@cF2`Yo>P3X%q8E&j9&hA$CJ8RzH3`mknkwT$%G( zSJEtK*b!HXC_d)ziPEOu9W9Vd@lt$oUOx%vyg-6O!fka+9`Gw>UDOAj?8bnPy*|;LZCN4Q6Ju4BWG0$1Sj=gNB=B1l)D{MB=!Uq%r!vq zi!pN@Wr~;U=yw7L$@_s_sq9@{CF%_g&C$t*_8Y&X|>np#Hc9N)xtj{+~2@rWC zt<4nToCUQ7lm2#a?}av^wb32Yw0)`|w2Nr@as-K-yI(7pr19szCFjra@#5A5sF(KP zov`PV`*{0Kq7L|D;ofrId08Qdb4)A4+jra6MUf$sno z%EQC;pl2n+>7si&xz)b{G8V~6*sj)& zX|`pdO0EacbDvh_uGL9|2Ru^g)Qs_gaOO5!i#1NKO=$&3H>U76?m&(}pO`Ur6kGUCxNxKr|Pd!ipv zX4r`A{zx*QVcj|ZHoXBj%3j*_5`E#P7?!As?8p-VI6~F=@}mXJ4EVioL;+EI1vrTd zmAL$Zc%n#NIg?fhq0{tygUjU7Mr2X~wAktc7t&eV?l9IN37pbOCkAodRRV2Zd@rp| zpHtXk2LwO0Z@RRKyl^5Cfz7a9Z5PP>opz=WR zyAR+ge78UQyQX*4L~svB(3}iNq?a4)?a(^oU zqen_PR`rFI-Pq&vza^Jq#!(Gu8go1G{80{A9gg6#6fH4=WLCC0u6_|1lV}x;VC4RT z#M!K5>q)j+8xgct8fd?Yv|AMyd6q@VjX3uKY*`)o7nXztk*kei$OeXaZB>H41Ts&hi}nGzS;=h?Op%Sn@%kdN5A~-i;PW6l4z`W z4JJbzQeHL60^yf+`mA*aMq~iP5v6`3k26NRakmXYoOG3w)XZO=P%(5YGj+LDBB`2q z31y*%+2Jh@xtwF3uau~xkx^dF{Zbpz^#iruJnpfQ#3l;M*hyxaTjqid4A=XSgJ12- zTeo?6z=~d!W%{O1&KpOBqs~pKj^WAGummZBdWInfa$YXc4qn`n$wYc{y?fS##4_Jo zbLaQEPqv^?Bof=d+3Gi2Uq0n%}y zXX_neo#`KzQ{t5eQchv>t$gld!`hfLH!d%F#eUF~4HGFS*i&TY1%4<;9&{==skXc6 zlqyWG0Mxb$rET)$=7i#dVA0h{FC*@gTzEgUXZBm|2=qE zfQ&I`o6Qr}lR#RB&0k0IUfr`IaA*YLYGUgsny>Y4me*mZ<16Z_Ctyae$JVSgsFCo9 z`UIX*an%c0M!a?kXFuIiZN~|Ec3VaTPywF{Ud3hR0tV2UIQs8s!6JAvNsxd0#L(PB z)a`QjjjypH;!Dk&T^0C~F_dN9^yMK3M&~+~D~@bwP)~XxeC^Wm<2zm!|8e$1d)X!k+Wykm`}*Y2D>HFN)I7JFG_s zky$F*m@Ta;c53f){S#YIhLdLUd%6xk^->-AO1sSsH)C{xoFNc30IbrWHE(rdef#pk zN1b%8peMOnV+k!M9xO{@W#DSu7&}+KNkKwQ_iM ziEV59Cu9gCi1n1?TXYTgI~%n^p=k^R2xNZxfdx_dkT>}*%O46vm--&DzKXfKG!$)@ zZnIWHE~g1^pO8l~CaNARcJ)n!XCbjzEL+7|^f$`(pe-V`xw@A9(05L*FRVho5%07_ z+uxiJ<9Xeook_UEAKA=-Cj8CV^fi^i@gOU&oq;a^)aqp10S~4vPFU2J?)EQ1FdGH5 z3jN>@O>F$>A26K>vZ#N^yhYf^&4U>H!V>VRdB3VZMluT0^PFleo1GD0cV=IbV&tDy z&KTS4yk~Z?-cqiCfmg^>nRSx}(+sw`M|NW6E-*L9xlC9F9B5(!q5TwOsCJTeAvBSd zA{w19nGqHOP>#27cS!uZ)?6)sTiNlX^yyau`obkZ)s$dzl)_X$e20OZMUxbld2jgT zgGtv;U*#MbR1-%Brolh0gNdV}-n5W%Q!vejrER?EZ+Zxe>QQupskHr-OK`x*t2Xp&a~?%M{@#&7^Jzp< zTvdb-t%`kS1ErK7zP}f4TFAavLIHyL8f9yp)vM*%@Gc<`35-MbW9hmB$Pv_#c{zcED@r0lzGWi(&m0Vz1`R8i4 zM?I|IF-iI*(bsK-CDew%>02vsE`cuBrUz#r!E_4PG@3&nr#x`+|B)8|^+z=(!USeC zUh^kZ=UI&)x$24#GNd4;Q3ye*Cm0y4Kf@MQg!%CHkn7%TpnsZsyFt8lOo`M|bhB-H z#4Fw(Xo{Dwd;8UvSStiaO^*pY_3)L#&SHQ~JeKT5sbGRkr1{=85qg@!nE6$C;>8^@ z|6oQzJE3eM>~)!Z)_VJ$yH-orU8YD9U)~mA=~dYS_m=@LYp)FVFUwx2oQ^n8w(!4E}Bc67tME z#|WPrA5^(s7CO#pmYFgbS~RoNqN?MtU)JJ%zTUe%N94OYz7J&$ZN7aeS1uK=HTV;C z=nKgQHc~L%?*vV`q%a6ID+Z~$UjJq5*D&Fa{5KJHxn-MfaHapyT#c{m0JFlbkc$WJ za_jc~(IK>{nbk71pjYofJmz`rCZIUwG?D+}{FcJ&`@=n|Nu_z@y55M-rB7rtrnN^{FLmoXE2wB83sRuqsNr3NzXkOWK5Guh%e-(L zVook{wLX`Pes8=pwhoUh8Y!HV1@~@q(2qK%cUP#uDQs29F8m5x2XnxKmPHGsnz`f9 z7_meDw53a;!1mTg>3OW1kZztJa*O{wSAWR`fz1->6@TH9VCl-~GB!;J5 zgMwRT@$w@|JeXLzg@ErK^@Z-yfdVr6f7|H!oeFVpku2S6nG6H%RLK9;>~wGxIq zG+p=-?Xwgdxx{8D)iD3^R?kpdA`005Gy}J%k=~-lxH|nfG2l=C=_Mjm!_{G}lcOyD zhEtof@e$0T)Q_kiJ1wgZ;pku4w(oE1+DI2c92xKw1YUmSRpCT!@N+8Yd#y%ogFAg| zzI<)jV!4y8JK|Sn}6$EIE0t^b2kyxArv(I^98GU@i(3B z>?>$k6ar|yUIuW2d#~~?#bk*(r5GXGtw(6SvF3g+>^&{9e&M~%*i@v~j)|9w$A`)) zB=9{bYTNehK}M@i_2BrXw+;dkGVB09`9C&JPS1X=4HkPXmlT$T-|aY7F}Zh5*$eWW zKS)U?>eus!N0rhxc?&V3^t$H2TWTg%{w*`R{KQ|s@2S!Q))GX0?37yNZEH#E%oM}h zNcIb5*l(KJHPRX$cpZ0sna5h+!vF{m#hXfcpM=I&+9pUPAYiwnG zW&{Y!r{f#`JGC>~&<4xM(d^4-fQ0FvgzJG#dEuxS$yD%)Yk`P+tEKbJGq{78Fxlr% z%jLb+${&o(XUo7+>rj<++)!>T#GP8Mv?mHQS*^caiHrLyGUto;<%52_8nzh^4v%1Q zEO?eEt@E6ITlg8r?FOyap|txs#Grm0&F_x=)oUD^``+}{!E)UNY%lDV!k3qo?vVu( zs~GH;Rp-HBsLSeiE=S+G(X8+y?swE7_ur2tNFOd>cg&H)mJ<8H;%$LNDi?h+RIQDp z@TK>@HiF7@j`kx)Crj?vIDrV(Wep#X@pCDP<9wlxwj64$43n4-;6pU_v^f2Jr@#*Q zh$oN#xlEPvL-2OHa8G4d(1e|SrTD@)L~l3fNBBndI71Q8VO=p zNtl~nPVwqT4L5_5A)9Moq1mz6Iz%=BD<4N70>!3w1q+>an11Q}zk`R3&Q}xiH)RM8 zvGjKqbQWj6-oA_~?xvC5FPQenjXUCeHB_>@)y|8x3gsYY%>@zEwf>lZj)y=?7xYN{ zR_a;2&H!qoTRAQbNSgn>>Hiz% zkUNq|orP_nc&p@nhKZQPI~L6MCzl*y@Sly13%&qpvng&*rfJ!c3QG4=GAG#+%u&=)$@Uu!6xTZ!#0o*aB#?Q=ngiX5nEa3ODqIEcuhLZ}xYmVGgLD2ixw37xW zCYj4R?z(|2U zH6NPvec9=Cl8yJdqtQErgcII&pttT;Hk4$?r%E&p(b^4=4ltXB5~KE%&h=qn(!m3( z%7&Z-M}Xihc_3>yN~WrWgepA#W^foq99)&JYc*|ZN}ertqr+z|v50HC0Q{H^ z%G-oPHdCyiW|^-a(a!#c3H%Pv#;>;A?Dai%N2awx|Ii|v3;E2L6pf zg16n3Wa+{mJ zkVzop!Ivf?|Na^DiKy~=x8tPYbRH$mnGR{1N}J@lus4Y&_hV~<66@K`%@*8!V-Y{f ze}b?Pr{VmYLv3yqYk|C9k^)?>Ju;|!4suyzKl57aE~~$s4DUMrBFB-lFv!l1L;Tqs zzbU7{S6w`Cv^~mJKi1Ejvbq7B5gMlK}`3L{os-O z!xWO;V@zIo0`nPBWA0e=o1*QYjfYoGA5g16N5#FSLq=h_WW+=rr56hMI2&t`hB@-g zQr>IcQ_RL6jsJk6aR4SzEsj>_*3Jw-W^{Pc3<6IS|?dY$0trE=gdSLUJd&c?`yua8Q)wZxlRA8GI!#lBzIzVe}6K$ z+LQj1g%Kd`JxkL}s9lo=hkhjHIf?xGIv~lJwqIMc$=y#&eg9UsZ|YZ9vzMCH0Uc0bB<-CeD5KPCI^OeFbf0*F>DYkn^4BmVVlSiHAZl_4QFv}k{-+^OwZPr$=V zBx%jREbYT+Tw{@T-$7ESf9!ZtS5VF9npu2ozTw>lgHDgPyU&@)iNFfZqW9?eq6Y4P zFgP_;&$*?y2QqnV%;&Q7)_d9Ub3m6ePE#;32SKgM<=?lzMEYPxCO08#V92_Clr62} zz9rj|YUNDJw%(rI5+ZoAk|$5I6p|t&R5f`DsP! zsWa3Q8Tay+JAuRoH{P0bQ8yF&Y9T)B{Ztww<8^AC@>WwSV)*Y-zWeVAwB5er-k7Hq zKO)l_6l5cJ+(3cNDj+GE4EVXol4~y=vtOweQ%Vf3WXX# z^vZTWw@a7YjMQ_5XnlEb{dHDku*C3(tPfamZk#Kma$U{FA?oE5;>L6AaNy?+0HX{ixEi)H(CPG8BBJ%Q4veF>wVXnSB>K-F1HJ@m_1)Z$wn zX=JilV1P9$Q~B0D2Z`0CMl?G|zQdy5J3(ngDf=XX`ZUwT7c>Bc2PR~rFJE`?SH$CPibL0KBtJA6HmX4Q!4Oh}UlEdn( zR-L;Z?_RuMO546m@-vYa;^3j+6irRL{Iv9rq_O>7-I5FNR_Olg*HbcCB~m$m){wnE z{EEb;_KU4b0qH$c@zbW6E~k;wXYn+gSVA8O@pIfGj$7$e+|i^XWU`n_vg{+Eg+(k- zc=)OKj|`Btd;?ZFIlgE01*aO7VN;r*4Kt#rrt+d%20nlP)xGR`Dp+ii8IaE)!AI~k zTxn3AiDn4*A!DfgLS=}g%_zMs6W_^5Vbs$JR;R72K559(YoSbbgwG_@F=b%#^LZ(U zJ&mwx&QoOH~cmH14-il?|Rp{FFAvO3+;OGw9FokItPfDO>5&QLH-FD@OI3_)YS_ z3hsN3pE!fE)K+kV$PIcB_2%=V?HKTUL_;$-KiSKabV9gcj>HgPeOYZBi}{hMVhFU~ zNi_?`C>#1m(Vf@~H)Aoekv=ves`_R+-5V@b-u>CDpWClLS4XQh;B3k!s$A_r`50IE zZ^Uk-OGh`!ONGhf_1jH#S-Q790PWD$+S*gUkCDe=(OUm?jvEIQv=wxWn*G z=hv7A|MHfg+T+y;8z%I*c!fsTQg${+sG!X@HIMaeJFe`djNV(r z?oK=7=H2*yI}@tYkJ@}1n4+^Ja!d~N{v!@2Nsl)mbM`%T&1<%}PFb~7<}w=h$o(DV z$|cfYI0Ze{NQKw{CnntWn*{s4L{ohTY}rm|C>rZU6ST=VEz08o?B$0_dg=hXk~fvQ z8YJPH-32w8Vlvon6xl5PSAhZGN5%&#rm{IhvmM^G^+;7JS9)vrEei;zh3_@gQ#xPl z{Rs>hn{u(=-p`h{Z?L3g{&mru?VshMMrfW3AUbI3b_CN?orOq}>@n_MvVHOCd|hT2 zZ4~owQ(!1z{mK)UBTrT!qj8;bnP(P}Z)t{uwA+Jzq9S-3Mc_aD{CdICuT{lF|`hI`v&TjAkI*_lo(LXr>_teJcnm)S$wwcg% zA8Pxn3THf%=~^n!mmBRZJMyE$H)d;^N;Zxi!GHEyqWHbT7Ru^UnVnoJyBCLd42$2C z3=sXb`tit{&2keCgFd9IsJ=vA<^RAF^tt=<7B^4_GKTzHg@ok&m0W zUTOFirL?UfX`XsFW2eb#Sp^P+i}ctH^Gr5{%8*}Q3fLMtm?vFo8ZC8muz&Tx*%%{2 zd^GIOz8xJB0@UyLV{P|(|GfUgoPLx$M@p&){ESzXJ5Tzs1Az4NCm!)s ziMKE}d+h^RqIYXS<9uslh7XKvv73ryO4|53sWZe*oq8=Vwm@U!uZruzy=87UsueFM z{_z$JdnQG?=aTvhAN{?PLhC^C+2BU!sqPv*D~w(pgAE|%IY~V zl3hzJj-sFTVKIOBws>3UAiU76rYCPk+vm7Sspk*o=M*u1-Zcf{CVMG!MF580dal0F zhA7;z`CW2nCvxF9oG~ISf~ucRVb7yo#PYGdgK!?7mU>0%2D^uQVF+}+^-apBsN*u} z==mL4+)7=4I_-239oZEIWbv<8K*D;JGaf8x0DExi)mRSJ(_3EJ6`|2@s?`N7{1f#W`7QtSCozj->)RrTZ3gM(z; zUz8GBm)Q>npj&e*sx3BMOj_TeRRj^2DU%qyB6a7?%et0&IQH3D?;>@ep+h3!DLITX zVXmK`9FJU!*AP67Na5s^_hSu$k-!huOFuc`d)$61fB&tbmx@))WKd$KqXEZ)ul@XJwFUUF!dIpw zmb(}|caHi~jT(MV5Y_PcGcj==pO>LV@cDrR|laq&O zdI#Bl7b@$j83Ts%XT0M(`M8l}c`@g%bLPe0tso2U@G!!5UY@m zdMq!fVw+<0mawu#4hT!V3Kj!x?o02I$6yDXeYU8?lq&*q+_c!jY)Af~p8B1`X^Vn! z3dP7?$t<_a#HY$cVJp1bt#QE{HmLd{7dr06XG|UuN=2w_uKRF_Amk2{hZS~E5XtW* zp)8ln(87d>as<+7UT0+bn2u${aploLXHyWUro&MqeN-NvcM9W*{fe?S?j+YRF#6wG z03Qh=8!M|-#=~Qxov`)?5@<}7Ac{VFhY`S&(`erM1rKt|2;b{PSqji(&wO-kiUXP7 z$0xh>BNDKw=S@8#9)CpGj|uHW^5*8TJ5hJ-1tp;?D?ZT9N{<1cxCIGG<9aA^Zl3QK zezlQoG%NVmoP*F2)vfN<_W&0c3k>p#3B|z$0h&T$`lv*!3Cloh)Qs*Bm z{nISid6N6WthO+>^vhFVChhkOUL13neXH%+vlsOv=|R6M?fBcz%B9?M)^S&Gs#wrp zb2u&1udD_Pjnl?|wcp34Jm*_a5J6Fge{ZOg7FrFXK4bpMf#*gxyq==7H|1ylusNKE zCK5bl{+8d1+D2$iq;o*Pnr8fQTgV}X+``(`hh0e;!S!M{F0v_!uYbx8@!rC3%@)_GGQWd_&HP_dq))6l{3?%vI3E@c4q559Kz7`Oi! z&wDk48p&mYgZ#pqRyfl|mM^B_YGPgziW#xgy9?m9+#Yh$^%c2~u&&7WNu_GD_w3t< z_T)15(|l?p(b1XUA&SBZX@^TT#*D2a$|63$_%24LakptVC-GB>|Ij{ch9mtWktb z66jsX)I`!)ktu9`Or|TiK1`N+%5|6)T|kw=M)r!lUP>B0!ybv5#u{Nmu9X|aAd&f{29D;}DnBsSSpDk7IE9EJ8Q3dZ$+(MYU7hGCc{3Gdhqk`l3H(nS*F$-XlVK7YknA%TetNYeaFGbo2Cl> zhAqO{}d6LdKRoVIF zhfr}=p9yL_=~i{TO3|uk$?U_NJxifJgc%QGBS}VPmN489tKPDxKGVbO=BxEq%)CHV z$*&qiYw_9`xP{;A@wKjAB4I6gX3(EL5AoY|zl1y7xsxX~2C+&co&1{0X`wQ#e z^TZ0&r+y1~Tkwzx0Y1vpYfHoECMWjm4@W(eB5)XWl3`{>rZ>~t<1NBOeM1M3e+by? za%OBY0IPq6h<+VV%b4*b>6JK4MfJmzDU4dGk7e~m_lXNKMn)}wUrJDwMss61ewkmL zRwmYuKKrsLTN{vasy-K;v zb)KrrJtY*x1u(n&@RUu;y+&_A;GA@QT|^F*L!~EV6 zlVq8W%%OHcHxkdyyHr&tz?Mat`(u*BDLy<{GCUe^+kM(gXxx$W=4~P@ z1RToT>?)&FGkGG(QJP>YF^>Po&#q-zzQ-Lt%; zh9YG~Z`Qz#4({%IKR}mk}@Gm5++E)U2w2-=5SS*m4QqO99>GfjrbR=D*&Ue%G zo2eTlV5i)(Tu|GL+7~#?K(7}YfrzV*h=nS8;uq}P+djJs=k5fLv%lTNXW)Y-MK_ze z-(?B@={#?}gOTFz(KbQ$!zFy)ESswk{S0^c6IDSO5^wB>Qv*+Ck(Gzg@o8m4+pM<- z8qBZw2O`2QJfxu%ii$;^m&(Oh{K)bvpqFeCB1I8^51>7U3Ln%26FEjs=N!fTY*Wyz zvZ0{i^xm!wGQ!?UpXLgl?8wSEBFa{9cRU)#B>-i#m?9od0Hf(nV%hNl1_r-gkItuv zdK}}j6$?ZjsGI^$73wM4(l6f5bMv&F8S{I?j9=GzpyQj-p$T>U_89(L@5|v?WH_rARUHMlkTUHR2CdO@th!PU z5yY*}vokdDzTj!tBAk!BnJzm0%rD~kqWX<<*%>{d(=W8eIxKgA0N@*Q^@^&D(V4Bv z+)9pAIiYq>x;80}zbnJ@sp4X4TgEl9>cUN3gTCj)fq)JOSwKnJJ$T!GbHC_Ia8IsX z8CH+X$Bx%dWY2I!@dGL@g+^z_D9q#;fz$@;pW@uWKjRX~L4>ZxqPp=QmJ)$zcj6Se zycVwyhhy{lxW~O{i*Us)BPjb+Hi9l*JlT{gI#{ppzh-s;9At~z)k(8*12CXGTpcpJ zqBFe-6Sc{5K0G0j8B5{O&AvA^r9$?#haq14qmTb-!_ zc*kj8+UxY;w(mr!&`wQ#1AHE>z~jzs$73$}Rq$rXy!FO5$j@B3Nr3Lc2DrC78my%S zwGsoR9*unik~^gHY7F1t-c==Gk?)vw@iFL@GWsRggyaZ{PfIUn>Q4xQdO008Hz=gr z>n}z3t}{g5GX`b$`yJ_N^$>YWhcoY2v^M z9Q9Ww-qrmw26P(!m!T6w(ThM?KYxkpK3xbx;VXR4}ZYwk3JHKF3?i;JRc14-~L_uN69wqFkDsAa$ zZVA~Axwc$KVT7M1?$qI|IF@>Bs($Ln_9V0fL_WKWw||M%no#()@4(L@$#olZ#_;jj zC!?D&x6@Ua&F#Uq!p96jKORTazvOd1{~Z5`Czp(jQJ-$?B46MgEM^`Sl$rTntMs(T zWZx|m&sZmHcYcClg43u5^(Vj$&N>Y@$eX&n*!w!`pS$dU0^xA1W(Wopsri?g}#q8v`*S=2BJiH`TCOG1(HWWw3GthdGbU8mZW4!LE!71^Dr&ewmW! zfmq0x-BrayYL|*-kXSfe5=RxVPglO!0ONL={(7qr`jI2$ue?}4J^MAGl;?LUKfE%e zlWF*@dPJcS9l2UKg&@5citV8SeZVIJG7qzhsH}k^76%=teJwr=s+90B`>oz4dA(pI znV+|a{uMvu!zWFgD#rdY6O>OtM9n3F6$*?B*Rj|;h)1eij~bH5M+GB2Q+#k_r0(-s zz;}q$5{$;lZh5wo9S*)HZ6AaJeP6X(!nj?uU+l4-zA*rfbvavXa{Q#jSijdR2fadM ztQAYup+lawzVUMJf{Kd5zkZ=3hvV>IJPlxEk#(F)ERi1o2mPbO3f^U}tb@5upNJB^ z^whr*J*>c1gGNh}OS~?qW(7Eq2#p6g!Q$G-+TL)X(~{)2GdNeHtAx%C-bVk#^3l5f z)(MbI{9@TlFNVz(>%Q#mHb;ncM|9ZQzc-|B%-!kwTsIuSLPme7qFBTNx%fEHgD~mP ztGD8hx?7cbw{Ybl+VYn-<6!2DmD6g54)fCm@?iuV{GgmL^qDtv3*PPRnd%GI=Cb4c)Z;-@vS*Mcwn z(*>M)P~GG?NgQK72-1`*9jtbe)5lGd5DGT5hJz@<18M`O4NNL?8*&mDCAX*`B{x%0c0_lA|#R`=xFO_Ny(XW63N2=d;vuv z{8jL8x5tIsf4y^I_0bsDIvmm@?CZ@$Efes?pbtde=7}ohl83BNFkq56MwVtp`4t@D zXkfXfJAWnqI4?!Y0q|UK`P3|Gp6vJ`sz?3GCR=vpEcwaut4V^ibv$yUAU=^)LzMY9 zP>yUl^h5;l#J79jXQa%d9ZvJ98)7enpI zOU0ofZGD@MmVYo5b!EiO0r#BD1&!px!LT)`Q9?0qni0+AGldd1FqM6>YnuF*cwpJ- z{v_K9KYsMz%ZIv+1^g55?KGQ{y5IB_?ebCJBqvKX{-n)dMQPB_h}_Qo*1BfV_cT%m zE`r9W*>T1X=akuMBjsqu$`H^@x`>QPEzGC8@k-|-CgfBNj~{XtbHX@b=XP^Z4(t7r zx+IL$<+iPN*PPj^c249n_^mGV{Oo6zGsg_?m)d}4Y@c9oy-}^UcyfW1`)cJCDXc{< zCvl%m`4AQSP=y~7MP7FYb)_(BYmDz54uPgP(opp--IO;F%7(Kr>YZtC%p zu1t@|Fwti#&d|LbMAFv5L3<6viKbmj94aKF%nW!8Jx>YW**JvbyahIt1Of-(Fp0YO zYzpISO$_@luv1@Easb6gCLrJ?>}21%_G&yqZyS+!`h=pOU!t9{A(O zyo^_@Yb~{ZQat~N zr^x?msvGzp@|gcWy$?x(gj z^V}|M4tj*WhP-%ki|JaM_1Hik65#?#Rmr+Eu?1ob9t9f$IrCZU!YE)x(XoAe$AKloy^{nn)-j>y$cbKE~6?&a0+C^d5MjqUWY~I@54JGCw?y4&N^PqXcOsB(X8FUye8T;paOhFGEa z-5pjO*=HM>Y$}0I!j@d`Xru_21(ek$4St7HK{xOdGyEe6y(#-q0BV2jnRov;*Qfsj zYe1C0(~DQq0DM2oYv^AhvgIg-+jR_$_wP>NAi>Eq{|Td`kQbw@t}!wDh^=PlF|u{aMdChYS43FpD3MR5KE^u_w0;(YViZH^Gw_k7 zKf8D}{S}cp|MKZG=_#Bpu>wYavbh@IGmL?E3@1y!H^5Zqn0)ltLGU?A`zbx%pCj)u za;3Aa$P|xSsUO)s?4$s=Zf7)#=snun_M3&~+`ln)suEc$^N(`Rhi;Fv-T(Z`b++a; ziP#!UFTj&Q>irCw-em>!EsW?tIe#Vnv(v}ZzkcD_)TCaWgMm41r*p5=FCWL~2M3r` z?}LVy;e*bqH!vDMz%h7>{(qkF?uWqogR#T8&3V3ONkWSEY;xFjaMbgD2qC@eZ$-Qg^&6C23^ufL$Cj*sqxhFY31+#GR=MXX6TY` zwvP>d^Y>H#YrmJ)e)TtL?tPp)S1*K4QV;1gvLd%pFS9i+p5Z83U{RyN;zSc?QSUdt zg^s}?LsZesJ3mXa@Bac_$hI-<`fQ#>nuRz1D&76bpQYiKUr%HI&HtDN_8%E98$3Qjt6TwqzEUZaq0 z&xNb!(+XSGmbl+V+tl#nb0H zpSq8pi05*+Yk(*z^}2j9F)`k&q=UjJ4kz5#7{fm}e=R0GqeS~0KTI?x{Y#N`LjB=| z>k+YJA91UTlZIAD0S*c;%>m~Z?_WqpO8DF@5=I}XJ6!5 z)LYE@BeFtWSE6hbFSk0YCyb5}R@VQsM1oG@{35b~@q5%o9y7yRb<7%z08Y*>;utfk zSm%u^PI({tcr0_fuGTJNOfAyJ& zyy^_Q5uHjuGp~AbggO_OEN7$!3lG%c79DBSc_2J>y|##TsB?1%&W<+QYi(sA+T6Fy zim&)G)^<_OsLI(0YpV zMu)I%sIyQRY&m5FA9bj2^1`Hj?>0O)DJMBY{LF338fU}~>Ao*fy@JQ8FSEhBG2etan6@YXG{ zM!mTOQ@dH&%4Y(C33+TYoqCRW<}>*_$_MckEWVu2D09<2M7s0L=sS7l9Op1Lm9ikb zCFIaC+x9%A!=G<)Sqt8~h&};-F2rsH?f`s6`jxX}%7JcvCPcNr;Ke?P<#HRnf z*Pcz?*!4zrhf|ySW*86N`S@};eC?;U=TV}h_62Y9c&DS&T@gAC%Un!{ZN(_Q>r`{# z$>2cte0)*3gfYe7GkQ4Q%;sw7I>zfFtCenwdG-IQRh~?W%Mlb^AKKNByxk%K}+dqr59{PFhuxBT!9M({<@#<$* zfcu#QHpWiHHUL*-4b7kb^kaUrdWX^LO0sw_UAqul2D&*9!$ZXx*#VW$J9z9SQUfp; zs>n6;T*Wv1+bv%zI>&Ga;2RC2aHd1CY5ce%5qotR=wc9uN3o)X64C&3pS_dNmI7!Y znvDrpGc=0*gwX^J)@2lvD^=?Z-YQvPxhmG(<+(qcm4ae%wao9ba$z|QVk9H#QyFMb zE>l*=Oj)TsIC6;4)O`^S>Pnc3SHoB5js}*-u`2~`$1|czB^VJv4A`#rYHYX)VpPl# z46iW;c=H;b?W%*J{V@>$4<`knv5~YKNZ!|2_1kEzVGIvfLaZx{J0}y!h^v_z#~L!v zonrEZ>?qe7t~#(Z{^Y*~pQ~0H3dI;!>DYcI@z)SkA(cJruyOBF1#1|ke~LC3q^(|L z!hYNd3Ox2~3*WBh8ALj4E~2F0WSg1kWsJk4!$!-vB8AhC_V^fwlo3Q1aHM?a;Anc4 zh_x5slMy5PFyf51(TQ>yr`-Yu#D4CFA=aV@Ws6f;r-ZQF)^rEBM+uSt_QCx)f}HGh z#2GZ;rGHXzdip*XO2#S@-$*!-UIM4!5sn6(cW!t4kgZ|g7#~R&q2W~J)b*KX1lcxcnE=c#HzHze4xV}D zlYPi-h|kh4PEtJ|$%rtw$ay~1ri472hPf|%&k`_7Or9GNWYmulKfwt33jFruFAXTq zFt|*6yZzAZc2~ju9Po7fodZWBuiTF2MCUeqei!&3z_06^|F9pqmd*ADpam^%JG@K# zm$Q(brp<(N)HVaZ3C1X$kUEmyUZ22$m{&Ao9BW7Lv9G}l83L_1)5j>9rCWnU5?u{L zS!aj(D9hYNJGZKpUzB&u*Lk7?sLL~l4zX1Py)n+#swLjK5mgS!m0P{H&XND)2Vst>4=zJwmt}%Wf5dSJ;-@Uo<+ERdNz|MZIwJY zu`nvh?dR&)#XC5p(e0hQg|#_)1ij9dWoXcGLPwgM_^5lFSm?YtfAtRj3>Hn) z0opGtfR%GfjG{Ffv7dgU9&+-o4g=9U~z(~Gs(%7y6tJU)r`;&<#mAb52UK6rP~RwtN8X~^`Tqr4x#7O!}3D? zr6WmaRrGb+EVBx|#~UN7$-fg*xAzS>c48yQW49qSdWr{;s^SrM~9t}Ma8D@j4Hdx zAm3$@F95d^+pf0tFlDrdKE6ml1#cG=*N_$ML+vANDV=iW)eg~qi#|rn+ic3Zc8lLS zJ#6a{aL}%^j&QKhrnb>_wi@dEbmFW7)?}LqXHSwHlT;io%FHQ`aKHxEJ_|<@eNr3B z_BEQ%HWY`PK!oRx-Y#&i(l?b=X>&19G?g#;A)iLziSn$Y#4V}Tx9tL7ba2Q1?9)gMfqn#+<^jRo*xzb}JB5Qbn?4R(1ra1&6N>Qq9*o{TR!MtLKO5W2x^M(JgsieMt5Ms-`4x>J&iCQlI^*00 z99bIXMkzI)emRZ))Bi5E4!EWgA&wZT-9%9>U%$kP@UPMYE5>f8JN%#jw`usb?{Qv6 zR)k^vGA#695D%PtfmLEwXc^2Rq6tTpholZxZ==hOF;*R^R|izkX52>`ZI2^68JG1~ z;du4g6M*Lo1qMnLrYi#3m{^Zca6@S?vQp{RDF-{lu@5lV!Js_YXBLOzaP@L~6dlqr z;21RB_5|Z%;C7%^!RmnOqV4nQU|$%=-CfgRaH#N9=&ph}h>8z|BWaLi12Zc@mC-ef zSXXmQD!o^pIgy@aKXvT;2i9SfzN=}|C=dtea1uek`_W%u#lY3Y4&Z1Ezsu^P2A_kz z&ZyU(JxOE}t7I5SPKLcd$|}SWjB-bOjb;^lObDb6T$PbC7%2ybae7fkBgMl_GwwOW zI-f1EkJZW>>FtlNM0hT!=Juw%t0acy$I2HptJs`4gtF$h#_%DW3esyEXcUN}xCE~7 zSX}okp{z~cg%?kue09Y~gZod3ba~HhRm!EtGj*^F7yL56HJP**+7m&)7$q|d#r-kf^RdK7NF}7_a1W zYu$6mhmNZAIAlHn_n%xN@&qT^86%#kr=NUBnP|BM&k2prIeBSYS^pXO#B1btOQL+T zZ`vmZsjppjbW(=gbH5jvqbmn(Z?UpzJ<4tF-I=Fb|4NH|(xCbeoDuaGL?T_AnWbhcPy((-EBA;-`bsHZaPnBpqU&AN3T5yJddw$iX-R zt4UqThG%x!p8pY9yyL`58Rp&&16Be1NZ&>wy7kNhu@jCC50PE~k43g@E%SQ%jg;GW z{RgS(0u20IV}Ymd*;m5pwL)aC6&iKB zNW^asPAMaJ3%?Psxb8%+dq}v^Lp_x5efq`JB*LlNh$_m*);kt(x*1;@*aY474U$nw zz0e#wL>TaS>C&h07l$3uU!9cig=gwi<<;$iMx%78-;gg;@Hnm>_YeGq$3|?p@ZfoV zPCU+?I231exOL4l&+K$D)yE584JsA?r}M!>YTH zuF*f01h$JrQTm?agV96k|AW-wxhqE7xOL45ra2Fx13g3M{m-re>ywC3xPwDrA3DHE zwsu9deXvT%PSWz$sux%^dH&4NaJH$h#7#VO#AKcs;X+>}V#v0XCr6!VF|jscKpVh? zD9=oZ2|8stIuV;;Bqqb3p(}OtIGF>9i1JW3%1^&_stCtLrg3;4+eWPe2cUF1;nX1` z&f-_1PsGs)=^T;UM%x$xWkkpsw%vT|`BO0|)$VXIY5D$YqN<}&r<}6*4Q-C-ql`z< zDWbI=|Bp=wJW!bxKLbt0SrBe4jVWnevxFL9Wp?4SocOxivs9V9MyX-|EFM$+z7 zzI0M}&uz%lbI1kby^bF3C1otm5>hVrD9IP`Q?7LMYTIdtIYuZ?wyScXyo>t~eWci% z!5jLrcq@ZLL>+0PI?*?}PBfJ(7Yuc9Dns&7XJp6^{Iy=?M_3yv+r!GR`pmJxK7Qud zXhcd`zw)c2R6E4qwwd;`DUT%uh7Qxr^LS`Ax!c80|M*iHqR;akslsUrnbryGc|X#B zmI%Y|yl^@kv~G8daR@@C-)MhjIXlU)pQ}M99A;fEnqs%NIc-8VsLW`qQ^*WMO)Ol? z3-M_>pEE_D%a{HYPjQj13fql`)s^?n7jqGEak*_0j(pZ}>0;Q~V+Yc!&m51rft)FA zBTsckIesY*FgffO=`pg@-5N0_GRIJcb;PTKCI4ar^O4y5pfmgPdIq#xN%fe@$57@Mh(0C!=R;-_PinuU>!7m$_j;1I~q_9><(HE zoDRC-$cg%cdvGrQ3tHorfAX_Um~hKt@o9TtY2oX<>gd41qG3uVR|>qR{!=L%ebmdR ziNP16D}SSAt1=VDk_thCSVxOxHIOw1_hEEug!&dY6`o4L`yM3e%9n3{8msceG8(BG(5`q7;hfNDv^|46mJiGV+MfN<*&yh{ z)OT3rN_~@j=AmP1F-Mz8+cYv0bdlQwk9)dt-XZrP!?e!|eZnnCzCFx)CvbE;i*sU( zGk)yv{g#Km1JHJg$<+YSe8f`(9df^wK*suXplPge8BMtaZ74o-oM1*9dEQVo&=!7Y z`dM+azmxw6w0swb(U5QM;&wjXW&Tb z!TIBMYTr!OLnM!S)3L-izO8cZUvPys4-HA1>tqrq&p+}Ur3PiX)vxfolfJe33|rdJ zWlQKVqmO(`DWfkRhJ+ABJ{(Z@y__p_a@dHgiv7&@5kb>hB8JIOYCU-T2&PTuse=!1s| z{n<=w)A?X>b+d6&G|{Qbq&nKV^QV%{`^o55Jn#2gIDFJU>I+w>jjTD%IWNZ; zKU`pN{BV3y=jvEdzg)V0x7DArEasK@>$g1HkLWSzG#cjs6H!;H&7*DzX9<+JxZpW~ z>Mx^k-DZ;02A6|g%Bc^Xv>!(A?bmToIb}LJ`_bw7B@})~P+KZ0fKRgxs+^7i8JJM#*VcyfT87J{{V&$aW$+l;?=)my&zi1`0M_c;~ z{B`aWeQI4!qSv9vNtXJ~a-LTbx{0#B06~5`W;=NoANeyn%y=nG#)_Wxyx7uQ>q$!u zHxU~>jJ%V<$*vQ}Cc3@Bcf3k=>InRfEyOmDcM|%PGhyCGr6?8FHWG6tT&qaf1 zA7oGYxrL1BT!=Gxa9nzQ0UOD&RcEQr1{c;EOn@yL3@zjY)A_?~ZA+t;lq1_A+6gC{ z4l2)(F?z-59UYA>V1@$)8L*G-Wm2w8+F)+2vTrpRSCkh7AlgqyZR8_wuAOg5(Q>$?mVz>Ahtgmk!V?Mzrxtk zFKn}|tjW)+oEh=9%GzNB*q|jcfSb0hW0=uMZhteX-N-Scwi@VsoqRa~hrIr?zD~}C z<)V6)`;c@LXP|j{84u)*6}oWUq_3EQ=c47K3P!fE<8kYckJ_qe-EUn~4>$5q;GU$k zYxm@K^s)1cQ#e6;kxL`(9GgnX%u9X&RJrcx%+Xx-1N-nPoIg5%9HYZAihSFnjoj2> zWOSrF&>>YKEE_$H1sh0`JI@8|3yw9mr{jh;l@VC>jlic>reG)bb*ee=b}EzRH1~ypaY6kVbcdHvbD6K5J~bUkA5bVasEI!;mne8*O}ygX3uz#R_Rl5WJEIb6839&?n#t{ z5m8N7vN22pg;vA1^P&AZ!->C6iXd{HRfl4Txi!Hvsx)3Snp9A(`fBjz)v8h+8H2X` z;a=1a54b1Brc6G(6EWJe6GfNP4)|10AN8vRRfCJ(cmo68gQF}INH$HC!8IQez2AX3<#bKdjU$3}>B;W!2ezbcsRIfJ<^4)X*D86TZM zWANYv4yZiYfd(tAcx2?`A&iYP(C=ALmzc!ez#$gKn6!#`d?8EleZA@n`6iGYlY|vC znDGHgn5|F6PYXx+2b87lG?v_I=4#w2+UA=~ZjG3V_HMzY4#z^|#x^z5%xJL_$iWM| z%`fVwDn-FFBb;C#M1~Fn$Ed%s6otioovppzvX{ldc@E<*J|E zq+p}tp%J$|Ihrw=2s!u6q)v2<3oE|W%#pxv7fa&oA-Ay+ts5gm@Dd)uXP=p`xo>}J zj^KDw|G)>MiPq!(fwWA->=b6OizqrN?cb)*ML3Imvue*t7G#J#>K!{7V^x>>jyxTV zW8EF-!wR1zoHz4VFQgvMG;AIvVhY)3))w&M4|SIVvYSLt4X_QccOT~g0;f~`X{MHL zmUnR{iWWY~6YqyTpiG(!?{%rQl;iH6B*$gPv<~{5TPmECHsGt13Ku7I9%P-R zeq&O_B-=7>k;-K&Gy*XeL(sV{Zg@V65iFilW4>9oSlE}<7f!^CBGzeQd#bwBV;)}1 z013c~0o7HRj5wK%GlFafcm+n$zjWF~#0T)KUs!!C>X??W2YiE2Y<8em!ot$s>HI>Bf@e6}fTb?IhJ#1G$q{vYzN%osSLsn-O26&o?^fbBH>2Hr@jwup z|Kuz@Qy;l4&}glQo}$gvF$+sN<>XV243kGDoehpn+JKhT`L&K65HdvFp&xVBJ^3p< z>+y|TMhf|9R65pv(WFNY1$KM)N0Rx7k4Wl>{oA)b z3CHhKobhDDx|4S&|DAk-4%#uCW9?W_9;wMP6Mf2l)kd_mg0vdt>4DfPF$`6cF8{*#kNSk*lZqyPK2DM+)MdD(q1-AB-$)h_M@;JTIY*3H z3|j85cO_7z8xD_q>a7U7=l9toH(s3Z9s>r>LiT*217v$^DJzt*7n9_v~6( z5Z^m&7x3I3-%93IFyFZ4Sv5wE=t#0XOh$jmTenKV4vCHX{?(b_$FqMt`^c?GCHXvP z;#ogR`4Iiqb8yxmPunvma`E4-OGm0`loxOu{e~`h1)l#MhhpDBrcdBtF^a~scYK|_ zZ;#XY6j3c2G$1D4nfv)~ zAP1uM=x`Ear06upBIJ{)ke515Em34>)0`OkA|eSMc+Qb4{5e&1C==l1);rH}T7YiP zDw@&>EpyCfo+@!C?v)WX-{yJbsq%BPYNq^n>tFMl!S4b(09P5?>f2gWl$->6=(x#k zh-Zk}7?IZ%JV6CV_$|NPHaAH->KyaUd|9?~#nR|+<#G_COFrb#Z=dIWVPt|6JmFe* zlrMO5@AF8xcJQJ;Cv7?9yZ5WFO(AN+)_$oDmb*E!gDm4y?x z*F~b3HQH7#aA?fCZLEh61AOQ)CvuKe-E1-IuA+vF_Ki21dI&wqbC!zJ$3v@|ZnI-M zwsPq~+2ZU%42qR@fL{8Uiz&VZZt2bq%5oM3xaz=&6i9)VdYJOvoL$s==x8kR2ru{W zGA2_-{Bz0HUqKYUW*WKC%9nYbpSSbBReaAbB-=@^4h;LREBH>9_q(l>{^%jxE!=6# zCOmVZn47dbfdc+#=R)3g-OJ=bhr{H|0^hv#57<6e7OY3TtfNL9Vm^6bU9Cw_MWJo$ zq$e*>&<2q+*C!H1ZwS+<2_t;g=|ldWV;pgdosl!bSGSI{g=>B>>n^JxTj*OSL>@33 z5mQ7UiK`B!)&yJtl4}93)zQk2kxh9#1Q%dfuUUgZk-hN9oA3yj%jinCt7*F!5oC0L zyfAWFyTvg>zF0rx2Rz|gb$crH$b;ZRdFTV~MM=Nox;(7Dxu?E1^2&3ne|h4$h_=7byg&4vMFQ`$-LVo+e8Pb z(M(1Nz4OV{c#(;?=PmafD9>WsAbJ<$G3paeJ2gVKI$xNQw>ql-G9H=l$TS&jw^(Sc zc$;zB9Y0Q_B~It9bN3z*j5FfbY`e) zq;Z3epe%k%?-tGkJw$p$Tqk0+HZ6?*%wW_k)_C3|EW3fTjanK;YgC+=wCs&S6fost+vZ8 znexvv1Zc?6?_MCq_(=p(4of)1$V_;#>*9i9v z2P2F$8(T2jku2AO=Q>xD>*Ci24KY~zlT0G+XOEJ#2u&l)5bIxla6ZEE-Gdv(FWV#> zxHNinA~~pO9C|GtExIavTIk&u#Wvb@O~(!Z#bG-bwinMdbR4LQp%yghe_H6~kbRv_8WOe_R`EUcZ{-52XjDn|$H2W`4j-_|*_N5P5N%u`+AF+kXNsh0PH?rkrwuO1v@~eD% zhLDpO-=eX^MBB5B+;ZnODeH<^NL5Swm&UHRimo*2v@xWKt~GL&k7i0XG{b zS&mz>1B{9@?bXD!_mw^Q;{n)CzA}%34Y@ipHK4<&FP|F?CcopM`P)-zgZ6vv*E=Dy zDNo5H-d>mA?U{DCrv7L`ia4p!VK=P71bvZOptvBfQ6&xj*e@n=BDrNUyP%{i(`FGR zN@t};c)2fLEpV;7?RU8Oc_vACB=~tUotth;`}q@jQYTg9^UY2EM_tIW)uRzaVI-< zV%(N|Xms+|Kxd#x!@;~khwW5-s;XNg(18B)w-b;Y5$vm=ZxS5SwnLagxoeih0;C)=KdHPnAM z2!#*fDrmTZtkk0woK5m z8$R14k&n9VuR0sE9Bshb5zall0!S;hU)vV)*n6Arm)|bPdXA_nCeBY~e0Ka%Uboi2 z^>?evxDcsa$35t^GVc7OWO;CoQj6c2vfY%?!K$;yc8>hEZ(F5pzp~8o%yCcWtYe;A z4SlhXPGsB3`?@w%O_|`IwpYpbiQ3f-OQU_KYABJnIRLtXGxK(L5cnUTk2Xh{PbL81VN87z} z8Qt`| zkJ*^d_0ler(~Z`Ru~j64ex!2%pdxd<>oNU{1GBv|imyAO_@ZU0n=xy6PGqQbzT~|F zbTtLvR~Q(6$a1#-`_8@a-uXV8CZ1FrDu>HJ(k10kvwtCXZPl5=AVX)K%|~; z{m!g?B6UfY$~SNMS!$Iomo@z!n`}=&YrEieJf%Eo?HMyc_no~5cbBmFZVVpxJW2VPjsxmPdMbifR2z^NaaFr))CDj7)4;4(BlUI|L_kv4mhl&S3 zBIoFVxACg^x5}J2CAd~!8{P#tK^`+hK+oUwtTPWJ#hdKBxbU07|Sa zUi|V`VGsgLc`uvgP^q7@uk%a#+7!p+0FU8wCgbjT*SL8{ZdODN6!V9iMc0W zd@lzd5s&x*69BaVX+$akajC+TVCi3(3me`6Zy=GahA0{5=6Z1xM)U_9{~=o^beI@GgXNhk|p z?tuc&;k^h>@ywq_%=X5@G)qgdN>g9HU{myJ>8h3(cchIk? zEJs2aSb~FAkr%k-efRbPOY{i&+DP93SZiyx<20$KzEdV_3B2-65dIDm%4`plq*dGs z7U^@nkhSP$Wx1^1-Sn^=ImW?Ln%r|C(>t7ChXDLFNM>*2SN^hDBp>$jx zL){#Vt75Fd%T2O9B}~tpZ!=5zA747phRGbk%>D~{nCJoY0I?5U@X;Wp$B>u!L;1>TF#a&g=t#I6 z)3KLIMT%JeYto#&{&xR6vof8VMK42#P+25E@BkmUmyedD5q0B|u`%1ObsCxO%r(Es z*BTa;qSoH+dt;)M(7*CYMMCaO&3Ao|Y+ozXxhLMaTy4c!;A-puE6>qBD3d%j0i8!_ z41r z&8*$Tx`R`9XredzHYHwo*ExU*eWXSY!6*Lkm2%Yq zu17&%Iy)Jg6FW$;U#W#CVxqS)(WP|m?NXBqwTStqA+!8)=M_5YeJ1(zMsVqeMtbx1 zgd$Hub?{@KwWIlw$o8*+#C=0tqG3N5jkuLiI7}T$OUk*7jC;V6bo+2mWcA2xPsUQ0 z2~RNc9%P{9k~#4fPwOPI;*V6=nQetOVbR`O=Li`5u?}7S7ia;?$$z%@ z@aAd+(ef!_l>WAi(Dc%_D=ktUouSuz0-^k%%x?~oOj)2DbX+yeWM_C+f}?4o&XD&S z!DsP)QzmG~Ve6$cdy_cj_u znVw-oVE4CZz=1@8UzysA9fPEDS-?Do3kMZ;BA$5j7RMZS(n)Wy$Aq1Pu{F%B&{=r= za*%Hi`s8c^2huyZ2-iyBHV={@l#pFYwILy(?srcuWe z*fhSp&H(0*&P*4R>~o_5%9B_aMHb6TTkKD?Ud$0!15qk35Cj^}%nHJ1?v3<_y*M5* z1CvfaBa>P=xF_xl>j5|}AsfS3C5@{=yyR8sqA_#^yr!u?)5=Q@f^JY3KjSo{Pw~e4 z4a?o`@N9nLK|2mPhYvhe&1H5j3v&t80(njjrolkq4}52z^t5^gE+%zJmdoxSn%0pp zB(;lrP*UZ}uQGE~=^wpRqO4f7ikp=w*dGE`FVTB)gVcu?j?!HwTku>sM z9px0R5@zOa6Kh1$fTm&0y{=|x8%1DcB#DO8fiU^iNIuWAG zCAH#3nf_obW8|D5QaApgzGH7L@^z`%=8JjcP+CeB%IG#5bn|Xw;c2An92hQ_t|hg= zJ&)q#HTt%#W%kUu$7!ag&&P(_mM@H_Q70rPk8PHDc64DZpFYcG+vn*|JUUs!Lkk?O z$|Hr;?(@D;VNA+K4jYe#f#^%hyj$V$()lNt8j_UpPQs`e*#CGWd>rG9j8i6t?x2Ah z*>B&V?3HAx_`m^A;ph?UPkAOx_ond#INPFfx;Z4B!~r37r0hM0U|#a0{izd8&Y~L} zM7ytygPoAKylbw}oHjgQlnwH?H1}5tjZc*iyWIw{I_U3^&9UzY$H-%}xqS8%Mw#SA zZ9T^z4Stol;T;6cmH#<=!R!pmud*hZapisbRQWCd>Q7xo`sfYiAl6J=g5oK34*1kB znnRYU4lZM(bFMT|+6{XGmU=@Gc1m*^33^ZpZ-3eJVT=yI>2{4YSa}~=m~0Pb2*}%S7dA4H}LYlvnD~N|#g)2-FE(xnYuD~`WJc^M!hIKIe)xn9qHoPb zN2zi!Jc!XDg5@u8d1iKCW}MUPp0R%Zam@6-#)%$SoV0Y=f29Dp}dNR9xi$iIp_^O z*)EN_z~~~stMKI_S2ttU7^8tAl3NziPh!w|onzzASq|VlWWTBp*<)!OejN9Nw!jON zmY0jq*x*%TlqZR~pOS`=6ohnqK_y9}i5r&RrjbeUFt#~R68jG5MgF?J&#{-AaXh{5 zeT<%0G3vAOPet-e*Aob+E?|ZnS9T9dy%(o};Tv2woXO*5Y z42%tW7P>dm8Q{Im(#b2-QPA6RiBuJ_NO3~`h#$cdKRfYkg(<^Wv&p`D$!iq@LofYA zP)_O@NK>YJsd5D^8VM3)Qg(7OmYZI$-n~(*oxNBLTz-QIk5lA{t9=iffZk^^05$v~fIh z-S24N(woJ=mA5&BLL+n9M?!eV7fO0jzApD|!q637Q%w4FMjMgt)`9X}*-n4KX=9#X z7l_L5M+iY%L6E=wp39YcdOA4{{#o$>`{KQO^?Y&h!fC!^{AMHcXL!IdtU^1{6W|ht5&Nx=*<0`4)zx?KgwhirK5WSJ8=>O1y3k}Kj=>z#K#h6pD@j_baB!P9ml+tw42$Twu7 zSAOb0Zj@_Z=xKW{rISXXKcQ3gD+5iJGUlFu9CU)p5JomWA3LFrf$&ZydZObkc=coU z4!Q?V>v`qrY#Q$@_c#Vc_j1v|kLyKX2VH}|m*7o0xER}DdsXD8C zd>kRe>A?8q2K{>F(R0Vdr2Ov&&qMUHMaXjvNqV+CEcC|pSxTvPo54% zVSWBdv3mb@(amW!4g^AAqTsFExn3-O^2;zd^`SHmy#8L%Gci@>TPj&K9I6P28_)S$ z^^^DcTl4#)Jd>Bk0%sIGiA66IXD2*aU}(UNo|EB2;pT}bi_FFtPwyZ%|BRV{oIm)v zluQGYC%!pzk!CuUi*W?4#}RvyK|0PjZP?itopkOUGN5#|xaO;6-%h~21iEp%wNnt6 z2|780aU_|FZOp(3ju$F^p2YTaVKC~?nIvbC&av#!0l8&!FA1&sp4Rq|O`s>4Jv=)# z8t(gmjgDTzkD@$ZWY*377(T}6!| zD$X!tAr3L90El>IWjn*DUHPT~!WmOHF8$>vw~C+q`dZ95dj#%fjbwOmFF2~Do&>v0IX83f5Hf6dEB6Tn!$?sU*ib+e|-EjPO6g*XD~8pl;L9oTGa=? z#Mj-URHHZC%e(TIdpk*s+t6oaaWOoh+(6uHH6Y#Pw}f1p0{3rEjIsYy-H?>C^%}g3 zVZe>Sd*Opc%IRrn8en@Vk8vPVkj1iVw6Q)jj7H?-qmE(}dYZ36`baB{Sd;x5#kz(h z{&}*Vbt9lY@_&cfw?|7$#s7<;_9KjPAI~ooe?S?AsrzNhWqb`%mRX~po@=j@Mz5q; zaww8{9L}xy`P(bxI1#UzzxPV`GT*EiXrOVUdz{lkIgCNt>-Dp8`*RkI!O0rTc;~MR zubuE_Mb;V;J7tV32>5|tlp(*)50NN8)IVxOcNxdlDxR8mZxlUKct*mvebdMpWb*0* z4g>h?*Wh)n=m6hdJc*nPLFMPC(VOK;Oppw`{eH3d!B2|in;#cT*FKE?uy>kSZ@iQ& zgD0KFDQ(oBr@Qr^zk=5i6D#nC9!cBQe}q(qsIfq|8Rs(EewUyH{DpD3`_$>8dvXRe zScb(W-)oN^aJ?N6JUAoqJ+g_ms$oogk0$^}`Y?cWBE#iqE=SN{G{SN#y^@9o+!ULA z+8A`}y)g&B#|*!1OEfHpvWfoUJQElW{xs~ntmN_hBBx5dpiks*2->G1$wB%bWT{>Q z4z4spInWmuVbv(gO-r^2Ncm40KPQSSaG;eNL+hd7gp2vyonvAN`kk>&gRe5(=nj*_QO4H0ZMqCAkI-wZnW(o zCF7)XQp`!As4wE!zw3D{FSyLblf$~9C0|kV~}Q3~@jp;JS(d;RYTM zA6>r}6J@jPg{YxZ@9bXccMSfvY&VeM>Mt(8QEv-cqKmW&*1G7l3W2NN{kThs)-PwBm)zGUd`sxK+BjjC7Lw*18eUn}G&kg3&MU1DkZ^x|@3^%*8(`+D7H zH0gH92%-3?R8r)e|GZ?u;j=FI&Gwqw2a`+auF8!W#Qg9e`IF@{3@z?0=H#x=_RWEd zNb}!&<(MW5>t0@7f>xevH%^A%v*bhaTqb=2AYmrEg7VQOF#uULM|S3TM0 z!B2k4?b0;06$z>e0Yo}s*tUG+tqgKCf#VK0d$*0*CiSqgPUF~ zAzf<1patFhMgt3v2?k&8>EJA?%L}W2YnLs(-j9DdT1XtERk6A(KV%{Fhdxj;8UNsup0XG-{1qvM3 zli$K+T|?G06d#Y^bxFn?N}(N=hB1RQmwT-F5e5%#sO`XB1^q`4m|g$Hhq2lA7#*D6 zK6CKNCVYIhzo+;u_E6GTpi$rn10=6@rW!4-VC-0>O}jkq4n~E)ynU~@1+DcC(ufhu zwJ;>i@pDO&?_B1j@x#1>INtnSvti~f>O&8nY2s@v(St{Wi^ho;3@BZe_dbS} zH}Gs3vV1XcXYga;Q0gzS3Fi-IP8FYF5c#XS4~l=pE9naFJP}Q!h|9-5As@YazJ-U( z_t|Wn=^lx<~=8W_c#2F8}lqa(!>N=|HIEuE@C1fiJEcC#)eyw3`olux>oV5dicNo_r=1` ze-vQ#j?$RdcV@O2dhkt%9O&q&VL3nKWV5@E7K)#KbOVo#g;M^e zft<3`fxtq+KrUx4U3jmD(kN|dnq$9b@$OKz>zU*!KBh^7CI%WrcY?)%u<-2PUT43c zH|fj5NCsW(XYHSi(@h))Gf@F0FC@M&Ta7Hl1D<`qc+>)Etb1|MxMH9B(T%&sXLlaP zfZ2Xpyh3N7zY-^n`u4+bv2RH9s{tzI;eL*9Bl!4V(`1cL7GMYe_hBd^_{=^ z$wx6^V?Pr=U}!Y@7XAFRo(HDYc!eQWI=Ixt!MOwOzyA5hQCG2NBmJfQuM^dvjC7yHHG^7e>E9VbvqmeJpr*qey@nBHkqa zkVlOtZz$KrIbx>jN=c6RH!tzMiSGOBYj+|)>)k=RMi}|i2~;Nz?>u}KOBG$NXx%%| zw+uD(!9^VH$3)RM(n7kaw>mjvTd@rpY!5C|RX?@Osz-(%6W{4$ffa3$`U(S0q&mbO zbeUmvk(tSnFsx_*yfgO{BQTR0!1H7yq%ngwyjAE_XK)@?xEr7bv!SpkXv0E>1kj2Foq0MBa6nD32@O{(9?{a zfVOQ&9qFK9`s(cIv;_n>Y=c__;3@kCp_#n+^N()^U#d$hca>e1&Gv5{=xJrSvQfKE zL32uz%Ft)^U%o?LCw}x29XPb_5v$%_EB4>{F#tg*_w( zLSxTRe($7X=^>3?NqQMAe)RKV;aC3}L%{*2cz3XWg?PIpB04^YPW$1# zXQ51<%B`S#{lYY{w^5j$heGzrtp{N=2!jL4k)4Bk2fcA&CJaSu2x$#L8HQ=S*E7b> z@c}an8XcZ6kbI8!h@FvxQfCb&Q7S#@MR*!qG=%IK=osuY+{|NP)pNxFGthJg(IIv9V7#FtIg3)K(s(aA^rwNR%HkqrP_b4>yK$nkYZv+T(aC9O z3F9N>(v{y1^E~lYo)zRO1{#FigGX3)26kLe>1b69W)bp$jo`H#7%Cvs;NvXSJW9nc zKUog1A7?V0Ikl5e(RI%OJ5l#pa8}1o!_HBf+EF^cBfag!R0wo0*AU>*#t$*liJu!n zI(V>?*PvxuPqXp#96R=%olfdd?eOeKH8@FgJ6}(Yu&zRJqC|YI*FE)k3dLE^kh6F- z+`v2LFK^z7cjpHO!!tbd zNT-ozg0?rV(m);RiK7waz46gvp8NY47u=xRI?Tk@%jq5+`L8gr{1jSvO4}Lo9VFg! zc-PIdHB6Z9ZR+ZGPE8hXV7$^`Q43|KY`K1DT|)D}#Utr+3@xAFL11orTxlHA!|F{8 zQQu=T??0TGDo(>cXnr+Nwh!a$RWky=SRTEf#3z*IO3b})(;!9z+GUJxm>2MbT8PcU zCsoX7CvJMa2v55a^?Mj>-hocjlpU=vmSNP}Y-3JtMt+X=d4uwOfU)dX{A3diKaVg{ z%nl6|-=$uDaF&b2LD_C!{DhQ?{$o06SNx`r@l;%%%!Vk z@1$+I=TaT7kyWgkJLOEsh&;~X!+N{joDKQoLaqiqj>)DFN+tSeh_8v zf;N3;FBF56t#^h#?g$RRM+-~zqRAeZeo=J6z&d$t%J0Z-^3926?AmGcfX zH7YztZb#cJi|j?2)){>tn`|S)_43h(bO-Mm`#fcOcuzn`m(FM)aID8|BNDPG%0M?f=C~1C)JPcqs-hQeclDA{e49kISmoF_zw75+o+M zp3p9UrLilFVU$z&ZVv8XbCJU`LKeX<(H_#DdS;Zxy?xR^7cW_iD4(GdX;hbHrnL?) zotcV#fToFWdlKU)6Ln^vtf$fSd+sIlFuW3^XD?;->VPs%!i83$3(+@+5l#KY-K+yz z9i`*A%LXzHB-V2+>_X~@fiYs6NhU(!q+S1&^i0P;fUh|LI;eZTtZ8b()X=H%(!hfATCX{fP1>g}zb zB}~f*z%aO^0_t^xw>R&dgxgn7ZpI;r=`dtmz^hT&sEqN~*T=qi;GkhbS@BzM%~B5& zq3faU*X4r!gaIDVSm+61vv>*LdhnFxxN{NT_L>YHc#R{2+y_*B^9&P1lju|$WUSqg zMOG{Kp{v0I8o5p*ryijnm`($gIx z1Fk&!8snNt#!a5Qrw5ock5CpX(>ZyW;T>iB1TW*+j`s^8&!D5}u3!ir!64^K16N(# zX0r4ix~EHlUE(aB8dsIk#+Pq2$Xow`j%|Q4YK*%MZKbbekOq#Mg3uU4@AP;Y;4Q{W4sz{xQrDa69y2H>kN$kZ?_U(9HHiQr)v2N)Dcnh4x z8|S-kx|#HIgjt#aQ90C*p;*-jp<=AC9>BB2*|c-c#8I{!?Cd;+K;b58E*mqRN`T%< zb{q;u6*2c55|*8ERwOb`8D3n88%jBOz5nJcN+3(4=oIW|w&|VRhsy64@XCp$NjwWD zQ^x#WJCBfO-y>(1^tMvD3_X-(Y<40VNK_gX>~@%muE|)FanW7grE+ni-Q;kTOXb3j zK?TtAn&)NkaK^`3g1kX^^c2LAJ`Nr%zpl4w2ATc?9Hz$^EYewcDvlkbMkKwvRAw|l zSr;h>yXF+UT6T>^cG_<;J266e(~P6G42*2R)_6A!KF)4L2ie@=_)LD*@w->xPwL7} z%Z|(e%oxTeac|wk(jY~zp8xdfY;0OR4^70)rHf+y?@)JF!0&wwEzT^u98Q9L3#C)T zitgk&4DkHB9)C*0dl(`0SXu{|<#g|!K?XrCAJZ_jqLB+@(j|-vVGK}#qrDGNpYP&{ zGXU5(Fa$keQ|fh)*Wja(Nt}$Yr_%-KeVO{2bq|jheXqYmzlHIS@(vKE^VDeuY#ghNV(fl>_G$KKqilq&fu|dTQa9eQ1`R+X zKlrnV-_3+e-}%ciklUu;lMdniL!afOjD1e`#B{NWSCQUD?t!I2O2btjUP!}lf4At% z1|ZWis5B^#dYSMT{?_k?r<+Ej@FZg2C5<_jqv%JrgR4us2hZbe!M$~48;$yqALvu# zAPYQw$NubJ6obGSc=vlDOIZ&cc^&3Y$bNXmneaaQT4>yT@y+n+rEtM>(p1A(KSm^% zzy(c^f5#Ov4)*;X%8WSVR1(+&$+X(~*^cpW5C^^~Tph>hF|vq(QxG{ruKpU{O5qi0iw-LoE(7KhuMelD%Cx> z+wVKkLl>3$Kk}KP9}Jlg(#OcHL(rJ=InWkQ>8mG%gGluQ@uirddst3s=%8%?qqJT* z*25%nSmRuW{8$Yp=D+a1^V<1h{`5pyKH(-~zESL_)h|MJ04D+;^dz&-6^Ijc8e!BG ztgGx*I+)aRg|6Pp3$VdmLmPJC9 znC}4WdubyYPJVd;o&4d1nZ^RWN^HL!9m)h^6VE+%-1OW&Q2LD02gLxNKGUG#$W!db zhe%5rPp7h8Qk_Q>bP;K=U7Tm2y!H0Q&>Q8G0r)OxLOS_9GW2rT5XDxOaYLPWm0!P$ ze(4^Q(MJ4*;SS%gE4uWDN~Z@__Mex0YiX?`$XQ zH{07z75&R=MtKKAX`&tVYNvxas-@Y8`p6e8{KsbSA$hglzwJjl^yXUc&KOq8hu zi-T==f~Bj{#nMF}Q_E|6c#RXn)KO#U*h=zV3V4*q$+pf;mO5H4+PiJva{9fBcf8Xbr>?}Vo~nx8q!a~iX3_v*9K z+zrE1xk{$7jFi{+@8eyUtm%)uW1f0#jiDdvWu#0})+-0qpJHMb z{Nyq77-al(ZkoQAGN_*iHpB%Pc)hRq7V>v`9Iswq5yJ4s)p{{6`vJ!o$~`yWwhfxL zR$tt;Lz@C8fdUJ5Ybv_@vq7qu*!kKJ%Qn{%UB8*$y;1b+ z@Lq+&&d#t`+K9lPoQLkg?h~ZIwdrnROj^71IX4X2S+byV9(yzZU-wv*NrQR%Q>a*u zb%dB36FXQp1YBVF-hp54G!;ZDjT#3OC^4hR_gtn(e8fM(+C;%$XP}{HjLVu7gk%?P znRY^Uim@3eFO6$i`Y??3QbuW$6&H;pdK|4X5VW&#GfrJQH5e%X5xc~O4#ZRN_qm^i zG%~XmAvz*y25xH(gv#3DRphC#Hl+e}5Cela3fp(4m9A+VEVy4y-` z>(&m$j#?!>$4!PH@hWMb5o&Qwe96oUuo5LHJ4&GSA`jXr+g_p$TckHnVXVWO8te3| zk_1z97&noyDi=Tjt9#tBG^mrLX0Xsz*hxm2D%fd2`T&5_P;VVk`n+3+a?yrvvRv)I{qVEmmv}}^ga2C?%l;UJ>$EY+YCfwpWASUS$Y8$iu-{^dsDx=N9F5DOzlREwr@~zcZS&hLd6=$>Afc{az*c^~9z`*(jK=@DK}B#I^N z$VKUAo0o@dheyq8H+;ljxv1PvK|W1)db+Av{OmV3i+}sGj~V!k6@UJN?-ZvQAb8>> z*v5CuVf(ag`o=nUhFdQr2Vm+V4y@c`$NF+`Qtiv6X_oEYTUP7A_L18UVB>wzjCwSl z9wu3K_`McD!tZ(k*(OKUfOT)Wls^+h@cbf%GLNBm;5Y;h8cozYY%}Jc;aUgcBoE|wxqLnw){#4_(jts0 z;#(TaxLR+f9YiiEiwsp>jrxKvPIQT%dWRk$&)EQ2Lzc7|V_+zsJCIh7(lF!1UZzC= zJ;p!QmAq)3>vb(X4jRNM^N?*d<13}4OAf|Uwi#F1Y95x$tFdY@_$GggOP13aMy9`V zMH#J8IPTj7*xCNKEQAsZLe z%R>K!HZCEo2JzaUUD%gNUv)rfaM)lS*iPM=LjIHY)a~sn-0LjH6X2oCkns$bF;-EO z^=chkce$OZ2dSG1Z)uq&Tfnw2(tt7vOvei9KuQFIxb$Xwq zfxP2Hw{pYyhM*1fT!TLx&n;6P`ysuKV$vENNL+%yNtflO<&uWE&b=Cx&FdkbHLfK& z;rNn>@%n*zs8fz&e|H`JGem-d%HQFdt?or)pWA=AOf z0B;|-Ar5G;(dfc$9z}|oVfP<8Xh>oXg&m>Wi+7M4g}R5#CW1-fg%kmhN7>mU_J3qH{rT7~K5S_inb>>8vk8U2*EtHWjPR0hA!5i673CN7dr=MFM~#@2#HQXnq)YvL)&?0r#aAR2bu;aCW}s48kFUWGVV+oZ^S>0 zvwCn)4hMa9=+>VD6GIbOYU#X@KV0rGNM}7lCqGHqGz7Sp&A-!wD`D{zw} zh9F3$3S}cs&?nPA#oF;#C9&VUqn;T|A_GUDmkP9b`CXp!KI0`^+mt6pYG^WZjq38@ z3+m0~fD__peVIS#-Jq>+vIN^b z1@%PId)PiM;hl_O=3foManR9E-z?5uWxtqtqZ;=x_sX0YfR(3?Qn)Z~+9%5rQq<*? z@%|Onzq~$(@q4f{FV`cx%#X-Omm|A}j{~CcE|OKLf1Ny1SC-m~?Et|XvdsaDgQ9_c z28axD<=sKrjkEAE!!3WyFTBq*^nG_%@&d8Msq`sMdd@rnzSJS4De-J<1(gnCU?u4(WJItl0i z@T&QRUMH=YILYPrd!}KIYkk;%%d0hvEAlnI!zbZ&Q%11OS3{{nv>tCtoa|#tV&n(oY&B{?j0q>e0G2 zM1L8ToM7iVmKKh6#6{h4G9-8)>u9!$Px6M*r$bm>#@{j4`xxD7OkCz#d+BLmB$X{{Q@Fo9Sa%EyC zv-yE&+6cQJ6%z*P@)G!86yU&Xt*tG(Fqt=3|*6G1%ItH$_V&NiS>g zD;>Ws$|LOE6Kk<4S9DZHFFTnga7~YO{MB6?{4~=l&Ac^ZRRdT{1jx!{7EB4(uGHn2-IAGA);>n$+Y1Oz#A|Uw{wy zi59Q|Ck8aIR~Ht!LGpKS>1zkj$|4`&s68^w-|}^jsJnh5ZQzoY!;B3l`J8bGngkxZ z0%-j~m==zZ>1lsf#Wd4{f2dQVJM+e7|mwPjlWaI?}XC&O&v;|H)?IEIZ4z-=C|gLJ|7eh>5^%~ zUBIQ!!)W0WM)$Z}E|*s|kg-*k764EhoiwO*c5t9U7(Ky-_LF!td87>XYY-p-?_=^; zy}rz#MhCtfD3r8ljC)YM41~)vH>N-26EI>z8r@7Ux;|(i*zkUH2CWRu{eOPncrEJ{ z0P3&<{d&yTb=dv%-x{n;Kl|!B3h`Ool-<=()$7xHBOznpcRgk;m-T2p*;%@}w2$GzMU7&`a5n^FzRr9D0RYwgz7G zLp!u7a6&0?!VF1uDn%pm(Z*GOec-#CzTWT;oU|4(!|+9?vz?ugOF0BWirFlZcFRLS z83~%okY4k*>3zhGL`k6d{3?Rw%D*T~Wa2w-;!ogKz5vbEHIV6(W-S|qX8xA(-wj6o zUcFY+#(gFK)|jN!&FRu}eQ;HlY}fMajssNbu&Jcnp_DHo`flaQxTy4-N*cvYna;#H zP5|Q1Zm{zAd~Hq_&n1oaqEAlm)inNMKtrN~ro85tWB>p_07*naRLf~&S(Ih5!%Ht~ z28vvcjAwo}T64&F_lr)XIfI^{HhV}%#;`^ZOBP?o~$ESoZf!T)+o-Jix8=QJ}L zHN69$y~>kul0Skjyf{#N6PiGLDX1rrxi^&i2f0aklpR_NH~ed&Y&nNR(?v1 zm|8xHwBIUWUsz@R?ia5Da^Kr3jg9QHB@Fs-J0_*UcP`zkGb7thq`e(2WX za&y=`O`C)HRrBoXGUs}^GVF|Je$MHu*WlJvfuXy~npGY4ATX-%yw7RkK7TZieQ0F5 zNZZ^#O8f+6Su*@R%T=b?D^5mjuWQbWOrzb#o`+>k^G320;?J&in#*NBk$5C?isjuu z{a*OBzqKjwMN;6z8j_CM(2ttlN@#`=%{{kuzwc91N$%~86>pk_VhO7`&wa9Zsn3mh zBw;6@9r*5oW6rb@PJWxNdwpw+x%25|)9#s$vh1vhoq6XstznfNgOLtR8`9QE9oe+b zBV(}p^<~plAsz)rL)uy=2=peJG{mcBUb(NvN%d0_sR>pk$&T9$Bhpv#F=lFPJJRg- zBEw4Ga|bV+!*EZEf5;Rit|?~?V%i?NAxC(dnSY5x%?s$TI3>`4IW%Tv%GQ^cf!CZS zFiP4}t(>iMtG{iAQUB6-chGo^sgCdIF!=`^TGUT^zZ0kG`$qAiGK#5(8C;k5rBDu5 z!>Ck;6)@_LE$>?4m0QO_(pP~Vh91>)d)@B_^RT$>l}Gcppf`2pQZ-LB`{>%e;)BmQ z8IPqh&ffdos&p?f@$fHf{Hl@n;J4)g60w(y%6%i zaz_SDFT&G_&LJ;RYEe1CZ%xS#^|*VP8o}%pFR{eI>ZA%MvyuaB%Q(=dXOYcCU*8~J zBNEjovm=V|^!)45`!hlvk9uB>uw7bVhu@OGF{r)w<*x3K$`+zFa=N?7+iV^+P66oWsJeXCb;1hOM(34NKryZ{Nw1jR*l%W_ z*kEs^7>4>`w;;puIode!G*$*60e-E5axBruGI}a*Zx73Ab2)b-Q6_`mO&`X#G{nhC zzgA>w7p6^tHU-)gH~|zmQKz<@0D81hXj7m~fi?xc6bj@S=`}XWe7v~G(e~@H@n=7V zfnoM}h~+R~R#T|I6fE1Bo?Hr~5mcPE!=6Yh&lig2g{N%R&8d|$XW1~DO|CU0wMSLQ zT7|YpH@AJk5;pg|Ndx_qg?0hSt#^f+#tyR~x0}A!mPQ;aCT(C}B?`zVdKc+wlQ$*a z%|=o!m-Z6lxlfJzXQftOjq&xkmp+*`vB^Kyq5bHSX(NSfV|m+!uLniirD#*2O@THA zP96o?9!V#!PTCl?DbS|CH;@7<&IV77nc)<1s1FF~$x~eHsdtfr~G`5cI>9NZYJ+@ zEzUQ9KJ6m5ra&@uxbb>ke$_hOu5U}LN^14H#`?;B$n^1Su(VX@5Jf55+#yo5qqZr~ zra+qlZ3?^u1=@zBmmpd@Nt*&~3bZNkDkxAh7P+s@cG{@jfoE-0`YJMQ!~EJ&K%TL4 z4}S&jtJ^hy?FxGW3KYgVjYPW`1^k}caodP=0;=mop&)OxLu(2o%J8>MqSgdzzim^X zO@THAzGMot4M|^e1+=qoQ=m14HXGQaHtbNmEu zf|%QEZP1$=Z^ckhGZZ(aZ9ldt(566}0&NPkDbS`sn*v`o3bYMLU$q)*AXC+7xJ0piO}`1=Qdl@8w_Sf$P8L908a^ks;U z8vl3$0&V`WRw|*R+#Vg>$0OoH-^n`9*G;fhiv6YBLhFuB7}2)y1)pqachzBC+YSuk zuub3DqESXaTJ~=dl}K4fs`aq}Fd)_c)L;ob{@2pgBc;2Phxa1Z{FUbn%e;MTg=mXp zM|rh_M*9J?j1}4#y+*rSCcbZN<-#-qOe7>?=83XCYA^y|W10g!l{jUR9lBg*5-{p? z-?P3NikI}Ez3MAY_7theXD1EQhgP)Q8MgsePPo@~N4aD_aLM@8>Ma9Fg_D+hmC4w9 z(kO$gUQ3w!!8J+ryYfj6@YHU7S;V3|;?uHiWf;3%%lgc`ExeFhL8sjkRo<8B6Az{K zTlsqA+n_}*+ded`;ck2?aksXZk!q;Jc)2$}H^euyCjQEB^EJgMqHzs@e_akovC^OP z{UCq$C|Besj7+a;#=eib+PjQKJ1OCW8j_$QN>fMg5Of;En6y}Itvo5V)>hCsQK~dB z_4N~nlZ|RU+esm58;_TzfNj`g&ZF)%L~LxnEU2$#I)zUtH(m|W$e#2B1+m#g@!Ft6 zT?0Q_aWT-{RrJuFqXT?JxSDVD=kbTm8XThYct!br8DOpI4a%jW(hU!E<$>iFj7+T) zG=F&!d^$E6@PbFlnO`tQ->mH9b;m%fOsvQcwyl09TuRf!4q{{lPrg}VLdG!M(?fen zDZUID60%HLFAfggH4gpO`hGvw#y%5t%_zE*Vg4;G!iqYl4}`uaMP z-^t1j+C%iGYe+HZ9&+s!7n-U>)q($e&||YKr%iXzM7L-Y(MG_*_O4tFV`At>Ny>Jh zA7ve>)}EqGHA!JJ5{OsI))5Aj)ZTa-p`Hh6GmNq?&2a)q4URHji7!}RzM3HHPWAjM zjUub&JS$};O~?91u^lpnd)t%rMGuL3H7fm8$2#Z=Av8X4db`*is$^6^~>uCt4bjF)*<*d;AfeU-G9RuM81yOoPbnKs-CqlS-pG?c4E zLIN63UMBzMdQADIzX=v_q*0BhGfp{X1-G*7(rY)Pkl6>Rr9-!TQl__l<-PiwoI!MT zL9@(gJ!%8UI0eOf#yhue-^FKF~r3- zBR(abwllM>21`_{Uum&RTg+*Un`9p;GtbE{ltZPnTK8rZ@Qs&okuK)3vAetqTJGTK z)YqE4CO>liwT5MGy9DZ~V+#iuX;G=0fajmUPkL4OC*V>FiI&?Sa_ppsj?Jap{~E0S z)rmtCCfQtDEjE`Hi;aaR#pXP}l_gLuir!N*MgN7@imsvI@O;?8sVA;5ZG`rtfXz|4 zwz|HK9NsK?Xw?0^Js}&L1<`)6zK-7&C7X6^cADHvuPy}9mt{<7rg4Z~L94}4{VE6_1-8$xGc!zy9{|y+$f$}C-2iCm^=;+`M3+x2N7yotW z_H=cvc(%G;%&)Cc|J%jv&|qKbv?jnn~*+>2qck9K3&)%$ z=)c9qpKY`{NE4(u2A=k>Yseer<2H$UrL!`pq=88d!Z40>SS7yj2l{$L&su}F(qH=KJdI$S2pJ#g zmfv|T3DX13tS{*=z3fAq%N5b9Y0LY>2OWX8_1M7`ROul8 zd8`+DN<+l-Af1y;LjJ*X2HTSO`^$Xkd-ElF&^f2ER&8^(dCT7%Uz*qCaMX+K#qnoq zFT&fzu)j(>v;L%)b>X#TFae0j3(6tOWf@H4d+SX&3BOX#@+reLoAVz%lViih z#E8a(B%HoX3S{BDg7R=@ak=>MgGa^xdFyU5(bre}7w^AaoEpWuP-h6raoiy~AZW0> zx?23xqo>6JeDucHNO56!u;^nplTUjK{^LgInBYaZ=qOcSfB9s-`1zwJ#it9;ql5X2 z%NL4sBg1JnV{84Ga@-qCs^jfq8TvlLfN>9ge6+GsoW>yWom1l&1bWrN+wypCGQRZJ zpg#)hDsAlE(o*ri-@IG=!^6kLpItaxT!t4y;n2IM457GiuY#gd|G|@I#ee%FBiMDe4S~2&({SU>`wU3HZfBGL4r~ci4R`g6vCu0)*rERSpjz9ru=#~k~x2_d0 zKKwbfohrtE=MRhSp($px&?_r#=?J;BOd2!X^y>@rFN$A$a;uno_B;ku(-Wh`yRXd_ zlT%~K^8i`fK_9jWp7$O-EABjeT0BSY+BaM|H(k6oYrE+vp3~>vdpuvMB(JN;3kxdSmjw@vE^rz6~!_V&qZnh2OUI<)%)VC>qHj732w)>Cfi_dO9 zj5_(=JC}`p7pgo-R&} zj|AL>=S#(}uiYsgJ$)W9jH3ZFnKOK<2M3-GxGzBSOZ+C#r+j|0uvFZ-|AhKlDwbB( zsITM!VclmsIoO^Y8-gBFW2k__Cw2+_)w!mGS+lNxgB5+V&Jnp)^pe303K9$0%5?)L43FHLJ`mV zw2jBlUK9(|zaH~lt>hy;R(#C2_4QejrTOVP7a`urS%Oo$qhYXp3w;h+-&OS#T$VcKBW#SqHY$BfRzmvMX z0zbd^`fSk$epqR3I?n#%5QChhJP)2M1ibkd%VAKHM&_--OB}}-zr4;E*tTJthzW$I zyiUsFi83T>xwQAZg;x6A0n_l`L9U;L+@pu7Y`s8^icl~`m(W!K3pvio> zOP;Utb>^MmU;eD4ggm2z{S1T0-`smxoJMdzLjYeupudP(2K+pCMWqI(65~+(2%jvY zl>7>Vi=G$5D3Iq+E($zn1Gaw-0r1H9RLXIrTk2ME@tn5t0Ojfyij`g@4#cD0k5Kp3 zG9HLDD2 zyS;CfZ}nYHchLN+Y4d#zhP;4Jk{)XFc&GjP>9gXm?>{WAqWG$K$`8h^;-2NJ%Fheh z;VqO-y_ue%$Z7PgDsDNy{jN29%81qcc1vp-aWOyheF)$DbnZ#n*0G8`15rJ&SLW^(%Xh99>(3sAaY^GwM+XJ~ zGCD9`z)X>wQuyAmLmx@6_xyX5v;xlQy?>efP1hu0~^!F19_SEGBA9lL1hvhM1ADggBgIQd06HL;ADxE zA<8?iF&HGCUNJG@hHM$bxaB}Q5LEAg`Jd5mI^cJJY0&T_-WtR#pL8&ugEi?hR~kz! zmqwD&q5hzqMxO-+$}7lUL$p_}ffIVF#tscP8VPbE)rfD)GoRJjMV38W=UC$wv-IC0{}U{Q=z} zPe^Kn8ejs!I0kXkP~!xy1|p4V(nh1h#k1fmQqE5ppa15g0Y#&x29nFfxo~=d2%}Lh zjYD@Z5PWj;e&nH-oxE7h&z#NIx|Lo-1AWEn%tTqA1)e_eGrF94JY^8Ckw*^?2iO{c zyqazSTzb^?fF=!vTMsE0%QjD)J$kkfMkVpn@F@@J<*z|Wc;P8x9r8OjJpxi`(+q^bauxd<4xj*@x8C6Lcr}K_i+-YrNCj zMwr%7@c+&ItgD-MvW2D$gzQVOQR~CBY!fNKl?E5tip4abpstB;aHY0 zA$s_~mJ__Bsclp_CQZz1fS>qi5V0LaTLBNtW1VP}{pi}g!1oOdcSV1qF@J+MLgoNl z9ucOnZ&OywXMTJt2H_ij;dKezcu9HpfP;oEK&eF_;Y{Lk+^ zj5c7t%78G^lZp5`iEsY$z3nuW2Vi8G@eSbokykbRXs{C{+qz_md+=VsaC8gY9P4Vh zw>?=N^N}Bvui`KsZSCk~`3uU^;^!Y0^FRHYV)^!GMfcR1 zV(9YQ#qjt3u;?75Vm8)`fthnf*T^{2tjxSb?KF`%;90#k&{7-7@1o(-cumK*Fi5q6 zQ3YAzCqqOaexwt&Y5CqV_}-t)L=a{ zH%4h%qP%N9(a7`HJ>utht?J2d{`{tk=sdG3k7!tr5lD+P?r5RVyjd`|f|XJ6nu=7? zo{NQvGLg^Agp6n4Bm96T@9YoF?1Ki)bOxyq5PN{A<;zUP*78;e9tD}5?pbEHqLR!2 z{^{8AhOkFnqzK3NH}Df&lr0gO+T?|nbrdHAqRY#o{Ci}Z_%Hx9#8N@F1Kcf`W5Bju zlGiHj@uPV<9F)fQrhMsp zHMLn>veN{&D=ceMe(e`(G$LY^mhnEzGfI_sx^?+#FrcwC&NWOV42b$v|Kq=122L|JeTy5w{2s2d0I0L`Pnpm)b$kS?iaEv-A;U1J$HTYJP zp+#M%54-=}0V79!30OJlp|3KWh=dm<^@{E@{QG}ebYFb4=zsgSiq2uYRG4gQ=HrHZ zf(9iWY}Y;mFVZkcXHBbX#6B?Az0E?;8+DG7Piycm^gy$1CO!g1)2hr%tm6!zsVs4zq`spL^b})}`p*=DkU<7td7x?D4n$WOU^~ch zaIP^V>JOYYH_)LNFgbg#htMno;o$*h>OooKf&(|d>A~Qvx}G_Dwm7ptg5f22q)5w^ z*753axs<>sMusq4jK-i{1A_*u7Yv3qs62Z5qBu)F?_E6~Gx&NTI0!P+W1*DDgS!LL zbKo$9A;msj1B@O5;hhbh8cy^c86O^qL0mruGrg~(@MV4JaUy+IDVsFc_|n^RyLgR3 zzs9&Oy#}DCysmLUF9zvsuzr+F!at9v*u-#uvCg~HHuz55C1B6tWgu>2!y`d|4IK^; ztvC5qqt$?Akf{jjf3XCNG7QtN3x5Q>rDJ>t7h_7x7@Sh))5z_hvwR-!UE1anA@}9k z4uhBVWgHD7%i#5lfva8*KmX`vadZ4h@vYa+WB8ejC3}{^JdA4|mRVz?C|cJM%043K zDbBzQ?|25l;^vYcX_hc8Ckx=^HE2CVdGzKQ9m4Y!8b@AD<t3F-PUImCdZv+nHH^8D zH#BZYzsb?TXfsitwMZG)m$}kMKnrM`cuHuO5bB+$O2Ciat1%@I?MS8HOYY#q>|m9A#(a*5`O5{e7|i ze4*%>IbRGjOEmt+|E%a5JH@8k%*azQos4|+UTPWmUQ-}!!T!p3+ZdmynP9%8Od(zT zZm`0uq4Iuf6J=M0%b+5rAkRP3HK#S6`BXwpxS1X%i~!7dZ$luzcrV;m@G7v5a+{Sy z6^s}Zi9P@=uAN+TE>U*q=!X{Jpp0$2fV~S0DIXnb**PuJxD-ALNb&0-z3!#~#6RNK z0+z1=Wv)h7Amp;8^7uWKO<=~^E95+kCVncqxy-^*n0XaYD^T%`w5G{|pq3}eq;n_~ zTr=Eee9X7A(?Py72%9w*T{~SBihxTTAjCZ-tkIwu)^R@9XhVVABggu2!{1PP!2#|q zdf@Rv20gKNj&y1fvO;Bg-9gj86wxj`&&v3w6^&BeAhaVwS9m&T+;DFu2dBc72Xg%g zBX}TX(ePIc{bu~sE`m!Ut>45o&#DP0!)yR@%n#zyStGqnJ;;d zir+KW_-Y*Rk1`TFmotP0`x=!?`CvC5h!mzHP4FL?1U-P0`6tt+6;GRA%GEo@Ho^fXQPpUa z)7Zu|4h=Fp=KGweN~=`HRDSU}%`EgEvxH2!a)|-rFiTW&SZxF^yRmJQvdoj$~KQ13;T+KHbPA}{$b@`R2#?*@Q8)ZTMrQuiCN zo_sHTGfn|b1a=8lmAM4JlrVC1VaWp-mT_Kf09$z0ux-A>44%LJdqwY+w~K)@vqk3s z-lrOcB0{?C_?t9!iIAdErfrN5KF0>$N@x2<^%>(5G@67i<3qKKri})wbaAZiOj!>Lo*zovmWNXaLaUCOftqdk>+*arY>d3Fe88S zfClj4=HiQDyj_;4{B6JmK(nUSk&ugU7sBmU- z3=b6LJ^6#O{e84Qu3JnTWmzCL-aCL%erb$wVC>*oo>k5-GthO(+5vm%wXP<$AdnkF+fc`lvt{A>tQU5 zafx29gVto13z`W(WUn-bRxV|+PSmwDBAJh}!K?b}Q@pnvBx>NW&aPmb>cc=F&6H_o zIs{G{0&4vV@74X4SH5@onFGsF=qdTC^2WM%xy&dNNE+6X2UU_=5s&!NW^802JcO(h zb!d%#1AQ*@nP=OGCsFr))1%@v{AIq%NJHdlxyehO(Q8G1bi5@CIuO?2p|L}wgq|&F zz)gE0j_IY3K^VT%IOM?l47?%zq=U50!FwmtrI~S4xf=P|e!Rc0yEqR|>nSC@HQH%N zw5{lArpM7J@b$J5j`$k9W<%022F8`K^)OCZR~jWAJYkGMA9?{phucJ~0K1n77HOoB zC;Dp3mcYfc^xh_(UN0H~{boFW@}qc#R~URK8bKGn2~Pvj#WNEaT!y2~NXI_P_UPFn z#?ATASxhS~r!&ofVV?F=8l5bsaH9>|j{QMzJq|S_na4V9Tl(kVy=_$3dZW3=QXp23 zlA|8}3NnI`M>EgLdwLIvo69jFPT9w3tWD*y?(;8DwNTX%X{2cy`{?x*9#@n@Ii%A; z7`%A~A~IX{N*tKrB0qp(z32rbPixE#y4dG2Q6QY_@Q=oIgPv3y)L-V`cnNZrYaIiR zX{E1TVe*yviDuv*k)?yJ+3H28~; zZA)G@RO>QcoUlJ%t|6(cfi0DE6?oPwZ=v*TEIcdLp3ET`+3X6>qn^Q`qL-TLatCxO z1$8 z=#j2YgP`#3G}dLN&N9g2*`S((-*en((3J?j5At9Tw6lti3myo8DY^Klq9YD|&%p+& zKvXzn*j{~yiI`|$LVPXb3Lb@!o#hZS482^d>=U7B~$hIVD})C7$RE+rd`CZ1J!t&pdY8_IPV0+bC-htdpLxploVwLertx5cx;~>95OJ(8 zPxLTt&M$x*;~(J)UvD3O(q!DW30-7QPu+@8;=fzg!jKlyyBB_>0)yu|Dcb{N(IVF| z+Qk@kp;5x`o_J?xn1lDyVW1=JAs{?yFYNfnb8ONny`hV>pbBN48Uw3!Mv~wm_ZW!0 zka01aOZ~qra(SM87dxq|r_}K?yj0u-+#ugfK&Ml&M3&_* zlq(F4RLxU`|oCH9DT zI){AgEBek|q`>`5#<9->6LRW70_;6?hS%`*##)>ZSIa?zjr>X44r$3hb)-%8GhlY9 zmod@TiQZy7jX3U45uIeFkoZtXou0A_PKD{07ty8A!xquQ(7)8B-0!lZi{)Gev%=sO z2BET_$@l;u^3j7zo^k-%i5GiEuSIXsV8o%3 zXp8+dw_zlbHlw&Tt8sOIR=5z3iFx0;s=#R4;#mPJe-rYRU z;SJT(#jHKFN#8NT;H4ch9b6oM1&@@7WOSsBa!*ee@zyKvN@HPVQK^vQD0C0OPkD-g=|AzsRV>8tVL)YwQEUPRk`a`4%BruRqY zQSDK@0Fvb5U|(M`#X$-VcGZE$MmCEn>dShxtQx$ov78Q2fuGBQy39sE{LCYk?tqUd z3PTSsAKYu`I^Gnn95_40GBz5=E%wF9`amjD8pvmS9r%yXKe)`tpZQkl9r*K3<4w@U z2>ee1Tb>rJ!~+xXfXn*yDAKD$qnmqKeZcZ4$1%4tY-%hz#~|N9d;*l}Wds>+ds2Q| zC-$*@kMPcbXlxW?BVMqGH#~NWve5^0`K-H^7q9tBH-hsFL}j#IBv zx3=FK>lH0YL1|)W^$Bt?tl{^%|PwCp4Y8KYaD6x%2rx+8ZD zp;WPFl1fClL|X(J39T&@1W$Tf2d0(MH*+@NI^(qV_(8FH|8^LvJn_xb-}+9^7CpF- z$Lv*x!!?e*Tu+7{)2?HHTEzugr9|bV7X`0(W+g4LlXG%qd#^&JLG1ZFiX0p1DNHD< z3f=lM)x1ssXhU2V{9haSkaTNFlg|4os4(_FqZ^bLVlSaYfx^ZE->6>tD_zLC`I@jnp ziEl7ntDs`H&5W%^8q4Yo!#o{^Zo+=kFh%CkSu03Fl9YflOgG2e0QW@%l$$Ds5aT(N zMrfvhm;(P;YYPBTF>PFVc3^JDZ--!K>?~X|8nIsz>BPlZm^Ay6>eYJiS;6USk+4d# zwJPSJ-epRO{|1{oFG3r=3GOn0yGt1zn68xu2=`z~3anW~!5Z=smi4ts=WpCQ&{i0p zNVZDbaiBcLz~wyOE>OWkv>z2p#c8TMNPV$QrIe-!T$af8K>l+OE86@9>u6o<()#RQ}o=P@_;MeQM&54C?8KSQHb1~NT0smI`6ltCGrx)PKap*Bk z`Oae~lQy~C2W`MZI%tq_U~lL4h_qo0;M+3tP!E%vEK%~rycLYU>bRcP=YW0`e$=>V zd8`M&dGCO81z1m@v+(ZOPD)(NYl_Zv0v^zSCarVbXIcx(cA)X`!7_V!@Nbqj<>3E0 zb+5O}=j_w<7~C|FO#|-|%B3gWxxuFCpAb(vj8bL|5Ax5i8N?0PcKCjZx|WW@`Zas` zSaxY`KGHMOsa7s=3c0osds@j$^7SJo83v)j6J*dd`vINeIv`IIUl^u0_zZZu59A_w zEh2j~+FZl?OQWX0)5vF!JPw1Mkxgg9O2~D;3XLCAM)}D0CXa7WU)D_@@SU77|7O|> zDuXng8oKe*GOdDY8acR-o)Ov zWF6jMkB2_$`l*U1UmQrwm$o-$tUO~7ufPeo8|(C+v}+A@E|Ig%3vaB40W;rc1}G!X zxhw&o{39P4^axnnTrK91Be&!i%J&>xMJ^0dlmx8fx)9T#4faS@$J$Q;c3HJY! z_nuFBC0BlCmiGV(t_)BGZyN2p+1+fCO>#IiW5m!<)a=BrLW+&pulwidgCce#Hng!j ziq%Yvs1=7RikzVvns!7xG|=z`pb8!ZpisU5=y^`w`?{(E&<%i%h)tm0_rCY$%{+PX zWai0Vo;=wsxE)JL)Q)RkO}LY)(t-mp=MB(=nRuT;DS4S?W{*F{M;gCKhOf7+?J%S zs-9@Mocu+3U6_M5x22VRhCpB$ZHD0ml|=&cD1zXWbg5TrnX|@44Dzf6T*{{I;&`R* z%X%vamY&ez1@s)FioUc(C(h(?n#uQ++Y<2=B>16Y=%}xPM}7&Es9y_$SbhP=(C_L0 zoNQ+wG0kbD@YCa#p_0xbnX*`sr{!`&t^U1+`jON!engB+PlTKvb0mj z>bS-UK1YoF;rl!Y`n4F*zm))mpq-$C@oi6MX&1_XK#&V*u7b%+$0ipnjRPEKh*HTvd8t2 zKf|w_uDq-8k4$WjG+HWxKArsiD5^c^mX^3(=4fFg&N5TiF6=sgvQ2AsXgeE&$8OJ! z{)>Ly_&JT5l(=-BsP&a?zOsKF@s5TD&VlU@Y=7;oI%m)IP?8hhk!E^*-v+~7CFRO#Ir;nm^urkB}3-fGqyNk8XJbsf5`Vi{b z_gw1#@^=DIQhBl(?T9qjRzdLn$zT5@t~Y%3PgBRxNV@UnKcuM--b9#KVIz+_C|u2a zuYKifX&q^`3nsZT*^!kF_s!3oILzOnG;{ppupH7yQ_wKaN(dlU8@`&lckWMXUi>n7 z?BWrtTqqzaBr0h-aGXB!KILM+Emla2SXl`sb!^#@diFkt!sE#-CqpsPyjnoZINiIR zO6>z9oF3ALT+x`I`C)@6FVL%{i=gEPkDQME#i3kK4|cK&vIDe&Z%4d|fy9Ak*gdG+ z3%>EbRw$k@qB&qRJ~~iW2}TAs(1{~X@;Bt3(;Wb+4jvhcj`#UJqhSu%WP>Iz8?T76 z9kQmU)NO3ArOh<-d7wGE=C79BvOS6*|ExDBwr*=Wi1PT})$0Ky^u)jj zZ^%f?mLVp8I@s14qR}EnTI?8|xc!h*r+#zsN`Php23lD-fpn5AXwwBR2N@V&<5VZN zRkbZ*cqGC<@G11~#Srtoms+6d_i9jQhX?HQl%f7zdL!BNqJlP-j30z!=I4mQSRHW zmgrw*;i;g>8=T_x(#G{13y-y&%8Kn7CEyb#*q*{Ce-{|E#{t^T0=ftc2r|v^Jq|b^ zKq2s^Jb04ZZshUB4gIOJn@K9vMEOWpOPsgh_q&`zCrBb_XAG@&w7fBm5tN~4aNzyS z`gQ3kcfuUhj@7uusd*1RCdBohUJtDm30n06D_ zlhXF~>!*xNpe4pGz7KOUn&mo5AL;fj0Z@;(*TJ-Mv=QF!;(dL1eF3>{LwV6^(LP1R zLjd^_z?k*+n+sQPT7N5O70gsB1?BdyU6cBeJ!RTJuN>c%kC6GMbP>M(5{LI^s4usV z3EJwQbBKw;SK-3}R?R(CF+h1+h;LrMc zx~Q|Y$f%8CjPaCw~5Y*IiNQ*yZNBKQ?bu!wld78eVmpoqCyfJ-k%RmO*uyTk} zQnpzDHbK85Oo-mbVYesPS%#S7Qeh+$W5_Ul#~%+5rk9av2MPf?+o|bOzIffnfji2HT4~Mi7^m*{DY+k6KZRg%$jWM}5TN9DSL<#Vr=@ z)UjeRW#f9p!T!DlYqrqOqyyS3zyo>3bH`!yK)0)C`6$nFC^=I6EWS*`NxXEcr-mhd zF)^&XTK`J$k=%>B8Eo)}b>}2EkGb4{*X81|x@`2TK~70ikwj6ejyJww_3T>V{;+vOXuB6xg2}E zVSS@Z^q^-1m_!|FwQ`y?wlYW97IAVqzMJ2t9Z^=dkVgBEHw2#rKo~qz&zJA{SwyIO z$g4aT(qGR7^alYg{pH93`Q^4zw=TI*yl1pHEGW{FAYO-V+hhd3LQa$^0EHSJr> z@4D1a{LR0RJ9yIyzvB+~Ge_&Gl;tMx>3Z_H!*&Xv<8EDj6z{?R3YbTlE?Nb6O}lX( zr!0iEA39hQ<4`MYQl&NPVaNl4-n&Tb$^N3}{XBx~kwFMOgW3G~GoiG=*>HI1b}w1z zfW72tB`6{btbl3dLobTc$-cCf)h#;{1tXYk|$LwQYBl#g(}Ei{p{L zl)F0Ts+d+!Z9H?!h34&R`}$dNLveuzh~vXok^)cJ1aJ;W59qa zh?Y-LAO-hY$|K6)Aam&WSvoDi4DxZMQQrmf#l5zo>6YL3?HDbyevew^HPG&k0{2@$g1g|* zTdRXp$c0t}&oM#OibiXJvh%Jd!BfYU;#9Z4J$WXqR(2p~S|2!p)LKEnO6vs&L{}a5 zF!|pBZ>J^@JhkjbE+j}jDHZs3D{lC)GPs`b-rPfltOZ>%vL9Ab>!J)+mBUkQ6|IAc?#-2($_& zZtq(RK<>2Bm`m~sm&38wug zKq$-n0>F+whXh=dzpKce{1ChoIML!wVBz$wTlg31O?v?r_OAEz6bh~25m%7reL$oi zjg2Fh+1g7BqEY%tt#;1R-UTnWQh(CB1Nj+LxwcfM6S9tzO6vUAoW}O!v**(pfUR9j zFa;0C>AMAwZj#oH-rfq&1$F9zzxh*ct!L}b{H(uI{MJ9x0M=<+;j3-JKFbv9dJ5B^ z9fh?J@*$8Ys43tp57$Ar;MN6J3k8YXR(OhUE#Q>TwbZAHDi2z$ou(YNQ`=?U3c(ma z`UWkO1nUg)Tv|+jiz}-VMZI8inR(5X_46T?WB&w?{sEc~E&tqFK#U$ln0 z+Xg*aOBv*qd1T^lr$g5!)88IH9l)r4nHG7bGKMLKa1Yq1c*0vT$vn*N(D!DM_cwjMX4~~xl9=Gu+ISp8&&f$q~ z9mCt=zSh*fqq_jDlEP{UYoJ9ejs#rX&ZVV}&)U{A`AJv~G~w9v)F-6%_o;Jud*~?Y zEdn-fb2FZnz8`6I4DX1A zvn8#up-<^+$9h~L>>AyT1r%1-FzX@J0OkP6w+QZ`G3#8406bp896Q|RyqaTFjRxB#An5o{Sdx2>{CM~PN!V5VP zP?A4uUj8a_NSX+S((|Z-Y_h+15)@m*kU#YS^&q$F+~Kd8KCEEF{j&|?lqddt$xA=f zP591}W-XQh=88ns8}x%y#Efq%!aMOtybRnhS`_KnO4Uo+7%My4u>7O#*pEI^<`k4H z|B6D@WF?jPUX;1KUjAaVeM{zqR`5ug)j_4-$)S3f|5gs(&wR@H`MdZFUgr0V!AKW+ zLZ+uNwH&XcS$bTkb!)!5q55O!04ww{k(>3=m}K$WdI?t=(nH&eh!A9dW_>#ejqzOG}M0C*D7D( zvxhOrIAPga8;NW0hdfGmA&b>0Qdd0BG4msCYoX>`MW^uk028!up;i9XTBGF`NIPd_ zV)<6_nM)oIxl{kFE+UT{)0{`C!wVvf;$KNU+#Lk&9B`pstCO%Is(ZQoQKTQ)#Yf1C zr7TKX(8-OOIOW^%VJx_V4CQgI{w?2aD0(%*Lta&SM#vGQ)v~P{ zmm;1_ro=i=blYKWWB16cAkg`xTNmA#B7gN8<-AQf5mb`b*Gc;lbFBg9QCb0(g_Ap( zE8fxv7c$$)LOsBi_2WIQ7?n-ui1xYm!Tw$?6l5`1bqOmdPvxyP)aISvqgpic z;hSj|#lzq2gQKY(g{W<4TUJ&W+`Cx0YVN{1$bsGpENhr|O;>@|(!lLWDn53st*of6 z2MB5bplM;kqEh8*R@V-|A<~NR(cG~Ta;0kS%&~L}?7SrHE_AqGA>E9;=X8D(m{XXln3U+1_)j*i5ENb4XcR-5`U zx3^(A*}5lsXUMX-lA;g}Jt96KKO zUc2Yj`=>cN*JE8hZr5@N+(?HVh>D}WkTm^vML}yK9q;XAK+P*K-Lb{YvP$RW4cX+k zkJBjZe46hRD^ir#F2IS@D30$`R6;>S`S$o$J09cPIXdtO7@cCUwtUt_Cv-XLyK>}U zzP1O;89HSN#KU1j2I9rhDXizw4BJ2C4>tEAX-ZG}SXBMZ>ojLG_GCienKb4*gd zwwaY5^4z_qH$>wK9pibnFq~$9JxM$#uTI<=)gH^*L3%G^z0fo|6x(&STNdg{3m;e9 zoGiYLrNZx-e11%w{pYcB02~|A3xEYZopf8Iu^9#XTSJ@D^ORX&z)9^-&R>p6`-`M~ zK+ur%3|cz0(}8O-(HBZo!5PvqY-TWd6=2{QCZt;cAOwpXn=Vi#l^K(UL}>$MTI*-xmN%FEN>wuUg}1u@r))u-zjlf7XHE z%nweVOWUch*YPFs?Ez$qLG3zn^%mgA7!&+MOwJc*Ya1#1C?H3ai+px65&ssJTmm-& zw8sDz4lx){bM2O`tyq5SC%#~d6M47KY3VWy5THZnkVEQwZvJ-qgg)gU`Tgd?WdMM= z^a6m)^C$;yJrkr5xDZ%4jDq0S#KV9fuWp_VD-p}W{|M2y6gpZC!<*C7#1p`rW4sHCK$KW5x�Xq0Gcp@j;oC4b%NH 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 z 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ΠSg75xp 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

ofW%P$!|det&tw( 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ÐAw#c?T! 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%0D2z<##J8$h<91l>l@HB#wpsA2ZYZQ94u2I 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

3#%yDkEZtuXUUC1)H2i3QttuE>eG{5> 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 z

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 a-*y>1LXL87C}xQXe;AY#@`aW6t^Wlz4L8^T literal 0 HcmV?d00001 diff --git a/docs/img/autocat_icon.png b/docs/img/autocat_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..df146db31a777072a76c97d4b91b87e4300756df GIT binary patch literal 7250 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 A)&Kwi literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 21564add..00000000 --- a/docs/index.md +++ /dev/null @@ -1,17 +0,0 @@ -# AutoCat Documentation - -For full documentation visit [mkdocs.org](https://www.mkdocs.org). - -## Commands - -* `mkdocs new [dir-name]` - Create a new project. -* `mkdocs serve` - Start the live-reloading docs server. -* `mkdocs build` - Build the documentation site. -* `mkdocs -h` - Print help message and exit. - -## Project layout - - mkdocs.yml # The configuration file. - docs/ - index.md # The documentation homepage. - ... # Other markdown pages, images and other files. diff --git a/docs/javascripts/mathjax.js b/docs/javascripts/mathjax.js new file mode 100644 index 00000000..5bf8e9aa --- /dev/null +++ b/docs/javascripts/mathjax.js @@ -0,0 +1,17 @@ +window.MathJax = { + tex: { + inlineMath: [["\\(", "\\)"]], + displayMath: [["\\[", "\\]"]], + processEscapes: true, + processEnvironments: true + }, + options: { + ignoreHtmlClass: ".*|", + processHtmlClass: "arithmatex" + } +}; + +document$.subscribe(() => { // + + MathJax.typesetPromise() +}) diff --git a/mkdocs.yml b/mkdocs.yml index 843b8d31..8fabab2c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,9 +7,17 @@ theme: - navigation.sections - navigation.top - toc.integrate - logo: img/autocat_logo.png + logo: img/autocat_icon.png markdown_extensions: - pymdownx.highlight + - pymdownx.superfences + - pymdownx.inlinehilite + - pymdownx.arithmatex: + generic: true +extra_javascript: + - javascripts/mathjax.js + - https://polyfill.io/v3/polyfill.min.js?features=es6 + - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js plugins: - search @@ -24,13 +32,24 @@ plugins: nav: - HOME: - - index.md + - README.md + - User Guide: + - Structure Generation: + - Bulk: User_Guide/Structure_Generation/bulk.md + - Surfaces: User_Guide/Structure_Generation/surface.md + - Single Atom Alloys: User_Guide/Structure_Generation/saa.md + - Adsorption: User_Guide/Structure_Generation/adsorption.md + - Sequential Learning: + - Featurizers: User_Guide/Learning/featurizers.md + - Predictors: User_Guide/Learning/predictors.md + - Sequential: User_Guide/Learning/sequential.md - API: - Structure Generation: - - API/Structure_Generation/surface.md - - API/Structure_Generation/saa.md - - API/Structure_Generation/adsorption.md - - Learning: - - API/Learning/featurizers.md - - API/Learning/predictors.md - - API/Learning/sequential.md + - autocat.bulk: API/Structure_Generation/bulk.md + - autocat.surface: API/Structure_Generation/surface.md + - autocat.saa: API/Structure_Generation/saa.md + - autocat.adsorption: API/Structure_Generation/adsorption.md + - Sequential Learning: + - autocat.learning.featurizers: API/Learning/featurizers.md + - autocat.learning.predictors: API/Learning/predictors.md + - autocat.learning.sequential: API/Learning/sequential.md From 7a4552d5caca8c7a1026e69736208d56eb83dead Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 3 Dec 2021 18:40:41 -0500 Subject: [PATCH 156/239] add data section, start writing for struct gen --- docs/User_Guide/Data/hhi.md | 6 ++ docs/User_Guide/Data/intermediates.md | 0 docs/User_Guide/Data/lattice_parameters.md | 0 docs/User_Guide/Data/segregation_energies.md | 13 ++++ docs/User_Guide/Structure_Generation/bulk.md | 8 ++- docs/User_Guide/Structure_Generation/saa.md | 64 +++++++++++++++++++ .../Structure_Generation/surface.md | 14 ++-- mkdocs.yml | 5 ++ 8 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 docs/User_Guide/Data/hhi.md create mode 100644 docs/User_Guide/Data/intermediates.md create mode 100644 docs/User_Guide/Data/lattice_parameters.md create mode 100644 docs/User_Guide/Data/segregation_energies.md diff --git a/docs/User_Guide/Data/hhi.md b/docs/User_Guide/Data/hhi.md new file mode 100644 index 00000000..6f08cc05 --- /dev/null +++ b/docs/User_Guide/Data/hhi.md @@ -0,0 +1,6 @@ +The Herfindahl-Hirschman Index (HHI) is an index that measures market concentration. +Thus, in the context of different elements, it can be used as a proxy for cost, +as proposed by [M. Gaultois, et. al.](https://pubs.acs.org/doi/10.1021/cm400893e). + +From the tabulated values in the reference above, we provide HHI values for both +reserves as well as production. diff --git a/docs/User_Guide/Data/intermediates.md b/docs/User_Guide/Data/intermediates.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Data/lattice_parameters.md b/docs/User_Guide/Data/lattice_parameters.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/User_Guide/Data/segregation_energies.md b/docs/User_Guide/Data/segregation_energies.md new file mode 100644 index 00000000..d0d92d3c --- /dev/null +++ b/docs/User_Guide/Data/segregation_energies.md @@ -0,0 +1,13 @@ +When determining the stability of dopants within a host, one +important factor to consider is the segregation energy. This +predicts the thermodynamic preference towards pinning the +dopant at the surface of the host versus burying itself in +the bulk. + +Segregation energy values are tabulated as reported by +[A.V. Ruban, et. al.](https://journals.aps.org/prb/abstract/10.1103/PhysRevB.59.15990) +for multiple combinations of transition metal hosts and dopants. +By definition more negative values indicate more stability towards +keeping the dopant at the surface. +Values where the host is the same as the dopant is the surface energy for +that species. diff --git a/docs/User_Guide/Structure_Generation/bulk.md b/docs/User_Guide/Structure_Generation/bulk.md index 4022a951..6e303a77 100644 --- a/docs/User_Guide/Structure_Generation/bulk.md +++ b/docs/User_Guide/Structure_Generation/bulk.md @@ -1,9 +1,11 @@ -`autocat.bulk` provides tools to automatically generate mono-element +[`autocat.bulk`](../../API/Structure_Generation/bulk.md) +provides tools to automatically generate mono-element bulk structures. These are structures containing only a single chemical species with no vacuum and 3D periodicity. Multiple of these systems can be generated and written to -disk via a single call of `generate_bulk_structures`. +disk via a single call of +[`generate_bulk_structures`](../../API/Structure_Generation/bulk.md#autocat.bulk.generate_bulk_structures). ``` py >>> from autocat.bulk import generate_bulk_structures @@ -22,7 +24,7 @@ Ru_bulk_hcp structure written to ./Ru_bulk_hcp/input.traj In general the following structure of the resulting dict is generated: -`{SPECIES_bulk_BRAVAISLATTICE: {"crystal_structure": Atoms, "traj_file_path": TRAJFILEPATH}}` +`{SPECIES: {"crystal_structure": Atoms, "traj_file_path": TRAJFILEPATH}}` If writing to disk structures to disk via `#!python write_to_disk=True`, diff --git a/docs/User_Guide/Structure_Generation/saa.md b/docs/User_Guide/Structure_Generation/saa.md index e69de29b..656f4c77 100644 --- a/docs/User_Guide/Structure_Generation/saa.md +++ b/docs/User_Guide/Structure_Generation/saa.md @@ -0,0 +1,64 @@ +Single atom alloys (SAA) consist of a transition-metal host +with lone dopant atoms embedded at the surface. This +dispersion leads to unique electronic properties. + +With the [`autocat.saa`](../../API/Structure_Generation/saa.md) +module, we can generate structures of these +systems to study them further. The main function for this purpose +is [`generate_saa_structures`](../../API/Structure_Generation/saa.md#autocat.saa.generate_saa_structures) +where multiple SAA structures can +be generated simultaneously. + +```py +>>> from autocat.saa import generate_saa_structures +>>> saa_dict = generate_saa_structures( +... ["Fe", "Cu"], +... ["Pt", "Au"], +... facets={"Fe": ["110"], "Cu": ["111"]}, +... n_fixed_layers=2, +... write_to_disk=True, +... ) +Pt1/Fe(bcc110) structure written to ./Fe/Pt/bcc110/substrate/input.traj +Au1/Fe(bcc110) structure written to ./Fe/Au/bcc110/substrate/input.traj +Pt1/Cu(fcc111) structure written to ./Cu/Pt/fcc111/substrate/input.traj +Au1/Cu(fcc111) structure written to ./Cu/Au/fcc111/substrate/input.traj +>>> saa_dict +{'Fe': {'Pt': {'bcc110': {'structure': Atoms(symbols='Fe27PtFe8', initial_magmoms=..., ...), + 'traj_file_path': './Fe/Pt/bcc110/substrate/input.traj'}}, + 'Au': {'bcc110': {'structure': Atoms(symbols='Fe27AuFe8', initial_magmoms=..., ...), + 'traj_file_path': './Fe/Au/bcc110/substrate/input.traj'}}}, + 'Cu': {'Pt': {'fcc111': {'structure': Atoms(symbols='Cu27PtCu8', initial_magmoms=..., ...), + 'traj_file_path': './Cu/Pt/fcc111/substrate/input.traj'}}, + 'Au': {'fcc111': {'structure': Atoms(symbols='Cu27AuCu8', initial_magmoms=..., ...), + 'traj_file_path': './Cu/Au/fcc111/substrate/input.traj'}}}} +``` +Here we generated SAA slabs with Fe and Cu as hosts and Pt and Au dopants under the following conditions: + +- for Fe (Cu) we only need the 110 (111) facet +- the bottom 2 layers are held fixed + +When writing to disk the following directory structure is used: +``` +. +├── Cu +│   ├── Au +│   │   └── fcc111 +│   │   └── substrate +│   │   └── input.traj +│   └── Pt +│   └── fcc111 +│   └── substrate +│   └── input.traj +├── Fe +│   ├── Au +│   │   └── bcc110 +│   │   └── substrate +│   │   └── input.traj +│   └── Pt +│   └── bcc110 +│   └── substrate +│   └── input.traj +``` + +**N.B.** by default, initial magnetic moments are given to the dopant species based upon +the ground state magnetic moment of the species diff --git a/docs/User_Guide/Structure_Generation/surface.md b/docs/User_Guide/Structure_Generation/surface.md index 5f5304b5..4a161678 100644 --- a/docs/User_Guide/Structure_Generation/surface.md +++ b/docs/User_Guide/Structure_Generation/surface.md @@ -1,10 +1,13 @@ It is crucial for many heterogeneous catalysis studies to be able to model a catalyst surface where the desired reaction -can take place. `autocat.surface` provides tools for generating +can take place. +[`autocat.surface`](../../API/Structure_Generation/surface.md) +provides tools for generating low miller index surfaces for mono-element surfaces with a vacuum in the $z$-direction. -The core function of this module is `generate_surface_structures` +The core function of this module is +[`generate_surface_structures`](../../API/Structure_Generation/surface.md#autocat.surface.generate_surface_structures) where multiple slabs can be generated at once. ```py @@ -16,7 +19,7 @@ where multiple slabs can be generated at once. ... n_fixed_layers=2, ... default_lat_param_lib="beefvdw_fd", ... write_to_disk=True, -) +... ) Li_bcc110 structure written to ./Li/bcc110/substrate/input.traj Cu_fcc100 structure written to ./Cu/fcc100/substrate/input.traj Cu_fcc111 structure written to ./Cu/fcc111/substrate/input.traj @@ -40,8 +43,9 @@ Here we generated surface slabs for Cu and Li under the following conditions: - the supercell dimensions of the slabs are 5 $\times$ 5 $\times$ 4 - the bottom 2 layers are held fixed - for structures where the lattice parameter is not explicitly specified, -their default values are pulled from the `ase.data.lattice_parameters` -library that used a `BEEF-vdW` XC and finite difference basis set +their default values are pulled from the +[`autocat.data.lattice_parameters`](../Data/lattice_parameters.md) +library that used a BEEF-vdW XC and finite difference basis set When using the `write_to_disk` functionality the structures will be written into the following directory structure: diff --git a/mkdocs.yml b/mkdocs.yml index 8fabab2c..cbea655e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -43,6 +43,11 @@ nav: - Featurizers: User_Guide/Learning/featurizers.md - Predictors: User_Guide/Learning/predictors.md - Sequential: User_Guide/Learning/sequential.md + - Data: + - HHI: User_Guide/Data/hhi.md + - Segregation Energies: User_Guide/Data/segregation_energies.md + - Lattice Parameters: User_Guide/Data/lattice_parameters.md + - Intermediates: User_Guide/Data/intermediates.md - API: - Structure Generation: - autocat.bulk: API/Structure_Generation/bulk.md From 0436eae3aa3b62f394f9b766d7b59bc2c1326092 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 6 Dec 2021 12:10:01 -0500 Subject: [PATCH 157/239] start adsorption doc --- .../Structure_Generation/adsorption.md | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/docs/User_Guide/Structure_Generation/adsorption.md b/docs/User_Guide/Structure_Generation/adsorption.md index e69de29b..e670963b 100644 --- a/docs/User_Guide/Structure_Generation/adsorption.md +++ b/docs/User_Guide/Structure_Generation/adsorption.md @@ -0,0 +1,175 @@ +Tools within +[`autocat.adsorption`](../../API/Structure_Generation/adsorption.md) +are geared towards generating structures with adsorbates placed on +a candidate catalyst surface. + +The core function of this module is +[`generate_rxn_structures`](../../API/Structure_Generation/adsorption.md#autocat.adsorption.generate_rxn_structures) + for generating multiple adsorbed structures with a single function call. + +For the oxygen reduction (ORR) and nitrogen reduction (NRR) reactions, +AutoCat has default starting geometries for all of these intermediates +which can be found in [`autocat.data.intermediates`](../Data/intermediates.md). + +In addition, by default initial heights of the adsorbates are guessed based +upon the vdW radii of the nearest neighbors to the anchoring atom. + +```py +>>> from autocat.surface import generate_surface_structures +>>> from autocat.data.intermediates import ORR_INTERMEDIATE_NAMES +>>> from autocat.adsorption import generate_rxn_structures +>>> surface_dict = generate_surface_structures( +... ["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 +... ) +>>> surface = surface_dict["Pt"]["fcc111"]["structure"] +>>> ads_dict = generate_rxn_structures( +... surface, +... all_sym_sites=True, +... ads=ORR_INTERMEDIATE_NAMES, +... refs=["H2", "H2O"], +... write_to_disk=True, +... ) +OOH at (0.0,0.0) written to ./adsorbates/OOH/ontop/0.0_0.0/input.traj +OOH at (7.623,6.001) written to ./adsorbates/OOH/bridge/7.623_6.001/input.traj +OOH at (6.93,5.601) written to ./adsorbates/OOH/hollow/6.93_5.601/input.traj +OOH at (9.702,4.001) written to ./adsorbates/OOH/hollow/9.702_4.001/input.traj +O at (0.0,0.0) written to ./adsorbates/O/ontop/0.0_0.0/input.traj +O at (7.623,6.001) written to ./adsorbates/O/bridge/7.623_6.001/input.traj +O at (6.93,5.601) written to ./adsorbates/O/hollow/6.93_5.601/input.traj +O at (9.702,4.001) written to ./adsorbates/O/hollow/9.702_4.001/input.traj +OH at (0.0,0.0) written to ./adsorbates/OH/ontop/0.0_0.0/input.traj +OH at (7.623,6.001) written to ./adsorbates/OH/bridge/7.623_6.001/input.traj +OH at (6.93,5.601) written to ./adsorbates/OH/hollow/6.93_5.601/input.traj +OH at (9.702,4.001) written to ./adsorbates/OH/hollow/9.702_4.001/input.traj +H2 molecule structure written to ./references/H2/input.traj +H2O molecule structure written to ./references/H2O/input.traj +>>> ads_dict +{'OOH': {'ontop': {'0.0_0.0': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/ontop/0.0_0.0/input.traj'}}, + 'bridge': {'7.623_6.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/bridge/7.623_6.001/input.traj'}}, + 'hollow': {'6.93_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/hollow/6.93_5.601/input.traj'}, + '9.702_4.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OOH/hollow/9.702_4.001/input.traj'}}}, + 'O': {'ontop': {'0.0_0.0': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/ontop/0.0_0.0/input.traj'}}, + 'bridge': {'7.623_6.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/bridge/7.623_6.001/input.traj'}}, + 'hollow': {'6.93_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/hollow/6.93_5.601/input.traj'}, + '9.702_4.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/O/hollow/9.702_4.001/input.traj'}}}, + 'OH': {'ontop': {'0.0_0.0': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/ontop/0.0_0.0/input.traj'}}, + 'bridge': {'7.623_6.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/bridge/7.623_6.001/input.traj'}}, + 'hollow': {'6.93_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/hollow/6.93_5.601/input.traj'}, + '9.702_4.001': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/OH/hollow/9.702_4.001/input.traj'}}}, + 'references': {'H2': {'structure': Atoms(...), + 'traj_file_path': './references/H2/input.traj'}, + 'H2O': {'structure': Atoms(...), + 'traj_file_path': './references/H2O/input.traj'}}} +``` +In the example above we are generating adsorption structures for all ORR intermediates +on all of the identified unique symmetry sites on a Pt111 slab. The unique sites are +identified using the Delaunay triangulation, as implemented in `pymatgen`. +Additionally, by default initial heights of the adsorbates are guessed based +upon the vdW radii of the nearest neighbors to the anchoring atom. + +One such use of these structures is generating free energy landscapes. +With this in mind, there is also the `refs` parameter +which allows specifying the reference states that will be used. This generates a separate +directory containing the isolated molecule structures so that their energies may also +be calculated. + +In general the dictionary generated has the following organization: + +``` +{ADSORBATE_SPECIES: + {SITE_LABEL: + {XY: {"structure": Atoms, "traj_file_path": TRAJFILEPATH}}}, + "references": + {REFERENCE_SPECIES: {"structure": Atoms, "traj_file_path": TRAJFILEPATH}}} +``` +When writing these adsorbed structures to disk it is done with the following subdirectory +format (mimicing the organization of the dictionary). + +``` +. +├── adsorbates +│   ├── O +│   │   ├── bridge +│   │   │   └── 7.623_6.001 +│   │   │   └── input.traj +│   │   ├── hollow +│   │   │   ├── 6.93_5.601 +│   │   │   │   └── input.traj +│   │   │   └── 9.702_4.001 +│   │   │   └── input.traj +│   │   └── ontop +│   │   └── 0.0_0.0 +│   │   └── input.traj +│   ├── OH +│   │   ├── bridge +│   │   │   └── 7.623_6.001 +│   │   │   └── input.traj +│   │   ├── hollow +│   │   │   ├── 6.93_5.601 +│   │   │   │   └── input.traj +│   │   │   └── 9.702_4.001 +│   │   │   └── input.traj +│   │   └── ontop +│   │   └── 0.0_0.0 +│   │   └── input.traj +│   └── OOH +│   ├── bridge +│   │   └── 7.623_6.001 +│   │   └── input.traj +│   ├── hollow +│   │   ├── 6.93_5.601 +│   │   │   └── input.traj +│   │   └── 9.702_4.001 +│   │   └── input.traj +│   └── ontop +│   └── 0.0_0.0 +│   └── input.traj +├── references +│   ├── H2 +│   │   └── input.traj +│   └── H2O +│   └── input.traj +``` + +Instead of generating the adsorption structures for all unique sites, +the xy coordinates of individual sites may be specified using the `sites` + parameter. + +```py +>>> from autocat.surface import generate_surface_structures +>>> from autocat.adsorption import generate_rxn_structures +>>> surface_dict = generate_surface_structures( +... ["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 +... ) +>>> surface = surface_dict["Pt"]["fcc111"]["structure"] +>>> x = surface[15].x +>>> x +4.1577878733769 +>>> y = surface[15].y +>>> y +5.6011665451642 +>>> site = {"custom": [(x,y)]} +>>> ads_dict = generate_rxn_structures( +... surface, +... ads=["Li"], +... all_sym_sites=False, +... sites=site, +... write_to_disk=True, +... ) +Li at (4.158,5.601) written to ./adsorbates/Li/custom/4.158_5.601/input.traj +>>> ads_dict +{'Li': {'custom': {'4.158_5.601': {'structure': Atoms(...), + 'traj_file_path': './adsorbates/Li/custom/4.158_5.601/input.traj'}}}} +``` From 2b055f77e72384badcd05ca76882cdd469db2767 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 6 Dec 2021 15:24:14 -0500 Subject: [PATCH 158/239] refine example txt, add struct gen figs --- .../Structure_Generation/adsorption.md | 1 + docs/User_Guide/Structure_Generation/bulk.md | 6 +++--- docs/User_Guide/Structure_Generation/saa.md | 9 +++++---- docs/User_Guide/Structure_Generation/surface.md | 10 ++++++---- docs/img/struct_gen_figs/adsorption.png | Bin 0 -> 37223 bytes docs/img/struct_gen_figs/saa.png | Bin 0 -> 35358 bytes docs/img/struct_gen_figs/surface.png | Bin 0 -> 35477 bytes mkdocs.yml | 1 + 8 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 docs/img/struct_gen_figs/adsorption.png create mode 100644 docs/img/struct_gen_figs/saa.png create mode 100644 docs/img/struct_gen_figs/surface.png diff --git a/docs/User_Guide/Structure_Generation/adsorption.md b/docs/User_Guide/Structure_Generation/adsorption.md index e670963b..a6bc4b67 100644 --- a/docs/User_Guide/Structure_Generation/adsorption.md +++ b/docs/User_Guide/Structure_Generation/adsorption.md @@ -1,3 +1,4 @@ +![Adsorption Figure](../../img/struct_gen_figs/adsorption.png){ align=right } Tools within [`autocat.adsorption`](../../API/Structure_Generation/adsorption.md) are geared towards generating structures with adsorbates placed on diff --git a/docs/User_Guide/Structure_Generation/bulk.md b/docs/User_Guide/Structure_Generation/bulk.md index 6e303a77..ad95cce1 100644 --- a/docs/User_Guide/Structure_Generation/bulk.md +++ b/docs/User_Guide/Structure_Generation/bulk.md @@ -14,11 +14,11 @@ Pt_bulk_fcc structure written to ./Pt_bulk_fcc/input.traj Fe_bulk_bcc structure written to ./Fe_bulk_bcc/input.traj Ru_bulk_hcp structure written to ./Ru_bulk_hcp/input.traj >>> bulk_dict -{'Pt': {'crystal_structure': Atoms(symbols='Pt', pbc=True, ...), +{'Pt': {'crystal_structure': Atoms(...), 'traj_file_path': './Pt_bulk_fcc/input.traj'}, - 'Fe': {'crystal_structure': Atoms(symbols='Fe', pbc=True, initial_magmoms=..., ...), + 'Fe': {'crystal_structure': Atoms(...), 'traj_file_path': './Fe_bulk_bcc/input.traj'}, - 'Ru': {'crystal_structure': Atoms(symbols='Ru2', pbc=True, ...), + 'Ru': {'crystal_structure': Atoms(...), 'traj_file_path': './Ru_bulk_hcp/input.traj'}} ``` diff --git a/docs/User_Guide/Structure_Generation/saa.md b/docs/User_Guide/Structure_Generation/saa.md index 656f4c77..dedbd658 100644 --- a/docs/User_Guide/Structure_Generation/saa.md +++ b/docs/User_Guide/Structure_Generation/saa.md @@ -1,3 +1,4 @@ +![SAA Figure](../../img/struct_gen_figs/saa.png){ align=right } Single atom alloys (SAA) consist of a transition-metal host with lone dopant atoms embedded at the surface. This dispersion leads to unique electronic properties. @@ -23,13 +24,13 @@ Au1/Fe(bcc110) structure written to ./Fe/Au/bcc110/substrate/input.traj Pt1/Cu(fcc111) structure written to ./Cu/Pt/fcc111/substrate/input.traj Au1/Cu(fcc111) structure written to ./Cu/Au/fcc111/substrate/input.traj >>> saa_dict -{'Fe': {'Pt': {'bcc110': {'structure': Atoms(symbols='Fe27PtFe8', initial_magmoms=..., ...), +{'Fe': {'Pt': {'bcc110': {'structure': Atoms(...), 'traj_file_path': './Fe/Pt/bcc110/substrate/input.traj'}}, - 'Au': {'bcc110': {'structure': Atoms(symbols='Fe27AuFe8', initial_magmoms=..., ...), + 'Au': {'bcc110': {'structure': Atoms(...), 'traj_file_path': './Fe/Au/bcc110/substrate/input.traj'}}}, - 'Cu': {'Pt': {'fcc111': {'structure': Atoms(symbols='Cu27PtCu8', initial_magmoms=..., ...), + 'Cu': {'Pt': {'fcc111': {'structure': Atoms(...), 'traj_file_path': './Cu/Pt/fcc111/substrate/input.traj'}}, - 'Au': {'fcc111': {'structure': Atoms(symbols='Cu27AuCu8', initial_magmoms=..., ...), + 'Au': {'fcc111': {'structure': Atoms(...), 'traj_file_path': './Cu/Au/fcc111/substrate/input.traj'}}}} ``` Here we generated SAA slabs with Fe and Cu as hosts and Pt and Au dopants under the following conditions: diff --git a/docs/User_Guide/Structure_Generation/surface.md b/docs/User_Guide/Structure_Generation/surface.md index 4a161678..6788082c 100644 --- a/docs/User_Guide/Structure_Generation/surface.md +++ b/docs/User_Guide/Structure_Generation/surface.md @@ -1,3 +1,4 @@ +![Surface Figure](../../img/struct_gen_figs/surface.png){ align=right } It is crucial for many heterogeneous catalysis studies to be able to model a catalyst surface where the desired reaction can take place. @@ -25,13 +26,13 @@ Cu_fcc100 structure written to ./Cu/fcc100/substrate/input.traj Cu_fcc111 structure written to ./Cu/fcc111/substrate/input.traj Cu_fcc110 structure written to ./Cu/fcc110/substrate/input.traj >>> surf_dict -{'Li': {'bcc110': {'structure': Atoms(symbols='Li100', pbc=[True, True, False], ...), +{'Li': {'bcc110': {'structure': Atoms(...), 'traj_file_path': './Li/bcc110/substrate/input.traj'}}, - 'Cu': {'fcc100': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'Cu': {'fcc100': {'structure': Atoms(...), 'traj_file_path': './Cu/fcc100/substrate/input.traj'}, - 'fcc111': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'fcc111': {'structure': Atoms(...), 'traj_file_path': './Cu/fcc111/substrate/input.traj'}, - 'fcc110': {'structure': Atoms(symbols='Cu100', pbc=[True, True, False], ...), + 'fcc110': {'structure': Atoms(...), 'traj_file_path': './Cu/fcc110/substrate/input.traj'}}} ``` Here we generated surface slabs for Cu and Li under the following conditions: @@ -67,3 +68,4 @@ will be written into the following directory structure: │   └── substrate │   └── input.traj ``` +**N.B.** by default, initial magnetic moments are given to Fe, Ni and Co diff --git a/docs/img/struct_gen_figs/adsorption.png b/docs/img/struct_gen_figs/adsorption.png new file mode 100644 index 0000000000000000000000000000000000000000..5d2de92863edfccd6ec54b406f7b5b8df2bc0bef GIT binary patch literal 37223 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 literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index cbea655e..203bad85 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,6 +14,7 @@ markdown_extensions: - pymdownx.inlinehilite - pymdownx.arithmatex: generic: true + - attr_list extra_javascript: - javascripts/mathjax.js - https://polyfill.io/v3/polyfill.min.js?features=es6 From 39ea277bf3a1e5b3a45fd0aa794e1edc10ecbaba Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 20 Dec 2021 13:54:12 -0500 Subject: [PATCH 159/239] start predictors doc --- docs/User_Guide/Learning/predictors.md | 71 ++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/User_Guide/Learning/predictors.md b/docs/User_Guide/Learning/predictors.md index e69de29b..18426f85 100644 --- a/docs/User_Guide/Learning/predictors.md +++ b/docs/User_Guide/Learning/predictors.md @@ -0,0 +1,71 @@ +In order to iterate a sequential learning pipeline, +a regressor is needed to select subsequent candidate systems. +For this purpose, there is the +[`AutoCatPredictor`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor) +object class. This contains two key attributes: + +- a regressor that can be fit to data and used for predictions +(the class provided must have `fit` and `predict` methods) +- specified featurization methods that are available in +[`autocat.learning.featurizers`](../../API/Learning/featurizers.md). + In particular there are two currently implemented approaches, +structure methods that featurize the entire structure (e.g. `sine matrix`) + and adsorbate methods that featurize locally (e.g. `SOAP`). + +Generally, this predictor object behaves similarly to regressors found in +[`sklearn`](https://scikit-learn.org/stable/) +with its own +[`fit`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.fit), +[`predict`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.predict), +and +[`score`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.score) +methods. + +As an example, let's train a random forest regressor on some +single atom alloys. + +```py +>>> import numpy as np +>>> from autocat.learning.predictors import AutoCatPredictor +>>> from autocat.saa import generate_saa_structures +>>> from autocat.utils import extract_structures +>>> from sklearn.ensemble import RandomForestRegressor +>>> saa_dict = generate_saa_structures(["Cu", "Au", "Fe"], ["Pt", "Ru", "Ni"]) +>>> saa_structs = extract_structures(saa_dict) +>>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) +>>> acp = AutoCatPredictor( +... structure_featurizer="sine_matrix", model_class=RandomForestRegressor +... ) +>>> acp.fit(saa_structs[:-1], labels) +>>> pred, _ = acp.predict([saa_structs[-1]]) +>>> pred +array([0.]) +``` +Here we have chosen to featurize the structures as a `sine matrix`. +Note as well that the `predict` method will return uncertainty estimates +if available. To see this, let's train a gaussian process regressor with an RBF + kernel. + +```py +>>> import numpy as np +>>> from autocat.learning.predictors import AutoCatPredictor +>>> from autocat.saa import generate_saa_structures +>>> from autocat.utils import extract_structures +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from sklearn.gaussian_process.kernels import RBF +>>> saa_dict = generate_saa_structures(["Cu", "Au", "Fe"], ["Pt", "Ru", "Ni"]) +>>> saa_structs = extract_structures(saa_dict) +>>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) +>>> kernel = RBF() +>>> acp = AutoCatPredictor( +... structure_featurizer="sine_matrix", +... model_class=GaussianProcessRegressor, +... model_kwargs={"kernel": kernel}, +... ) +>>> acp.fit(saa_structs[:-1], labels) +>>> pred, unc = acp.predict([saa_structs[-1]]) +>>> pred +array([0.]) +>>> unc +array([1.]) +``` From 8138a9f908eafb5afabfd1e461f3efd27de28a03 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 20 Dec 2021 20:24:42 -0500 Subject: [PATCH 160/239] add latt param docs --- docs/User_Guide/Data/lattice_parameters.md | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/User_Guide/Data/lattice_parameters.md b/docs/User_Guide/Data/lattice_parameters.md index e69de29b..136a6cce 100644 --- a/docs/User_Guide/Data/lattice_parameters.md +++ b/docs/User_Guide/Data/lattice_parameters.md @@ -0,0 +1,44 @@ +In some codes, optimizing cell parameters on the fly +during geometry relaxations is not available. +For this reason we have compiled +calculated lattice parameters +using multiple different +calculation schemes as a convenience for high-throughput +studies. Every calculation was conducted with +[`GPAW`](https://wiki.fysik.dtu.dk/gpaw/index.html). + +There are two axes to the settings applied here: + +- exchange-correlation functional +- basis set mode (finite difference or plane-wave). + +Available sets are as follows: + +- `BULK_PBE_FD`/`BULK_BEEFVDW_FD`: +``` +These are parameters using the finite difference scheme +and PBE / BEEF-vdW XC functionals. Obtained via fits to an +equation of state (https://wiki.fysik.dtu.dk/ase/ase/eos.html) + +FCC/BCC +h = 0.16, kpts = (12,12,12) +fit to an SJ EOS + +HCP +h=0.16, kpts = (12,12,6) +fit to a Birch-Murnaghan EO +``` +- `BULK_PBE_PW`/`BULK_BEEFVDW_PW`: +``` +These are parameters are obatined with a plane-wave basis set and +using the Exponential Cell Filter to minimize the stress tensor and atomic forces +(https://wiki.fysik.dtu.dk/ase/ase/constraints.html#the-expcellfilter-class) + +FCC/BCC +mode=PW(550), kpts = (12,12,12), fmax = 0.05 eV/A + +HCP +mode=PW(550), kpts = (12,12,6), fmax = 0.05 eV/A +``` + +All of these lattice parameters are available within `autocat.data.lattice_parameters` From b25f2114c257c3860f1fe813d05e08ddc30fec48 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 20 Dec 2021 21:54:45 -0500 Subject: [PATCH 161/239] start doc for intermediates --- docs/User_Guide/Data/intermediates.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/User_Guide/Data/intermediates.md b/docs/User_Guide/Data/intermediates.md index e69de29b..90ed656e 100644 --- a/docs/User_Guide/Data/intermediates.md +++ b/docs/User_Guide/Data/intermediates.md @@ -0,0 +1,40 @@ +When characterizing a surface in the context of a +specific reaction, calculating adsorption energies +for all of the intermediates is often important. + +Here, AutoCat has default structures for adsorbates +of both the oxygen reduction reaction (ORR) and +nitrogen reduction reaction (NRR) intermediates. + +The names of all of the reaction intermediates can +be imported and fed directly into +AutoCat functions: +```py +>>> from autocat.data.intermediates import ORR_INTERMEDIATE_NAMES +>>> from autocat.data.intermediates import NRR_INTERMEDIATE_NAMES +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.adsorption import generate_rxn_structures +>>> pt_dict = generate_surface_structures(["Pt"]) +>>> pt_struct = extract_structures(pt_dict)[0] +>>> orr_dict = generate_rxn_structures(pt_struct, ads=ORR_INTERMEDIATE_NAMES) +>>> nrr_dict = generate_rxn_structures(pt_struct, ads=NRR_INTERMEDIATE_NAMES) +``` +In the above example, `orr_dict` and `nrr_dict` have all of the corresponding +intermediates at every identified unique surface site. + +Alternatively, if you would like to access the +`ase.Atoms` objects for the intermediates directly, +they can be imported as a `dict`: +```py +>>> from autocat.data.intermediates import ORR_MOLS +>>> from autocat.data.intermediates import NRR_MOLS +``` + +**ORR Intermediates**: + +OOH\*, O\*, OH\* + +**NRR Intermediates**: + +NNH\*, NNH$_2$\*, N\*, NH\*, NH$_2$\*, NHNH\*, NHNH$_2$\*, NH$_2$NH$_2$\* From 584f76b0febbc7948b46ce856cc2f2039b0f112c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 26 Dec 2021 16:08:47 -0500 Subject: [PATCH 162/239] start sequential doc --- docs/User_Guide/Learning/sequential.md | 105 +++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/docs/User_Guide/Learning/sequential.md b/docs/User_Guide/Learning/sequential.md index e69de29b..5ac30512 100644 --- a/docs/User_Guide/Learning/sequential.md +++ b/docs/User_Guide/Learning/sequential.md @@ -0,0 +1,105 @@ +## AutoCatDesignSpace + +The +[`AutoCatDesignSpace`](../../API/Learning/sequential.md#autocat.learning.sequential.AutoCatDesignSpace) +class object is intended to store the +*entire* design space. As the sequential learning +loop is iterated, this can be continuously updated +with the newly found labels. + +There are two key components required for this object: + +1. `design_space_structures`: *all* systems to be considered as `ase.Atoms` objects +in a `list` +2. `design_space_labels`: `numpy array` of the same length as the above list +with the corresponding labels. If the label is not yet +known, set it to `numpy.nan` + +**NB:** The order of the list of design space structures must +be in the same order as the labels given in the +design space labels. + +```py +>>> import numpy as np +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.learning.sequential import AutoCatDesignSpace +>>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) +>>> surf_structs = extract_structures(surf_dict) +>>> labels = np.array([0.95395024, 0.63504885, np.nan, 0.08320879, np.nan, +... 0.32423194, 0.55570785, np.nan, np.nan, np.nan, +... 0.18884186, np.nan]) +>>> acds = AutoCatDesignSpace(surf_structs, labels) +>>> len(acds) +12 +>>> acds.design_space_structures +[Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...), + Atoms(...)] +>>> acds.design_space_labels +array([0.95395024, 0.63504885, nan, 0.08320879, nan, + 0.32423194, 0.55570785, nan, nan, nan, + 0.18884186, nan]) +``` + + +## AutoCatSequentialLearner + +The +[`AutoCatSequentialLearner`](../../API/Learning/sequential.md#autocat.learning.sequential.AutoCatSequentialLearner) +object stores information regarding the latest +iteration of the sequential learning loop including: + +1. An [`AutoCatPredictor`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor) +2. Candidate selection kwargs for score calculation (e.g. acquisition functions) +3. Iteration number +4. Latest `AutoCatDesignSpace` +5. Candidate system that is identified for the next loop. + +This object can be thought of as a central hub for the +sequential learning workflow, with an external driver +(either automated or manual) triggering iteration. + +```py +>>> import numpy as np +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.learning.sequential import AutoCatDesignSpace +>>> from autocat.learning.sequential import AutoCatSequentialLearner +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from sklearn.gaussian_process.kernels import RBF +>>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) +>>> surf_structs = extract_structures(surf_dict) +>>> labels = np.array([0.95395024, 0.63504885, np.nan, 0.08320879, np.nan, +... 0.32423194, 0.55570785, np.nan, np.nan, np.nan, +... 0.18884186, np.nan]) +>>> acds = AutoCatDesignSpace(surf_structs, labels) +>>> kernel = RBF() +>>> acsl = AutoCatSequentialLearner( +... acds, +... predictor_kwargs={ +... "structure_featurizer": "sine_matrix", +... "model_class": GaussianProcessRegressor, +... "model_kwargs": {"kernel": kernel}, +... }, +... candidate_selection_kwargs={ +... "aq": "MLI", +... "target_min": -2.25, +... "target_max": -1.5, +... "include_hhi": True, +... "hhi_type": "reserves", +... "include_seg_ener": False, +... }, +... ) +>>> acsl.iteration_count +0 +``` From 6822092ba383db9123770294299b18fe25459888 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 6 Jan 2022 19:10:47 -0500 Subject: [PATCH 163/239] refactor sl docs AutoCatX -> X --- docs/User_Guide/Learning/predictors.md | 16 ++++++++-------- docs/User_Guide/Learning/sequential.md | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/User_Guide/Learning/predictors.md b/docs/User_Guide/Learning/predictors.md index 18426f85..3696e22d 100644 --- a/docs/User_Guide/Learning/predictors.md +++ b/docs/User_Guide/Learning/predictors.md @@ -1,7 +1,7 @@ In order to iterate a sequential learning pipeline, a regressor is needed to select subsequent candidate systems. For this purpose, there is the -[`AutoCatPredictor`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor) +[`Predictor`](../../API/Learning/predictors.md#autocat.learning.predictors.Predictor) object class. This contains two key attributes: - a regressor that can be fit to data and used for predictions @@ -15,10 +15,10 @@ structure methods that featurize the entire structure (e.g. `sine matrix`) Generally, this predictor object behaves similarly to regressors found in [`sklearn`](https://scikit-learn.org/stable/) with its own -[`fit`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.fit), -[`predict`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.predict), +[`fit`](../../API/Learning/predictors.md#autocat.learning.predictors.Predictor.fit), +[`predict`](../../API/Learning/predictors.md#autocat.learning.predictors.Predictor.predict), and -[`score`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor.score) +[`score`](../../API/Learning/predictors.md#autocat.learning.predictors.Predictor.score) methods. As an example, let's train a random forest regressor on some @@ -26,14 +26,14 @@ single atom alloys. ```py >>> import numpy as np ->>> from autocat.learning.predictors import AutoCatPredictor +>>> from autocat.learning.predictors import Predictor >>> from autocat.saa import generate_saa_structures >>> from autocat.utils import extract_structures >>> from sklearn.ensemble import RandomForestRegressor >>> saa_dict = generate_saa_structures(["Cu", "Au", "Fe"], ["Pt", "Ru", "Ni"]) >>> saa_structs = extract_structures(saa_dict) >>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) ->>> acp = AutoCatPredictor( +>>> acp = Predictor( ... structure_featurizer="sine_matrix", model_class=RandomForestRegressor ... ) >>> acp.fit(saa_structs[:-1], labels) @@ -48,7 +48,7 @@ if available. To see this, let's train a gaussian process regressor with an RBF ```py >>> import numpy as np ->>> from autocat.learning.predictors import AutoCatPredictor +>>> from autocat.learning.predictors import Predictor >>> from autocat.saa import generate_saa_structures >>> from autocat.utils import extract_structures >>> from sklearn.gaussian_process import GaussianProcessRegressor @@ -57,7 +57,7 @@ if available. To see this, let's train a gaussian process regressor with an RBF >>> saa_structs = extract_structures(saa_dict) >>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) >>> kernel = RBF() ->>> acp = AutoCatPredictor( +>>> acp = Predictor( ... structure_featurizer="sine_matrix", ... model_class=GaussianProcessRegressor, ... model_kwargs={"kernel": kernel}, diff --git a/docs/User_Guide/Learning/sequential.md b/docs/User_Guide/Learning/sequential.md index 5ac30512..5f93386c 100644 --- a/docs/User_Guide/Learning/sequential.md +++ b/docs/User_Guide/Learning/sequential.md @@ -1,7 +1,7 @@ -## AutoCatDesignSpace +## DesignSpace The -[`AutoCatDesignSpace`](../../API/Learning/sequential.md#autocat.learning.sequential.AutoCatDesignSpace) +[`DesignSpace`](../../API/Learning/sequential.md#autocat.learning.sequential.DesignSpace) class object is intended to store the *entire* design space. As the sequential learning loop is iterated, this can be continuously updated @@ -23,13 +23,13 @@ design space labels. >>> import numpy as np >>> from autocat.surface import generate_surface_structures >>> from autocat.utils import extract_structures ->>> from autocat.learning.sequential import AutoCatDesignSpace +>>> from autocat.learning.sequential import DesignSpace >>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) >>> surf_structs = extract_structures(surf_dict) >>> labels = np.array([0.95395024, 0.63504885, np.nan, 0.08320879, np.nan, ... 0.32423194, 0.55570785, np.nan, np.nan, np.nan, ... 0.18884186, np.nan]) ->>> acds = AutoCatDesignSpace(surf_structs, labels) +>>> acds = DesignSpace(surf_structs, labels) >>> len(acds) 12 >>> acds.design_space_structures @@ -52,17 +52,17 @@ array([0.95395024, 0.63504885, nan, 0.08320879, nan, ``` -## AutoCatSequentialLearner +## SequentialLearner The -[`AutoCatSequentialLearner`](../../API/Learning/sequential.md#autocat.learning.sequential.AutoCatSequentialLearner) +[`SequentialLearner`](../../API/Learning/sequential.md#autocat.learning.sequential.SequentialLearner) object stores information regarding the latest iteration of the sequential learning loop including: -1. An [`AutoCatPredictor`](../../API/Learning/predictors.md#autocat.learning.predictors.AutoCatPredictor) +1. A [`Predictor`](../../API/Learning/predictors.md#autocat.learning.predictors.Predictor) 2. Candidate selection kwargs for score calculation (e.g. acquisition functions) 3. Iteration number -4. Latest `AutoCatDesignSpace` +4. Latest `DesignSpace` 5. Candidate system that is identified for the next loop. This object can be thought of as a central hub for the @@ -73,8 +73,8 @@ sequential learning workflow, with an external driver >>> import numpy as np >>> from autocat.surface import generate_surface_structures >>> from autocat.utils import extract_structures ->>> from autocat.learning.sequential import AutoCatDesignSpace ->>> from autocat.learning.sequential import AutoCatSequentialLearner +>>> from autocat.learning.sequential import DesignSpace +>>> from autocat.learning.sequential import SequentialLearner >>> from sklearn.gaussian_process import GaussianProcessRegressor >>> from sklearn.gaussian_process.kernels import RBF >>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) @@ -82,9 +82,9 @@ sequential learning workflow, with an external driver >>> labels = np.array([0.95395024, 0.63504885, np.nan, 0.08320879, np.nan, ... 0.32423194, 0.55570785, np.nan, np.nan, np.nan, ... 0.18884186, np.nan]) ->>> acds = AutoCatDesignSpace(surf_structs, labels) +>>> acds = DesignSpace(surf_structs, labels) >>> kernel = RBF() ->>> acsl = AutoCatSequentialLearner( +>>> acsl = SequentialLearner( ... acds, ... predictor_kwargs={ ... "structure_featurizer": "sine_matrix", From 857f4c5b43387ed397c02a68363fb10b6b6c7f83 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 8 Jan 2022 19:58:20 -0500 Subject: [PATCH 164/239] add doc for simulated sl --- docs/User_Guide/Learning/sequential.md | 110 ++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/docs/User_Guide/Learning/sequential.md b/docs/User_Guide/Learning/sequential.md index 5f93386c..c81f9983 100644 --- a/docs/User_Guide/Learning/sequential.md +++ b/docs/User_Guide/Learning/sequential.md @@ -9,8 +9,9 @@ with the newly found labels. There are two key components required for this object: -1. `design_space_structures`: *all* systems to be considered as `ase.Atoms` objects -in a `list` +1. `design_space_structures`: *all* systems to be considered as +[`ase.Atoms`](https://wiki.fysik.dtu.dk/ase/ase/atoms.html#module-ase.atoms) +objects in a `list` 2. `design_space_labels`: `numpy array` of the same length as the above list with the corresponding labels. If the label is not yet known, set it to `numpy.nan` @@ -64,6 +65,7 @@ iteration of the sequential learning loop including: 3. Iteration number 4. Latest `DesignSpace` 5. Candidate system that is identified for the next loop. +6. Histories for predictions, uncertainties, and training indices This object can be thought of as a central hub for the sequential learning workflow, with an external driver @@ -103,3 +105,107 @@ sequential learning workflow, with an external driver >>> acsl.iteration_count 0 ``` + +## Simulated Sequential Learning + +If you already have a fully explored design space and want +to simulate exploration over it, the +[`simulated_sequential_learning`](../../API/Learning/sequential.md#autocat.learning.sequential.simulated_sequential_learning) +function may be used. + +Internally this function acts a driver on a `SequentialLearner` object, and can be +viewed as an example for how a driver can be set up for an exploratory simulated +sequential learning loop. As inputs it requires all parameters needed to instantiate +a `SequentialLearner` and returns the object that has been iterated. For further analysis +of the search, histories of the predictions, uncertainties, and the training indices for +each iteration are kept. + +```py +>>> import numpy as np +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.learning.sequential import DesignSpace +>>> from autocat.learning.sequential import simulated_sequential_learning +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from sklearn.gaussian_process.kernels import RBF +>>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) +>>> surf_structs = extract_structures(surf_dict) +>>> labels = np.array([0.95395024, 0.63504885, 0.4567, 0.08320879, 0.87779, +... 0.32423194, 0.55570785, 0.325, 0.43616, 0.321632, +... 0.18884186, 0.1114]) +>>> acds = DesignSpace(surf_structs, labels) +>>> kernel = RBF() +>>> sim_sl = simulated_sequential_learning( +... full_design_space=acds, +... predictor_kwargs={ +... "structure_featurizer": "sine_matrix", +... "model_class": GaussianProcessRegressor, +... "model_kwargs": {"kernel": kernel}, +... }, +... candidate_selection_kwargs={ +... "aq": "MLI", +... "target_min": -2.25, +... "target_max": -1.5, +... "include_hhi": True, +... "hhi_type": "reserves", +... "include_seg_ener": False, +... }, +... init_training_size=5, +... number_of_sl_loops=3, +... ) +Sequential Learning Iteration #1 +Sequential Learning Iteration #2 +Sequential Learning Iteration #3 +``` + +Additionally, simulated searches are typically most useful when repeated to obtain +statistics that are less dependent on the initialization of the design space. For this +purpose there is the +[`multiple_simulated_sequential_learning_runs`](../../API/Learning/sequential.md#autocat.learning.sequential.multiple_simulated_sequential_learning_runs) +function. This returns a list of `SequentialLearner` corresponding to each individual run. Optionally, +this function can also initiate the multiple runs across parallel processes via the +`number_of_parallel_jobs` parameter. + +```py +>>> import numpy as np +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> from autocat.learning.sequential import DesignSpace +>>> from autocat.learning.sequential import multiple_simulated_sequential_learning_runs +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from sklearn.gaussian_process.kernels import RBF +>>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) +>>> surf_structs = extract_structures(surf_dict) +>>> labels = np.array([0.95395024, 0.63504885, 0.4567, 0.08320879, 0.87779, +... 0.32423194, 0.55570785, 0.325, 0.43616, 0.321632, +... 0.18884186, 0.1114]) +>>> acds = DesignSpace(surf_structs, labels) +>>> kernel = RBF() +>>> multi_sim_sl = multiple_simulated_sequential_learning_runs( +... full_design_space=acds, +... predictor_kwargs={ +... "structure_featurizer": "sine_matrix", +... "model_class": GaussianProcessRegressor, +... "model_kwargs": {"kernel": kernel}, +... }, +... candidate_selection_kwargs={ +... "aq": "MLI", +... "target_min": -2.25, +... "target_max": -1.5, +... "include_hhi": True, +... "hhi_type": "reserves", +... "include_seg_ener": False, +... }, +... init_training_size=5, +... number_of_sl_loops=2, +... number_of_runs=3, +... ) +Sequential Learning Iteration #1 +Sequential Learning Iteration #2 +Sequential Learning Iteration #1 +Sequential Learning Iteration #2 +Sequential Learning Iteration #1 +Sequential Learning Iteration #2 +>>> len(multi_sim_sl) +3 +``` \ No newline at end of file From dc094d8cb3a6d5764df3b183ace4536c25d23c92 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 10 Jan 2022 19:56:08 -0500 Subject: [PATCH 165/239] start implementing Featurizer obj --- src/autocat/learning/featurizers.py | 180 +++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 5 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 4562e457..2a3a74a0 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -9,7 +9,6 @@ from matminer.featurizers.site import OPSiteFingerprint from matminer.featurizers.site import CrystalNNFingerprint -import tempfile import os import numpy as np import json @@ -22,13 +21,184 @@ from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.analysis.local_env import VoronoiNN -# from autocat.io.qml import ase_atoms_to_qml_compound +from autocat.learning.sequential import DesignSpace +SUPPORTED_MATMINER_CLASSES = [ + ElementProperty, + ChemicalSRO, + OPSiteFingerprint, + CrystalNNFingerprint, +] -class AutoCatFeaturizationError(Exception): +SUPPORTED_DSCRIBE_CLASSES = [SineMatrix, CoulombMatrix, ACSF, SOAP] + + +class FeaturizerError(Exception): pass +class Featurizer: + def __init__( + self, + featurizer_class, + design_space: DesignSpace, + preset: str = None, + initialization_kwargs: Dict = None, + ): + self._featurizer_class = None + self.featurizer_class = featurizer_class + + self._preset = None + self.preset = preset + + self._initialization_kwargs = None + self.initialization_kwargs = initialization_kwargs + + self.featurizer_object = self._initialize() + + self._max_size = None + self._species_list = None + + self._design_space = None + self.design_space = design_space + + @property + def featurizer_class(self): + return self._featurizer_class + + @featurizer_class.setter + def featurizer_class(self, featurizer_class): + if ( + featurizer_class in SUPPORTED_MATMINER_CLASSES + or featurizer_class in SUPPORTED_DSCRIBE_CLASSES + ): + self._featurizer_class = featurizer_class + self._kwargs = None + else: + msg = f"Featurization class {featurizer_class} are not currently supported.\ + \n At present only classes from 'matminer' and 'dscribe' are supported." + raise FeaturizerError(msg) + + @property + def preset(self): + return self._preset + + @preset.setter + def preset(self, preset): + if self.featurizer_class in [CrystalNNFingerprint, ElementProperty]: + self._preset = preset + elif self.featurizer_class is None: + self._preset = preset + else: + msg = f"Presets are not supported for {self.featurizer_class.__module__}" + raise FeaturizerError(msg) + + @property + def initialization_kwargs(self): + return self._kwargs + + @initialization_kwargs.setter + def initialization_kwargs(self, initialization_kwargs): + if initialization_kwargs is not None: + self._initialization_kwargs = initialization_kwargs + + @property + def design_space(self): + return self._design_space + + @design_space.setter + def design_space(self, design_space: DesignSpace): + if design_space is not None: + self._design_space = design_space + # analyze new design space + ds_structs = design_space.design_space_structures + self._max_size = max([len(s) for s in ds_structs]) + species_list = [] + adsorbate_indices = [] + for s in ds_structs: + # get all unique species + found_species = np.unique(s.get_chemical_symbols()).tolist() + new_species = [ + spec for spec in found_species if spec not in species_list + ] + species_list.extend(new_species) + # get adsorbate indices + adsorbate_indices.append(np.where(s.get_tags() <= 0)[0].tolist()) + + self._species_list = species_list + self._adsorbate_indices = adsorbate_indices + self._max_adsorbate_size = max( + [len(ads_ids) for ads_ids in adsorbate_indices] + ) + + @property + def max_size(self): + return self._max_size + + @property + def species_list(self): + return self._species_list + + @property + def adsorbate_indices(self): + return self._adsorbate_indices + + @property + def max_adsorbate_size(self): + return self._max_adsorbate_size + + def _initialize(self): + # instantiate featurizer object + if self.preset is not None: + try: + return self.featurizer_class.from_preset(self.preset) + except: + msg = f"{self.featurizer_class} cannot be initialized from the preset {self.preset}" + raise FeaturizerError(msg) + else: + if self.featurizer_class in [SineMatrix, CoulombMatrix]: + return self.featurizer_class( + n_atoms_max=self.max_size, + permutation="none", + **self.initialization_kwargs or {}, + ) + elif self.featurizer_class in [SOAP, ACSF]: + return self.featurizer_class( + species=self.species_list, **self.initialization_kwargs or {} + ) + return self.featurizer_class(**self.initialization_kwargs or {}) + + def _featurize_single(self, structure: Atoms, **kwargs): + feat_class = self.featurizer_class + if feat_class in SUPPORTED_DSCRIBE_CLASSES: + if feat_class in [SOAP, ACSF]: + positions = np.where(structure.get_tags() <= 0)[0].tolist() + return self.featurizer_object.create( + structure, positions=positions, **kwargs + ) + elif feat_class in [SineMatrix, CoulombMatrix]: + return self.featurizer_object.create(structure, **kwargs) + elif feat_class in SUPPORTED_MATMINER_CLASSES: + conv = AseAtomsAdaptor() + pym_struct = conv.get_structure(structure) + if feat_class == ElementProperty: + return np.array( + self.featurizer_object.featurize(pym_struct.composition) + ) + elif feat_class in [CrystalNNFingerprint, OPSiteFingerprint]: + representation = np.array([]) + adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() + for idx in adsorbate_indices: + feat = self.featurizer_object.featurize(pym_struct, idx) + representation = np.concatenate((representation, feat)) + return representation + elif feat_class == ChemicalSRO: + pass + + def featurize(self, structures: List[Atoms], **kwargs): + pass + + def get_X( structures: List[Union[Atoms, str]], maximum_structure_size: int = None, @@ -173,7 +343,7 @@ def get_X( X = np.zeros((len(structures), num_structure_features)) else: msg = "Need to specify either a structure or adsorbate featurizer" - raise AutoCatFeaturizationError(msg) + raise FeaturizerError(msg) for idx, structure in enumerate(structures): if isinstance(structure, Atoms): @@ -182,7 +352,7 @@ def get_X( ase_struct = read(structure) else: msg = f"Each structure needs to be either a str or ase.Atoms. Got {type(structure)}" - raise AutoCatFeaturizationError(msg) + raise FeaturizerError(msg) cat_feat = catalyst_featurization( ase_struct, structure_featurizer=structure_featurizer, From 30f969e649c9c89a44fe4f663c8f0e76ffdf858f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 11 Jan 2022 10:10:43 -0500 Subject: [PATCH 166/239] req ds structs vs ds, fix init, add chemsro --- src/autocat/learning/featurizers.py | 54 ++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 2a3a74a0..c8e57cea 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -21,8 +21,6 @@ from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.analysis.local_env import VoronoiNN -from autocat.learning.sequential import DesignSpace - SUPPORTED_MATMINER_CLASSES = [ ElementProperty, ChemicalSRO, @@ -41,12 +39,12 @@ class Featurizer: def __init__( self, featurizer_class, - design_space: DesignSpace, + design_space_structures: List[Atoms], preset: str = None, initialization_kwargs: Dict = None, ): + self._featurizer_class = None - self.featurizer_class = featurizer_class self._preset = None self.preset = preset @@ -54,13 +52,13 @@ def __init__( self._initialization_kwargs = None self.initialization_kwargs = initialization_kwargs - self.featurizer_object = self._initialize() - self._max_size = None self._species_list = None - self._design_space = None - self.design_space = design_space + self._design_space_structures = None + self.design_space_structures = design_space_structures + + self.featurizer_class = featurizer_class @property def featurizer_class(self): @@ -74,6 +72,7 @@ def featurizer_class(self, featurizer_class): ): self._featurizer_class = featurizer_class self._kwargs = None + self.featurizer_object = self._initialize() else: msg = f"Featurization class {featurizer_class} are not currently supported.\ \n At present only classes from 'matminer' and 'dscribe' are supported." @@ -87,7 +86,7 @@ def preset(self): def preset(self, preset): if self.featurizer_class in [CrystalNNFingerprint, ElementProperty]: self._preset = preset - elif self.featurizer_class is None: + elif preset is None: self._preset = preset else: msg = f"Presets are not supported for {self.featurizer_class.__module__}" @@ -103,15 +102,15 @@ def initialization_kwargs(self, initialization_kwargs): self._initialization_kwargs = initialization_kwargs @property - def design_space(self): - return self._design_space + def design_space_structures(self): + return self._design_space_structures - @design_space.setter - def design_space(self, design_space: DesignSpace): - if design_space is not None: - self._design_space = design_space + @design_space_structures.setter + def design_space_structures(self, design_space_structures): + if design_space_structures is not None: + self._design_space_structures = design_space_structures # analyze new design space - ds_structs = design_space.design_space_structures + ds_structs = design_space_structures self._max_size = max([len(s) for s in ds_structs]) species_list = [] adsorbate_indices = [] @@ -193,7 +192,28 @@ def _featurize_single(self, structure: Atoms, **kwargs): representation = np.concatenate((representation, feat)) return representation elif feat_class == ChemicalSRO: - pass + species_list = self.species_list + adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() + formatted_list = [[pym_struct, idx] for idx in adsorbate_indices] + self.featurizer_object.fit(formatted_list) + # concatenate representation for each adsorbate atom + representation = np.array([]) + for idx in adsorbate_indices: + raw_feat = self.featurizer_object.featurize(pym_struct, idx) + # csro only generates for species observed in fit + # as well as includes, so to be generalizable + # we use full species list of the design space and place values + # in the appropriate species location relative to this list + labels = self.featurizer_object.feature_labels() + feat = np.zeros(len(species_list)) + for i, label in enumerate(labels): + # finds where corresponding species is in full species list + lbl_idx = np.where( + np.array(species_list) == label.split("_")[1] + ) + feat[lbl_idx] = raw_feat[i] + representation = np.concatenate((representation, feat)) + return representation def featurize(self, structures: List[Atoms], **kwargs): pass From 43203557913b351b5df2a9ec866a389644779167 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 13 Jan 2022 15:37:43 -0500 Subject: [PATCH 167/239] make ds optional, add multi system featurization --- src/autocat/learning/featurizers.py | 50 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index c8e57cea..b5274044 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -39,7 +39,9 @@ class Featurizer: def __init__( self, featurizer_class, - design_space_structures: List[Atoms], + design_space_structures: List[Atoms] = None, + species_list: List[str] = None, + max_size: int = 100, preset: str = None, initialization_kwargs: Dict = None, ): @@ -53,8 +55,12 @@ def __init__( self.initialization_kwargs = initialization_kwargs self._max_size = None - self._species_list = None + self.max_size = max_size + self._species_list = ["Pt", "Pd", "Cu", "Fe", "Ni", "H", "O", "C", "N"] + self.species_list = species_list + + # overrides max_size and species_list if given self._design_space_structures = None self.design_space_structures = design_space_structures @@ -111,7 +117,6 @@ def design_space_structures(self, design_space_structures): self._design_space_structures = design_space_structures # analyze new design space ds_structs = design_space_structures - self._max_size = max([len(s) for s in ds_structs]) species_list = [] adsorbate_indices = [] for s in ds_structs: @@ -121,31 +126,32 @@ def design_space_structures(self, design_space_structures): spec for spec in found_species if spec not in species_list ] species_list.extend(new_species) - # get adsorbate indices - adsorbate_indices.append(np.where(s.get_tags() <= 0)[0].tolist()) + self._max_size = max([len(s) for s in ds_structs]) self._species_list = species_list - self._adsorbate_indices = adsorbate_indices - self._max_adsorbate_size = max( - [len(ads_ids) for ads_ids in adsorbate_indices] - ) @property def max_size(self): return self._max_size + @max_size.setter + def max_size(self, max_size): + if max_size is not None: + self._max_size = max_size + @property def species_list(self): return self._species_list + @species_list.setter + def species_list(self, species_list): + if species_list is not None: + self._species_list = species_list + @property def adsorbate_indices(self): return self._adsorbate_indices - @property - def max_adsorbate_size(self): - return self._max_adsorbate_size - def _initialize(self): # instantiate featurizer object if self.preset is not None: @@ -167,13 +173,13 @@ def _initialize(self): ) return self.featurizer_class(**self.initialization_kwargs or {}) - def _featurize_single(self, structure: Atoms, **kwargs): + def featurize_single(self, structure: Atoms, **kwargs): feat_class = self.featurizer_class if feat_class in SUPPORTED_DSCRIBE_CLASSES: if feat_class in [SOAP, ACSF]: - positions = np.where(structure.get_tags() <= 0)[0].tolist() + adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() return self.featurizer_object.create( - structure, positions=positions, **kwargs + structure, positions=adsorbate_indices, **kwargs ) elif feat_class in [SineMatrix, CoulombMatrix]: return self.featurizer_object.create(structure, **kwargs) @@ -215,8 +221,16 @@ def _featurize_single(self, structure: Atoms, **kwargs): representation = np.concatenate((representation, feat)) return representation - def featurize(self, structures: List[Atoms], **kwargs): - pass + def featurize_multiple(self, structures: List[Atoms], **kwargs): + first_vec = self.featurize_single(structures[0], **kwargs) + num_features = len(first_vec) + # if adsorbate featurization, assumes only 1 adsorbate in design space + # (otherwise would require padding) + X = np.zeros((len(structures), num_features)) + X[0, :] = first_vec.copy() + for i in range(1, len(structures)): + X[i, :] = self.featurize_single(structures[i], **kwargs) + return X def get_X( From c4782fec0a2e0dd9e5c9eacb05b529829f8a31dd Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 13 Jan 2022 16:15:33 -0500 Subject: [PATCH 168/239] fix handling of kwargs when updated --- src/autocat/learning/featurizers.py | 42 +++++++++++++---------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index b5274044..84aac83f 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -43,16 +43,17 @@ def __init__( species_list: List[str] = None, max_size: int = 100, preset: str = None, - initialization_kwargs: Dict = None, + kwargs: Dict = None, ): self._featurizer_class = None + self.featurizer_class = featurizer_class self._preset = None self.preset = preset - self._initialization_kwargs = None - self.initialization_kwargs = initialization_kwargs + self._kwargs = None + self.kwargs = kwargs self._max_size = None self.max_size = max_size @@ -64,7 +65,7 @@ def __init__( self._design_space_structures = None self.design_space_structures = design_space_structures - self.featurizer_class = featurizer_class + self._featurizer_object = self._get_featurization_object() @property def featurizer_class(self): @@ -78,10 +79,8 @@ def featurizer_class(self, featurizer_class): ): self._featurizer_class = featurizer_class self._kwargs = None - self.featurizer_object = self._initialize() else: - msg = f"Featurization class {featurizer_class} are not currently supported.\ - \n At present only classes from 'matminer' and 'dscribe' are supported." + msg = f"Featurization class {featurizer_class} is not currently supported." raise FeaturizerError(msg) @property @@ -99,26 +98,25 @@ def preset(self, preset): raise FeaturizerError(msg) @property - def initialization_kwargs(self): + def kwargs(self): return self._kwargs - @initialization_kwargs.setter - def initialization_kwargs(self, initialization_kwargs): - if initialization_kwargs is not None: - self._initialization_kwargs = initialization_kwargs + @kwargs.setter + def kwargs(self, kwargs): + if kwargs is not None: + self._kwargs = kwargs @property def design_space_structures(self): return self._design_space_structures @design_space_structures.setter - def design_space_structures(self, design_space_structures): + def design_space_structures(self, design_space_structures: List[Atoms]): if design_space_structures is not None: self._design_space_structures = design_space_structures # analyze new design space ds_structs = design_space_structures species_list = [] - adsorbate_indices = [] for s in ds_structs: # get all unique species found_species = np.unique(s.get_chemical_symbols()).tolist() @@ -144,15 +142,15 @@ def species_list(self): return self._species_list @species_list.setter - def species_list(self, species_list): + def species_list(self, species_list: List[str]): if species_list is not None: self._species_list = species_list @property - def adsorbate_indices(self): - return self._adsorbate_indices + def featurization_object(self): + return self._get_featurization_object() - def _initialize(self): + def _get_featurization_object(self): # instantiate featurizer object if self.preset is not None: try: @@ -163,15 +161,13 @@ def _initialize(self): else: if self.featurizer_class in [SineMatrix, CoulombMatrix]: return self.featurizer_class( - n_atoms_max=self.max_size, - permutation="none", - **self.initialization_kwargs or {}, + n_atoms_max=self.max_size, permutation="none", **self.kwargs or {}, ) elif self.featurizer_class in [SOAP, ACSF]: return self.featurizer_class( - species=self.species_list, **self.initialization_kwargs or {} + species=self.species_list, **self.kwargs or {} ) - return self.featurizer_class(**self.initialization_kwargs or {}) + return self.featurizer_class(**self.kwargs or {}) def featurize_single(self, structure: Atoms, **kwargs): feat_class = self.featurizer_class From c9daece79126f19aebc106fed65e62bd45ca77ef Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 13 Jan 2022 18:41:42 -0500 Subject: [PATCH 169/239] start adding docstrings --- src/autocat/learning/featurizers.py | 35 ++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 84aac83f..ee589aaa 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -1,5 +1,4 @@ from dscribe.descriptors import SineMatrix -from dscribe.descriptors import EwaldSumMatrix from dscribe.descriptors import CoulombMatrix from dscribe.descriptors import ACSF from dscribe.descriptors import SOAP @@ -41,7 +40,7 @@ def __init__( featurizer_class, design_space_structures: List[Atoms] = None, species_list: List[str] = None, - max_size: int = 100, + max_size: int = None, preset: str = None, kwargs: Dict = None, ): @@ -55,7 +54,7 @@ def __init__( self._kwargs = None self.kwargs = kwargs - self._max_size = None + self._max_size = 100 self.max_size = max_size self._species_list = ["Pt", "Pd", "Cu", "Fe", "Ni", "H", "O", "C", "N"] @@ -170,6 +169,21 @@ def _get_featurization_object(self): return self.featurizer_class(**self.kwargs or {}) def featurize_single(self, structure: Atoms, **kwargs): + """ + Featurize a single structure. Returns a single vector + + Parameters + ---------- + + structure: + ase.Atoms object of structure to be featurized + + Returns + ------- + + representation: + Numpy array of feature vector of shape (number of features,) + """ feat_class = self.featurizer_class if feat_class in SUPPORTED_DSCRIBE_CLASSES: if feat_class in [SOAP, ACSF]: @@ -218,6 +232,21 @@ def featurize_single(self, structure: Atoms, **kwargs): return representation def featurize_multiple(self, structures: List[Atoms], **kwargs): + """ + Featurize multiple structures. Returns a matrix + + Parameters + ---------- + + structures: + List of ase.Atoms structures to be featurized + + Returns + ------- + + X: + Numpy array of shape (number of structures, number of features) + """ first_vec = self.featurize_single(structures[0], **kwargs) num_features = len(first_vec) # if adsorbate featurization, assumes only 1 adsorbate in design space From 10978d4218cd7fc21fed96de1ee16c5aa33568ab Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 14 Jan 2022 12:59:08 -0500 Subject: [PATCH 170/239] fix featurize_single typos, fitting --- src/autocat/learning/featurizers.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index ee589aaa..2005ba78 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -64,8 +64,6 @@ def __init__( self._design_space_structures = None self.design_space_structures = design_space_structures - self._featurizer_object = self._get_featurization_object() - @property def featurizer_class(self): return self._featurizer_class @@ -77,6 +75,7 @@ def featurizer_class(self, featurizer_class): or featurizer_class in SUPPORTED_DSCRIBE_CLASSES ): self._featurizer_class = featurizer_class + self._preset = None self._kwargs = None else: msg = f"Featurization class {featurizer_class} is not currently supported." @@ -188,39 +187,41 @@ def featurize_single(self, structure: Atoms, **kwargs): if feat_class in SUPPORTED_DSCRIBE_CLASSES: if feat_class in [SOAP, ACSF]: adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() - return self.featurizer_object.create( + return self.featurization_object.create( structure, positions=adsorbate_indices, **kwargs ) elif feat_class in [SineMatrix, CoulombMatrix]: - return self.featurizer_object.create(structure, **kwargs) + return self.featurization_object.create(structure, **kwargs) elif feat_class in SUPPORTED_MATMINER_CLASSES: conv = AseAtomsAdaptor() pym_struct = conv.get_structure(structure) if feat_class == ElementProperty: return np.array( - self.featurizer_object.featurize(pym_struct.composition) + self.featurization_object.featurize(pym_struct.composition) ) elif feat_class in [CrystalNNFingerprint, OPSiteFingerprint]: representation = np.array([]) adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() for idx in adsorbate_indices: - feat = self.featurizer_object.featurize(pym_struct, idx) + feat = self.featurization_object.featurize(pym_struct, idx) representation = np.concatenate((representation, feat)) return representation elif feat_class == ChemicalSRO: species_list = self.species_list adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() formatted_list = [[pym_struct, idx] for idx in adsorbate_indices] - self.featurizer_object.fit(formatted_list) + featurization_object = self.featurization_object + featurization_object.fit(formatted_list) # concatenate representation for each adsorbate atom representation = np.array([]) + # TODO: order species_list so that this is no longer needed for idx in adsorbate_indices: - raw_feat = self.featurizer_object.featurize(pym_struct, idx) + raw_feat = featurization_object.featurize(pym_struct, idx) # csro only generates for species observed in fit # as well as includes, so to be generalizable # we use full species list of the design space and place values # in the appropriate species location relative to this list - labels = self.featurizer_object.feature_labels() + labels = featurization_object.feature_labels() feat = np.zeros(len(species_list)) for i, label in enumerate(labels): # finds where corresponding species is in full species list From 48182bdd90c588428b21b23fd69fa9714d5885cb Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 14 Jan 2022 13:00:53 -0500 Subject: [PATCH 171/239] start adding featurizer tests - test attributes - test featurize_single --- tests/learning/test_featurizers.py | 189 ++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 2 deletions(-) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 3564ad12..7a1653a5 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -18,19 +18,204 @@ from matminer.featurizers.site import OPSiteFingerprint from matminer.featurizers.site import CrystalNNFingerprint -# from autocat.io.qml import ase_atoms_to_qml_compound -from autocat.adsorption import generate_rxn_structures +from autocat.adsorption import generate_rxn_structures, place_adsorbate from autocat.surface import generate_surface_structures +from autocat.saa import generate_saa_structures from autocat.learning.featurizers import full_structure_featurization from autocat.learning.featurizers import adsorbate_featurization from autocat.learning.featurizers import catalyst_featurization from autocat.learning.featurizers import _get_number_of_features from autocat.learning.featurizers import get_X +from autocat.learning.featurizers import Featurizer +from autocat.utils import extract_structures from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.analysis.local_env import VoronoiNN +def test_featurizer_species_list(): + # test default species list + f = Featurizer(SineMatrix) + assert f.species_list == ["Pt", "Pd", "Cu", "Fe", "Ni", "H", "O", "C", "N"] + + # test updating species list manually + f.species_list = ["Li", "Na", "K"] + assert f.species_list == ["Li", "Na", "K"] + + # test getting species list from design space structures + surfs = extract_structures(generate_surface_structures(["Fe", "V", "Ti"])) + saas = extract_structures(generate_saa_structures(["Cu", "Au"], ["Fe", "Pt"])) + surfs.extend(saas) + f.design_space_structures = surfs + assert f.species_list == ["Fe", "V", "Ti", "Cu", "Pt", "Au"] + + +def test_featurizer_max_size(): + # test default max size + f = Featurizer(SOAP, kwargs={"rcut": 12, "nmax": 8, "lmax": 8}) + assert f.max_size == 100 + + # test updating max size manually + f.max_size = 50 + assert f.max_size == 50 + + # test getting max size from design space structures + surfs = extract_structures( + generate_surface_structures(["Ru"], supercell_dim=(2, 2, 4)) + ) + surfs.extend( + extract_structures(generate_surface_structures(["Fe"], supercell_dim=(4, 4, 4))) + ) + f.design_space_structures = surfs + assert f.max_size == 64 + + +def test_featurizer_design_space_structures(): + # tests giving design space structures + surfs = extract_structures(generate_surface_structures(["Li", "Na"])) + surfs.extend( + extract_structures( + generate_surface_structures(["Cu", "Ni"], supercell_dim=(1, 1, 5)) + ) + ) + f = Featurizer( + SineMatrix, design_space_structures=surfs, max_size=20, species_list=["H"] + ) + assert f.design_space_structures == surfs + # make sure design space is prioritized over max size and species list + assert f.max_size == 36 + assert f.species_list == ["Li", "Na", "Cu", "Ni"] + + +def test_featurizer_featurizer_kwargs(): + # test specifying kwargs + f = Featurizer(CoulombMatrix, kwargs={"flatten": False}) + assert f.kwargs == {"flatten": False} + assert f.featurization_object.flatten == False + + # test updating kwargs + f.kwargs.update({"sparse": True}) + assert f.featurization_object.sparse == True + + # test rm kwargs when updating class + f.featurizer_class = SineMatrix + assert f.kwargs is None + + +def test_featurizer_featurizer_class(): + # test changing featurizer class + f = Featurizer(SOAP, kwargs={"rcut": 12, "nmax": 8, "lmax": 8}) + assert f.featurizer_class == SOAP + assert isinstance(f.featurization_object, SOAP) + f.featurizer_class = SineMatrix + assert f.featurizer_class == SineMatrix + assert isinstance(f.featurization_object, SineMatrix) + + +def test_featurizer_preset(): + # tests specifying preset for class object + f = Featurizer(ElementProperty, preset="magpie") + assert f.preset == "magpie" + assert "Electronegativity" in f.featurization_object.features + assert not "melting_point" in f.featurization_object.features + + f.preset = "matminer" + assert f.preset == "matminer" + assert not "NdUnfilled" in f.featurization_object.features + assert "coefficient_of_linear_thermal_expansion" in f.featurization_object.features + + +def test_featurizer_featurize_single(): + # tests featurizing single structure at a time + + conv = AseAtomsAdaptor() + + # TEST STRUCTURE FEATURIZERS + + # test ElementProperty + saa = extract_structures(generate_saa_structures(["Cu"], ["Pt"]))[0] + f = Featurizer(ElementProperty, preset="magpie", max_size=len(saa)) + acf = f.featurize_single(saa) + ep = ElementProperty.from_preset("magpie") + pymat = conv.get_structure(saa) + manual_elem_prop = ep.featurize(pymat.composition) + assert np.array_equal(acf, manual_elem_prop) + + # test SineMatrix + f.featurizer_class = SineMatrix + acf = f.featurize_single(saa) + sm = SineMatrix(n_atoms_max=len(saa), permutation="none") + manual_sm = sm.create(saa) + assert np.array_equal(acf, manual_sm) + + # test CoulombMatrix + f.featurizer_class = CoulombMatrix + acf = f.featurize_single(saa) + cm = CoulombMatrix(n_atoms_max=len(saa), permutation="none") + manual_cm = cm.create(saa) + assert np.array_equal(acf, manual_cm) + + # TEST SITE FEATURIZERS + ads_struct = extract_structures(place_adsorbate(saa, "OH", position=(0.0, 0.0)))[0] + f.max_size = len(ads_struct) + species = np.unique(ads_struct.get_chemical_symbols()).tolist() + f.species_list = species + + # test ACSF + f.featurizer_class = ACSF + f.kwargs = {"rcut": 6.0} + acf = f.featurize_single(ads_struct) + acsf = ACSF(rcut=6.0, species=species) + manual_acsf = acsf.create(ads_struct, [36, 37]) + assert np.array_equal(acf, manual_acsf) + + # test SOAP + f.featurizer_class = SOAP + f.kwargs = {"rcut": 6.0, "lmax": 6, "nmax": 6} + acf = f.featurize_single(ads_struct) + soap = SOAP(rcut=6.0, species=species, nmax=6, lmax=6) + manual_soap = soap.create(ads_struct, [36, 37]) + assert np.array_equal(acf, manual_soap) + + # test ChemicalSRO + f.featurizer_class = ChemicalSRO + vnn = VoronoiNN(cutoff=10.0, allow_pathological=True) + f.kwargs = {"nn": vnn, "includes": species} + acf = f.featurize_single(ads_struct) + csro = ChemicalSRO(vnn, includes=species) + pym_struct = conv.get_structure(ads_struct) + csro.fit([[pym_struct, 36], [pym_struct, 37]]) + manual_csro = np.array([]) + for idx in [36, 37]: + raw_feat = csro.featurize(pym_struct, idx) + labels = csro.feature_labels() + feat = np.zeros(len(species)) + for i, label in enumerate(labels): + lbl_idx = np.where(np.array(species) == label.split("_")[1]) + feat[lbl_idx] = raw_feat[i] + manual_csro = np.concatenate((manual_csro, feat)) + assert np.array_equal(acf, manual_csro) + + # test OPSiteFingerprint + f.featurizer_class = OPSiteFingerprint + acf = f.featurize_single(ads_struct) + pym_struct = conv.get_structure(ads_struct) + opsf = OPSiteFingerprint() + manual_opsf = opsf.featurize(pym_struct, -2) + manual_opsf = np.concatenate((manual_opsf, opsf.featurize(pym_struct, -1))) + assert np.array_equal(acf, manual_opsf) + + # test CrystalNNFingerprint + f.featurizer_class = CrystalNNFingerprint + f.preset = "cn" + acf = f.featurize_single(ads_struct) + pym_struct = conv.get_structure(ads_struct) + cnn = CrystalNNFingerprint.from_preset("cn") + manual_cnn = cnn.featurize(pym_struct, -2) + manual_cnn = np.concatenate((manual_cnn, cnn.featurize(pym_struct, -1))) + assert np.array_equal(acf, manual_cnn) + + def test_full_structure_featurization_sine(): # Tests Sine Matrix Generation surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] From 05be6b232f704e5afcd3d96c66b113c5d91d6d02 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 14 Jan 2022 14:04:59 -0500 Subject: [PATCH 172/239] flatten vecs in featurize_multiple --- src/autocat/learning/featurizers.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 2005ba78..0aa3f86a 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -181,7 +181,7 @@ def featurize_single(self, structure: Atoms, **kwargs): ------- representation: - Numpy array of feature vector of shape (number of features,) + Numpy array of feature vector (not flattened) """ feat_class = self.featurizer_class if feat_class in SUPPORTED_DSCRIBE_CLASSES: @@ -234,7 +234,8 @@ def featurize_single(self, structure: Atoms, **kwargs): def featurize_multiple(self, structures: List[Atoms], **kwargs): """ - Featurize multiple structures. Returns a matrix + Featurize multiple structures. Returns a matrix where each + row is the flattened feature vector of each system Parameters ---------- @@ -248,14 +249,14 @@ def featurize_multiple(self, structures: List[Atoms], **kwargs): X: Numpy array of shape (number of structures, number of features) """ - first_vec = self.featurize_single(structures[0], **kwargs) + first_vec = self.featurize_single(structures[0], **kwargs).flatten() num_features = len(first_vec) # if adsorbate featurization, assumes only 1 adsorbate in design space # (otherwise would require padding) X = np.zeros((len(structures), num_features)) X[0, :] = first_vec.copy() for i in range(1, len(structures)): - X[i, :] = self.featurize_single(structures[i], **kwargs) + X[i, :] = self.featurize_single(structures[i], **kwargs).flatten() return X From cc73884d4835c381f7da1f0356cce005853b4e2c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 14 Jan 2022 14:05:29 -0500 Subject: [PATCH 173/239] add test for featurize_multiple --- tests/learning/test_featurizers.py | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 7a1653a5..f1d89d27 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -216,6 +216,40 @@ def test_featurizer_featurize_single(): assert np.array_equal(acf, manual_cnn) +def test_featurizer_featurize_multiple(): + # tests featurizing multiple structures at a time + + # TEST STRUCTURE FEATURIZER + saas = extract_structures( + generate_saa_structures( + ["Au", "Cu"], ["Pd", "Pt"], facets={"Au": ["111"], "Cu": ["111"]} + ) + ) + f = Featurizer(ElementProperty, preset="magpie", design_space_structures=saas) + acf = f.featurize_multiple(saas) + manual_mat = [] + for i in range(len(saas)): + manual_mat.append(f.featurize_single(saas[i])) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) + + # TEST SITE FEATURIZER + ads_structs = [] + for struct in saas: + ads_structs.append( + extract_structures(place_adsorbate(struct, "NNH", position=(0.0, 0.0)))[0] + ) + f.featurizer_class = SOAP + f.design_space_structures = ads_structs + f.kwargs = {"rcut": 6.0, "lmax": 6, "nmax": 6} + acf = f.featurize_multiple(ads_structs) + manual_mat = [] + for i in range(len(ads_structs)): + manual_mat.append(f.featurize_single(ads_structs[i]).flatten()) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) + + def test_full_structure_featurization_sine(): # Tests Sine Matrix Generation surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] From 63e9967d60a90695b7f3196798551c33403e3799 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 14 Jan 2022 17:49:39 -0500 Subject: [PATCH 174/239] only allow kwargs via attribute --- src/autocat/learning/featurizers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 0aa3f86a..8e995d20 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -167,7 +167,7 @@ def _get_featurization_object(self): ) return self.featurizer_class(**self.kwargs or {}) - def featurize_single(self, structure: Atoms, **kwargs): + def featurize_single(self, structure: Atoms): """ Featurize a single structure. Returns a single vector @@ -188,10 +188,10 @@ def featurize_single(self, structure: Atoms, **kwargs): if feat_class in [SOAP, ACSF]: adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() return self.featurization_object.create( - structure, positions=adsorbate_indices, **kwargs + structure, positions=adsorbate_indices, ) elif feat_class in [SineMatrix, CoulombMatrix]: - return self.featurization_object.create(structure, **kwargs) + return self.featurization_object.create(structure) elif feat_class in SUPPORTED_MATMINER_CLASSES: conv = AseAtomsAdaptor() pym_struct = conv.get_structure(structure) @@ -232,7 +232,7 @@ def featurize_single(self, structure: Atoms, **kwargs): representation = np.concatenate((representation, feat)) return representation - def featurize_multiple(self, structures: List[Atoms], **kwargs): + def featurize_multiple(self, structures: List[Atoms]): """ Featurize multiple structures. Returns a matrix where each row is the flattened feature vector of each system @@ -249,14 +249,14 @@ def featurize_multiple(self, structures: List[Atoms], **kwargs): X: Numpy array of shape (number of structures, number of features) """ - first_vec = self.featurize_single(structures[0], **kwargs).flatten() + first_vec = self.featurize_single(structures[0]).flatten() num_features = len(first_vec) # if adsorbate featurization, assumes only 1 adsorbate in design space # (otherwise would require padding) X = np.zeros((len(structures), num_features)) X[0, :] = first_vec.copy() for i in range(1, len(structures)): - X[i, :] = self.featurize_single(structures[i], **kwargs).flatten() + X[i, :] = self.featurize_single(structures[i]).flatten() return X From 8d5ec960c0df9e49d1632eef17b226d44521253f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 14 Jan 2022 18:04:18 -0500 Subject: [PATCH 175/239] update predictor to use featurizer --- src/autocat/learning/predictors.py | 304 ++++++----------------------- 1 file changed, 55 insertions(+), 249 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 2461e48d..c8f51ff4 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -6,15 +6,16 @@ from typing import Union from ase import Atoms -from sklearn import metrics -from sklearn.model_selection import KFold from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.metrics import mean_absolute_error from sklearn.metrics import mean_squared_error -from autocat.learning.featurizers import get_X -from autocat.learning.featurizers import _get_number_of_features +from autocat.learning.featurizers import Featurizer +from autocat.learning.featurizers import ( + SUPPORTED_DSCRIBE_CLASSES, + SUPPORTED_MATMINER_CLASSES, +) class PredictorError(Exception): @@ -26,15 +27,8 @@ def __init__( self, model_class=None, multiple_separate_models: bool = None, - structure_featurizer: str = None, - adsorbate_featurizer: str = None, - maximum_structure_size: int = None, - maximum_adsorbate_size: int = None, - elementalproperty_preset: str = None, - species_list: List[str] = None, - refine_structures: bool = None, - structure_featurization_kwargs: Dict = None, - adsorbate_featurization_kwargs: Dict = None, + featurizer_class=None, + featurization_kwargs: Dict = None, model_kwargs: Dict = None, ): """ @@ -91,32 +85,15 @@ def __init__( self.regressor = self.model_class(**self.model_kwargs or {}) - self._refine_structures = True - self.refine_structures = refine_structures + self._featurizer_class = None + self.featurizer_class = featurizer_class - self._structure_featurizer = None - self.structure_featurizer = structure_featurizer + self._featurization_kwargs = None + self.featurization_kwargs = featurization_kwargs - self._adsorbate_featurizer = None - self.adsorbate_featurizer = adsorbate_featurizer - - self._structure_featurization_kwargs = None - self.structure_featurization_kwargs = structure_featurization_kwargs - - self._adsorbate_featurization_kwargs = None - self.adsorbate_featurization_kwargs = adsorbate_featurization_kwargs - - self._maximum_structure_size = None - self.maximum_structure_size = maximum_structure_size - - self._elementalproperty_preset = "magpie" - self.elementalproperty_preset = elementalproperty_preset - - self._maximum_adsorbate_size = None - self.maximum_adsorbate_size = maximum_adsorbate_size - - self._species_list = None - self.species_list = species_list + self.featurization_object = Featurizer( + featurizer_class=self.featurizer_class, kwargs=self.featurization_kwargs + ) @property def model_class(self): @@ -151,120 +128,37 @@ def model_kwargs(self, model_kwargs): self.regressor = self.model_class(**model_kwargs) @property - def refine_structures(self): - return self._refine_structures - - @refine_structures.setter - def refine_structures(self, refine_structures): - if refine_structures is not None: - self._refine_structures = refine_structures - if self.is_fit: - self.is_fit = False - self.X_ = None - self.y_ = None - - @property - def structure_featurizer(self): - return self._structure_featurizer - - @structure_featurizer.setter - def structure_featurizer(self, structure_featurizer): - if structure_featurizer is not None: - self._structure_featurizer = structure_featurizer - if self.is_fit: - self.is_fit = False - self.X_ = None - self.y_ = None - - @property - def adsorbate_featurizer(self): - return self._adsorbate_featurizer - - @adsorbate_featurizer.setter - def adsorbate_featurizer(self, adsorbate_featurizer): - if adsorbate_featurizer is not None: - self._adsorbate_featurizer = adsorbate_featurizer - if self.is_fit: - self.is_fit = False - self.X_ = None - self.y_ = None - - @property - def structure_featurization_kwargs(self): - return self._structure_featurization_kwargs - - @structure_featurization_kwargs.setter - def structure_featurization_kwargs(self, structure_featurization_kwargs): - if structure_featurization_kwargs is not None: - assert isinstance(structure_featurization_kwargs, dict) - if self._structure_featurization_kwargs is not None: - self._structure_featurization_kwargs = structure_featurization_kwargs - if self.is_fit: - self.is_fit = False - self.X_ = None - self.y_ = None - - @property - def elementalproperty_preset(self): - return self._elementalproperty_preset - - @elementalproperty_preset.setter - def elementalproperty_preset(self, elementalproperty_preset): - if elementalproperty_preset is not None: - self._elementalproperty_preset = elementalproperty_preset - if self.is_fit: - self.is_fit = False - self.X_ = None - self.y_ = None - - @property - def adsorbate_featurization_kwargs(self): - return self._adsorbate_featurization_kwargs - - @adsorbate_featurization_kwargs.setter - def adsorbate_featurization_kwargs(self, adsorbate_featurization_kwargs): - if adsorbate_featurization_kwargs is not None: - assert isinstance(adsorbate_featurization_kwargs, dict) - self._adsorbate_featurization_kwargs = adsorbate_featurization_kwargs - if self.is_fit: - self.is_fit = False - self.X_ = None - self.y_ = None - - @property - def maximum_structure_size(self): - return self._maximum_structure_size - - @maximum_structure_size.setter - def maximum_structure_size(self, maximum_structure_size): - if maximum_structure_size is not None: - self._maximum_structure_size = maximum_structure_size - if self.is_fit: - self.is_fit = False - self.X_ = None - self.y_ = None - - @property - def maximum_adsorbate_size(self): - return self._maximum_adsorbate_size - - @maximum_adsorbate_size.setter - def maximum_adsorbate_size(self, maximum_adsorbate_size): - if maximum_adsorbate_size is not None: - self._maximum_adsorbate_size = maximum_adsorbate_size + def featurizer_class(self): + return self._featurizer_class + + @featurizer_class.setter + def featurizer_class(self, featurizer_class): + if featurizer_class is not None: + assert ( + featurizer_class in SUPPORTED_DSCRIBE_CLASSES + or featurizer_class in SUPPORTED_MATMINER_CLASSES + ) + self._featurizer_class = featurizer_class + self.featurization_object = Featurizer( + featurizer_class, kwargs=self.featurization_kwargs + ) if self.is_fit: self.is_fit = False self.X_ = None self.y_ = None @property - def species_list(self): - return self._species_list - - @species_list.setter - def species_list(self, species_list): - if species_list is not None: - self._species_list = species_list + def featurization_kwargs(self): + return self._featurization_kwargs + + @featurization_kwargs.setter + def featurization_kwargs(self, featurization_kwargs): + if featurization_kwargs is not None: + assert isinstance(featurization_kwargs, dict) + self._featurization_kwargs = featurization_kwargs + self.featurization_object = Featurizer( + self.featurizer_class, kwargs=featurization_kwargs + ) if self.is_fit: self.is_fit = False self.X_ = None @@ -276,70 +170,28 @@ def copy(self): """ acp = self.__class__( model_class=self.model_class, + featurizer_class=self.featurizer_class, multiple_separate_models=self.multiple_separate_models, - structure_featurizer=self.structure_featurizer, - adsorbate_featurizer=self.adsorbate_featurizer, - maximum_structure_size=self.maximum_structure_size, - maximum_adsorbate_size=self.maximum_adsorbate_size, - elementalproperty_preset=self.elementalproperty_preset, - refine_structures=self.refine_structures, ) acp.regressor = copy.deepcopy(self.regressor) acp.is_fit = self.is_fit - acp.structure_featurization_kwargs = copy.deepcopy( - self.structure_featurization_kwargs - ) - acp.adsorbate_featurization_kwargs = copy.deepcopy( - self.adsorbate_featurization_kwargs - ) + acp.featurization_kwargs = copy.deepcopy(self.featurization_kwargs) acp.model_kwargs = copy.deepcopy(self.model_kwargs) - if self.species_list is not None: - acp.species_list = self.species_list.copy() return acp - def get_total_number_of_features(self): - # get specified kwargs for featurizers - str_kwargs = self.structure_featurization_kwargs - ads_kwargs = self.adsorbate_featurization_kwargs - if str_kwargs is None: - str_kwargs = {} - if ads_kwargs is None: - ads_kwargs = {} - if self.structure_featurizer is not None: - # check if one of dscribe structure featurizers - if self.structure_featurizer in ["sine_matrix", "coulomb_matrix"]: - str_kwargs.update({"n_atoms_max": self.maximum_structure_size}) - num_struct_feat = _get_number_of_features( - self.structure_featurizer, **str_kwargs - ) - else: - # no structure featurizer present - num_struct_feat = 0 - if self.adsorbate_featurizer is not None: - # check if one of dscribe structure featurizers - if self.adsorbate_featurizer == "soap": - ads_kwargs.update({"species": self.species_list}) - num_ads_feat = _get_number_of_features( - self.adsorbate_featurizer, **ads_kwargs - ) - else: - # no adsorbate featurizer present - num_ads_feat = 0 - return num_struct_feat, num_ads_feat - def fit( self, training_structures: List[Union[Atoms, str]], y: np.ndarray, ): """ - Given a list of perturbed structures - will featurize and train a regression model on them + Given a list of structures and labels will featurize + and train a regression model Parameters ---------- training_structures: - List of perturbed structures to be trained upon + List of structures to be trained upon y: Numpy array of labels corresponding to training structures @@ -351,45 +203,7 @@ def fit( trained_model: Trained `sklearn` model object """ - self.X_ = get_X( - training_structures, - maximum_structure_size=self.maximum_structure_size, - structure_featurizer=self.structure_featurizer, - maximum_adsorbate_size=self.maximum_adsorbate_size, - adsorbate_featurizer=self.adsorbate_featurizer, - species_list=self.species_list, - refine_structures=self.refine_structures, - structure_featurization_kwargs=self.structure_featurization_kwargs, - adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, - ) - - if self.maximum_structure_size is None: - if self.refine_structures: - ref_structures = [ - structure[np.where(structure.get_tags() < 2)[0].tolist()] - for structure in training_structures - ] - self.maximum_structure_size = max([len(ref) for ref in ref_structures]) - else: - self.maximum_structure_size = max([len(s) for s in training_structures]) - - if self.maximum_adsorbate_size is None: - adsorbate_sizes = [] - for struct in training_structures: - adsorbate_sizes.append( - len(np.where(struct.get_tags() <= 0)[0].tolist()) - ) - self.maximum_adsorbate_size = max(adsorbate_sizes) - - if self.species_list is None: - species_list = [] - for s in training_structures: - found_species = np.unique(s.get_chemical_symbols()).tolist() - new_species = [ - spec for spec in found_species if spec not in species_list - ] - species_list.extend(new_species) - self.species_list = species_list + self.X_ = self.featurization_object.featurize_multiple(training_structures) self.y_ = y self.regressor.fit(self.X_, self.y_) self.is_fit = True @@ -398,36 +212,28 @@ def predict( self, testing_structures: List[Atoms], ): """ - From a trained model, will predict corrected structure - of a given initial structure guess + From a trained model, will predict on given structures Parameters ---------- testing_structures: - List of Atoms objects of initial guesses for adsorbate - placement to be optimized + List of Atoms objects to make predictions on Returns ------- - predicted_corrections: - List of corrections to be applied to each input structure + predicted_labels: + List of predicted labels for each input structure unc: - List of uncertainties for each prediction + List of uncertainties for each prediction if available. + Otherwise returns `None` """ assert self.is_fit - featurized_input = get_X( - structures=testing_structures, - structure_featurizer=self.structure_featurizer, - adsorbate_featurizer=self.adsorbate_featurizer, - maximum_structure_size=self.maximum_structure_size, - maximum_adsorbate_size=self.maximum_adsorbate_size, - species_list=self.species_list, - structure_featurization_kwargs=self.structure_featurization_kwargs, - adsorbate_featurization_kwargs=self.adsorbate_featurization_kwargs, + featurized_input = self.featurization_object.featurize_multiple( + testing_structures ) try: predicted_labels, unc = self.regressor.predict( @@ -453,8 +259,8 @@ def score( Parameters ---------- - test_structure_guesses: - List of Atoms objects of structures to be tested o + testing_structures: + List of Atoms objects of structures to be tested on y: Labels for the testing structures From 78494f05afc1228123fdff8819063ac9973b95f0 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 15 Jan 2022 17:42:11 -0500 Subject: [PATCH 176/239] fix logic for when changing featurizer --- src/autocat/learning/predictors.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index c8f51ff4..c6b8f3b1 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -26,10 +26,10 @@ class Predictor: def __init__( self, model_class=None, + model_kwargs: Dict = None, multiple_separate_models: bool = None, featurizer_class=None, featurization_kwargs: Dict = None, - model_kwargs: Dict = None, ): """ Constructor. @@ -83,16 +83,20 @@ def __init__( self._model_kwargs = None self.model_kwargs = model_kwargs - self.regressor = self.model_class(**self.model_kwargs or {}) + self.regressor = self.model_class( + **self.model_kwargs if self.model_kwargs else {} + ) self._featurizer_class = None + self._featurization_kwargs = None + self.featurizer_class = featurizer_class - self._featurization_kwargs = None self.featurization_kwargs = featurization_kwargs self.featurization_object = Featurizer( - featurizer_class=self.featurizer_class, kwargs=self.featurization_kwargs + featurizer_class=self.featurizer_class, + **self.featurization_kwargs if self.featurization_kwargs else {}, ) @property @@ -140,12 +144,16 @@ def featurizer_class(self, featurizer_class): ) self._featurizer_class = featurizer_class self.featurization_object = Featurizer( - featurizer_class, kwargs=self.featurization_kwargs + featurizer_class, + **self.featurization_kwargs if self.featurization_kwargs else {}, ) if self.is_fit: self.is_fit = False self.X_ = None self.y_ = None + self.regressor = self.model_class( + **self.model_kwargs if self.model_kwargs else {} + ) @property def featurization_kwargs(self): @@ -157,12 +165,15 @@ def featurization_kwargs(self, featurization_kwargs): assert isinstance(featurization_kwargs, dict) self._featurization_kwargs = featurization_kwargs self.featurization_object = Featurizer( - self.featurizer_class, kwargs=featurization_kwargs + self.featurizer_class, **featurization_kwargs ) if self.is_fit: self.is_fit = False self.X_ = None self.y_ = None + self.regressor = self.model_class( + **self.model_kwargs if self.model_kwargs else {} + ) def copy(self): """ From 5b548dcc44f7d8567dc9e1fd57b814a6d9420523 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 15 Jan 2022 17:53:59 -0500 Subject: [PATCH 177/239] make sure feat kwargs wiped when changing class --- src/autocat/learning/predictors.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index c6b8f3b1..c680cd33 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -143,10 +143,8 @@ def featurizer_class(self, featurizer_class): or featurizer_class in SUPPORTED_MATMINER_CLASSES ) self._featurizer_class = featurizer_class - self.featurization_object = Featurizer( - featurizer_class, - **self.featurization_kwargs if self.featurization_kwargs else {}, - ) + self._featurization_kwargs = None + self.featurization_object = Featurizer(featurizer_class,) if self.is_fit: self.is_fit = False self.X_ = None From d25af1e8c9978a41eeee843ff7deae8c94b110a2 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 15 Jan 2022 21:45:30 -0500 Subject: [PATCH 178/239] rename featurization_object to featurizer --- src/autocat/learning/predictors.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index c680cd33..b32fdd56 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -94,7 +94,7 @@ def __init__( self.featurization_kwargs = featurization_kwargs - self.featurization_object = Featurizer( + self.featurizer = Featurizer( featurizer_class=self.featurizer_class, **self.featurization_kwargs if self.featurization_kwargs else {}, ) @@ -144,7 +144,7 @@ def featurizer_class(self, featurizer_class): ) self._featurizer_class = featurizer_class self._featurization_kwargs = None - self.featurization_object = Featurizer(featurizer_class,) + self.featurizer = Featurizer(featurizer_class,) if self.is_fit: self.is_fit = False self.X_ = None @@ -162,9 +162,7 @@ def featurization_kwargs(self, featurization_kwargs): if featurization_kwargs is not None: assert isinstance(featurization_kwargs, dict) self._featurization_kwargs = featurization_kwargs - self.featurization_object = Featurizer( - self.featurizer_class, **featurization_kwargs - ) + self.featurizer = Featurizer(self.featurizer_class, **featurization_kwargs) if self.is_fit: self.is_fit = False self.X_ = None @@ -212,7 +210,7 @@ def fit( trained_model: Trained `sklearn` model object """ - self.X_ = self.featurization_object.featurize_multiple(training_structures) + self.X_ = self.featurizer.featurize_multiple(training_structures) self.y_ = y self.regressor.fit(self.X_, self.y_) self.is_fit = True @@ -241,9 +239,7 @@ def predict( """ assert self.is_fit - featurized_input = self.featurization_object.featurize_multiple( - testing_structures - ) + featurized_input = self.featurizer.featurize_multiple(testing_structures) try: predicted_labels, unc = self.regressor.predict( featurized_input, return_std=True From 01389a4e17d00caf13b65dc674ea2db0fce89576 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sat, 15 Jan 2022 21:47:15 -0500 Subject: [PATCH 179/239] update tests for predictor w new featurizer obj --- tests/learning/test_predictors.py | 250 +++++++++++++++++------------- 1 file changed, 145 insertions(+), 105 deletions(-) diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 0a410bf0..5259a0a4 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -1,157 +1,197 @@ """Unit tests for the `autocat.learning.predictors` module""" -import os import pytest import numpy as np from sklearn.kernel_ridge import KernelRidge from sklearn.gaussian_process import GaussianProcessRegressor -from sklearn.linear_model import BayesianRidge +from sklearn.gaussian_process.kernels import RBF +from sklearn.utils.validation import check_is_fitted +from sklearn.exceptions import NotFittedError -from ase import Atoms +from dscribe.descriptors import SineMatrix +from dscribe.descriptors import SOAP + +from matminer.featurizers.composition import ElementProperty from autocat.adsorption import place_adsorbate from autocat.surface import generate_surface_structures -from autocat.perturbations import generate_perturbed_dataset -from autocat.learning.featurizers import get_X -from autocat.learning.featurizers import catalyst_featurization from autocat.learning.predictors import Predictor from autocat.learning.predictors import PredictorError +from autocat.utils import extract_structures -def test_fit_model_on_perturbed_systems(): +def test_fit(): # Test returns a fit model - sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] - p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) - p_structures = p_set["collected_structures"] - correction_matrix = p_set["correction_matrix"] + subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) + structs = [extract_structures(place_adsorbate(s, "OH"))[0] for s in subs] + labels = np.random.rand(len(structs)) acsc = Predictor( - structure_featurizer="sine_matrix", - adsorbate_featurizer="soap", - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + featurizer_class=SOAP, + featurization_kwargs={ + "species_list": ["Pt", "Fe", "Ru", "O", "H"], + "kwargs": {"rcut": 6.0, "nmax": 6, "lmax": 6}, + }, + model_class=GaussianProcessRegressor, ) acsc.fit( - p_structures, y=correction_matrix, + training_structures=structs, y=labels, ) - assert acsc.adsorbate_featurizer == "soap" assert acsc.is_fit + assert check_is_fitted(acsc.regressor) is None - # check no longer fit after changing setting - acsc.adsorbate_featurizer = "acsf" - assert acsc.adsorbate_featurizer == "acsf" + # check no longer fit after changing featurization kwargs + acsc.featurization_kwargs = { + "species_list": ["Pt", "Fe", "Ru", "O", "H", "N"], + "kwargs": {"rcut": 7.0, "nmax": 8, "lmax": 8}, + } assert not acsc.is_fit + with pytest.raises(NotFittedError): + check_is_fitted(acsc.regressor) - # check can be fit on corrections list - p_set["corrections_list"] - acsc.adsorbate_featurizer = "soap" - acsc.adsorbate_featurization_kwargs = {"rcut": 3.0, "nmax": 6, "lmax": 6} acsc.fit( - p_structures, y=correction_matrix, + training_structures=structs, y=labels, + ) + + # check no longer fit after changing featurization class + acsc.featurizer_class = SineMatrix + assert not acsc.is_fit + with pytest.raises(NotFittedError): + check_is_fitted(acsc.regressor) + + acsc.fit( + training_structures=structs, y=labels, + ) + + # check no longer fit after changing model class + acsc.model_class = KernelRidge + assert not acsc.is_fit + with pytest.raises(NotFittedError): + check_is_fitted(acsc.regressor) + + acsc.fit( + training_structures=structs, y=labels, ) - assert acsc.is_fit + # check no longer fit after changing model kwargs + kernel = RBF() + acsc.model_kwargs = {"kernel": kernel} + assert not acsc.is_fit + with pytest.raises(NotFittedError): + check_is_fitted(acsc.regressor) -def test_predict_initial_configuration_formats(): + +def test_predict(): # Test outputs are returned as expected - sub = generate_surface_structures(["Fe"], facets={"Fe": ["100"]})["Fe"]["bcc100"][ - "structure" - ] - base_struct = place_adsorbate(sub, "CO")["custom"]["structure"] - p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) - p_structures = p_set["collected_structures"] - correction_matrix = p_set["correction_matrix"] + subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) + structs = [extract_structures(place_adsorbate(s, "OH"))[0] for s in subs] + labels = np.random.rand(len(structs)) acsc = Predictor( - structure_featurizer="sine_matrix", - adsorbate_featurizer="soap", - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + featurizer_class=SOAP, + featurization_kwargs={ + "species_list": ["Pt", "Fe", "Ru", "O", "H"], + "kwargs": {"rcut": 6.0, "nmax": 6, "lmax": 6}, + }, + model_class=GaussianProcessRegressor, ) acsc.fit( - p_structures[:15], correction_matrix[:15, :], + training_structures=structs[:-3], y=labels[:-3], ) - predicted_corrections, uncs = acsc.predict(p_structures[15:],) - assert len(predicted_corrections) == 5 + pred, unc = acsc.predict([structs[-3]],) + assert len(pred) == 1 + # check dimension of uncertainty estimates + assert len(unc) == 1 + + pred, unc = acsc.predict(structs[-3:],) + assert len(pred) == 3 # check dimension of uncertainty estimates - assert len(uncs) == 5 - - -def test_score_on_perturbed_systems(): - # Tests that the score metric yields floats - sub = generate_surface_structures(["Fe"], facets={"Fe": ["100"]})["Fe"]["bcc100"][ - "structure" - ] - base_struct = place_adsorbate(sub, "CO")["custom"]["structure"] - p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) - p_structures = p_set["collected_structures"] - correction_matrix = p_set["correction_matrix"][:, 0] + assert len(unc) == 3 + + # Test prediction on model without uncertainty + acsc.model_class = KernelRidge + acsc.fit( + training_structures=structs[:-3], y=labels[:-3], + ) + pred, unc = acsc.predict([structs[-2]],) + assert len(pred) == 1 + assert unc is None + + +def test_score(): + # Tests the score method + subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) + structs = [extract_structures(place_adsorbate(s, "OH"))[0] for s in subs] + labels = np.random.rand(len(structs)) acsc = Predictor( - structure_featurizer="sine_matrix", - adsorbate_featurizer="soap", - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, + featurizer_class=SOAP, + featurization_kwargs={ + "species_list": ["Pt", "Fe", "Ru", "O", "H"], + "kwargs": {"rcut": 6.0, "nmax": 6, "lmax": 6}, + }, + model_class=GaussianProcessRegressor, ) acsc.fit( - p_structures[:15], correction_matrix[:15], + training_structures=structs[:-3], y=labels[:-3], ) - mae = acsc.score(p_structures[15:], correction_matrix[15:]) + mae = acsc.score(structs[-3:], labels[-3:]) assert isinstance(mae, float) - mse = acsc.score(p_structures[15:], correction_matrix[15:], metric="mse") + mse = acsc.score(structs[-2:], labels[-2:], metric="mse") assert isinstance(mse, float) # Test returning predictions - score, pred_corr, unc = acsc.score( - p_structures[15:], correction_matrix[15:], return_predictions=True - ) - assert len(pred_corr) == 5 - assert len(unc) == 5 - assert mae != mse + _, preds, uncs = acsc.score(structs[-2:], labels[-2:], return_predictions=True) + assert len(preds) == 2 + assert len(uncs) == 2 + # check catches unknown metric with pytest.raises(PredictorError): - acsc.score(p_structures[15:], correction_matrix, metric="msd") - - # Test with single target - acsc.fit( - p_structures[:15], np.arange(15), - ) - - mae = acsc.score(p_structures[15:], np.arange(5)) - assert isinstance(mae, float) + acsc.score(structs, labels, metric="msd") -def test_model_class_and_kwargs(): +def test_class_and_kwargs_logic(): # Tests providing regression model class and kwargs - acsc = Predictor(KernelRidge, model_kwargs={"gamma": 0.5}) + featurization_kwargs = { + "species_list": ["Pt", "Fe", "Ru", "O", "H"], + "kwargs": {"rcut": 6.0, "nmax": 6, "lmax": 6, "sparse": True}, + } + acsc = Predictor( + model_class=KernelRidge, + model_kwargs={"gamma": 0.5}, + featurizer_class=SOAP, + featurization_kwargs=featurization_kwargs, + ) assert isinstance(acsc.regressor, KernelRidge) - # check that regressor created with correct kwarg + # check that regressor created with correct kwargs assert acsc.regressor.gamma == 0.5 assert acsc.model_kwargs == {"gamma": 0.5} + assert acsc.featurization_kwargs == featurization_kwargs + assert acsc.featurizer.featurization_object.sparse + + # check that model kwargs are removed when model class is changed acsc.model_class = GaussianProcessRegressor - # check that kwargs are removed when class is changed assert acsc.model_kwargs is None - acsc = Predictor() - acsc.model_kwargs = {"alpha": 2.5} - assert acsc.model_kwargs == {"alpha": 2.5} - assert acsc.regressor.alpha == 2.5 - - -def test_model_without_unc(): - # Test that predictions are still made when the model class - # provided does not have uncertainty - sub = generate_surface_structures(["Li"], facets={"Li": ["100"]})["Li"]["bcc100"][ - "structure" - ] - base_struct = place_adsorbate(sub, "S")["custom"]["structure"] - p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=20,) - p_structures = p_set["collected_structures"] - correction_matrix = p_set["correction_matrix"] - acsc = Predictor( - model_class=KernelRidge, - structure_featurizer="sine_matrix", - adsorbate_featurizer=None, - ) - acsc.fit( - p_structures[:15], correction_matrix[:15, :], + assert acsc.featurizer_class == SOAP + assert acsc.featurization_kwargs == featurization_kwargs + + # check that regressor is updated when model kwargs updated + acsc.model_kwargs = {"alpha": 5e-10} + assert acsc.regressor.alpha == 5e-10 + + # check that featurization kwargs removed when featurization class changed + acsc.featurizer_class = ElementProperty + assert acsc.featurization_kwargs is None + + # check that featurizer is updated when featurization kwargs updated + acsc.featurization_kwargs = {"preset": "magpie"} + assert "Electronegativity" in acsc.featurizer.featurization_object.features + + acsc.featurization_kwargs = {"preset": "matminer"} + assert ( + "coefficient_of_linear_thermal_expansion" + in acsc.featurizer.featurization_object.features ) - predicted_corrections, uncs = acsc.predict(p_structures[15:],) - assert uncs is None - assert predicted_corrections is not None + + acsc.featurizer_class = SineMatrix + acsc.featurization_kwargs = {"kwargs": {"flatten": False}} + assert not acsc.featurizer.featurization_object.flatten + acsc.featurization_kwargs = {"kwargs": {"flatten": True}} + assert acsc.featurizer.featurization_object.flatten From 8336c5ea8fd47594d02ce8714c9f1bba7fac36b9 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 16 Jan 2022 10:50:25 -0500 Subject: [PATCH 180/239] update sl to use new predictor w featurizer --- src/autocat/learning/sequential.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 390e5729..01ab4802 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -43,7 +43,8 @@ def __init__( """ if len(design_space_structures) != design_space_labels.shape[0]: - msg = f"Number of structures ({len(design_space_structures)}) and labels ({design_space_labels.shape[0]}) must match" + msg = f"Number of structures ({len(design_space_structures)})\ + and labels ({design_space_labels.shape[0]}) must match" raise DesignSpaceError(msg) self._design_space_structures = [ @@ -214,8 +215,14 @@ def __init__( self.design_space = design_space.copy() # predictor arguments to use throughout the SL process - if not predictor_kwargs: - predictor_kwargs = {"structure_featurizer": "sine_matrix"} + if predictor_kwargs is None: + predictor_kwargs = {} + ds_structs_kwargs = { + "design_space_structures": design_space.design_space_structures + } + if "featurization_kwargs" not in predictor_kwargs: + predictor_kwargs["featurization_kwargs"] = {} + predictor_kwargs["featurization_kwargs"].update(ds_structs_kwargs) self._predictor_kwargs = None self.predictor_kwargs = predictor_kwargs self._predictor = Predictor(**predictor_kwargs) From 91e1c02b9f86c16d15ce8b2ae423170b72950749 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 16 Jan 2022 10:51:56 -0500 Subject: [PATCH 181/239] start updating tests for sl w new predictor --- tests/learning/test_sequential.py | 70 +++++++++++++------------------ 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 7e8b401b..34721e25 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -7,12 +7,16 @@ import tempfile +from sklearn.gaussian_process import GaussianProcessRegressor + +from dscribe.descriptors import SOAP +from dscribe.descriptors import SineMatrix + from scipy import stats from ase.io import read as ase_read from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES from autocat.data.segregation_energies import SEGREGATION_ENERGIES -from autocat.learning.predictors import Predictor from autocat.learning.sequential import ( DesignSpace, DesignSpaceError, @@ -47,16 +51,15 @@ def test_sequential_learner_from_json(): sub3 = place_adsorbate(sub3, "N")["custom"]["structure"] structs = [sub1, sub2, sub3] labels = np.array([0.1, np.nan, 0.3]) + acds = DesignSpace(structs, labels) + featurization_kwargs = {"kwargs": {"rcut": 5.0, "lmax": 6, "nmax": 6}} predictor_kwargs = { - "structure_featurizer": "coulomb_matrix", - "elementalproperty_preset": "megnet_el", - "adsorbate_featurizer": "soap", - "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, - "species_list": ["Au", "Li", "Mg", "C", "Ru", "N"], + "model_class": GaussianProcessRegressor, + "featurizer_class": SOAP, + "featurization_kwargs": featurization_kwargs, } candidate_selection_kwargs = {"aq": "Random", "num_candidates_to_pick": 3} - acds = DesignSpace(structs, labels) acsl = SequentialLearner( acds, predictor_kwargs=predictor_kwargs, @@ -243,7 +246,7 @@ def test_sequential_learner_iterate(): structs = [sub1, sub2, sub3, sub4] labels = np.array([11.0, 25.0, np.nan, np.nan]) acds = DesignSpace(structs, labels) - acsl = SequentialLearner(acds) + acsl = SequentialLearner(acds, predictor_kwargs={"featurizer_class": SineMatrix}) assert acsl.iteration_count == 0 @@ -310,7 +313,7 @@ def test_sequential_learner_setup(): structs = [sub1, sub2, sub3, sub4] labels = np.array([4.0, np.nan, 6.0, np.nan]) acds = DesignSpace(structs, labels) - acsl = SequentialLearner(acds) + acsl = SequentialLearner(acds, predictor_kwargs={"featurizer_class": SineMatrix}) assert acsl.design_space.design_space_structures == acds.design_space_structures assert np.array_equal( @@ -320,37 +323,24 @@ def test_sequential_learner_setup(): assert acsl.predictions == None assert acsl.candidate_indices == None assert acsl.candidate_selection_kwargs == {"aq": "Random"} - # test default kwargs - assert acsl.predictor_kwargs == {"structure_featurizer": "sine_matrix"} - # test specifying kwargs + # test specifying more kwargs + predictor_kwargs = { + "featurizer_class": SOAP, + "model_kwargs": {"n_restarts_optimizer": 9}, + "featurization_kwargs": {"kwargs": {"rcut": 5.0, "lmax": 6, "nmax": 6}}, + } acsl = SequentialLearner( acds, - predictor_kwargs={ - "structure_featurizer": "elemental_property", - "elementalproperty_preset": "pymatgen", - "adsorbate_featurizer": "soap", - "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, - "species_list": ["Ir", "O", "H", "Mo", "Fe", "N", "Re"], - "model_kwargs": {"n_restarts_optimizer": 9}, - }, + predictor_kwargs=predictor_kwargs, candidate_selection_kwargs={"aq": "MU", "num_candidates_to_pick": 2}, ) # test passing predictor kwargs - assert acsl.predictor_kwargs == { - "structure_featurizer": "elemental_property", - "elementalproperty_preset": "pymatgen", - "adsorbate_featurizer": "soap", - "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, - "species_list": ["Ir", "O", "H", "Mo", "Fe", "N", "Re"], - "model_kwargs": {"n_restarts_optimizer": 9}, - } - assert acsl.predictor.structure_featurizer == "elemental_property" - assert acsl.predictor.elementalproperty_preset == "pymatgen" - assert acsl.predictor.adsorbate_featurizer == "soap" - assert acsl.predictor.adsorbate_featurization_kwargs == { + assert acsl.predictor_kwargs == predictor_kwargs + assert isinstance(acsl.predictor.featurizer.featurization_object, SOAP) + assert acsl.predictor.featurization_kwargs["kwargs"] == { "rcut": 5.0, - "nmax": 8, "lmax": 6, + "nmax": 6, } # test passing candidate selection kwargs @@ -592,7 +582,7 @@ def test_simulated_sequential_histories(): "aq": "MLI", "num_candidates_to_pick": 2, } - predictor_kwargs = {"structure_featurizer": "elemental_property"} + predictor_kwargs = {"featurizer_class": SineMatrix} sl = simulated_sequential_learning( full_design_space=acds, init_training_size=1, @@ -626,7 +616,7 @@ def test_simulated_sequential_batch_added(): base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} - predictor_kwargs = {"structure_featurizer": "elemental_property"} + predictor_kwargs = {"featurizer_class": SineMatrix} num_loops = 2 ds_structs = [base_struct1, base_struct2, sub1, sub2] ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) @@ -654,7 +644,7 @@ def test_simulated_sequential_num_loops(): ] base_struct1 = place_adsorbate(sub1, "H")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "N")["custom"]["structure"] - predictor_kwargs = {"structure_featurizer": "elemental_property"} + predictor_kwargs = {"featurizer_class": SineMatrix} candidate_selection_kwargs = {"num_candidates_to_pick": 3, "aq": "Random"} ds_structs = [base_struct1, base_struct2, sub1, sub2] ds_labels = np.array([5.0, 6.0, 7.0, 8.0]) @@ -707,7 +697,7 @@ def test_simulated_sequential_write_to_disk(): base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] base_struct3 = place_adsorbate(sub2, "N")["custom"]["structure"] - predictor_kwargs = {"structure_featurizer": "elemental_property"} + predictor_kwargs = {"featurizer_class": SineMatrix} candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} ds_structs = [base_struct1, base_struct2, base_struct3] ds_labels = np.array([0, 1, 2]) @@ -778,7 +768,7 @@ def test_multiple_sequential_learning_serial(): "structure" ] base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - predictor_kwargs = {"structure_featurizer": "elemental_property"} + predictor_kwargs = {"featurizer_class": SineMatrix} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) acds = DesignSpace(ds_structs, ds_labels) @@ -802,7 +792,7 @@ def test_multiple_sequential_learning_parallel(): "structure" ] base_struct1 = place_adsorbate(sub1, "Li")["custom"]["structure"] - predictor_kwargs = {"structure_featurizer": "elemental_property"} + predictor_kwargs = {"featurizer_class": SineMatrix} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) acds = DesignSpace(ds_structs, ds_labels) @@ -828,7 +818,7 @@ def test_multiple_sequential_learning_write_to_disk(): "structure" ] base_struct1 = place_adsorbate(sub1, "N")["custom"]["structure"] - predictor_kwargs = {"structure_featurizer": "elemental_property"} + predictor_kwargs = {"featurizer_class": SineMatrix} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) acds = DesignSpace(ds_structs, ds_labels) From db5b56e353b2c1aba910f24e25802e6c2aebecef Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 17 Jan 2022 12:48:40 -0500 Subject: [PATCH 182/239] add defaults, add ser deser, deepcopy pred kwargs --- src/autocat/learning/sequential.py | 60 ++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 01ab4802..63c97994 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -2,6 +2,7 @@ import numpy as np import os import json +import importlib from joblib import Parallel, delayed import tempfile from typing import List @@ -12,6 +13,10 @@ from ase.io import read as ase_read from scipy import stats +from sklearn.gaussian_process import GaussianProcessRegressor + +from dscribe.descriptors import SineMatrix + from autocat.learning.predictors import Predictor from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES @@ -216,12 +221,19 @@ def __init__( # predictor arguments to use throughout the SL process if predictor_kwargs is None: - predictor_kwargs = {} + predictor_kwargs = { + "model_class": GaussianProcessRegressor, + "featurizer_class": SineMatrix, + } + if "model_class" not in predictor_kwargs: + predictor_kwargs["model_class"] = GaussianProcessRegressor + if "featurizer_class" not in predictor_kwargs: + predictor_kwargs["featurizer_class"] = SineMatrix + if "featurization_kwargs" not in predictor_kwargs: + predictor_kwargs["featurization_kwargs"] = {} ds_structs_kwargs = { "design_space_structures": design_space.design_space_structures } - if "featurization_kwargs" not in predictor_kwargs: - predictor_kwargs["featurization_kwargs"] = {} predictor_kwargs["featurization_kwargs"].update(ds_structs_kwargs) self._predictor_kwargs = None self.predictor_kwargs = predictor_kwargs @@ -272,9 +284,22 @@ def predictor_kwargs(self): @predictor_kwargs.setter def predictor_kwargs(self, predictor_kwargs): - if not predictor_kwargs: - predictor_kwargs = {} - self._predictor_kwargs = predictor_kwargs + if predictor_kwargs is None: + predictor_kwargs = { + "model_class": GaussianProcessRegressor, + "featurizer_class": SineMatrix, + } + if "model_class" not in predictor_kwargs: + predictor_kwargs["model_class"] = GaussianProcessRegressor + if "featurizer_class" not in predictor_kwargs: + predictor_kwargs["featurizer_class"] = SineMatrix + if "featurization_kwargs" not in predictor_kwargs: + predictor_kwargs["featurization_kwargs"] = {} + ds_structs_kwargs = { + "design_space_structures": self.design_space.design_space_structures + } + predictor_kwargs["featurization_kwargs"].update(ds_structs_kwargs) + self._predictor_kwargs = copy.deepcopy(predictor_kwargs) self._predictor = Predictor(**predictor_kwargs) @property @@ -289,7 +314,7 @@ def candidate_selection_kwargs(self): def candidate_selection_kwargs(self, candidate_selection_kwargs): if not candidate_selection_kwargs: candidate_selection_kwargs = {} - self._candidate_selection_kwargs = candidate_selection_kwargs + self._candidate_selection_kwargs = candidate_selection_kwargs.copy() @property def iteration_count(self): @@ -431,7 +456,19 @@ def write_json(self, write_location: str = ".", json_name: str = None): ) # append kwargs for predictor - jsonified_list.append(self.predictor_kwargs) + jsonified_pred_kwargs = {} + for k in self.predictor_kwargs: + if k in ["model_class", "featurizer_class"]: + mod_string = self.predictor_kwargs[k].__module__ + class_string = self.predictor_kwargs[k].__name__ + jsonified_pred_kwargs[k] = [mod_string, class_string] + elif k == "featurization_kwargs": + jsonified_pred_kwargs[k] = copy.deepcopy(self.predictor_kwargs[k]) + # assumes design space will always match DesignSpace + del jsonified_pred_kwargs[k]["design_space_structures"] + else: + jsonified_pred_kwargs[k] = self.predictor_kwargs[k] + jsonified_list.append(jsonified_pred_kwargs) # append kwargs for candidate selection jsonified_list.append(self.candidate_selection_kwargs) # append the acsl kwargs @@ -474,6 +511,10 @@ def from_json(json_name: str): design_space_structures=structures, design_space_labels=labels, ) predictor_kwargs = all_data[-3] + for k in predictor_kwargs: + if k in ["model_class", "featurizer_class"]: + mod = importlib.import_module(predictor_kwargs[k][0]) + predictor_kwargs[k] = getattr(mod, predictor_kwargs[k][1]) candidate_selection_kwargs = all_data[-2] raw_sl_kwargs = all_data[-1] sl_kwargs = {} @@ -741,9 +782,6 @@ def simulated_sequential_learning( sl.iterate() if write_to_disk: - # for now does not write out model class - # serialization needs to be implemented - sl.predictor_kwargs.update({"model_class": None}) sl.write_json(write_location=write_location, json_name=json_name) print(f"SL dictionary written to {write_location}") From 8a3134c8e814e2e961db5c323717673e7f25da75 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 17 Jan 2022 12:49:40 -0500 Subject: [PATCH 183/239] fix io tests --- tests/learning/test_sequential.py | 59 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 34721e25..1b2c562b 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -11,6 +11,7 @@ from dscribe.descriptors import SOAP from dscribe.descriptors import SineMatrix +from matminer.featurizers.composition import ElementProperty from scipy import stats from ase.io import read as ase_read @@ -79,6 +80,9 @@ def test_sequential_learner_from_json(): written_acsl.design_space.design_space_structures == acds.design_space_structures ) + predictor_kwargs["featurization_kwargs"][ + "design_space_structures" + ] = acds.design_space_structures assert written_acsl.predictor_kwargs == predictor_kwargs assert written_acsl.candidate_selection_kwargs == candidate_selection_kwargs assert written_acsl.iteration_count == 1 @@ -117,12 +121,11 @@ def test_sequential_learner_write_json(): sub3 = place_adsorbate(sub3, "H")["custom"]["structure"] structs = [sub1, sub2, sub3] labels = np.array([0.1, 0.2, np.nan]) + featurization_kwargs = {"preset": "magpie"} predictor_kwargs = { - "structure_featurizer": "sine_matrix", - "elementalproperty_preset": "deml", - "adsorbate_featurizer": "soap", - "adsorbate_featurization_kwargs": {"rcut": 5.0, "nmax": 8, "lmax": 6}, - "species_list": ["Ag", "Li", "Al", "B", "Ti", "H"], + "model_class": GaussianProcessRegressor, + "featurizer_class": ElementProperty, + "featurization_kwargs": featurization_kwargs, } candidate_selection_kwargs = {"aq": "MU", "num_candidates_to_pick": 2} @@ -147,9 +150,18 @@ def test_sequential_learner_write_json(): assert structs == written_structs assert np.array_equal(labels, sl[3], equal_nan=True) # check predictor kwargs kept - assert predictor_kwargs == sl[4] + predictor_kwargs["model_class"] = [ + "sklearn.gaussian_process._gpr", + "GaussianProcessRegressor", + ] + predictor_kwargs["featurizer_class"] = [ + "matminer.featurizers.composition", + "ElementProperty", + ] + del predictor_kwargs["featurization_kwargs"]["design_space_structures"] + assert sl[4] == predictor_kwargs # check candidate selection kwargs kept - assert candidate_selection_kwargs == sl[-2] + assert sl[-2] == candidate_selection_kwargs assert sl[-1] == { "iteration_count": 0, "train_idx": None, @@ -163,27 +175,6 @@ def test_sequential_learner_write_json(): "aq_scores": None, } - # test writing when no kwargs given - acsl = SequentialLearner(acds) - with tempfile.TemporaryDirectory() as _tmp_dir: - acsl.write_json(_tmp_dir) - with open(os.path.join(_tmp_dir, "acsl.json"), "r") as f: - sl = json.load(f) - # collects structs by writing each json individually - # and reading with ase - written_structs = [] - for i in range(3): - _tmp_json = os.path.join(_tmp_dir, "tmp.json") - with open(_tmp_json, "w") as tmp: - json.dump(sl[i], tmp) - written_structs.append(ase_read(_tmp_json)) - assert structs == written_structs - assert np.array_equal(labels, sl[3], equal_nan=True) - # check default predictor kwargs kept - assert sl[4] == {"structure_featurizer": "sine_matrix"} - # check default candidate selection kwargs kept - assert sl[-2] == {"aq": "Random"} - # test after iteration acsl.iterate() with tempfile.TemporaryDirectory() as _tmp_dir: @@ -201,9 +192,17 @@ def test_sequential_learner_write_json(): assert structs == written_structs assert np.array_equal(labels, sl[3], equal_nan=True) # check predictor kwargs kept - assert sl[4] == {"structure_featurizer": "sine_matrix"} + predictor_kwargs["model_class"] = [ + "sklearn.gaussian_process._gpr", + "GaussianProcessRegressor", + ] + predictor_kwargs["featurizer_class"] = [ + "matminer.featurizers.composition", + "ElementProperty", + ] + assert sl[4] == predictor_kwargs # check candidate selection kwargs kept - assert sl[-2] == {"aq": "Random"} + assert sl[-2] == candidate_selection_kwargs assert sl[-1].get("iteration_count") == 1 assert sl[-1].get("train_idx") == acsl.train_idx.tolist() assert sl[-1].get("train_idx_history") == [ From fb896a631a4f01e1de07cf10c385ff31a3485e45 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 17 Jan 2022 12:50:15 -0500 Subject: [PATCH 184/239] rm legacy code --- src/autocat/learning/featurizers.py | 571 ---------------------------- tests/learning/test_featurizers.py | 328 +--------------- 2 files changed, 1 insertion(+), 898 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 8e995d20..65ae3fc9 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -8,17 +8,12 @@ from matminer.featurizers.site import OPSiteFingerprint from matminer.featurizers.site import CrystalNNFingerprint -import os import numpy as np -import json from typing import List, Dict -from typing import Union from ase import Atoms -from ase.io import read from pymatgen.io.ase import AseAtomsAdaptor -from pymatgen.analysis.local_env import VoronoiNN SUPPORTED_MATMINER_CLASSES = [ ElementProperty, @@ -258,569 +253,3 @@ def featurize_multiple(self, structures: List[Atoms]): for i in range(1, len(structures)): X[i, :] = self.featurize_single(structures[i]).flatten() return X - - -def get_X( - structures: List[Union[Atoms, str]], - maximum_structure_size: int = None, - structure_featurizer: str = "sine_matrix", - elementalproperty_preset: str = "magpie", - maximum_adsorbate_size: int = None, - adsorbate_featurizer: str = "soap", - species_list: List[str] = None, - refine_structures: bool = True, - structure_featurization_kwargs: Dict[str, float] = None, - adsorbate_featurization_kwargs: Dict[str, float] = None, - write_to_disk: bool = False, - write_location: str = ".", -): - """ - Generate representation matrix X from list of ase.Atoms objects - - Parameters - ---------- - - structures: - List of ase.Atoms objects or structure filename strings to be used to construct X matrix - - maximum_structure_size: - Size of the largest structure to be supported by the representation. - Default: number of atoms in largest structure within `structures` - - structure_featurizer: - String giving featurizer to be used for full structure which will be - fed into `full_structure_featurization` - - maximum_adsorbate_size: - Integer giving the maximum adsorbate size to be encountered - (ie. this determines if zero-padding should be applied and how much). - If the provided value is less than the adsorbate size given by - `adsorbate_indices`, representation will remain size of the adsorbate. - Default: size of adsorbate provided - - adsorbate_featurizer: - String giving featurizer to be used for full structure which will be - fed into `adsorbate_structure_featurization` - - species_list: - List of species that could be encountered for featurization. - Default: Parses over all `structures` and collects all encountered species - - refine_structures: - Bool indicating whether the structures should be refined to include - only the adsorbate and surface layer. Requires tags for all structures - to have adsorbate atoms and surface atoms as 0 and 1, respectively - - structure_featurization_kwargs: - kwargs to be fed into `full_structure_featurization` - - adsorbate_featurization_kwargs: - kwargs to be fed into `adsorbate_featurization` - - write_to_disk: - Boolean specifying whether X should be written to disk as a json. - Defaults to False. - - write_location: - String with the location where X should be written to disk. - - Returns - ------- - - X_array: - np.ndarray of X representation - Shape is (# of structures, maximum_structure_size + maximum_adsorbate_size * # of adsorbates) - """ - if maximum_structure_size is None: - if refine_structures: - ref_structures = [ - structure[np.where(structure.get_tags() < 2)[0].tolist()] - for structure in structures - ] - maximum_structure_size = max([len(ref) for ref in ref_structures]) - else: - maximum_structure_size = max([len(s) for s in structures]) - - if maximum_adsorbate_size is None: - adsorbate_sizes = [] - for struct in structures: - adsorbate_sizes.append(len(np.where(struct.get_tags() <= 0)[0].tolist())) - maximum_adsorbate_size = max(adsorbate_sizes) - - if adsorbate_featurization_kwargs is None: - adsorbate_featurization_kwargs = {} - - if structure_featurization_kwargs is None: - structure_featurization_kwargs = {} - - if species_list is None: - species_list = [] - for s in structures: - found_species = np.unique(s.get_chemical_symbols()).tolist() - new_species = [spec for spec in found_species if spec not in species_list] - species_list.extend(new_species) - - if adsorbate_featurizer is not None: - # find number of adsorbate features - if adsorbate_featurizer in ["soap", "acsf"]: - num_of_adsorbate_features = _get_number_of_features( - featurizer=adsorbate_featurizer, - species=species_list, - **adsorbate_featurization_kwargs, - ) - else: - # chemical_sro - num_of_adsorbate_features = _get_number_of_features( - featurizer=adsorbate_featurizer, - species=species_list, - **adsorbate_featurization_kwargs, - ) - - if structure_featurizer is not None: - if structure_featurizer in ["sine_matrix", "coulomb_matrix"]: - num_structure_features = maximum_structure_size ** 2 - else: - # elemental property featurizer - num_structure_features = _get_number_of_features( - structure_featurizer, - elementalproperty_preset=elementalproperty_preset, - **structure_featurization_kwargs, - ) - - if (structure_featurizer is not None) and (adsorbate_featurizer is not None): - X = np.zeros( - ( - len(structures), - num_structure_features - + maximum_adsorbate_size * num_of_adsorbate_features, - ) - ) - elif (structure_featurizer is None) and (adsorbate_featurizer is not None): - X = np.zeros( - (len(structures), maximum_adsorbate_size * num_of_adsorbate_features) - ) - - elif (structure_featurizer is not None) and (adsorbate_featurizer is None): - X = np.zeros((len(structures), num_structure_features)) - else: - msg = "Need to specify either a structure or adsorbate featurizer" - raise FeaturizerError(msg) - - for idx, structure in enumerate(structures): - if isinstance(structure, Atoms): - ase_struct = structure.copy() - elif isinstance(structure, str): - ase_struct = read(structure) - else: - msg = f"Each structure needs to be either a str or ase.Atoms. Got {type(structure)}" - raise FeaturizerError(msg) - cat_feat = catalyst_featurization( - ase_struct, - structure_featurizer=structure_featurizer, - adsorbate_featurizer=adsorbate_featurizer, - elementalproperty_preset=elementalproperty_preset, - maximum_structure_size=maximum_structure_size, - maximum_adsorbate_size=maximum_adsorbate_size, - species_list=species_list, - refine_structure=refine_structures, - structure_featurization_kwargs=structure_featurization_kwargs, - adsorbate_featurization_kwargs=adsorbate_featurization_kwargs, - ) - X[idx] = cat_feat - - if write_to_disk: - if not os.path.isdir(write_location): - os.makedirs(write_location) - write_path = os.path.join(write_location, "X.json") - with open(write_path, "w") as f: - json.dump(X.tolist(), f) - print(f"X written to {write_path}") - - return X - - -def catalyst_featurization( - structure: Atoms, - structure_featurizer: str = "sine_matrix", - elementalproperty_preset: str = "magpie", - adsorbate_featurizer: str = "soap", - maximum_structure_size: int = None, - maximum_adsorbate_size: int = None, - species_list: List[str] = None, - structure_featurization_kwargs: Dict[str, float] = None, - adsorbate_featurization_kwargs: Dict[str, float] = None, - refine_structure: bool = True, -): - """ - Featurizes a system containing an adsorbate + substrate - in terms of both a full structure and adsorbate - featurization via concatenation. - - In other words, - catalyst_featurization = full_structure_featurization + adsorbate_featurization - - Parameters - ---------- - - structure: - Atoms object of full structure (adsorbate + slab) to be featurized - - structure_featurizer: - String giving featurizer to be used for full structure which will be - fed into `full_structure_featurization` - - adsorbate_featurizer: - String giving featurizer to be used for full structure which will be - fed into `adsorbate_structure_featurization` - - structure_featurization_kwargs: - kwargs to be fed into `full_structure_featurization` - - adsorbate_featurization_kwargs: - kwargs to be fed into `adsorbate_featurization` - - refine_structure: - Bool indicating whether the structure should be refined to include - only the adsorbate and surface layer. Requires tags for the structure - to have adsorbate atoms and surface atoms as 0 and 1, respectivel - - Returns - ------- - - cat_feat: - Np.ndarray of featurized structure - - """ - if structure_featurization_kwargs is None: - structure_featurization_kwargs = {} - - if adsorbate_featurization_kwargs is None: - adsorbate_featurization_kwargs = {} - - if structure_featurizer is not None: - struct_feat = full_structure_featurization( - structure, - featurizer=structure_featurizer, - maximum_structure_size=maximum_structure_size, - elementalproperty_preset=elementalproperty_preset, - refine_structure=refine_structure, - **structure_featurization_kwargs, - ) - else: - struct_feat = np.array([]) - - if adsorbate_featurizer is not None: - ads_feat = adsorbate_featurization( - structure, - featurizer=adsorbate_featurizer, - species_list=species_list, - maximum_adsorbate_size=maximum_adsorbate_size, - refine_structure=refine_structure, - **adsorbate_featurization_kwargs, - ) - else: - ads_feat = np.array([]) - cat_feat = np.concatenate((struct_feat, ads_feat)) - return cat_feat - - -def adsorbate_featurization( - structure: Atoms, - species_list: List[str] = None, - featurizer: str = "soap", - rcut: float = 6.0, - maximum_adsorbate_size: int = None, - refine_structure: bool = True, - **kwargs, -): - """ - Featurizes adsorbate on a support via `dscribe` - and `matminer.featurizers.site` functions. - - Parameters - ---------- - - structure: - Atoms object of full structure (adsorbate + slab) to be featurized - - species_list: - List of chemical species that should be covered by representation - which is fed into `dscribe.descriptors.{ACSF,SOAP}` or - `ChemicalSRO.includes` - (ie. any species expected to be encountered) - Default: species present in `structure` - - featurizer: - String indicating featurizer to be used. - - Options: - acsf: atom centered symmetry functions - soap (default): smooth overlap of atomic positions - chemical_sro: chemical short range ordering - op_sitefingerprint: order parameter site fingerprint - - rcut: - Float giving cutoff radius to be used when generating - representation. For chemical_sro, this is the cutoff used - for determining the nearest neighbors - Default: 6 angstroms - - maximum_adsorbate_size: - Integer giving the maximum adsorbate size to be encountered - (ie. this determines if zero-padding should be applied and how much). - If the provided value is less than the adsorbate size given by - `adsorbate_indices`, representation will remain size of the adsorbate. - Default: size of adsorbate provided - - refine_structure: - Bool indicating whether the structure should be refined to include - only the adsorbate and surface layer. Requires tags for the structure - to have adsorbate atoms and surface atoms as 0 and 1, respectivel - - Returns - ------- - - representation: - Np.ndarray of adsorbate representation - - """ - adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() - - if refine_structure: - new_indices = np.where(structure.get_tags() < 2)[0].tolist() - # update the adsorbate indices for refined structure - new_adsorbate_indices = [] - for ads_idx in adsorbate_indices: - # wraps index - if ads_idx < 0: - ads_idx = len(structure) + ads_idx - new_adsorbate_indices.append(new_indices.index(ads_idx)) - adsorbate_indices = new_adsorbate_indices - # refine the structure - structure = structure[new_indices] - - # Checks if species list given - if species_list is None: - species_list = np.unique(structure.get_chemical_symbols()).tolist() - - # Checks if max adsorbate size specified - if maximum_adsorbate_size is None: - maximum_adsorbate_size = len(adsorbate_indices) - - # Selects appropriate featurizer - if featurizer == "soap": - soap = SOAP(rcut=rcut, species=species_list, **kwargs) - representation = soap.create(structure, positions=adsorbate_indices).reshape( - -1, - ) - num_of_features = soap.get_number_of_features() - - elif featurizer == "acsf": - acsf = ACSF(rcut=rcut, species=species_list, **kwargs) - representation = acsf.create(structure, positions=adsorbate_indices).reshape( - -1, - ) - num_of_features = acsf.get_number_of_features() - - elif featurizer == "chemical_sro": - # generate nn calculator - vnn = VoronoiNN(cutoff=rcut, allow_pathological=True) - csro = ChemicalSRO(vnn, includes=species_list) - # convert ase structure to pymatgen - conv = AseAtomsAdaptor() - pym_struct = conv.get_structure(structure) - # format list for csro fitting (to get species) - formatted_list = [[pym_struct, idx] for idx in adsorbate_indices] - csro.fit(formatted_list) - # concatenate representation for each adsorbate atom - representation = np.array([]) - for idx in adsorbate_indices: - raw_feat = csro.featurize(pym_struct, idx) - # csro only generates for species observed in fit - # as well as includes, so to be generalizable - # we use full species list and place values - # in the appropriate species location - labels = csro.feature_labels() - feat = np.zeros(len(species_list)) - for i, label in enumerate(labels): - # finds where corresponding species is in full species list - lbl_idx = np.where(np.array(species_list) == label.split("_")[1]) - feat[lbl_idx] = raw_feat[i] - representation = np.concatenate((representation, feat)) - # number of features is number of species specified - num_of_features = len(species_list) - - elif featurizer == "op_sitefingerprint": - opsf = OPSiteFingerprint(**kwargs) - conv = AseAtomsAdaptor() - pym_struct = conv.get_structure(structure) - representation = np.array([]) - for idx in adsorbate_indices: - feat = opsf.featurize(pym_struct, idx) - representation = np.concatenate((representation, feat)) - num_of_features = len(opsf.feature_labels()) - - elif featurizer == "crystalnn_sitefingerprint": - cnn = CrystalNNFingerprint.from_preset("cn") - conv = AseAtomsAdaptor() - pym_struct = conv.get_structure(structure) - representation = np.array([]) - for idx in adsorbate_indices: - feat = cnn.featurize(pym_struct, idx) - representation = np.concatenate((representation, feat)) - num_of_features = len(cnn.feature_labels()) - - else: - raise NotImplementedError("selected featurizer not implemented") - - # Checks if padding needs to be applied - if len(representation) < maximum_adsorbate_size * num_of_features: - diff = maximum_adsorbate_size * num_of_features - len(representation) - representation = np.pad(representation, (0, diff)) - - return representation - - -def full_structure_featurization( - structure: Atoms, - maximum_structure_size: int = None, - featurizer: str = "sine_matrix", - permutation: str = "none", - elementalproperty_preset: str = "magpie", - n_jobs: int = 1, - refine_structure: bool = True, - **kwargs, -): - """ - Featurizes the entire structure (including the adsorbate) using - representations available in `dscribe` and `qml` - - Parameters - ---------- - - structure: - Atoms object of structure to be featurized - - maximum_structure_size: - Size of the largest structure to be supported by the representation. - Default: number of atoms in `structures` - - featurizer: - String indicating featurizer to be used. - Options: - - sine_matrix (default) - - coulomb_matrix (N.B.: does not support periodicity) - - elemental_property - - permutation: - String specifying how ordering is handled. This is fed into - `dscribe` featurizers (ie. sine_matrix, ewald_sum_matrix, coulomb_matrix) - Default: "none", maintains same ordering as input Atoms structure - (N.B. this differs from the `dscribe` default) - - elementalproperty_preset: - String giving the preset to be pulled from. - Options: - - magpie (default) - - pymatgen - - deml - - matscholar_el - - megnet_el - See `matminer` documentation for more details - - n_jobs: - Int specifiying number of parallel jobs to run which is fed into `dscribe` - featurizers (ie. sine_matrix, coulomb_matrix) - - refine_structure: - Bool indicating whether the structure should be refined to include - only the adsorbate and surface layer. Requires tags for the structure - to have adsorbate atoms and surface atoms as 0 and 1, respectively - - Returns - ------- - - representation: - Np.ndarray of structure representation - """ - - if refine_structure: - structure = structure[np.where(structure.get_tags() < 2)[0].tolist()] - - if maximum_structure_size is None: - maximum_structure_size = len(structure) - - if featurizer == "sine_matrix": - sm = SineMatrix( - n_atoms_max=maximum_structure_size, permutation=permutation, **kwargs - ) - rep = sm.create(structure, n_jobs=n_jobs).reshape(-1,) - - elif featurizer == "coulomb_matrix": - cm = CoulombMatrix( - n_atoms_max=maximum_structure_size, permutation=permutation, **kwargs - ) - rep = cm.create(structure, n_jobs=n_jobs).reshape(-1,) - - elif featurizer == "elemental_property": - ep = ElementProperty.from_preset(elementalproperty_preset) - conv = AseAtomsAdaptor() - pymat = conv.get_structure(structure) - rep = np.array(ep.featurize(pymat.composition)) - - else: - raise NotImplementedError("selected featurizer not implemented") - - return rep - - -def _get_number_of_features( - featurizer, - elementalproperty_preset: str = "magpie", - species: List[str] = None, - **kwargs, -): - """ - Helper function to get number of features. - - Wrapper of `get_number_of_features` method for `dscribe` - featurizers - - If `matminer`'s elemental property, calculated based off of - number of features X number of stats - """ - supported_dscribe_featurizers = { - "sine_matrix": SineMatrix, - "coulomb_matrix": CoulombMatrix, - "soap": SOAP, - "acsf": ACSF, - } - - supported_matminer_featurizers = { - "elemental_property": ElementProperty.from_preset(elementalproperty_preset), - "chemical_sro": None, - "op_sitefingerprint": OPSiteFingerprint, - "crystalnn_sitefingerprint": CrystalNNFingerprint, - } - - if featurizer in supported_dscribe_featurizers: - feat = supported_dscribe_featurizers[featurizer](species=species, **kwargs) - return feat.get_number_of_features() - - elif featurizer in supported_matminer_featurizers: - if featurizer == "elemental_property": - ep = supported_matminer_featurizers[featurizer] - return len(ep.features) * len(ep.stats) - elif featurizer == "chemical_sro": - return len(species) - elif featurizer == "op_sitefingerprint": - f = supported_matminer_featurizers[featurizer](**kwargs) - return len(f.feature_labels()) - elif featurizer == "crystalnn_sitefingerprint": - f = supported_matminer_featurizers[featurizer].from_preset("cn") - return len(f.feature_labels()) - - else: - raise NotImplementedError( - "selected featurizer does not currently support this feature" - ) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index f1d89d27..7bb5ded5 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -1,12 +1,6 @@ """Unit tests for the `autocat.learning.featurizersi` module.""" -import os import numpy as np -import json - -import pytest - -import tempfile from dscribe.descriptors import SineMatrix from dscribe.descriptors import CoulombMatrix @@ -18,14 +12,9 @@ from matminer.featurizers.site import OPSiteFingerprint from matminer.featurizers.site import CrystalNNFingerprint -from autocat.adsorption import generate_rxn_structures, place_adsorbate +from autocat.adsorption import place_adsorbate from autocat.surface import generate_surface_structures from autocat.saa import generate_saa_structures -from autocat.learning.featurizers import full_structure_featurization -from autocat.learning.featurizers import adsorbate_featurization -from autocat.learning.featurizers import catalyst_featurization -from autocat.learning.featurizers import _get_number_of_features -from autocat.learning.featurizers import get_X from autocat.learning.featurizers import Featurizer from autocat.utils import extract_structures @@ -248,318 +237,3 @@ def test_featurizer_featurize_multiple(): manual_mat.append(f.featurize_single(ads_structs[i]).flatten()) manual_mat = np.array(manual_mat) assert np.array_equal(acf, manual_mat) - - -def test_full_structure_featurization_sine(): - # Tests Sine Matrix Generation - surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] - sine_matrix = full_structure_featurization(surf, refine_structure=False) - sm = SineMatrix(n_atoms_max=len(surf), permutation="none") - assert np.allclose(sine_matrix, sm.create(surf)) - # Check padding - sine_matrix = full_structure_featurization(surf, maximum_structure_size=40) - assert sine_matrix.shape == (1600,) - # Check refined structure - sine_matrix = full_structure_featurization(surf, refine_structure=True) - surf = surf[np.where(surf.get_tags() < 2)[0].tolist()] - sm = SineMatrix(n_atoms_max=len(surf), permutation="none") - assert np.allclose(sine_matrix, sm.create(surf)) - assert sine_matrix.shape == (len(surf) ** 2,) - - -def test_full_structure_featurization_coulomb(): - # Tests Coulomb Matrix Generation - surf = generate_surface_structures(["Pt"])["Pt"]["fcc100"]["structure"] - coulomb_matrix = full_structure_featurization( - surf, featurizer="coulomb_matrix", refine_structure=False - ) - cm = CoulombMatrix(n_atoms_max=len(surf), permutation="none") - assert np.allclose(coulomb_matrix, cm.create(surf)) - # Check padding - coulomb_matrix = full_structure_featurization(surf, maximum_structure_size=45) - assert coulomb_matrix.shape == (2025,) - - -def test_full_structure_featurization_elemental_property(): - # Tests the Elemental Property featurization - surf = generate_surface_structures(["Cu"])["Cu"]["fcc111"]["structure"] - elem_prop = full_structure_featurization(surf, featurizer="elemental_property") - ep = ElementProperty.from_preset("magpie") - conv = AseAtomsAdaptor() - pymat = conv.get_structure(surf) - manual_elem_prop = ep.featurize(pymat.composition) - assert np.allclose(elem_prop, manual_elem_prop) - assert elem_prop.shape == (132,) - elem_prop = full_structure_featurization( - surf, featurizer="elemental_property", elementalproperty_preset="deml" - ) - ep = ElementProperty.from_preset("deml") - manual_elem_prop = ep.featurize(pymat.composition) - assert np.allclose(elem_prop, manual_elem_prop) - assert len(elem_prop) == _get_number_of_features("elemental_property", "deml") - - -def test_adsorbate_featurization_acsf(): - # Tests Atom Centered Symmetry Function generation - surf = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ - "structure" - ] - acsf_feat = adsorbate_featurization( - ads_struct, featurizer="acsf", refine_structure=False - ) - species = np.unique(ads_struct.get_chemical_symbols()).tolist() - acsf = ACSF(rcut=6.0, species=species) - assert np.allclose(acsf_feat, acsf.create(ads_struct, [36])) - - -def test_adsorbate_featurization_soap(): - # Tests Smooth Overlap of Atomic Positions - surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ - "structure" - ] - soap_feat = adsorbate_featurization(ads_struct, featurizer="soap", nmax=8, lmax=6) - ads_struct = ads_struct[np.where(ads_struct.get_tags() < 2)[0].tolist()] - species = np.unique(ads_struct.get_chemical_symbols()).tolist() - soap = SOAP(rcut=6.0, species=species, nmax=8, lmax=6) - assert np.allclose(soap_feat, soap.create(ads_struct, positions=[-1])) - assert soap_feat.shape == (soap.get_number_of_features(),) - - -def test_adsorbate_featurization_chemical_sro(): - # Tests Chemical Short Range Ordering Featurization - surf = generate_surface_structures(["Li"])["Li"]["bcc100"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["OH"])["OH"]["ontop"]["0.0_0.0"][ - "structure" - ] - csro_feat = adsorbate_featurization( - ads_struct, - featurizer="chemical_sro", - rcut=10.0, - species_list=["Li", "O", "H"], - refine_structure=False, - ) - assert csro_feat.shape == (6,) - species = ["Li", "O", "H"] - vnn = VoronoiNN(cutoff=10.0, allow_pathological=True) - csro = ChemicalSRO(vnn, includes=species) - conv = AseAtomsAdaptor() - pym_struct = conv.get_structure(ads_struct) - csro.fit([[pym_struct, -2], [pym_struct, -1]]) - manual_feat = csro.featurize(pym_struct, -2) - manual_feat = np.concatenate((manual_feat, csro.featurize(pym_struct, -1))) - assert np.allclose(csro_feat, manual_feat) - - -def test_adsorbate_featurization_op_sitefingerprint(): - # Test Order Parameter Site Fingerprints - surf = generate_surface_structures(["Au"])["Au"]["fcc100"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["NH"])["NH"]["ontop"]["0.0_0.0"][ - "structure" - ] - opsf_feat = adsorbate_featurization( - ads_struct, - featurizer="op_sitefingerprint", - maximum_adsorbate_size=4, - refine_structure=False, - ) - opsf = OPSiteFingerprint() - conv = AseAtomsAdaptor() - pym_struct = conv.get_structure(ads_struct) - manual_feat = opsf.featurize(pym_struct, -2) - manual_feat = np.concatenate((manual_feat, opsf.featurize(pym_struct, -1))) - manual_feat = np.concatenate( - (manual_feat, np.zeros(2 * len(opsf.feature_labels()))) - ) - assert np.allclose(opsf_feat, manual_feat) - - -def test_adsorbate_featurization_crystalnn_fingerprint(): - # Test CrystalNN site fingerprint - surf = generate_surface_structures(["Ag"])["Ag"]["fcc100"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["OH"])["OH"]["ontop"]["0.0_0.0"][ - "structure" - ] - cnn_feat = adsorbate_featurization( - ads_struct, - featurizer="crystalnn_sitefingerprint", - maximum_adsorbate_size=4, - refine_structure=False, - ) - cnn = CrystalNNFingerprint.from_preset("cn") - conv = AseAtomsAdaptor() - pym_struct = conv.get_structure(ads_struct) - manual_feat = cnn.featurize(pym_struct, -2) - manual_feat = np.concatenate((manual_feat, cnn.featurize(pym_struct, -1))) - manual_feat = np.concatenate((manual_feat, np.zeros(2 * len(cnn.feature_labels())))) - assert np.allclose(cnn_feat, manual_feat) - assert cnn_feat.shape[0] == 4 * len(cnn.feature_labels()) - - -def test_adsorbate_featurization_padding(): - # Tests that padding is properly applied - surf = generate_surface_structures(["Fe"])["Fe"]["bcc100"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["H"])["H"]["ontop"]["0.0_0.0"][ - "structure" - ] - soap_feat = adsorbate_featurization( - ads_struct, featurizer="soap", nmax=8, lmax=6, maximum_adsorbate_size=10 - ) - - species = np.unique(ads_struct.get_chemical_symbols()).tolist() - num_of_features = _get_number_of_features( - featurizer="soap", species=species, rcut=6.0, nmax=8, lmax=6 - ) - - assert (soap_feat[-num_of_features * 9 :] == np.zeros(num_of_features * 9)).all() - ads_struct[35].tag = 0 - soap_feat = adsorbate_featurization( - ads_struct, featurizer="soap", nmax=8, lmax=6, maximum_adsorbate_size=10, - ) - assert (soap_feat[-num_of_features * 8 :] == np.zeros(num_of_features * 8)).all() - csro_feat = adsorbate_featurization( - ads_struct, - featurizer="chemical_sro", - rcut=10.0, - maximum_adsorbate_size=4, - species_list=["Fe", "H", "Li"], - ) - assert csro_feat.shape == (12,) - assert (csro_feat[6:] == np.zeros(6)).all() - assert csro_feat[5] == 0.0 - assert csro_feat[2] == 0.0 - - -def test_catalyst_featurization_concatentation(): - # Tests that the representations are properly concatenated - # with kwargs input appropriately - surf = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] - ads_struct = generate_rxn_structures(surf, ads=["OH"])["OH"]["ontop"]["0.0_0.0"][ - "structure" - ] - cat = catalyst_featurization( - ads_struct, - maximum_structure_size=40, - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, - refine_structure=False, - ) - sm = SineMatrix(n_atoms_max=40, permutation="none") - struct = sm.create(ads_struct).reshape(-1,) - species = np.unique(ads_struct.get_chemical_symbols()).tolist() - soap = SOAP(rcut=5.0, nmax=8, lmax=6, species=species) - ads = soap.create(ads_struct, [-2, -1]).reshape(-1,) - cat_ref = np.concatenate((struct, ads)) - assert np.allclose(cat, cat_ref) - num_of_adsorbate_features = soap.get_number_of_features() - assert len(cat) == 40 ** 2 + num_of_adsorbate_features * 2 - # Check with structure refining - cat = catalyst_featurization( - ads_struct, adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, - ) - ref_ads_struct = ads_struct[np.where(ads_struct.get_tags() < 2)[0].tolist()] - sm = SineMatrix(n_atoms_max=len(ref_ads_struct), permutation="none") - num_of_adsorbate_features = soap.get_number_of_features() - assert len(cat) == len(ref_ads_struct) ** 2 + num_of_adsorbate_features * 2 - - -def test_get_X_concatenation(): - # Tests that the resulting X is concatenated and ordered properly - structs = [] - surf1 = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] - ads1 = generate_rxn_structures( - surf1, - ads=["NH3", "CO"], - all_sym_sites=False, - sites={"origin": [(0.0, 0.0)]}, - height={"CO": 1.5}, - rots={"NH3": [[180.0, "x"], [90.0, "z"]], "CO": [[180.0, "y"]]}, - ) - structs.append(ads1["NH3"]["origin"]["0.0_0.0"]["structure"]) - structs.append(ads1["CO"]["origin"]["0.0_0.0"]["structure"]) - surf2 = generate_surface_structures(["Ru"])["Ru"]["hcp0001"]["structure"] - ads2 = generate_rxn_structures( - surf2, ads=["N"], all_sym_sites=False, sites={"origin": [(0.0, 0.0)]}, - ) - structs.append(ads2["N"]["origin"]["0.0_0.0"]["structure"]) - - X = get_X( - structs, - maximum_structure_size=50, - maximum_adsorbate_size=5, - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, - ) - species_list = ["Pt", "Ru", "N", "C", "O", "H"] - num_of_adsorbate_features = _get_number_of_features( - featurizer="soap", rcut=5.0, nmax=8, lmax=6, species=species_list - ) - assert X.shape == (len(structs), 50 ** 2 + 5 * num_of_adsorbate_features) - # Check for full structure featurization only - X = get_X( - structs, - structure_featurizer="elemental_property", - adsorbate_featurizer=None, - maximum_structure_size=50, - maximum_adsorbate_size=5, - ) - assert X.shape == (len(structs), 132) - # Check for adsorbate featurization only - X = get_X( - structs, - structure_featurizer=None, - maximum_structure_size=50, - maximum_adsorbate_size=5, - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, - ) - species_list = ["Pt", "Ru", "N", "C", "O", "H"] - num_of_adsorbate_features = _get_number_of_features( - featurizer="soap", rcut=5.0, nmax=8, lmax=6, species=species_list - ) - assert X.shape == (len(structs), 5 * num_of_adsorbate_features) - X = get_X( - structs, - structure_featurizer=None, - adsorbate_featurizer="chemical_sro", - maximum_adsorbate_size=5, - species_list=species_list, - ) - num_of_adsorbate_features = _get_number_of_features( - featurizer="chemical_sro", rcut=5.0, species=species_list - ) - assert X.shape == (len(structs), 5 * num_of_adsorbate_features) - X = get_X( - structs, - structure_featurizer=None, - adsorbate_featurizer="op_sitefingerprint", - maximum_adsorbate_size=5, - species_list=species_list, - ) - num_of_adsorbate_features = _get_number_of_features(featurizer="op_sitefingerprint") - assert X.shape == (len(structs), 5 * num_of_adsorbate_features) - - -def test_get_X_write_location(): - # Tests user-specified write location for X - structs = [] - surf1 = generate_surface_structures(["Pt"])["Pt"]["fcc111"]["structure"] - ads1 = generate_rxn_structures( - surf1, - ads=["NH3", "CO"], - all_sym_sites=False, - sites={"origin": [(0.0, 0.0)]}, - height={"CO": 1.5}, - rots={"NH3": [[180.0, "x"], [90.0, "z"]], "CO": [[180.0, "y"]]}, - ) - structs.append(ads1["NH3"]["origin"]["0.0_0.0"]["structure"]) - structs.append(ads1["CO"]["origin"]["0.0_0.0"]["structure"]) - - _tmp_dir = tempfile.TemporaryDirectory().name - X = get_X( - structs, - adsorbate_featurization_kwargs={"rcut": 5.0, "nmax": 8, "lmax": 6}, - write_to_disk=True, - write_location=_tmp_dir, - ) - with open(os.path.join(_tmp_dir, "X.json"), "r") as f: - X_written = json.load(f) - assert np.allclose(X, X_written) From 4a1851ae92b696576d837a065abcaf446ee3df9d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 17 Jan 2022 16:10:34 -0500 Subject: [PATCH 185/239] add repr methods --- requirements.txt | 1 + src/autocat/learning/featurizers.py | 20 ++++++++++ src/autocat/learning/predictors.py | 15 ++++++-- src/autocat/learning/sequential.py | 60 +++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4e04b52d..e880031b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ numpy==1.19.1 pymatgen==2020.8.13 fire==0.3.1 icet==1.3 +prettytable==3.0.0 diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 65ae3fc9..3c157222 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -9,6 +9,7 @@ from matminer.featurizers.site import CrystalNNFingerprint import numpy as np +from prettytable import PrettyTable from typing import List, Dict @@ -59,6 +60,25 @@ def __init__( self._design_space_structures = None self.design_space_structures = design_space_structures + def __repr__(self) -> str: + pt = PrettyTable() + pt.field_names = ["", "Featurizer"] + class_name = ( + self.featurizer_class.__module__ + "." + self.featurizer_class.__name__ + ) + pt.add_row(["class", class_name]) + pt.add_row(["kwargs", self.kwargs]) + pt.add_row(["species list", self.species_list]) + pt.add_row(["maximum structure size", self.max_size]) + pt.add_row(["preset", self.preset]) + pt.add_row( + [ + "design space structures provided?", + self.design_space_structures is not None, + ] + ) + return str(pt) + @property def featurizer_class(self): return self._featurizer_class diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index b32fdd56..656f505c 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -4,6 +4,7 @@ from typing import List from typing import Dict from typing import Union +from prettytable import PrettyTable from ase import Atoms @@ -27,7 +28,6 @@ def __init__( self, model_class=None, model_kwargs: Dict = None, - multiple_separate_models: bool = None, featurizer_class=None, featurization_kwargs: Dict = None, ): @@ -74,9 +74,6 @@ def __init__( """ self.is_fit = False - self._multiple_separate_models = False - self.multiple_separate_models = multiple_separate_models - self._model_class = GaussianProcessRegressor self.model_class = model_class @@ -99,6 +96,16 @@ def __init__( **self.featurization_kwargs if self.featurization_kwargs else {}, ) + def __repr__(self) -> str: + pt = PrettyTable() + pt.field_names = ["", "Predictor"] + model_class_name = self.model_class.__module__ + "." + self.model_class.__name__ + pt.add_row(["class", model_class_name]) + pt.add_row(["kwargs", self.model_kwargs]) + pt.add_row(["is fit?", self.is_fit]) + feat_str = str(self.featurizer) + return str(pt) + "\n" + feat_str + @property def model_class(self): return self._model_class diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 63c97994..1f4d4b74 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -8,6 +8,7 @@ from typing import List from typing import Dict from typing import Union +from prettytable import PrettyTable from ase import Atoms from ase.io import read as ase_read @@ -57,6 +58,19 @@ def __init__( ] self._design_space_labels = design_space_labels.copy() + def __repr__(self) -> str: + pt = PrettyTable() + pt.field_names = ["", "DesignSpace"] + pt.add_row(["total # of systems", len(self)]) + num_unknown = sum(np.isnan(self.design_space_labels)) + pt.add_row(["# of unlabelled systems", num_unknown]) + pt.add_row(["unique species present", self.species_list]) + max_label = max(self.design_space_labels) + pt.add_row(["maximum label", max_label]) + min_label = min(self.design_space_labels) + pt.add_row(["minimum label", min_label]) + return str(pt) + def __len__(self): return len(self.design_space_structures) @@ -119,6 +133,16 @@ def design_space_labels(self, design_space_labels): msg = "Please use `update` method to update the design space." raise DesignSpaceError(msg) + @property + def species_list(self): + species_list = [] + for s in self.design_space_structures: + # get all unique species + found_species = np.unique(s.get_chemical_symbols()).tolist() + new_species = [spec for spec in found_species if spec not in species_list] + species_list.extend(new_species) + return species_list + def update(self, structures: List[Atoms], labels: Array): """ Updates design space given structures and corresponding labels. @@ -270,6 +294,42 @@ def __init__( if "aq_scores" not in self.sl_kwargs: self.sl_kwargs.update({"aq_scores": None}) + def __repr__(self) -> str: + pt = PrettyTable() + pt.field_names = ["", "Sequential Learner"] + pt.add_row(["iteration count", self.iteration_count]) + if self.candidate_structures is not None: + cand_formulas = [ + s.get_chemical_formula() for s in self.candidate_structures + ] + else: + cand_formulas = None + pt.add_row(["next candidate system structures", cand_formulas]) + pt.add_row(["next candidate system indices", self.candidate_indices]) + pt.add_row(["acquisition function", self.candidate_selection_kwargs.get("aq")]) + pt.add_row( + [ + "# of candidates to pick", + self.candidate_selection_kwargs.get("num_candidates_to_pick", 1), + ] + ) + pt.add_row( + ["target maximum", self.candidate_selection_kwargs.get("target_max")] + ) + pt.add_row( + ["target minimum", self.candidate_selection_kwargs.get("target_min")] + ) + pt.add_row( + ["include hhi?", self.candidate_selection_kwargs.get("include_hhi", False)] + ) + pt.add_row( + [ + "include segregation energies?", + self.candidate_selection_kwargs.get("include_seg_ener", False), + ] + ) + return str(pt) + "\n" + str(self.design_space) + "\n" + str(self.predictor) + @property def design_space(self): return self._design_space From 275acd67e0913f977fc48b74d0f4b63ed3e1e960 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 17 Jan 2022 22:26:03 -0500 Subject: [PATCH 186/239] add text to homepage --- docs/README.md | 62 +++++++++++++++++++++++++++++++----- docs/img/autocat_figure.png | Bin 461463 -> 0 bytes mkdocs.yml | 23 +++++++------ 3 files changed, 65 insertions(+), 20 deletions(-) delete mode 100644 docs/img/autocat_figure.png diff --git a/docs/README.md b/docs/README.md index 7b781b6a..2ad2080b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,14 +1,58 @@ -# Overview - -![autocatfigure](img/autocat_figure.png) - -AutoCat is a suite of python tools for automated structure generation -and sequential learning with arbitrary candidate metrics for materials -applications. +# AutoCat Documentation +AutoCat is a suite of python tools for sequential learning with arbitrary +candidate metrics for materials applications. It additionally has tools +for automated structure generation related to catalysis. Development of this package stems from [ACED](https://www.cmu.edu/aced/), as part of the ARPA-E DIFFERENTIATE program. +Below we provide an overview of the key functionalities of AutoCat. +For additional details please see the User Guide and API sections. + +## Sequential Learning + +One of the core philosophies of AutoCat is to provide modular and extensible tooling to +facilitate accelerated computational materials discovery workflows. Within this submodule +are object types for defining a design space, featurizing materials systems, +training a regression model, and defining a closed-loop sequential learning iterator. The +key objects intended for each of these purposes are: + +- [**`DesignSpace`**](User_Guide/Learning/sequential#designspace): define a design space to explore + +- [**`Featurizer`**](User_Guide/Learning/featurizers): specify how to featurize the materials for regression + +- [**`Predictor`**](User_Guide/Learning/predictors): a regressor for predicting materials properties + +- [**`SequentialLearner`**](User_Guide/Learning/sequential#sequentiallearner): define a closed-loop iterator + + +## Structure Generation + +This submodule contains functions for automating atomic structure generation +within the context of a catalysis study using density functional theory. +Specifically, this includes generating bulk structures, surfaces, and +placing adsorbates. In addition, functions for generating the single-atom alloys +material class are also included. These functions are organized within AutoCat as follows: + +- [**`autocat.bulk`**](User_Guide/Structure_Generation/bulk): generation of periodic +mono-elemental bulk structures + +- [**`autocat.surface`**](User_Guide/Structure_Generation/surface): mono-elemental surface slab generation + +- [**`autocat.adsorption`**](User_Guide/Structure_Generation/adsorption): placement of adsorbates onto surfaces + +- [**`autocat.saa`**](User_Guide/Structure_Generation/saa): generation of single-atom alloy surfaces + +Structures generated or read with this package are typically of the form of +[`ase.Atoms`](https://wiki.fysik.dtu.dk/ase/ase/atoms.html#module-ase.atoms) +objects. + +When opting to write to +disk using these functions, they are automatically organized into a clean, scalable directory structure. +All structures are written in the +[`ase.io.Trajectory`](https://wiki.fysik.dtu.dk/ase/ase/io/trajectory.html#trajectory) +file format. +For further details on the directory structure, see the User Guide. ## Installation @@ -17,7 +61,9 @@ There are two options for installation, either via `pip` or from the repo direct ### `pip` (recommended) If you are planning on strictly using AutoCat rather than contributing to development, - we recommend using `pip` within a virtual environment (e.g. `conda`). This can be done + we recommend using `pip` within a virtual environment (e.g. + [`conda`](https://www.anaconda.com/products/individual) + ). This can be done as follows: ``` diff --git a/docs/img/autocat_figure.png b/docs/img/autocat_figure.png deleted file mode 100644 index 91bb4e90cfa52b49af383d1eda12e84c29a6b445..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 461463 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%0A<(7Ta#wCTJJ>w;A`Faf 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�Xq0Gcp@j;oC4b%NH z0NK;yKclWskk`}nJ%hZvgLZ41bt}98(3|jgf;RKkr3}J;1&hY#wXQ{0 z^x-rKxG1>wK0I-|-WF*44)rwxz!q&DdZk4m=^Ege{?W7ov)_57{i=mE&D#J5SO;}I z{Yq-uyft*6MJ#|8fB1i+FR*Zh9XkY5x58x6l#IM{J&-1Q;Q!AE9YrggU zSQu+TH=Dn3HZ@~$edpHobOWn$EqFR`3f{J17~PIBTUut&mDp}JedHh(QO8r;7IdE1 zzJ&$%C|mjavEH2zAZZ5c?S-+U>DK$dL(dvXJ-ZK(&H&&R)=i`veH|o8lXbXo{zAI; zv;Ul0`vJbN-0pq-n_P4HRu5pg@sMw=!&|X1Z)PD+9yeKrfi0=)xi8WM1 zr;emK^s+X5MfJY+4Hgn4E81<+RGskupt=vxUr1atiBy3qbZ=}P5lbDk({lO%4>KLiAklbZlzJgV=x>Bt0)2C@s6jsoJ4sfmzCEnv7 z?XgX81)WzAucKBPcBJnat?oT- z>o~feAdb$B!_~ljvWVo(X4QB+SoK*wagr=BAs?M^yIAS=Gij7ogSFzd6L1%9)$`h; zW2~0d>;(J5zY%>IJt&>8(>v!6x_g)po%80cxu`EE`4xxDMXj5s^k{9ij1dy%jQAhod08&w3QieU%O0JL zNBf3eOTB6^k>auMDt~JjZv`)w(BZ91w;Z|%XsC%EzgPM&PK3%ud86T*{H%x4Doj9Q z5PeFE7Z*LLmmLe^` z!upQ;Wj$vrCuB@Ea$i0hlu7$*7xitgK@;t_8z9S*2OW!~`!w@x``2OTbhIby#rkOv zV}iUuZQG5*xFY2z_v3Q{l3)meeV0`;%~5+_cs+G6xVK<_eVdiCyQ~n+9%E$)fTv@W z)g+$xJpX!XR!P8VqLZwc{8vnjv*5{Qr}43ste<`R`Xpl2YyoSZzOQ~ab(;sugsV&{ zFe*B&Oa@ez-4`!{?`oY^L$jDr&+A(NA4==K^)FJ-ftLZG+$+wY%IeCo!J!DV;a+`%GC%m+OG!!B`LH+NTzSOq2w_xV$ z{`@}F!yoyXrwr{=if{H#b{Q%|iYL~vnpbPSp|USblg38Wq7zHI|( zKc=|Zj8c}Ar~)1+dXVsih_MsTP~ zdM(t?C&5Oh2N^ejW0f_hEyS$r1j^*op z1BuB?C)k1@S~hhxZA^c)e`k7*G|lM}lX3y8Ur}#A#ga$I>23jwhOR7=#y;wLk9`Gc zISBsj!sYOhbp=46pMmfdz`Z|ZtDVl#1)AijG8tQTsAuKE{!nY0Uz3M`k6V*AF-iP$ z-uvD*{G-5ct${k>=M)q7e}Cdk`uA+f6HL=jas4Xp?u!}uzQJZbbZkKUnknBMLtPXQ$Us~sb03$hTWvyh*8yM6KsG|{;NHd9B? zQY(K%dFY@eH3%-CPjBjZ@k?02v3StEDa~DAI~aP#GnE61QUHx^mC!d* z*UkfJ{h$0*YQ-7$9NW02-u%Zji9VxMP2X$ZL>GE3Eu!a41KeEsyZ;^Qqa)}_S7m&e zTIf3-ETUET0+!QVFTau2fA`<8rGIPaZVMOBhL!ddmPS**{cmBOzy8bLPH7a{$VZ@6 z%WN&g=kQ;of2F=Z`ir#gkH44N0A`{eM#nSGHRzrkKbdB*{%*m-$amMh`t{U=FH6Ck z;5nU<2cviZi@KZJ?%Ht^u;v?o0pGsNqT>iUWCjcG0=(%xM*m#8dLei=PrkY;=>O*TQ^&Ub=xXSpZZCuzF$T~MV!K;~JCF84u3N|U1H()< zv}jQOazgAR%1M|L`}+pcu>Ct>q5zq1HdJKXZT4DgyOm3wV*T3g(63OC%snSQ^VnucQx-k4O3D_Kk+FyJFhK&Dtyi*e)a26`3D(zspGV-({UkllrgqtCqfRGVw?ULDOvzMeKMJh4&)SkX_X6LNPFs8=}$a1mD^NV%vj zlL-nq=tjVA>2V@INqxK3sFQKn39nX7>au2E|GtDV_9KV!F6B}`_f%fz7KXB1UMr^p zpWD#G^(jO}g#PQZ#!%q~7hY@KYNeq;k;^ zq5MgE28kvyUPwpK;zw;Im-nkt zV{G8(H1h*bQ&f%w{K}%3uVAGI4%xQ|NVjylz?i51E7Da>{!?9*c{wlCQfnQ0lkPXP z`qN)-y|6TttIWwtN-=-yZ31PIr`#C>jJozC@(&t2{jJG1%09#rC za$1@JlKL>Sr4uWI+mC$S$*NOJ=UTkuV}|BRu&YBW)i^K4#F6K&7Pn*m+`a$B)V*~( zd))CaK;yZ42L%Cv(Wg=q3P%&bl4jja2%JEcev9;RbQeWdp8?&2qp2G}sTl!iYDRbx zRv-XEUAvwRGwlTwty`FE_u<1JH9eD+DCrP^4+PEGf$SadkKnGE%X^z2rI~cc0`F-0XWqD!)akawx znrjN2NJjCIsfe~7AFEQe8pYPR4sUHJb`0is2a~Y&Hq7gbjv{Xd(>Z9~hEir?8Kluq zI}Qg7I~xZ{UX2sy9FW#yjS^d+h(?c}){?#}P5^8`lfRePE+*z+*fLxkVdRj#0zvXocGCmy`Q0;VT_s2&yF;L5E2b69m9uB^t75 zpx=3Gn(=&3(@N{Zl?#szmDI?92m()1 zcChV95GZ^fkdI1^V4r2AWnxW2XH5MI+UV$4ze@r87=ibcpBD0o0fhW}m>k-P+f;ma z4ZLyk-5BIg!K&y7eD0Q(UM93!*tEKuOZuJ^=k^+P~IyW?km@^q$jhWRi!rSuOf(%gO!zo+n0_3Hk<{F ze2(_{NBS%RC~NO`ix~UG?em=IGI1HjN11ivME(Q=tqzp6plYN4%|ejVv>uoE(3)vG zRwD-h1V$-mcaN)^^fQ(N9(3{D32n1}6IYPX)li>mdFD2%HG<{56QB#rHxp- zVCI*)P}BrRRG(Fn!zzq8LdRzS#*PF2Y=p)G9LDfE{-!nvT;O-F!*PIx1~$f~XHd#t zV={Ld9=J_ZtC$xzu!Wg4Tt(Ev$L)gxFuUoWo&}^^kF{1z_K;=Ir`k^W1oz~P^jp8R zp&S>ZeK?j{Z}6{+@y9wVpVz+AcpYOXH{Ish)};e$(x7Y<9J6_ys?$~}7k8*Sv+{pJ#kDebh5n)Ya;zPS{G$~+&3TEk^Qj?X6;xw zbu_O_O&d0+1$7_miIm#uGrQW@IzPaiZ8Imfk>?V?O!)q(DO(Kdx9jQrRsTty1NfC3#u^De z>+i0)r3W4BZkl5|+wAUV(jrz)Ztd$B0<6L>l%s z$pwi!l+gvkyK`7Q!wZNZ8O96!VDhI`^KE2saTXs`@|?H=TO9ybxKT~Fd$NA$eJ8BV z=!#h<yq-9r2@&DrvW?{XXx9%Ba-ZXr) zHmoC-78;l0^Hquv*8uJ;o2p=OGD#{MMIsuXTiVCKxr$LW`C>_B-Zp z$e&yw1TIRdn>rQ>OdL9KF2*OV{RJbGGx=0`r{*dWgD}&Uua?WQ%nHa8ye@-dp4JA79fOJzjE5{le>wAkK z?e2QO!hn9&gK=`-V*W1rxH}*$SLknI?6`L}pYbW>gFa7{o@8vCnRPyu+r0^zukx|5 zn0;Gmz2)=A70l0hoQv!_|8`t+4k>-ggL=uzp`gj<{JC)Cyw3UB7Qh}|v6R-rZfCr~ zyze{!m7u#aA39RZnX9QsF-cZg7NBWu&Hi(>w9`PLv;b931f`^XHO^#xMVBG~tK?~a zYYQ!<%Xdx3vBi0x?JcKeV?d{)Pq*!OPyP$W-(idtWK*V;9k&8nKkLv%?VmKX=(CUJ zC4D?TsUh^L&y6^kZfN$HRly-FRGK*UzFA8nCBP1o>t?KnS{IolcCeQnizJ^}X)6Yk zpYHBQM6Pld^?1OjsOpo8A#$?PZF6ucbaWz*Ha6o*X;mC z9RNQaC^W86HSvd6EGoSkmtk!^0S_RWu<4iL%)tuRSOnMXL%LXhqtU~MS#I=qY% zR8SgJ6IFNw2?P%`8)`ieV;V{MuHcgC(k)dJaB}NI>49EpbA`>0 z!G`K~FCFmd`@o>@gC^1uP|(@i$iQ^Awz{6*iAbhTdYUY1Zs?#cO0DlBJ3}Xf%2d!C zc~g%L7`!ZHQQrlB$`0C&D-wC?e9LsixaZoFKbJ z$vUlVtFlQr%vIiUKyfmsKP0y`=@8fug9wAMCwFP}GB;-@noC^rNp-5?y8&L!@t0w! z3`EUQJ3ZVhgH=~ci}%alZpRW}FwI%y-YqR!xd?t(h8bkyma>G*1?|Lx4HDo7e~&&` zTK~Z7TX_VWcmmp9mgw-G=|I?P$;nx=b~ zpRH}k1J~g&78*j?B6Go^HOjG|Bxx~ngd8ZJuE5gRBR$tstrPoeLzIy+dq|O%L|O>F z1{nD|TQIi>KG{21CfkP|Lg|MDbKG93e4j$m)Nhdv&rLbxAK4Rp(O=XkyjhPgq!6^C zjV(}4fwC{+ug6o$PWgVN{Yh0d+<`WWZ~r7MGknXrOg;}P*Kt|*A=w52pN?Q&?w@W! z-z5MwK^;%*E8FsUXfYp^Ew}zjo4{XI?s6ZZ{LK4Dx_Z=-Z1dqTW`4mdp4sPBLP>=5 zBa5c1HKhGeY_UV(>Jy|lAL&(Jh_nE#0O7vC&Hr-de0mQcNq|cLxK&;gSHMbZMS){; zypB8^XAJ4-W~+|vL)kY?(sL|ngBHtI%C%1(X&~)5nbHNYCUl%uKqPgTit?Ci?Ou6C zT|15djM#6qs7t}!cFNL<4D0(xtCkk@DLj4!aMF%0(u%AtQpT(sMWG{`>{Ocefz?kh z)=1reHEq`UZLD=zeA7a^b!2y%!=ZIp3DIY|-EE$&c+I4XlEJpN>=V*4v>kn_!$0_| zN4vY|bZ$j#=CsT%ww!h2XnV?2dvPnUh)+!0V#r0*X;E_OA*_qA{?@v?3k&crEQ~q< z%9;hD@rP+pT3v*^>&d^=b<`U=SV&q7G1Mozq?S!COj_6KX)WAiN`{KZRzmf#BIJ6% ze;t(ZKkc_%cLMC>WW&0iu&y|PweofPh;kt>eBaz7Vf6^#cVX@Ak59o{ibcW!dc#X6 zH%?5{Yj$$rh(J=e3jpK`^XgKzZ**dHl7+QXYy~Oh-n3lI5R7tlU8{pFr00ZD@WFUx z+`0sxkRS5H;NcBj^!dH^Ay^GqCUwXFNYKmV-Q%n6Ohf)Ga)B-_CdKN)` z+$(tM>`n9=nPwXmnDG@c@3pjB7wgYb7h&uWZN`z@7`27V*DiPp_zC0gmKI!A1aE5JkLcA89@$O>by&LRKKT_{((}coqmS~gOq-k^zTwH!~$*p)JwAGfDk?31PAE{FD#9e*9KvKaD>mdu3S7oTL%<@T5 zXJw>@?;@5Jr*c4OW0k5y5XluVD%S>IR6lw0%fU|pU_zw;YVN#n^+Etjno+APxf-NG z6RC$_0DaIvIyMcZ4lI?Xxz_o16#z14LKGnjSC}PZb{_zT+vv=XyvvEO;7-s3-6}94 zfH{>%@>!mEF^6K}mMVpJib!#LNfNv_q=$g~vq*K9lb)j`O?@nBu_Wwlwo{d!M1~lIg-w93+wA^eJE;VBY- zOkejZ@&jZk?eek!y2OE zl8@kX?R~DzoS?ioNp?lgCZHwF@7Owan|D++mZ$??&vFGg%%FRSEo9b(?`V}W3ZRlL zfQXp&?3^~A0t70Nn`z%#7J156rYEArHE%_2S>EzlDVM&hDRzKQabin;&)N^gLm7#; zl`-$y&g84Nx#mx>PB8Q^^4!sSDZR@IQx`nd;!^&rNSek~{0WT%jMa!fmQ*QxyjOC>U_Cr=fLEbYP%q{CTJJc? z51=R#GDV*YE-9nAo!9i#IhG4<@Xga>JF!Y?!+JV+5PFnJV7-ZB?lzw5-|04sYD-gW zzr~@o7BDK$o+RfJ7>}-_ug%l;46DhZ@7w0|d!*%x6BqBP%iRPdQU2981PDC(ymU@$ z9Q)_mU>;70wK!VCpT8ad!e>tCTdvBpJsB%+!KhZjdyZ6Z1UhQdeA2F!dwJouJoU&k z_|SAARksWF`>XN!QI$iE%X`~{7VsMZ5Z!Jg;HS{#{xjz$?*n9nR^_7X6|Ae+C2#PL%QUj36T%$q49e)h+Mb{6wp>{+ZbsM6Ru^;{If46q7THO=a&1{l zFv@ycRiD0Jx+^{>GwMiv-BY2*@b}|lv*3npB>XY4mC(gu>rx9O=Obfm=@Jys7b_2< z-zw{E#cV#~P3zxu<*&f5-|{2hS3cx*V)ff4UzHd87%k9jyNc=@y1N#VxA|`2zI|t7 z>68ic)7ZdmkOEZ7p*_5FJhQ%Edk>w5cv^Al?nFKsd^g&m*ZkRs3!Ld&H}p;DHG=8e z?`oVdupaj%=oEruQ-Jcyi*42ZRpSmpC3nrZHPEs~z4317lSTV9?PJGVPL^FWV zv6jt`EQi)?K^Qlz^k)8|6XN@RA9tdjefpNw^k{H)Ibo2p zx*$@RCDK{g;Y8;`8g=zrP`Kh33W!!^u0|XiyU6Kw7a4qRM5p5*Y{x2j7Qlhejw67i ze6ub|^Ne||gDcnd9Pl@HET_->ek?&5z}%XS{feMYC>~@4y>`Nx1SbOwl+d96GPk`s z8B@0G^J*!JV$!Z|G0FQmK3jftW;~qdYN6q&O9QNqhc6!9ax&`V@dg8nEwHrDsx4;l zD0omEIDKxOl{)izAriIk#&sxVO@LXm=_mNO`89rP-pAC`D%ybzIDvGcY%E^cX(K720z-isU845Hgf6@AYZj`HFR+rA4>0ZzTlJa$@$4DyR9rytB=j zYMGhQscqkh3a#Fcbc|P~$G34SK|1~EeZKTbFXhuxPVl3ggw~Q$=2i20ajT{ukWODJicowJ&wC;9L4x%8F9Hcl>pV)1 z+o+DB0BI?e`>i62nlJM&+lfF-?q~5KLEEydD#lI<1ll4LG{rc?wSJk_F(DVUF$7=Y zYWas0=1F3ve|e0R@7V9j2c6V=0-7ht{l}AIVIekz#iyq$N=JJEj%%@~&!{FA8!dPJ zB!fJ-=bzw0@G^MkbrJ025b8SFDWAy(`OCPgMWlcB3-+-(IFAWL$*bZWUomw-B%ShW zZ5a5YG;qJ4G4Ew~d;@-uQC}aD&JS@$uhpC&p4NH0=&yC|ucPigSUBmYOR!ZMYbY;r zd^BHK_OhJi63CSY2{jb)g1<5^xl*U7q1dV@(=L|BiVTcZ<{b&deJz>VScr=)VBV$< zO}nro4*6|n!R-!;yn4`WbSKj{&TS4HS;FZxl@yC;q2;JQMH_{XV#1^I?slw-+Il%n z)vahgkKFytSNSLDnic?*#C~_;s9OBBq#!PO<_u_8U%S`OpfFuhp9Rfri{vsU$0ZtmZT^ZMm z=WpxXNw0ctHDrl(eAku209(ZL5h|FVj~*=^PGV&*=%-r;L8R?l2huvXj5gJ*$W#+V zX6%dcBL%Rvd+X+~__%@$ItlKhzO^V(*76uDVKpzurESM1EGXKUhOmMgWjmc#7Dg?{ zYoq3JX;caTU{R3YvK9J#^FUHl(p4F3ifG1Hr(zLy}4M~e6 z>mUGD(%0#87y7YafD7-o6&LEy05}O0?B6~V{h8@J0&ym zEz{M%*gpKL=^xAA{6~(fFNQV=ZEjPJXS^3r`7oO0*P6<{N2^fl&v?qQ)};2+hdE?H z1BBDakg~R8i~ex&|0g4e|D=T{0!Rv`+=Dmwc(jb4usMfNOc5w7eN;3;SbS1b;YG~4 zugHs9bu~1Ev6^5tUO|wids-5O6d)@8s|yW3P<-O&P z>ge}znPxS!k|A|b8-CO$9B=Tk;+x!*7j{1NE3@@!n1S!x$@#}CMOYrMeB!t9Ebm?T zCpdHth3zzoiF_Vmo1Cj$Dhu-2xXP9ydI28@o{CZ1>DQ*a>{>E{CO^z{S&Rq1kvqXi z4BAx;R(}<0RTb-bTaXYmgfN6;XFdnxa@v(t=>!)SII#}vy(65AbqMFkS{#issoSZA z1weofa|M!=hdAAd=kmtW#pI9vDHN(h|#J*;n8~0J?J)w{aRzzOpA7$iQ z8BiA7=H&$b85D%@pA&sjPPHUgS@GI;blOcLkI80j-d-DLRWhR>#Hfu>X3&^W@w*b0 z3xQDI9c8u2Qw+6cvoMuBjm!@LC+W=PYE91pR0zt1l0tlOnHYEC&HPTLyh?<1mHQ@J z<);hsH+W3?(za?q0Yr=Abh*j&D?q>Zut;*PZ-hE~hAoEKUll%mD2qka_m$iG%64I0 zuSQ!*gL%vL3LUGxP>sPGg4F<$@XS-`JZ-UuKGi;=_I0O1`r$L~0FxQFQhFkvfJOwK zF*BmB`qDQ5ibjZI+qTb{K_0boyF$PG`#YQj%Dv|Wl*qqBiCuy|zZQWEjR*(wv|v zW1uGu>EFn{Vc7(%$UP>M$gNvxHqu|}6H-uMegXe)=pHVlx%J6Kw+SpO+>WLn$Zq;9 z`%Hs8DT^Ec2XO`gIDFUbJo*;W(o%53)0nil_FxjX06Vti$@{7tEhzo`zcSE`??B3@ z)s2?<7p|U(VX}V5+m6hlN8%ZmEo1AI* zj+5pNtw1Q3@}_jHS}t2+Ws=Ox%R&R8WMIE9@SqN@^@<>sG8lR`bn4ooEYGs!KIIMb za(<+>$ep`wv8!PWD>k=Qxd3gu5&#j@lK0A3EXuPTyeq)`PnqEZM$cT)`8NyYwB-)K zz78y;q<48J_*x@F%i}znJC@&m+HJ7bk*8+s^GN;~9D?ftG%1fdclT5^jUEhIW%{lb zO^Qe87JR!+ZkA{M7Hq#jpCBYU(8~1on2Ztyi$tHb1a-whtuKQq4lw~I5S>yvVRpsaSfc);_^_$AXqNgfC$X$3W;&&^<3 zG0s+t_qks^IY81%{l-JEr>+SV6?)I)qR}Dvf=*Z%HL*gL@KNAC{~T%vG(F;b%#>Z1xV(2$;!L{D{kj|uY!fJx6VvHV~6?T91ao&31c zXVBV2+B`yCy5@QJf$51P{P>jg*YYl?`0O9%>>%vlO2OOZqkK)~_aeD`p?qnjM7>w@ z+%{L9boN)}-{*yROj$fQ^p~8n_kVB-<2vYiWAn!J zC44izMj88Pw>qL16qDY6Al`>qW4TgYDs6R@Uqe%Ek?uoE@fL63UrI-teJF9BLTK9) zjP!)Qri>&e@S=HlfvDA{EtKnI*KnX!HshV{)gZjr+BtvIZYec z%itETMAffQ|0}+nZw49nc>Z}ja?FgaGV}@CvEopxIR#*&^-l+<>I&%CAF89Q7`W{= zKLG?MJL)>l-`!s1_FWxuyW;IOF6G@xSGR`@tk7@DpK{WqC5scBZY+j9>Bx7_UAi6< zxYOg815(sUxU2NGjnJD`B=E_&ne`VeCID@=bKr^-+z(ESht-vqB{~MSAB&0NAAn@# z*DfV5*J&-pjgWuq`Cbj7N}RO11EU*@(N6lcme$3nU$>&USUi52`eO1ujzx{W<+O$n z7%EhpNmU>CO)OpOSa-+xIPG$2C_4+Sh)aD zqPNSv8x61>hvu#9?X^?36RdCQ{Nlfj)Xv(Z?a1voZRCzfF+sNsJj{b|{8)jo>a84| zNjn{2yq{msodM~XUzH9!C?fblzE1QVBvq1T00l&^rGpWbo`$U!&_^V!cmXNs^i`xp zdJWQFH0$_nKCTQ22HBC7t2?IE=4tZ0i92DI)shrjE zc(o*g(ELz2mzU-p)-Te<0BZWC7wv&>OGlKI1JNEuFqtSC-)80SSS43cWG`k}Pr+#- zL>*8UmO=Rt0CAJC)Npl}kf3QApe+LVxL_h}A&+LtGi5J)QArG8i1B^{ ztGa!Rh5FiRYr2+xe3LE8w0-$*e}9jnMH&zPk!i`5OCP2skg{OlJFIs6j^BJ5y?Di2 z_YWm=h*@*l_+I40&D*zb;y`;m^r9C1B)RoW;3%fP_%gz;WCn`ZhS1fUn(>(wize{6 z#nX0s*QTYb7h~Zq>Z4gl^!BYdw#E;ov@hM9Omk;Xq{S;2Lbob)sZ3x4k;GSSLy3>R z!{?uh0mLstnY8+!JVm#|wh>(qt5|e~5Kfc!`5!1Kqv|1^KC3=)5`D~tK#!8P&n!u9 zBqzw^rt+;Nh|ZLoaiSga9_jVP79+tWEuqg{OdN@gm7VRTy_BzNs=0~eEgFu=?Y!sK zRehCgMsL*ON$YF3-Pz}9c@Vl1V`@wyt4Tf7m2{M2i2b3uroZJwJmV_HTKh`J2cAbi zW}99`EXw|mjwdytgTx8Cl)-H>`nGf-QT?~833)J+#~MPKL;qE;jy@IIn+xaEby;&i zcpEe|7EV5}s7k$SZP`YDryL0GZDLZo8J}=>?%YV1srzpFH?7oq0hyX&d?J@JIliOB z8|X3oA)0aFm2Jgs zd!C}D4C=SY6J+gwa$;s_U%^l1CSJ?qOx!bhE+TD5K+?1&1doFr%gnfeOndU3R!7#+ zW@O(jPJ$wN!I|XAL!iyH1Z!<$%9B&tyCE9OCJ_%4a(a%P*0(%rYGs_G{+9>t z`JXaH8J9!*-j|0qn=I6P_UpvOUQ^m=!yr$*jjm>-&*mrcq8al1~RXa7RfFj*1oCS zARo2CXd#cvvLu!TG7@D66-zyor?gg5z8os6=@jKM{u2~vlK+1QE7fvCxvelN-o5_q z8wS%{&xy2fmWe*AWKBB9w)e8~J)#FEmHGpiJ@rYNKKel@T5UM=4oBMdPL;ZTfP5Rs zcq~7mM?ti)NuEyg*004$CXI?h@duqjGax~WPQN&H2f)dr2?bN)S*1*la~@ON8k1u? zR)swt*Or|IVkd7fhZ?5Duj)sRW5?h`J34f3)irDqFB>Y|Bd;34ue{pqs)|n3?0lwC z_VZy9cBlni&}&c`6SY?Pu|tcFAX9E)1}p??w9w(zPz8j$lG#8r^k25 zO(Y*_XfEk~U^_>_8O_GqC2#H#=1$5H6|91<{yNn;Vsh;A9wX>fA@06+jqL_t*5DoN00t>6^=@uautM2%+~QxPKH zaRzQJfKE_vPU=jnl9c(b7AlbnMwGv$nKVMSyvMbUpY==QQSck6`}HUxo?_(5N72a> zM|!3hus(*451BB#6;59~l>u-yIxGv7;VR$+a;IsxC!Pp=K5z&=tLFnyAji~tSZE2z z1kX#3NGCxKsU)u$?s0pSM=K^{xl52v$Jm(`GcOrkzUJ>D<#-1l540P7-Mohh_)jih zO)qkM`({rji>LzHTzD<-{Ii?|$ZxrpWl?4oIT6=)|3D#127PjT4gH~D>lG#-A7GKV zrl%(zSYy9I8Jf@pqP`%^0>nM8O~Cuqsr+1vHHS{mRXyD9m-|2qR1~UEj?Mz?ypQ$V z1eP-f!J!8UwjX^?xkb==(>g4c3gUg};D4okI9^1*oueeq9=*56BY&>!&CS z^kKW29a>XYfrlTX&)gtn#)*}qp>_bhbeuuL$^a-q;EM%o4N$( za}!=}jIZCh2Lzfvekir}bK>LH z9o%A^Xo40_Tf4i252Tyd(w!MjOJj`lq}vu2;*=4=ptf}zQd|GVG2A{>A506J`sRD#C&{{boB;wdV5NA#Z{)Tq$}C;xU5q!-8Dr}*fqwMF zDNm3Q%;|~=JSXEZ5z|V^KXt8m9@Kq`{3%`sW7c-IDX9zUtK=$u@HP6XDcVyXZ8@r= zl4NebK@U&lMv%o-6Du11Y~S58vMIKls6(g^KD}!Tby>70o8lu2P^Pe$wsq1d_d$3; zee_Ahp{}I&{()G~sxItFX4kPmyvl^s3934ldRfTn{S$hq7?DR&%%})cvq+5ra#y;Y zqvbN!vOahP-*n)v<(0v$Z36u}u$GU$+!umbD|0MlKXT2w#p?M-^2JwL?OqA^ zYy~pn)sjidK>_Rv(6|FYizK%zZ5!HvY-(XgLbdHRClJhwOOmJ~qf9#<@(dcXdlSYr z`UbaV>8#uZL9O)mj&4rR@7@x!=q?92Zz-KWIBp;OrG=4lXf1>hg3*Kbj5p{$`eXCw7Pi&u zV{3CiTQT8n%z1dnIjC*a`J6uA?9XljmilfJ&+ON0LoF+;si-Y(F6&EbUs~nbv?9Io zBAC8{&^FwXb}hCA6y1Fh{h0?9ao?zs3?I@yk4$O&;e1%PDDCB(;SXGItFU0%Z8e1_ zFUZwm@{n6yLyg>zVwoL(^pJc+JdyeC12P41Omur3%I%?^!r>%}GW6m<4FD0p7%H(7n8C>;!qfK!-PrLZKy&r?&Z9g+q{|0$4~Bq=~w(>Fo?4;uek9 zPTYpCu-kEZt>uqCg6w#mI7cH{p-j1`*{VoD$h2)%CCIU!{GGVk;JxqZYnCMfyb=&{ zzqk8k^>VM3kIwK+D?1jZz0R;@ffb93tPrR;bukd=*if`Pgrhih%>A=FQMBa$g)28> zYnmrCjF3IDmuu9v;wT6 zW4c8>dqnI6r}tUkDuMEK)JbMsn`t4w`7(7gN#02Q0w|_EhQj5xmjhS?xYeYK6_DBa z3?hwD7oLJ7V36Bh{*)YR&dIFS8*VGRh^$;f9%q;&d8GF|y!K?l^GuRIQ9*F>2A@0m zQ&yriSCcczO7OOmYMpfJgjbMiCzHVq0A%vUrsa|L0dC1yQgcGv$0~q8joa5eIm@jr zo@C|=o_67?u-n<*V?ud^ljb~~YmGosnR5d@@+aC9X(=bCF#UJn6j-#axCOw~!8YVD zwuJ;OWn#;}t`8 zlJ(cmMEg8I>^K)2wO##AdmG<8zE+y0OO3bPzV z24)KHE#Gb?AV3em?`}rpy zwSIXUXXFC7!&o+1{&wEKK)bT-$#Vfv z68QLorMK^7p#*(IE1!kS=hO6&gWQHPPB#mS8Tjnhu~_JWHrtPXw)bf8wS;mn_XCw# zV}2S@%V)JOAOi5yuy6$+>DUJ>ux(0RTefFChrAbWTuZZ95ltWaFw$=8-xSZZ=yv5@ z8rwH-Ngacu=@!?Ert`^raXOm-Q-Ce2yik8xmNK_5WwZYI{eDTW7EsoUPMXzmOiP`v3?^w&+{UBM8~y@|hdH|< z-@3f5FK+N+C;g*3ucxkLS65-0kwPQ8`E zT4Jn6|6(G~c%!c`^)PvEd3+{)^3{_O&D#^XVqu$mOYHGhPxh#J=R4~7f{q(D==_*I zk}_6;^rJVf>%*rY@og`PRq$?7vK}E=_*eCuqUw+|c^EM86|2C;c%mF+2H!Y{)a? zb)c882NYCBH#yItFQiaJglWjj8sf!*81*Qaq@2xha%4G|H*wRCc4_(%sNIlff5`lr z3Xm7vsnG6^nh<&k_v|2>}h<2C~Th3KbV8b}`aKnH+REs+8>wml=$%7gjYo2UFp4`+$JX zd-!7#(22%CgR!Bk6y$-A+SqQl_70}DMO+n=r@{*F@W_sMJu5DPU*UKTMaO(}!e$*h_&G4N z74+MA@k(@R!7D!;^x&7^*6)vurNbx3qbzGl!wI6ibTAJ7#=A8#3QyuxEC2+Rm=EzA z^1mH9p1wXbkiIiA$RxitCU!clo}k0LNhfP3U+uIlTHfk5ME&_^++*-^H++AJzs*bsa^zQcV_%{oFS{^og(6GtWn0sjP-PI6rPLCSj&3z`#LJE`8P z+y())M95@FvmHRNGNd!ckMO;4_|ydP0pO_f=l6}qs##kblY>G*&TrO#nzAdaPHx=- zID-6CKn9fejnu_H+M$4%6MlJZ**s~EtfG9TQU2AZzWkT*GO)7zS=_NNgC4DK1V4^Y zKSz;Y!J-%7vA{?<`4_P3WF_6nzsi64-gkxv(ne(EKb6S~vRP{#Q$uF`r3`|WZcrYb+D`)Fy?<>Ie?a)q zpf2p2?SqsbWqzIDBz3B+31Ifpmwe15?f*J4mcB{8f5Pugv{eDsp93cU^|90GD9`1` zApSY7tI(nlX_-W#@fZC(bl4|troY+-9UJ(zU4M+C{o}Fmv>#qYS3i>E}|0jLWM>qoivytHdwUr;8WeXk&aQ)4RGXc8(FZ*|;UHf;_|FNiX zXCVM#`v}Xt6J=HT+lFPF@~ZXGNz(o`Ak{mxd95sWbVDy~u$49^FYj{iu>|>LeI>SE zvx3mJ0edSzm)Wsn>CQA0<=L628$l3tjrI)#=p;D9)$+>VyX9ZLQ~iwl#;M-QSH(LG z4@%%<@%rU-_XNj=ufs9v=Hb-MLRt$>uBVTEl&=2bhiT^Ik?p?&qAUn{OOooUI)K(a@7R<9d($;2g_%c)nJbGx%%9+1TwV7abj$6`D0}{8T7L2@Lks!>eHK%Z7rNynq3@c+(zFzdiDbE%!VEx4*p}O z0+zLLv~tcXZ1*e^?N)u-xyNbJ7BH~A2wGbotI0|5nuk7LEl$p(_0Zf{Oaif_UOpIK56Ce31~9Q`PMxA_5dB{iX{gd z`B) zm+!Ck9k1&Hr#&2Ni{fjIBzmpDx({4s#YT7;6XgZ&ufy8N0Y)Syq_^ue(>1!(O%Cx?iY#ikZ)oLWhed<_inB4e4?rDf_`_gPwzfLMa=DmRi zW#`kvo_>{i8@B_;M(DAlvi?2gL`x#$+ejUx-S#F&hiJSWM`=UuWdbgrvsDPzDJVyq z+Sa6PgL5boQ(;wQnZVa#qU9=_>8r>HN|~Pg37LyRfI?G-UOH-(zbkYkZVz;jnx>PN z7FT0@-<2V^7uwmm0_J@O#f>Ork)Cuxe{_Zn$d=QxZ=LIpcq)}Qp-rdWS|qs-UHZ(! zK{;0ExmQ2LOJq)0XLBlG@h9jv~!A-g>nc4j4~;^zIILe1Rl>WO@-6#Uo#(<8B9t10WfP?T!1{){e0(t)Jpd5lP z$^%jx7Ci!gZYi^Et{5QNC&m$w3X7DLB0>wq$K-ww`B+EefQBk^hnbLgEW7nWwxkFthPcDLMBiD;;3O7jS`@}GmP51fK_W!e>KA=Kh?8S#JHNLhQ?IinQUFhXY)5!k zizF=^t;abglv+gzXpbvr@G>iwl936L7U{b|p3^+nqV631sbvtbR<`VYRZyitl5RIn zC5cy+@08oxbHV&pX#Ol`XjS4E2S#) zLuyPQS}p&+)w~#mp>liJ2z^e=j99?oTBPBsK*|`%4!2TYiruyVH@CSIjiC*i<6B>L zK=gwa5357rhH%qDxYhL|JI7cHki1z0&8@oT;a;zv`Z?vZ2@X$>1+REdW$JsSRWq=^xr)jNj z)(@{C&HQD84Ng43WNU3_&NrTf-wbxz@Z?d-IUe)`exIqQ zknf1A2bHhdW20LeIkk(0^5G;obx&i3Oni0L>@)ydNqer1@Z6^k)ZXH5Ja>oW3-2r{NE|JHYjBA`d=;iI2a7C#KdS@N zFj^aOc-5{ggn-y&sjs75ZL;FK(lHM{Ot4^(gvyuV$s3Tx6cBkbQKsBYsBPL4T8wc^ zTF{L3&c`X0?XMEq>b@vD4=%n`I>*9v)bl;JT%d{aC??#Exu>s&3zLyqJ8kXKe;$Z- zR${mOVQiMIh@AaEem;$s>DFiOce*YdI1GEk8FA+ME#KSbI^6Z5IOO)ZBCV^&=StIj zpBj2n`u<#OkxC;m4z7H16#qm6`gdwHFV5C2sP5Nps`(-103;CL{*}tIQ%+*HBAG46 zu$s1erX$ta4h&{v=u{_9@C1W@2d$(!z*x$868e}^ix48MHn8HQ!Xb(GssRs^qv{VC|DS7|MR-Iqk` znp$7ZgU{_jkb2@%Ix$E4(zyF^14);f`m{FMPFnesqWSvhK0(jo8CvZ5<36jz!V7^kPtmILAay6N`#-js5G z|CC$V|8~tVtnR7UJb^Aa4`Za9X?S%P$mP~7R9lq}V$pAwSa__38{6s(5z0X4e`9KO z><{~1_x9}xMNz2STyBl;N1P`!>I(X0o@@PW!^N9jPq8d&jv@DriXmhGgf9HIrXv@E zht8MM335f1^u3v4t+Mfm4?ZmdyLT|5H?2n&4&lJ28D~-~HwvCNn-E z?c^`Gh=OL9AbNTl{oADl)uC@ALMVOXgc|Le!X9yIpH0^3i||76=nfv1XU>5x-QglH z-mll1U(B;`;eeG8R@$@z!R%*KgGymQ=Zq)LYAG-Z$}vW<2-EUYX+xsjpG%p>Og(sF zlJw$g&s2IKX-7eK+N9hYWZFEDYcc;3qD@V)>-B4#G!#>d`^+@FH_#l}_BF>WJm;A06#lwNkRABubo?*%9_F-m011T8av~hhc&Y7$%cEtvM z4jSUyJ*nY6wkZY>>h}*~s#%sE59moFWq?ZM!}gP5X*M&(m_0(B3wDNDS_KD-lEA{#uJJr<6qSZt4aG z*5X(k-(k`cYjS<*CkXYGJ*WmHCA|ErIy-k`VE<&E^Le0vbni!exA+2WAWqu8UK@3% zpAt6vPs*FzMF*04rOCsz8u)F~IL&Vxy&HA+t!4s-FC?m&X#pJmqb`E17CJ5K{<%OsZb5T;t==~ z2F$(N61*NP{30TLIaV+pySCf_w0+{Z4oCDR(zAD#eC~nCNE)A<*x&P)`~BI&%0z&Z zHPJsj2X0;F-Fm1*mYvwcm-HfHxAZgJZTgvoP}2^wGGh+~{0XXqNjZ<8>p;#*-rL@k|5 z<0^f(ccbb2g&C&qxlVys zNcH$)1a%bK*aO+f<%|EwR{k6 zx4-M1Q-mbWuW;^&cT@;1-n8m}5$6z|O&h zrUvHfpvSIA`|g@+?owZoZ1>^PoGbeDlDDx6++ z{g$}28>ZI+tI0yNcTMkRW83X4_&=;t(p|o!LVf)eO-CXDra!wYVIS-j)a?bq0Le)o z?(yGAr2ppa%%6uxG=aF1+z4a$H@Ej`-I+`Z$oBmg7ee`< zRg$Fg(wbP8g}e9345Wk3t4q^S`uc?%?v8ou3;-A(;39tzRKG$vAiP_H7O|k25 z>Xvu};CLSfZ&AN!ySZS1zgbPD3Aa6-N#!iRJV^(wyP+k;sc*HoO>T7`zY8Gc9@pQ< z5WR>dS*CZm^M&n~L8V*Uqn4_>%VniC5rX)r2|`K?Mz=x_q?}@&ohal{(I{oQ9P>k# z#Ht*xlO@_iJ;9zIowK-rP?**Igt85$k3I+lbZHL53Av+Gus!~Fy)Pt2UE+Mg8Mv>`_glt0NSLaNDrC&*!s zm8ZuoQ0d-BOA>7+^z(;S@^)hmq%th&COT=f2@5d}D0*xv^j3(fc0mCoFN^(~?(&kx zGWYsa)jyhwpei1BJcg!Q4^pUTmrIyGNyrl=B9f?|aOxT&f-#Ke#yEVD{my-|LCTCG zcc?V=v$cD4?{yrEVqOqYKyCO;fXzqcMN1$3FvTLz)FM+8 zkN68268}Wp(>E`zQ46RnU<#qPb|uTrI9PEr@GmgllG?0MZ^%aQU=HE4+G2_rnB}`qcHYMHN4C$+I)9Eeb|*JS z=`PxH6Wz79UpBBtBsoIr*nJ6+z`sR_k22H6WXoUcs4A4X$2AM-h5V{OQXp8Hq}_>F+9>r%o z8>;nZmXj7^?AO8q4&L=MPYj?(9NeaPjMz%-*w z>7Cz(e^CH*Bl4c+MeAoi#?B$w)2;ryv#ALXgd{Wp_$d>J9EUe$8R5_uFJI}%`IKLnR~o)j6wR07Mg ze|uz5aeZm0fmmUm!*Z~JKB^@zF^PN~cvW;2Fayw;cP1|m*YA0Ev=CIZAUjn$Tc zgOgr0eb)SnGL!5{D)M2N@w;?JA5syi^)%uMyBwL$h#Hh~*O+5rXAl z+m1jy+>0Piedst_To39^ACygQc2c2>b*V~!#mvhRDQVaoKUi6nlbG>IOet9JZYcB| z*2(XzKV9Hk;MSMto}(irOp2raaYy?;@s765J@Pn~1kadveb>+;vX4{z%d}YU94(Ui?3^83ELH z0N#L=2cMB%I~oJnCqgA&Qkh@XSDibp(CH$bM=+3>CFVFS}oo6W%f-9d}! za#NgU9(eSAD%F9*-B`EU8UByC*>*;RykYA`%%Yx1YW!DpHP*nS&6LTxj2fi~8O2nY3oQ%u^SbntL?US$>}(PqQuU<5#!ZJ|LQT z>Po<4@OsS;5KXmtHm+R^urTt3je~@@mslNDL121InT0jM+ZB9lz+{ezU;hmD3qu1b zh#5ceYYcHHu^n2Gazn|?WU$?e94vUSwxQw}+lk>VEL%jP08KIMEapLl$Mx`Fa-bPD zXN`(Q!ASY=4Jj5l7Z zE{=`&tVo;Yp?UX&wp9&K6W>z6QD7e5PE?@%V5B7a_xb?5HxgPLm@vusS|;Lg97bXChc=UK;yY zSn!|@A<-S|fByu?&%zm`nbTL{U*ut=!=4_g3J5|p6JTJ_6&q=-k$S-zRTv~GN%B&% zo;ceT`-8GT?n`~x6Hn4FROz2D8i@(@NzvAo;6m{AAXJ!wA@o#qj#4btyCeqeCq<4s zQLqx}u##0@dV54LmY^*bf1&hblOg3X6$qGz4vdTKal|h+r1YzA>eNxI?**ivI0PK}^B~*M$Ui z^N5J{12F2(svrr2H3V|M!hn0YTVek~@Lk)feY$+J_8VdNJjm0zeKGoaWk}3Y+{Lj$ z&nmZ7PT)g1kXnD3Zcs(vzhQnpedzl%kv4-Rb>XEsRi^sVGW2Ju6J?3SFbfm*RGEjC zneQk<{)ZRE1vYe9=6G>TNttc~_5yICgIJJ_glcFKNSG(fxBM*p)P8XkR^7#w2*4B_ zXz1QGN%Yu-=0E-+Kq|AH)jhfbLdFX+(1uCMz>_b z_Y#Qch?6j?lKe6<3C3-1ZbT<&qto~YiHxSV+}EgwbZz$^f0duUYg3REg=@q_)^1NU z-)sw8wX5Ijv@&6`-&?PQ)bvF)w}F>#p`G%Ziam+G$vZL*^+F>Y`kS6k3^&wnS@~!M zm-(a$A6u?&Bv4-ez3Gq0{0j;QD>QhUV)}6SE3&#E(&a{9zafXTsHz_0c#1By#L90L zib96^uIDb<#8i@-IJ^M&_f%?_(@M`1y*aW{8jQy zqztW_P^FhPT7$jVhwKk#NBS;7)DA4`J(+zL>&wh2tGCpCn$H)1fp9hj7s8-E=lCH% zsFhAeE>bXbU;!%Pk@_gU3;I*wou_4mJq((0B2(G#-a?Q>rq_u${+oItBmOCW+$++b zrQh!`JlfQ}fk(&$qxdh1z4sAYHWjlPYW)3$EsdTP_$@s6yFUk4*-?e>h@YZQ5Do@| zj~Il%2C{-iz#D}Kh)y`d-RBAPTm0nvVOt_v^neZxB?c8t)uU%5d=Jk3I9Js_1f<0$ z@#o*`jLKMO;#Dz;u`Ds4T$V9XHPnR|NH2?cVc;cyx*`qw;W3+OR*ek0f#BY$t{9{0 zZ=>EjN_NQCBc@X(-ua&5>KE;WnRZbGYFth~$SK49MAhe@X47v)?dMmI2C2mvkuuj> zS8bsmvLB|tUJ5ht7J7#=-!^v>d5gV3^vjR3y8%7!8T%RE^zX2Z6L$EysRZsyKBS(y z?qj`pnU5<_bUAiYVY$3clSYw{zW7|~ZU@r|N&r~#8vcbXTp zLvgMr9_daB0V;@o@zVe;oY1pH?go>NY0eS=`=0=EBvGyds>j$o2Zql}LmQpQ5vxR82RVYCGgn;#A^)=scy!8j#NUi9r*5nA zknl8$+U^blyrxS2huuap7LwLylmf>{ z+|rs81BzNB;yR-l`D_o9#aqizx90`gXkz)qVuPVg?Owsva5=+)LGYCo2^S+FE%*5k zChF{%c3xz5)1Zco!b#{!#Y6SDB??_t!PJz)a(Esbu+cM}v>*ywELu9Z2D41TIHkfE zj60d*T6rF{DK=g5kkrDbiW3^0!>eGSWsOJE972dm@p@Oj)bBDh?>|DoGZPd9zDQTZ zz?5+u9EA_BSIwqI0GmYP%mmX0lN8hYl-m7~pHbWQ)VUupS+qJ+^v=H=FDj&f8$JM6 zXefCc^5VxWoy&@hGsme%qA5{wys<+LmR>9q)DlzO=MCOq&L|4#>U8M6;IUF2@beoe zoR<4)kg8jk?#Itm`>hd~XIN!rl_zL7siPkk&lvUCb(i>MV(sVL52`#Ak*rEcv*>CN zH)2)XGWB6u;hNxNQ=;I}qvDbxdd7dgMEldV#q`bJe$A8J;7XP-xF@_MVa!9XwwuC#ZuuYaSBVU_g6>)3T z_ZX-s(i?Hd632;|G-aBHLM+vY zI($#5Gh3-8OjKhT^OydfELi?Btea(|Lk*s;@XAD@klFxF@PyDB^;qy({;@N1jm&Skf*!QN7?o3XZLz1GwIz9MWzL(OA$-KkhtIVHfIy~^)wyS{+@h4Gl zV({Br)odRqMAu{=1pUq7%n08lXS&X%F>wA#GDbHS#$k)m`qM6c7lx(8LpmRs(|tZO zQRrD@0HZO}+Aq!J9BaF1u~OQE1-t}yPx6uos}zLBvDcu8Z~IT*la~H)3=$cUW-fB$ zg73;^(;}^-IRWmlG)W?iR5CeV378cphVnjSpWK&@YJE|5dy01mLBvK0{rZ=z{W~`V zN_R=?>ODv)n|R|g-JR-}6&H-knRS*#nRIX4Fx}Rxs%g;jJk$MEa7Y0MzggWbNb4}^jw9#k`PkXlbVzfkBjgJhH+CJ{4~1>Ct~WpZ;aL; z6HAZ$3E*%VRA@ROK$0Yu)O3f=oDTV`DS7E$Ds095{-tpA@gCWWT|kOr9gN?6wgNv@ zLUyb-;;a~Nl*A?eW8^!w)5V(CR=@~<#db#|KD+8CI$P%BNZLvL!Cu4NrpzK|LZ%Z; zSq~M*dZw1vJvnzms>Ww0i-pGb6D3%Mvx#^uk#f&mE(lpv_t)d{rf*p%GIpjrfeBJ- zo~XYRW1%@{eWf*;7k+G?V7X|i`a2tz++HcInQomK_89yOI1zbGA)5?7MhxDltxhj0> zKDKGi<(D~Nyp)dA z8r2R+7~Y(iNc6uS$405vFu0&Bt(ixZ-c_rI|7je@6dd1jFNVKG)Qc}>8%hl5wQhoj z9J$N#K{bD2dC1JRjnj5cT_^8s*_Rm_Vp+>KO{j89(d$xtQ>6*vm+w$U^?J~FdES~6 zwZG)h8s8G!kg{0~&Yr1HOBCQXUh{&FX-eorexNElWPHz?5cy+!;dpMw9hw7;mj;Hv zN-0CPbzKRpE7C0!AP3wol`7eKC-x6mOpJPBW>mcfzDe6Vf0pOr^exDr$&aF`t=UinnwYhjk(?)Q_-f zOA|7fzNANwblkyciH55UtR{19#Y@RZXqrxoWYG-AqHQ5Njn}JxLo+8yv4Jjt z{U>6wKxr#~Djj=-s?w2nQz5_8HV|DiO+&gY*vjC=>r}pPODa;uYbBw!tfETblzE9o zX+BPxYf5Y~H8nQV7i3CwFq?W@{~&irK|W4yRg$A>!JK`--GM){XhxW^89kZNU$hWA zEol0vGC)* zA)4XnPLq4mT6C1=;aF@sJzHSb;;b=0Fy#JEpt~CFlO(4GMv5QfUK`=x$;a=F#dgrU z7TdB2B~vkKNn-DsPI#Jxr?r2d+DX4JDPl)l#2c?^Z7ql6u|F_vux`!G$TRrSF!t*B zXLDG$!|BDHD%ah5bT1FA8UmJ*OM6uPWFFX+-P1a$EW%y-rf<9Ubv0xSpo>+-nJK9PpF--abJoIsHy8^3_VE)~mfElQE;9zB8BP z#Z=iv5WlwXxdmJ+{lZ9;fpiBf_;4%hr5=`@_iX$yip(@LRfWu}1o-Az=VzMzuBhs% zuSx?UAG3JD!;OlMCiixmvG|9V#0r&iz8e9#%{PkD z-N!O+OETHF2U8Aoe(DsBWMWziu`AWUZhj|tJL&i;FS?7w%xYIr?n$yDiu(?Ucdcvu7BW1bFSEnAiRs* z>-zUCGlW!^7UTS)5OZhT(d0Vh*2$!NRLEhJ`LXh$n%SSy)bn0?2TG~5yzDWn%s!G) zL6s_S(IuM5f0$?-tW#GfO^!NT7^v`G%&3Xq_*3Pk#+6wwVO=}n6cBSZnNQnXQxpAo zRDamSc0YfnKutR-_X*of*{QPi!@EFx+3y%W700iY(J2;9Dh?+-zUI7AmlTqG|AU}L z&E(XVrr5RYMgjWBPO(gx>s~cholR3+AEey*#$R~>^~!uyO%JzH+v95n?fYxWSr8b` zNXdmr^IC9TIi#u&^3U*?s)qmJU2m_X)VJ=rGBZ5__1bFArFOeAL`Pv)liE62K`oIA z2c4!Q_DD0L%9qNB>-Q_5KFw&yt*hoY(TtLJQ_Y14hGJT~aGrP{0Y?C-TaIWJnZ@_j zsc41MN%=FG{o`W)cGK>urdM0-Z4LD@P*-E3V%quYeychUjWrig!$zTOxH6EmmD$XG zH}?^5v3$fh1;eD80xIGHt-5=%S8e7tCivoS!lW@~StGHXywjy^2g{G(4(}!6rRJNy z5oU$R{D1ZWKo!7Gj$VoM=mX0+Sf|!2K7Fm;@+eae__0oLD>A-3;$=|VI;N3YGNJ;@ zYNLB&n~D^uS?+*qr$f_e?!E4pQ5+`=-4^BD)K{-&leD0!8f9H-BWgO#tzogmTSc{$ z*`Iey(B?m-Q9SMY;>(_@0vE3;VP^EBPo-_Ed(s7l#Zo$(ws9V~J{mc4)p+hEp_LNH zqIoNYNAz25;ybfnd6Z2P(7yMFO4PWj!^(qG>TurtiRZ3cXPo=hi)TqSbST`hOE z5y9@wxb}(ga}GC&_vGLo_lE-hNd5NMxV5vurA|!imKxZ;-jqR8+*f>0NcFt!XTBMlC(T>kp(s{~A>7>C~bhq~`0 z)>J6{3nO@AAj6Tx&$=P8ho&X8s=|+%Z=BBf4-gd{;k-SXs1t~>B7WZ@b91XDwV!BB zQ3;ZMqLTQ}8O3{dEOx6`=M^7Rs;l?%TUNwzbF0?&HMu;{Fv_gz8u%}YD!T|>0y)9r z0!epa{$rRcfpJ9ZnE{g6Y4|>vubwZ+i-;5#x*2NfOGPJ=?-v$kpHkT6#khfB3=Ky) zsZyDjaB6g3V(MrR%x_FTmvPjDxOG~Jn>4hnS@-0#-OBqI3k^h*5-8igZ~NE(IEq3% zw*6*5&s{IAb-N9wXC?8hqmL?>-sg4VE0WE-v=iC2gfg#Xf^*P0*VC8Sy%Y>|iY|FP zzC>OtwdID$k+kk&a0QpW?Qs;o_(hdIZ;RIHqd_r(|L4k`cS!9Q3bArgqcp2Ne9 z-RP+Xv6PW+Rg|Dx-^hamHR%rU?Cp8o%s z^S|r-FMs}1A^ux%{!2^#SAPCizy3`q{wW*(f1Vo!wb)_KnOSH-!e2Wt6jnDjd@hAC zioQ6M{8`yvCDaP5yo#Pl`sndTpjrMy7&_3$KcVQwYLY#h{V{7N1r|y^G3!ES1 zGnVff9CeQItD-VvL}zRBFmM_B*(r8w3cBV#PW$%dt@>Y2|NpSIc!2{tVq`|P8H2<& zCLZmkK|{>wfZfQ0a9m>Odk`_B#I(rj`a0|ekIoQx=<;&fm(Jum{HF2nWSxB4hsR&` ztJ|_}?b=%in7?5Sc)*q&uF-MIc)GjU^}Q@?oomI&#H%9*7nbu^wy)a@gw229<0|lQ zv2eO$ybrFHIp`lZR(6_cfQ%kwI6OVJJahSW-MIe63;2iSiY2{PN0#EnOV>mB#^NaK z(+ybInj=yEY7$voTKXeYJ~usEMCka*-Tk8HFBdihyvM`f?YeY^fvyr}OV~%=pSiVp zjP;Fm06HiA?XR;mrS8T)RQ+lC@WTT7J0 z#LlU%!6bP+Gaxw%r8{(V~k7L0cGoosv<+|e$a!7`iz|1Qy1 z$ey%V>A}1P4di`)|M)*=2Ru&#V*frc@*|q4n1D6$^>e_Z`NZrAp1#s2{1`{Hf) z`YwO@#Ta0N^K*wpNK+e(tDUkwY2H1G;^DV_GO!v{d+ikdh z47}`FUNZDzac25nd%!dC+n?%uL3CA9CodV3(q!~~LW*S=8^pIq6}YLk_H4~7QrAG?r*8H8Ij zyNFXYnx@{>i!vbGKSb7CB4Exc{LC=L%`T_Na?$~Y1dr28{@(F_@q3=&Ne2@}&5Pr2 zQ$X-$T>T%86(UAIs1ZV6l%5r9v6gx095_4$9}vgSfhn;d#3v!57UPJ_ucyAuaJ z{z-Oxx#)oBqo=&EL<;-yy9TeV6`@393-WtUWfx|e`tVd(>9w;zb4?B680cDAWR7$( zcin@=nPaXcg=`|Ruiwfm3GcL*$^aZ>Iykpvk-+K^a=p7|i-n5=1MbW}u-%WYi9gVUHb{R^TG{|>>F53)FF&f$P(OznhFZh@U zRVg}~WMBV(_2K^`WDvX~o?8?}m04$cUT!I|bGN6So>;Zg$RSH(N6EKvGh+7jKlkr& zdve0U{extI&SRlDWiC(J041*MUFEPvH3&qz!!YJB%fr&8Mee4CqXRUmo)`24qma<# zzR@%feBzK(S}kTz@(L=g)h0kF}S8Ip;{N5s5CkrajO| zB@+$l5*;!i9gUZY9vkS(4o<_G)wbqj>CkOiBNEs;zB|oul{|EzsP{8reQk&-PMr5t zWmgBj)*D(Q(M00l*B^|75Phw1%{X_pWIW$d#A*++V#FfL_@;ZL_(XSXkm;z(*Luh^ zyp<%Dw7!Ps1^)8}eR2eSR+f;yDVPxUrH~*V;0`{2hx2mqR}Xi0@uV8@VNL{sGv?u+ zOiLUaKj3@lf*Q*5Q4@g$zLbg6AU*HcbiEYTG@Mtst;+4E5=fl)!}tfgAAF6jW+1|c z0%do@RTScM#;JR0O!os=s7%=CGKHbaEL{{8?7w6*E#a6r)%~xaINq`4A<8R*Wz_Lg zN26Q7NSpM5Wp!jQhW=!-gTQU3Y=RGPSk%@j&pnC%aqYvmq|#lKF7%;oy=aI!zxd&^ zh3t^%q3M3k(I~S%{)Xn%#NSf=^#RQ-HNw(I#bhH>7o_5qu2w2RYo5l4Ywj#8d>A_t z8z@lMFkyhF>Hf>+v!thl%L$VVBC%b?MIbn**0#0>Vv=m?JA6=Ib1iwZH|O>Jh_2=W zY8S%{haNsY5MinMrk{G28gV@p)Ze6+Ht_e~6nel$te`TI3?Ql%&6||=@I`4*-gaZJ zvIp-&Ump>|<71`&jmX#WUHAkW=bM7TuLw$Q)F34{5vX$s4hAZy7ywao9ZSqj=8X(hvVCl0Wgq z3kDs*v^BkyA=?{8C4$}fUWBUAL`OyjoTj=Cn)N{ZdpOi#DFvyUixJm!aO-b|`FKON zA6(W@cLd$<0L3 zGLhVQExQlbE5Uy6EK62|L#k}vT@h~x=xbPPl<`_yx=M8{le4R}3)bs6XXR-lCf%5P zdT7)320?kr=3~Dmp@EqwZAZ+iaWvJ^Ch(gzv)LQFGUa4qCBg%8WBvBQa;Rq8>L`(U zrCS@#u*Uoi5YIus9$e6h_vrlUy@~HhOvGWi$PYy@yU)Yv?ml!RndP zwa&G}cQf$ZDySvS%lKK-RPLtM|sg&X3fefuqVlr0!8k=MXVHY^_Ih+-@ z<72;aPS{u8{w>A{T8S3~SARK2`xcURsEH-0>0;;2-AiA*_mQl{_agvnySqqm3X@_8 z0zzsP<+RauYK!~9v{&N*3JMvDXsS{EY(R5RfAGr))`;S7pLl=`ye{AoTEXJGz)6^1 z`Nv@^HLEdmw72Oe2y@6LFN=G-NDQEW@B6K_zgCcg0d`^Fp6%3wX@h*c-Gn6pOA-~E2Wo`w8cz;$JXj)xA>T` zmiITYkaf`M^B=>ER1P3qFH-|E;u_9oT6RNm@+4;zcut~=+Vd~Qx^)JF7UD!pWQqj3 zcfPG#Y2`rugZhdL7CI=vZ7mGk`e52oOS0a_wXIJ@Zeip`Gl{W@+LYCa1k-OK0V{>u z0$D`&{EcLNhY7e4y=EYTE3!v+QhRwfsch70@NNg2pEx`Jf&RVvFd8(U$uTPkD+E8u zF3HS)XoSv%exaFMyvj!G0KF@kVfQeIprHDijct|;Na&i$zk{shqHj_q($OY`N#q#m z>5e*LzIfx5#r4imOtAJU68zu`Ooy zMFI0dqgjWw%;+6$&`Q)=bHtJ*-RmSkk1&A+$uJ zogE+JE(D{{Jc?Y!i1js~BJN|dj*yML4*}uZ9ycAr=CD~>n zw1S(A;(XCn1u&jzQTY2o7r3NU+nv02;rrzQJ@|Q3oEO~Es{7qlSV{W%W? zeK>AxA%GDbH~P>Y>nGM=+lAyM+1w5I9<_eAu|y?WOf(6?_e## z7a0Sq*3ORJNb1t~i7Ys0cD#Zx(PFkEZ3I5D0j=sN$O+zZ>J=hQ=_hN z8#N`-wbtHsA@BUPcOyFt=Xi~(fVi)anQfF{AYM>Gb|N&a9~k>QZTZ%E`24HtC?ikw zO{TMhJ|8AGQ$g%cI?PMX9qp2c`!$t`pTAT+6Xybuk&(&nsV{wbn^BdC?t-@SCpYW@ z88EpD`O}?xsp0LZ=U(t9Y#=gB!*~Z&V2X7SK7#^b&ziRP5Z-61zg3Yd&9^zH!!b2P zV2IgyP}F=*WB%28&yfmujQ+nosY4k{;Mz+#PFVAd#wXdY6O@s~Je`EHeiF^BH|bpl zXN0tTHMzH!GeyqNpHM+`+lLe~{7FW@>X~NR7(O>a{(#ct`*+u>qnsHb$dW)P30>Gi zzGT%dHeCn!FHWtkDyTolUs>%^p<$mtvrgEJFvNN(D`T?exMEH%eMdnFqIe?cObr5FKP#aV(ze8cJS||)i|_K00*uNL z;;3vn=$mnF%MzloJaYuoU(s7l)3|$@*hb&HgZWWi05pYDL&nrV_>D!e(t{ua-`tDC z6<-^_AwzfN2abpav3seEE7ZYK4@?&&$#G;cgj6UduxHW&F&JK)BV#OlaV5QcZ1{Vs z+ryjbN%-3gh@Y`I!2N$FL4y_~{-HbYM)PDx-|rN|Pe18KFxc@og<{tSPr&sQ+daLm zp0&^1f@i}b!RptISR6bVF)>q}A|euOP}>jA$5B@$eKszc$_5h-@sx%09*ZfJ&?g9Z zeWAzBZhj=ox0~^}_aqF#N&1~BSqsNbk{@8>X28#54uP~yaM3WDRMK5_OxWLP zvQ3*lD<4V#us5;|jRA1Fiwwh^#`WNQ94*w#)-~=0If}bAPFq>HaQJ?fs@#FJzefcH z0ouW)VxU?>#Rq_2+4f8kYkcurV%tn|j9YfloWO*h2Ol0BUp6}o$Ir;P>T7Ifa3O}o zM#;VUl0K%b(>a)Qwe>laZzf-y=m~L|oM9a>fkqiFJ;kMoo(B!zJF(~8A0a*jMXO}* zK@NX!boO{n>`0f<$#x2U&o=<#-FHy5f-z?e-aFmN*ow%Z2*bxiZG$CWwlqBMTg99r z#OWzA8c9#6BJKhEjn}WSu4oRpq<Y( z{){>PcO&$}2$}m>*ldhs<53$(dD$ONk%q6GktK@AUsdPq%`yp4SN{)PZy6QGv+fJy zI=~DP90qqMI01rtfZz@TgkZr3g6rT6GC**5w-88hcMAz5xCer}UH;N6%0hPL`) zKF@>>^{p+84``{8eGrX)hxka0l#j-r2}zwyZZU80_a;mN^{penS+|*71#QbUyXVF# z>jgfuV*ibS{%=MtSVuNb6vjx7O$PaGdT1;CtHW35+@X9@m|pkzLplX5KCPL!odRnS zXmscij7qS+Il255LLG_U`m9vgI88HM(~2 zmWUJCm+C`HU5qCe`FAHxow7SC`e28lC|C5gLn3lad&Pq!T@W_Vq6MPibCO6Ok^FNh z(U`?kT16Qf+~91IC0Nh{!&>WdBaih&xEiW~$tPCdZz@&fy3=d76wyM%)*eu?Q9#%< zJH*B;TuT`#`tvDRd5BiK$G@Q_u)wLN0>Zj`gpR)sUB2dV( z^nry)!pH`$Bj!mf-?d^Tt(Rf{V*B*pZNVISKkL`_26Ipwr88&K9RMZr+6 z#v`+4T|J~*GaDEmYkiw6X5h8J(ttEW+!jk#?61u@LP5T(b-TwqH%K_|Olcm=0r^sf zbQk+_sQY;lj-xRx`3RS8Oqc9&MLkvYP1yO);qxWY&LEwBw8#uPBQwYMs&o=Sfk=B9 zV9f@H{06=_+pl#wlHk|;+P_8OL#0kx&R4?^mYDKa5?s#hj5afDLT*#GR%-?EfsP6v zoB2xIS{aY@abOQWTfNow$tu$}v)#2+!Q_>(l;=Agh@`dDe!m{?vmuDd7XCRQ!GPx; zlj`HKP}9~sv6XVBNT?h0&FBEB4TJ@JHG#F@&8<;h+uWzWQYuKhuB=>lU=z3Cx`$(Z ztU8Q`-NH@%ru+Mf7L}5EN0OJkA0OhX2=kC!>o>zqM&uVcv&+53ng{&4s^b&2^Q;u! zda|cK{~~h#4@F)}9b5uVn$gAte2s5(T5A9d`hXP4D9COdX&+sPA(Yk#DERr4;Juc! zP6)cb$$dPhhKpKnN4-AC3h3Y7ZRqN0OJ`9G)4j})9Jjl{8}KfTWjt8;Xf!iD|MSIT zYv;X^s^dh)NrX&aH(?we!eoe-B9XJhg)NcOjM99?htnmYoxh8@xAP_@VN80UEaD)q zn)8q{c@3}oWQoUR$*sYY>x%G<0X4R3cDfMqX6uot`OZ$5hf*{QIbvFg92#;=OzT6( z4Q>IWg2BA!DN`ev&oy90UZs@|PfC3**fQOTa}55qPiz^MhLeMl*^^n!3{>qq36{1l zH~kgyuo)|QmY4{!YqpV4VBbWA44J|spg6%q$lf0zA`4~}vsU$?IreQdX6mar!RM2& ze1z}Yynxn*S?H%P*wB%Kyk|%By~8uvTP9w$T~3jb4L*CsP5SbD(#vTBZvXZlyFA^n zY;!fRxg~{qoX<`=oAI0O|2b?X6U-7$LR&kh)g*%SQ*KH60P{H-`cwznZnN|H`;-a- z!A5jK$0`li%IuRPTBhgr^oz%@Ek%hTT`WhYdb9)-AeDy}jy#Vq>JVh#h*H`}F85ie z1dsNp1cuEy5eKF=PQ%eB1a0t*4?LOjJqv_S zjxWeH%6`HaA{Z|HPzZaEa8{22JUYakQPQh-p(4>~fRlb)0BV`AR$N}ToV-UhXRu;D z`=RJaDt4#nbLn?-`1RQC$Q0-4EzR4sO=L7ze1%xg+o&|p`bE&4N={mbH~9U#-RfbnrfYwU^ZP0`Oy-ns)-!X09XJFu^Iq+MBN%Fs5?}@>p0Qsr$v_KULPQ1t%-D_X4hWa zRgPDU6oO`&>^-JH&7ruLFp zkc^ue`)sv%0J1O)2?j8zD?xe|sET$!aNJ79bNV{NIG`A*HNj@{Ytlmp*JQcn^D5q8 zqj$(|9v&8;8>5J$z;J^e3b@hW`k0J~W-mv!i8sf%p|zr8cu4;Tb||aH+@UfRP7I`^ zm%$?}jj%N8>ECZUGFE^{hXnV>l@A{iS^CST6G(q4`-Q*O#Qk+&q4PZ3?K0JKPGR$m zybaVer<6lWPyHh8yILfWD5=O4qSekNFFPy8Dg#j(tm;e^UbAdzEyKq`#iH~8QNhM# z1A|`cr5>*Kb4Mi7#Udk|E=F~#)y)La8d_v`3?DJ;hZ_X3XuN*Vr}J6c8y(4>QZ%4u z>!GLLft(<5m>VxlIr0+3@P={LkTX8$6#?Z?`CBf^0){(SPMPBgIA(D2`JpmL4m0LP$d=2(1=Q1Hxhnpu=J~Ski8gXVU$O7Q zv)78ufXbPfFBoFGwqto1VBJxuOx14b66}pS?rxwynv2A?f(N{WlUd`zdOC3UuKG`>2C&aJAs7%u}9UJjm$cl~usRJ~h- zFkRFBJ1}ng|BZJd#OH&Np2Yg9e*6|9%I*(*69x_aZn>8&Ubt8fm6P8DWKY)T%HXsL zj;!XHHL~YFh^~^Qc^dtAg$%g|aNST)FU%kAZLbAP2 z6h0c`B;K2x7=6R_=l>Bvca(cYa_^gG-lqVhOEzwZAdiN_uCNq*BVjLb3#!lb(2uAZ z`hIM^x3|&B;Xd_(vtTU+S*OSh1@HNzZ}!*OUexl} zlKT|CsUctD|53-(O*j*puFM(3!2LF6GkTzs#QQXG&I?wTp|2Vpbv7nZcwAwX;=uE{y6wdB^MA`5E~nA)k!skSKs68^qplN*Q0Ny@?jIK zKvrZED>`dw9r;S9w46sENtR#wTH-l3oJP)lq`A~aX&Ux)yog?1K-|X652jI$KhMdY zSXNUkGrVxAm;?ubrB6_-u^HPN%h%ph-oE_`;85Y8-q(ejHqyI++m00qC5EsW?!s0= z?P|wB;a-DV??y06RJ>JHm%kwv@cY-}nFUHTSZ@Hli}gLQ?qGuX zhu53=1iictW)P36uJ9+zI{+%wt(Bi>g@=t`;R)E{ZhMBj0<~iih?u3g7GPquJ~np5 z8oVAN2smK8dY;F4t;7dDa$;fLTJ4LY{CP2_U;Y5?SypWv6}9KE8a$sJ-JA$Uo=tGX z%Nrc}n;#L>Yu&*H!e~aNM^Wk!8W{!WDSm9(yJmMvIl}hyeAK?|kn8S0h3GTNL8KmE z_w$9yQ%-w%!>8EE9yyb(Be^_CH&*z;<_O$uHO!)UgoMuDl^kq>oXJl+<0V6HJU)3W z^#om>S}AjU8TDa!)aIMf9TG)x6>tIOV*Ar^6XqDy;mt2553oKsdzL#|vB+3ezaJB5 z0hj7AY3g4wz~$aEG;l~-kA*lnq_vTIQO){bxfoL@Y$$n;g%T#hskxrPy}Kj>JK&-@ zE7oJf;(aK}zAQH%_YVs&#z;c@O|aIy&y*OwnYEeKuOQOy@#i~^&u`h8ysnZ0OFFj|98!yI%6GCO1zu!P)5qtX?c(JqX}>9!pSr$7 zQr4i2xoiwT3S?_E`wx(3ru1qYP&plI`c1U4{1ezJ^zf_G0_RW6mEoY?sX>w6pq?N| znt&A?xD=5c%*&Z$kGr47Z#%&iACcf}(sZ~q4iiU~jW50IumZ1>UY^s0!bQZ%x@>pL zK(;2|ciTc@*(J2WUc6m7BcmZd1jBnI1r#_MPl|Fp4$R~zu|KKSt-%m2jQW{U^InHg z;Whu)UvQ1Sd&?{vnyRcUbN_@$6Uz1-$QSjIy|gyp+CBSJQ(sA%gM^kJU|#QK@J;WO zD$4A|8;nu<7Ty}Zb8zOZyjc1mA!OE`dX-yL|Lqk=GpG=reK@eC3n?i?j2|2=zgxBX zFvAUdf%5Xo{zj7ng7UyodD30YYNb}KqFiMRsTU>**m=^%-# z+zEqU6;DTvE^|`oz0~wUNE4jhjo{(F`lKxMjkCEl%4b^YXdK?&Ja zzpyk*W|vgb(@lCjGuuR+>0bz8!u3QAJ1jpn;Kz=DSKps25gzmhrTvAsu{){C%fGosuT7EO2e0UQ)9Vs^^ryX|E zSr_F!gGjYkUE=h1B-q436WBznk3E*7oIhnnU(>3%J?y@#&4Zk&jicb8 z2a>n$A~TMSbL@fe8!E{+vHLI-@J3ncn4qmhEZF4e4mSf00G6D4>#o(bI{ zNQ7G4EiV#ZgqO{`*Luf`iJ&1OZsqWAzc0<*{=;F2T}HNza) z7NX;$;Y_m(Gix$74-i+FL73bh1~{5R!sh%ua{(%SJaxe{|2x9GbwY59K%D`hW@iun zumsk^4Sllf9f|UJiwv#8oXIoT*O7RbJLVzb4t`Y`({ZFf;}@~;p`c)`lf>3fmp*Bw zONcmXgzx}(iJo}(DvbQ$JB8#&SX(1kf&k*Bsn)7315sPfuqkKz0HzL6O%$iWicz!3 zn_Vig5KKIt=#a!QQsoumxfZA9r{ET!Co>u^g4}V6a#sZ>aUN}A7y8>iuS}g=aTQFh zJ_9rsSY+93g)}COY9hrh655<@93@N+RfM_TL-#39`j&tXFRD*`+3^5uFD@PZF8|kO ziJG@4hmq+;Ut?tvGcss7*Vl>nyL`SQ>HU8Q3$PO8JED#k{qmV0E6Vz?LiCdn?Kd8! zd5>zo21!|(@VXMyU9i6tmVI?VekUN&BqBuO7Tizkfd8n0_MIC32pw6Q#)?u8%wISF z{A4?NwXG7eNi>VSp6x^BMw*}`vHlJd`DD)$M?|D%NZfTlqjEG)}du1+RG#@~Te^Q+o)%Af{4I0zF@b)I(&95;5H z1_~V?Om=CJ93(-m!!_Wcf-ynbS(v*CpqlMn zT-d4}n^A}o8E0w8n;J3#+%l8s)*=*;>>+M7wdF_6Wl!@J?A*t-k7r-*1P)(Z{oS%( zvKfb|7az`wl!{OJC_y?<$2Syik@BmS$BI%Gvc=?ZQh;?`X&7uJw#`#MFP1uE?&&fs~to46QmC05ddB z2sN1M^5Z$b+xcfCsYf0P}(ez=dKMu-c|_;<U1cq054Xb} zR!p5L=TF}kYJzqu2(m-M1Sfex$_JmXp7BCsHJZh+g}2x^h=arQwE3)CVk@j8ffyUv zp9gP#a8qqI3l_S{I&w+4Gs}tb>P-osHNDP#%g)ztP%-32cb%mH*Ol#QG2Me*H5+s7@BT?pvJ64WUGU1Wfa`?BlavWbW9ime6I1@_?3}aLk=!Rc?ai#OiGX<=Cl^_{t zR#OWlw;gP#QZbA1XZmd$t2ugyY1QM=-6UE-q8suU=NS~(-C+S{@gVULO=o}_E<>X` zf%2TP&BbLsxCtedX%i8Dkq^JOv>K%h4!p1D)K&QT*7;%}p(L%iAmKc!ZCXhhWLtf@ zz6a;Ns$+H)9mc5or-T6g5?`hb#A)ZzecP&f%lCA$7nqCpGxhnq?=$a@Gg_7%W z@kLO(rlgZ*M5b%{@JdUt4$Z#cO|5zT<+adB1Jt>KG-UGD%zBscL(BHfze5Wm#3(Ld zN)0TN{MyItU92>_*)2Ap*7LrPUd}&r#GWq9{UA%Q#`_2xAP|Xcrt^CBlMhq9AYcQl z5G8>kR&7L*Zv#bE-g8wFg|08@6Nm5ZgvJ}kc6@SRTopUij+A%Z&^D|dqL9zY!9}Bk zfrar^$4ASt#;Wl&!M!r^DWAW2mHhYyJspvgWDKLA4xLWVO&K6hbRSVE)S&=)Cfg-pyjEV@?6Q zve|9r?;ukDXQ`z_PLDHDblDI3*<)2Q4D(aHJF|*)U==l7iJtZ43F61}aTQ`j24B&? zaD4`udz-0=8%4eEM~GP>IZ*b7)Vmiy=yh zX>97v5H&e`g!Sc+c$x8G%tNh5EHb1dH@sNpb;*IWkQvY~5nM_C){FQ^QLX0Grif>w z&x1EkmrJuL-E_Vz^X=){3W#GralhWiBq=-lA)WY*8RXp`oCn!xbmsN=&oZ&&uR!X% zeno-ob&7!D&bDt(%Pwd(kskaB?E77RV6r)iFN|>{&Pn`jcRr)9iPe7Ag2rCTHrS`N z4)Z++gm-R&^!_+3+G~joN#G+BAgkZAA|??71p}=EDDDh0JvV^zm>fk%xV&6pNA+K+ z%v9lJT-(SY&IySTRE|JgcWlX_Bp|2P^08x z6BjWROuDgdpQo16-UBCg>CaX3!&+LslShsv}cAW!~=tzR(JThkN6z z8p2rm-NdmN5cyg}3Y3h5;=T3mB(C1PU-+gJ#Glity8qAbBy{P`KQ3uKo$4rm4l zMV$g`_`l5&ig|U@1Nn=PmdR0}tyrO^>2zH8?}U*{up98p!`6lgGrYK* z)~GdcGU~(%PA21xp|;Wmz&vpb0)=vzVe#92%{uEp?jLq%PB{>o<1o7J;W>YV%*RmL{b13f8<>5V%(Dj=%TCQMBzz^Vu1slvJA1I+mR zt$3Qufmv!R4PX0MFJ3MAr#D+aW?v2vY*1LCNM@3l#ac_?UA-0T{%*hHzbBx&17kxE z9=Ns(KoSTZI_7O4(yrRlm1&9z@|jA{$#0 z*+W7f1(jZ5rI{+;Q{Rj5F#G(x692Xp(xZ!FbEwl7Z^3rI{fPx{pKIc1z4)O49NLe@HXvX-w-U)KP)^!RR@kb(9ymzx}X6(0R~m&6khv}dNgYQ z1#S$AWiZeTt?e)cZms#(`{YfTzckkLH*Ru;WEC%F0Y7Zc``o8(@5`_p(`HtpUVt0w z1`TSAg6R}teBkBqN6P{N7m|JvZA(tdg0Ut3D!Rn&7kY69#_;?oWXvcu`005+M=uD) zK~CgybciVsO`S&j7M4HBtzc zn^fJVc?MHWTAVk-;*A}9y4lm;mh988=*3|i!*@=M?pVAe21BiT5jWCJU0iiioQV^OA9MU|2~!%D?JOHvpN#_Fm~g~858s^iAHA zCk;Qw5zQ@mNO;~n8kO+xyrf>Llyd!<74GJ{h7hj}^g*wqW*Fc-2Mj!b8bdmI_zi(c zWgTpzin?f@P%quJt?Sb>c!)UG^nssS8^IR}U_+AJ zq}imcuQfn;*C?v4T0rGKQ(Z`Fz|Of2=Y$y&@;{!wzTc1cB29@`+lXT*0F61e6-tbP z={nn=#*S}q#w*u&q`ujrRe<10JS)ZL+-81Fs7V<@GH5c+Pa4aSK-p_u8Q;!I8;7)} z&DkN*;aPSR+>prgCOGxN<4&nLWjydcQ6W(=w$tf?#T{hrsD^ftS3^)XIn>JVU~Jwo zxux-6SpW%S>yiU8c~@w25wRhhih=e#jLTO9IrfIX-dv|ieKc47G&%eyx;Q^_Znn@X zV&~t{i%(b`e9!bAH#vcX;ebeH@<|Y%@YI408ZrdC{m~-jcykJc@&<4PmexHgN4@O!17nyi|?bdG7db?j;I_L#5jP~(`NIisBvV3P zU!F%KbvsxWtR6S?CL>jzSzB|g1VmJCYQ1gPKj8^X>qT8{vK=j3pV4ntEW+I66hmIK zf1WOFt3`aM!y_6_oU-A5L48m@($Jbo9xqBzPfcQMTL4$<>-crxce}@Dk(9teJO=jg z>C{cQ{8|C{R}yAH%sodBWfFyZWpJ)v`6k-w^{QvIg$>7?7lcQ}((yTwiRa)ir{ zoLX7tCe1h4+Oc^HW1z36YKu(EB>tbotup0t>|a@H1SPhY`9$4>o}59qi!H~f#@qyF z&L)Gjjp_cRV`fBVcpFeeMlS5CbdD?67VV&OB|vDpHvrmErbmvVj+Ck|;cVQUHu$RT zzhx%s#|U{C5l63=^EY;do>{fOH)|JXpJWFMG?sqwZjCliNQwQoQ?Y9)Cg`cA^Q-?( zQs-AJ!6Caj`&CNDB*s+o-}ERD1B+`504VFFlV=>ADBlbZY6H)&Ih{Z+vJI+%LUIL9 z3a?+Pwo1eAEQg0M`5{=>vGr^m0-UJ$8jur;DAAk4Gq0_h=h6?1@M z3LHzV%eSTF&}xZ=*=RrArqPnwqwpc|5yg4;zeuWAZ8uy@MJUPm`d8(RlXY&;M?C|c z{IWe|FKr2q4(f`WCSzwo09*2Muc;Oyk)Tp=Ryjf8-PcjOafh$~D?{=DA%7XQT4kT9 zpqGr;s@@Yrqc};L-+i=NarcRoo&gx>_>62)$J-A!0GF4)u~YzV5{4~uG@P~ZpIigw zi*z`waZQ_LLzF*XdU?r0ScJnJ5#0%jq^gwTF&L;1yow5M>rm;CkLiF@ZM*#id=;k^ z30m`o4xBo7n-8N&g|i(M;Gf^+-*Ix+k`41pdVTMR#DYBQMp0C zp!N*#+bBRMk8BEu1`CFLmhK$CQ@Oyi38O-bhI{R^G4VG?d>+`?9_VK)kC#Z_JVgB& z`S+))>LECwo6?2m1B84fOaeE9l5D5MF0#AM$fy~+R@G};oaL*5VfM^S8#m~cxI$fZ zHAhP#OnPxF-Hx2)$Zl{88)KSF^gVS%npiIN6*#FYw*x2()J;@Lw3h5aMw#`1^)M1S zMIQY6e(lFzE8oG&1#Le@PrhnY2g26+-Cin*8q>4V{A4HG)HAZESemeL z!@Y~F)B3DXOSl$2NKDw4dg97F+mj3>e8qr~k2EoV( z_KSVL!U->mLfr`3qDVxADj8Vl`RK3qcPG0e^6PL(WZ+8>wF6zB#emq$g|X=TcrDbE znSALvGI^*1^}sIk1YuK{#8l5aP zP??k3n(YN<1e50*e2iA~+Io)CYfNoLw+6IwozENnsfa>4sqXRd5JC#N7@zPS%~Wbnh+>s_bxv^_ ze7omzUSP_I1d+>Ni5e*||MA+ttkXE>ZMFh&2r3#7jra??C5o1Opk;!quv@+!PIJJ4_m8936?y7djCn ze5UkkOKW4)BA@z1TIzT^BqdAtcVYuiQ6N-dh9|LMPK&CNJz+}^D8Q`Bz6(1_=?>P% z7vnGaW)t7F7r4TL%oQ z?=X7Am;h8Vhn$d(n;+XBL!j=Zb{v`cZLDw-=y3)Ww;?;u-k(Ht-s;OJS>F=Y49s$^TSpCiro6r15 zN!D-Q5x}7ikit4Llu4Sbg=~k+04UB`xi%~@*(Z?6!~ANMM**m?9*weV1zGc{XCgqI zkl}nIq`CdGx>B(o##?fdI7Hh-X^2Rgw#(s(_Ac@xbATGGYISR~Afzp^tYWR8twSQC zpNicwDmG3*R`9Cf*^n(y&eR@NG*Mo#D8RwrR@po@!R$w`+5ID55y#HBUU}ROQ}j=PgI11HZ7yrgXLc$h0w)N z#h(_gnP&C?6%gG_O`{LTaSIUenw_2@IaznCx<=R$?b&l!r?>DAOi{*AquYMcR3eDq z><`#Khj)KHjw(EAw zeb!i_j-^7D^1#1Fo2JEJi>$?=YZV)vQZAOcYvhmnYf2B{n1r9Fb`p(-LTU0`iZJ!J zac1~R-2wQ025NrH{#3Ze6;ko&Fzpe$n#k)yTGj+FLjcdk%LP#lLTiryiOqIVz7G1x zYsgrQV>lJv+VOYP3iwNyF+u+i*1bJAuR6{{lPlB8P97vd?A6bC0Il^mKwD;or-c2s zY5!=5twv8ar^YE5ot*@0)e_5f>&WpI5M&~K0-KNJ4aa`!RZ0ZV4FQ%OvrgY?bdSU{ z8RESca;o-$G;DHJyu)cEq7+}vC+nl{UX491`Eq9C7T+T82WK*Ui9AE9JPddEcrxE9{41!s)lo4PmG|7ca9lZMH}2Ug?uleZTa3^y`uJ<4!d+UU^TFcFSO< zc658^n?85tdrC{&G;L&}!bhJBj8YgBOg}Aoih@NBUKFq0-pWAJ3SStYXQ0s#w zx}H_j;AqCaHhG^yDzHz-q?J>YhwrqcXwqu+v?GwBzyCjCFIJu8g6_2bS}tzNuhb+G z|MLlY_J3NqIG9!DB0TP@KYoboFQme3C!i{xliiLQpo@^>7b$>;fO<4q)#Cp zD2J|)E|CJxoaH|WF9n*3`E-6_{#3Q~$T%^2PvuknRNbY7(>&r%pK7RL_=v5QglvS| z&IG~r3MZO@r!{rN&OSuYDIebIlrJS!R{2LLtC?$-_ys1_RK@y|CwOorO9>SK`mMh9 z6Vwy@(Us)`oEC7~`ozeSVO%T`UzBcU>x~)LuA|lz^AjlPUAWEIb>;i)@wRE-HPrXc zRD3zL4P}`iXD>I)C@-Km!f3vr{QF%OqVr8qF9uC5t|8t}?9XIb;}-uLTN;MD95gYx zaQSYERe)4i=rmFAe^?~3V!yMAILA^^u#z+;C7o)?Aqp4O8V&3oEs@^sQy8RpB z>;DYw=O3v~O+`IRg$s)0lMB?2lkIH{;gOGSg#0PbMPQCuZH#g#dOT^7h(th;SAmJ6U)RAg2ktfdpVEA)QTGMi~Uvpf^=#FUh zBy-H;Typtx^Lw|K+P2MpdnShdx7wGIOup(zc+E^~gLF13HnhyfBRaUrz0um7inP(W z$x6Y)BSH`;lH#* ztD30DEJ=@ow?B>d{!J?VU!h`M^`H;*dq04_$p3ZbzthJ4cY?q_N6$;a)U)X4Q4r}$ z?_b;d|LZ!IIHdXyvoQ^4nUzH%ZSxbwO&z5kH|~Cyy`Uk6RXJ@Myr+Zz{0tr8SYzYk z;l{1b;m>VqdKwfF8QB|-OL}ltsF=_*GNO{6l?5m)Ed2f@J3G6sxtYYv+gny&Uw`-D z05Cc_YH4pj{E7e{KjOt zJ>KlB?ah`V6Yj3}$5_65$9jHw8G%Va6PCtfP66ukj*p9rt7~a_(bC$=W$NJIU|?bb z(9Ds*Mny%Bjx>W}I$SZYHyL+2Q}#pr9N;CC%UQox1zR(Gb~o`yao42@+x@p%-9iG7eF}3fy-l!q0XlBOf0hre}KAk38cjKxso4WfLpHt z_2F42nx60k2cT}P*;n=vzSbT2$>6+qURAwj3-?v3*{FPiR|zz z&0g&j#h-CwOo~3bare>+Jl#?Hqll%!*!JSTz3ZN!57t%O5&{1Z+wZ~0Uv}abOwHqL z@9dw)xri1WyLH(kLNw0i z^@(u1*->-)-+a{D?3JIJwa%ml?~59s`5_;RSDt)r;4gI97dhzhFI$Q`Pdt8C1)OYx zf-T&PcAoT{aF0Lg&>;;Y+tKl3 zgRG`1ao4ShdA=5#W-0%g5_O;GlvUaxVJL4bnwTk+DAb(RXf}i2gTd~m(o|~K z zRWRVB6C>)8*fI7FEF~ESBZ9t_e|#Sxyq@tH%)c8Rm?f}&Z%O2=Yk9tu=ZF3kj2!K* zfqI6A_FQ!dsM}EsVIaKB_BYntTuOVoZ)suS5%y&qaq*UA4+C)#4&8pM_xhB>N2;=- z9WTOE5V)uI?b~9m&*%P7r-o*Tp-;>wGx1z=QEIf~Jj*CjG&D4P8foZX={YrZb&_G4 z8aPG>N&;j2xgZ!%1`Ls>>Kaf}6eW zQKw)8c=PW{r;EYs6dCTdEqRJ#b)FFv_X!4;A~Mx$5yg-2II$tsj3$tY2*FQ}yGy6a z_(vK#<$8sNx=Dj zq3sb@rSKn(9d20CIsZh5Q(`<`e-Ts@VakpWC;D- zJ;hR{^DDK8_{&#O4WvS?dOb-KM=;jCKRH;xpQJvzyeKks`+a3|cxN_mYWme%bMk!5 z0wc5=KM^RG2v&zT$Qd{EdWH2M7&w=BqU+4>Z99Luw9s;ja&kDT$gr`7zx$M@oJcGK zWaJeyTfK56&ehp7`E7RUwYy zq92nRp{R*WQpH{+tf!atCO)z_($7@Cw@)zo%;Pu(z@BK-&B;}mMfl!<_jm1x9NweE)E zXfJ1{#k5|5$Q(1jKP z5C19XWaoYCe0H^>HixxW?D+AphF<`Wv;ld ze2=QNhXXfi)-A`pwdom|nzGh9_JHi-+Dfi$$b{Ac(on0ve%; zMIo92njjiAnwbicLY*8gbvN%UR4m=fZzq0Keie%^4Qrx(p9 zOQ)Qm)s7s(jO2Yz%?xS&(8rBkxrPlU|Jf+pOHO1_ix@!E;3bsOcDTm}n>R{OrK9_P z`4o=_UNtYb=?QK#{mNQMgdF~m{N}pU2C5QVN(^Pi;q5A0i$_9xxPDMon|YzF|IuB{ zH!m%U9#zd9m1Ek7%4u`GYty1-U{yrOQ{4Y*oM0vccVV_R@L}F#|5|#PzMV306G7X> zz>-Gl;>VK$OGY)<#JiwQv3WVp6<^9B+&OtAC-&-iAyHD2Z^~J?`xoo{FT zV)Q%*BSWQGfrUi!+R}uYd&4gu9z27X;wNfI$M%rNMBzE2=i_rfz3Ar`z~a$|w(wea z*%VrEIF`r|~y`e{*{vTAv%((sH$>tRzmQ&@& z^g)y19yi_BzVqUDcfEI=u%1P7iN@a|<`bfbAv-@`p$Ayo zh%v2+My}7_cVfR*viFTX@jfJeCm~8TNiPk&N=FHyM^Q5R`c9VtOa}$8)&)L2B9Wtk z(O!1A)I9)Dh>b?DY~SHA=AC>FieQ~WNFn6AN&$$agp2wrj6mbs>aELfXX`U!BO=SQ zAu6l#VmC{`vmGn#Lw~7nEO?{mU!;T?8HGe&iTE~89AI^XO*wPbWV zbQcgx@Ygm_kICj~1}Tj)&7S7i98+s9a&VP&xKKUFWt3QCJeDp)H{ymC1U0inKu6BS zI>eixdmkJ|Nfi1tfJoe%7ZP9G+y&L$4pnqrNNq#>95Ut>eny9*U~vM{+L6LPvRBf| z;7X3Kv!T-wa`?|Sp0QH9s~uDr%9ufVG!+weF7JQsQ9~=RAM>))IHu0WGp$b%vr2C;+Kn_zwj*9Gs&gQ* z0CG2dS5%i?`M6qb(a@M8-Bb0xmmS4Rbs>RNzgjdR=vitSV=<}sBO9@k64$l2as&3C zdNP+bB9UR%mx!l-+856-=Vgm1rJL!6oSpy9LpqgR5#%^Ch{-_@d!*rqhzTXw-O#5X86MV-uoFwlq|u$B(}7!O z$aSfM)u*)j*>fRCG~2VdB1H@)P9X|P-5GBDSO-g)s9cyqTge0 zfU>;wPuuwz+&q-Wp@+f{yeGEdhbpaWzHnB~FaVwX>6|smpP$pF$e=f)syb?q_ElbW zcc~GVwF*gjC{B}#(kbyU>MP-Apx_ivc@qTm0mur10F{7fpzAlaoXAClg5_1|N4OPO zzE}!a{Enoxr`d`=)MrahkVUVnR*u%IA{m=Rjex%|S_TBD?Ts>8n=Y-(#>~Iqu%`O@ zW3&3Kc#L}SN!qkssVt^}(>k3TWau{1e6YoCauA9w-u5msZj6 zRFVETdp?QeNTDaa!mn^vC$g!>87=(7K(v?L7;3=y%hpOO7ysx;C0C3us3Am@gSBXE zUL~@&a{OJz*sY*MahDFxee9dA$FV?61oK$$?-?WWL2$1$A?iHhFO78-Dn3G$*5nI( zSt^jL`Q>w3_9U%(Jo!ZZSYrX{kc6=WQ$3O2F z0e>B%%AIczV}wii4lDZ7o57o;oOru{kxYU`uE<{u;r_g%TCtc$0E*ANkrj? zYl-Oa9)jhYQUk{E68+qVE7W(R7APZa5`kB-v|6zZ0k+v*Y$zieC7|qp0d6^L zs_z24K9c%6PGg}oAHbg$M=I~zg*AaDXx88TO8WVik06n;JOEU^c;ipf>u3I#7;kE7 zQIbOGDrvP{N_V?g`R?{*xmyYk@;obX*nR?(EG$j}j=h`S{ODg$uSP7jKApOHu-1Zh zT7*4(c+fglzGo>Ogg)}XGK)kwIc_&UpL3Kx8-d< zE`&RGX+eLfK~G@?EK0RaGEZ+k00jgFv^H^TlR$!7mi%_xm0*y7gY(BqV59Q7GE3jn zQbj$o1UM?6KfT_dKnV(Yr=(~3y%8&4d0(3P?WY8RD&JL?uhid-XC7tS`CHIPYa#(4 zpOtZZPiNfaeZK{k%x7?LFh2Uk`uf+u9+qmCE?rtL%hs|!s^3o(kfbF}nCV#ibvayK z@GN9Ce;~A5J<2B~v3wkZr56ae?M_08{K8TiopitQa#$h_pbQ@A$IKj`NN6xoW-C?5@tj4@p{SSn5}_!@ z#zk2Mz8M_0tIv93#YL2Dy?!~_{D{QM`k9q=mfl+v;CD{L2W9?T28}lVΠSg75xp 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

ofW%P$!|det&tw( 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ÐAw#c?T! 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%0D2z<##J8$h<91l>l@HB#wpsA2ZYZQ94u2I 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

3#%yDkEZtuXUUC1)H2i3QttuE>eG{5> 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 z

6;BJ2oc`M!a(!$XC2F|q0}RIPtq2$9(1Wj8^FKgWeF589O8%9K{J6%{VagK=jY z&yyWgQtl8b(%+pT+}PrRWEY~?Xgsk9p5FR84{|sC!qvd=h`kIB45bO$gx&8wyvdQ& zuGxf6LHzw&1Xa5~;%{lFb;mq+<=%e>^-Ai!_hmjpQs_D|>&lvbI>t<8+uy^n*ybE@ zb8-|jp}Yt_o5c&%(lgKdguvIws-!6{9VUfb30g#$LC;CO@C&3JeS6a?luoG^b7ws( z;=%){)0Cv~RGjToeQ>KaDXb)TJ~Ovt8hS4+U)N5aAoQyUYkb`8%QK39BGuJ%vEE}z z45N4Y4y_q2h}h|09Ri4tp5W4_wm!~LtaQ7C0_`O6gY?M==}Q!dA+&|GoHI=c(KKuG zJ6Px5UQ6x756M;Fgbi{;(xf{xcyQ>Mdc^PQh)FGr2Q~{nz$N_D@+R)gG1N3WCUa88 z7k|nx#KD7mCBKuknflDLJK9 z)}+e@WUx59Jt2Y~YUXkh|1UAnUyrZ)U)!%zgNHV_3HWbq*8jQ#Pv^Fh8nzA&*sgSq zKf*KTHa4`oLOm$N!1 zCTjK(yj(fS+GvtmM#|Rc!VyBgwQe}XVg6*rg)OC)HOek`kB|}>HgTtr|9*Cx+>-%6Z1zhD?T|;gm>XfRJAB~`%G-R4C(5lsY zNdl1x&R454+B61IbTLW897ybn^6vAA<&+`@Fi7RH1fDi@<>zb%~|81$FJm1Y#N;J}G2HS*Xr0>`2ES#u)YzZ|eq{7T1SWX;ii}ZB~KE ziw_MvL(O(gqA&LGUOE~ot0r~)d&FD6&FqcTrISUeAp^~*t8@qo8W1Lv!{C?3`i;a6 zlVgQm>4Vv2@_%PaiN$V?^Q z*OahR+`)yy`(COirI3B(n`RrGTHHU)P0sP1R36Ht0GAhr-4EBmCli9J%cHQ)7*_fG zz9={M(^;#ly zj{z9Z9B4SZet_Z-5~FKx(RQOj=VKm@CG=fHuz8s>uSz0>kOQ$30*%ImeX~V{vJYuF z36bwSy|sxAltM+%e={m8@$&j1wdAK-JBF4*Kq_e0eT{d!*_7rdn@pntn1kzxn&~G; z_m2Z)6dKx-j(+@_#Ik5e!^Jg&=ca-RVx2)}ywb}O!kY==f7AtL^{Ug5?HpCy={;T5 z(6HqLlmN`M;lM{6-8h5^c^jy-8Be}(`De-0W%0y()ABN_iSjQ+XuF(31us^X?J*h0 zo(YYyCYECJNkHDLYxG2n8}yM6DLx6pRvX6#Tq<><&myl7zQ9lvDEo--%X{4d+BjbP zQ5~GU(HxWHMZ=}&{Y(kFb^$f7(?~`%1E+*`u7Ux>*|WXMoArImV~TrbzZ=*;29qAV zcE;dhN+p`3dj1!jl$2=;(OxIVpzUw;$cn|iP}X>X=DVXj-cIHDA;CG(ZMz+gJ_1=M zLPX}Bj|7X^5pldCv16ityZNgioPtPB9Z?^Lrf8^l^3-@?Ardo>*-flbu5=?9p(MjM z#&|N01rWHX-QSCq@kj5YlbwZwPXRA2Ut_#`M1M7o+eI74B(?r9C=i^Eu(Vq;y@Q}S znBA1U{jOrc{n%#r73MnswVh%QV#6>-ZNM0_Q*y4f)BCKC4J$R-V{IZw%lg$ZCK$H4 zP%M>%L+Fv?k5(!Ak6uZXVH8$F2oNJp8@V{?`5|8rnqBsqRe6g+~Z+CLcIcMyv>oEPlZ@NcO#{y`rjlV_*lyJ@C^< zoo!>5dBBDTJ?wVaMy@&eNmpcHnZ}?!WTjS8TF@H6J+oW z*>0UOaXc1@6i!f-av*^XKzyxn13olF>s`RUkD8p6T^DSb-o!8K1LKNPp{;^O^E#y| z!Qo(s(mvQa``Nll#jkuiD=p7plM|!W?kDa5%1@s3>VgO{)*!f4`hW~uhGjz4OYF@$ zU-u@ue+bro=>^RmJI&kMoB;YXrK>24+TgjQKpoeFBS7>IrX6{?*m4d)iDDp2FHct# zoB$biC5{|1B>GE(D=tynW(QIF_>)QGEiIE|N)Dw2J#`SJP5-g=FCOIKj_gvc3AJ|s z$(gYU9yuSr+0kc34Mj34MB9oQ>8zKk#BN_JEO1y6j)S&Ur1+@tEb~7&D)g}1xQ-GC z<^&Wvz`GsCQ&JqZ%y4!?@SBdzAU93?*K5{;-FDjQ$M49LcXn*F)i)oHw$TD&87DK; z9p-kzUkML4(zef(Y`nlKK_EgwVNyeVZ6jW0O9i$Kcs*y;geH!*+wB);{oasnn`bb4KAa6 z=%Wm?-*8#y?pA8+f7>$aG01}AV%H!_qv=O=+)G|?g>%xZ@a?`WO_nO$-8CmMKR~8K zcE7>))*&Ecr1Y+rR>y2e$CEpK2=`HY`uwo^C%I2SX~aqHZH2^UNjb%2IHszZa{gNQ zj-2u`zlw9Dvyfk$jCWQ;2*Ddhvq!%)AF`mz4B-||j*20s&+W`C?)EwR9}AFPSL63% zgod^F_9Fh;-;OXOxv<~4Uq}tkP6Mv89;f?+CjK<@`*K8(WHBEXm%CK`SdvXcOIQiW zD5d;VMb{V))H>6aB_9~_QzXjM5mzDD@smiQI|$hvuxA5Dr&N)L1e*~~A1+8R*H!NK`%>aw;kuSN@o1 z0v+Jl*3K)w94%V~S{>(hoSD6B5RcI8MFlJd2yU&^p=C|dsy4xa-)4F)J*thU#+>@j zzwmsi+@U_?VI!e5$JDUZYHUzs>Bx_iPKwHC*cw;W-bR`W4L-a;UAjQiu_&aqPxvlb_!=*GaY^B61PsqP45nr=> zn%9wq#-ZuK^0Bh~-tpX1saBuxB*#~tNH)%36ug_&0^{C5IhcJQ-<(napTdT73&9|xHoZ5 z)w+s*>}z@`@ndDP`{P;5T9C)z#@U{0Lmy!0SQN$x^~C84%c;kdD${uTW9D0NNvz#h z>w&P^wshSxn#P>t5g>31^Kw3}U)FHP$6|GVpcAla7soDARzQAurbOT8;Mk9o0vV=5 zSQ$gqnODt(iIo+Fb2zq>r3ToD7;(o3h|oZ2H@9G`s3d%J6QlMgv0!p0i3C{aeF^~2_@B&fSeWo%V&@HoT5+i2tho#=33t~TMA>RodZwOaL*)_{ogx6j;HW-k`clLmt6VZqLffPU ziGyhDn$lIK*GoAi9R_cg>enL$q0PZr%Z&tN?ZbmeW6g2f%ko2Vs0IgZd%4fR*i}fq zpAC_B;;^_Db>bio>-2fUT@S}5e6VK;NMMuTo7i7N51Bma6$+v_zgS+(y(<5;H_mC^ z8N+c(O?iXzn1_ovs#$~r|2$pnkkE!c89%=`4qIr9x-M6D@|2&ehy{mwq{~bYMr^3Ghmz9#m9mHI1#^FqMhvG31hXHwh_L&%Dh-#VRFCbCtC3I*Je)H#M zQnua7#=w;_QA%t);ov%b$AxvJs(%`$2s4^EFT7L&WFial55B0WHgiEIjb$#Y zWO|KplZb=zHzbt@GvlRL|5esrT1-odrf@mU>cw~4n0KoI$AR})SD_13{_!53v7t8c zlM}s@W;? zzv#s_2%8k;n~Vq4VJ65C@JkUUh)i;c?07m?v|vc)i#-Bo{>27{(?m0RIXZQsh$sXo z@Hj09R|VF_$^rCmHW^~+(HSrmXi8H(4Rrl91jR$F1aF-hOKO+Q)ijhPr1$^0OOX~M z>}!t^%|%4!xafb}XPs4&RFE9rL^OEZw;N(z;zL8XrVKY19;TZaOoZ#HLHaB@Y^9?C z@wH|j++8(zx^O^h3h#d{>vTaWsDJ96qlf}U5%tlXxgLAinhB-*`3UJoPiaEnB#_ZXk z_4k4ASVW20Ukv7PmNeP##8E??r(R(Luw@M-EBdTII&7U%(V94U_LUEgu&b{?Z6ZdF zsdT5fYh9m41@L_e-}Hs-6YE*z?v4*^Alh{kS_`xQ%4FXUn2 z!Yc+%?0`ySo{MHh^rvD%d=IYK<%$~KgsDW{glAl2W-FXY&Z_=@Oq~T=6o9ktcNZk2 zTWJIqq!FY`x?50?kX*XER%uBkmu^G^q@|JWT0*)PmTnf7xc<+%=brl=X6BiBXXf{E zHE``0$?|Ej2h=y`w6_UoA8Cza%j|XyhnoYz?{Z?)82CUM_fYD`gCqwaOW9@*!Qzuj zJcaWnv~Mc5wy~IiP&$t$VQGvWbL;&M_;7imQ4R_?FF!|Ch1?T#P8!_aM5ZxU#>}&E z1&72u981GuyvwFnz-2Eq*iCl(F)-iXdFzxZZeR^7iTjy#h9=AiI;k(b9 z8Udsgp-4jPOVfOw&F&!N5=#3YIy$z27Dr$VTWhBb{-HTiy&;#V6@r}^clN~nL#!tL zbt&HTHTB{4ViEXkDMoBl{;Q|ii{YKdqojVYxuMcO#Wz0>$LD4uUyFJFBg5T{q0f{h zfCQLRo!@>zcDvCOy*`cgjD_6`{Re*W&!})0{@itlsAFd@-0`YPigIaF?jE_&bUGbW zDOr!g3ioc8GrmV4BxNdRCVVceFDyeY567Ox+~-cD@oL7)uU@^W?25Cc^?Ld1$4Go_ z)sY0D2_8qCZiJJ~s==|}=Psc#;ERir^XZ-2C$~aSnWRHyiBPd$xd#)bKJ=qSRmT!v zf{`=(J8ba&xiPF?HUXl+%-Q#ef&x8{XH?l+*!bZvt(d~l$;W1#H~uZUG9%##p=SX;_kutc}M@Y4q%sQM8osYq3PB-+|z4x^b7xtMu5D+t~M`rm?-N#Mh+PV#j{> z6(;%E=UfZpt#dF>yfxp1yTiuoP2a>|yAbs5(ERjZ8~ZcyT_wAscX%&Gxqg=Lx=6>i zLxPXuJvO^o?8b^H7o_)`(D16AwArcE;470@2hO8sXuUoN(fzlGt~AVG;$898iP@`C zi3XZ$qy;uPdK^tv`g@g7K4bu&`DprLKT?Zo%H+1I@j7?H*#SsBPvu0|Uyhgul?(G| zf{&{}<5$DxEuwOqa{mJpz56cstGzwvXt^cr$BaE+Qba_=M$@sgbLsQIO6o6c(K2H? zOgu9t!g0_rJ*77<{^W4T#C3ift25*)pYr6oqxcg%zf#63^wC5ra+uSK|BGw#_`CVn z2-xWNSFYOvQ@_XJFf(J1&rJ;CJ!Iby`v`~&Ok+({Q%FT4_-x^&NwI7&-P@0(GVWBO zw!30=h-w;Z4nsM9QKZC>C{67!AQUr9f(@;-rnshnm0TlEMG!$T_v9n$DPoEp&ck`) z342Y`GX8Rr!TTzl;bpelYM&1rf9^uTld?namX|c_)$xmWC4YDq$2c1zo86TfkGz#} zrztKm12|1~kp#V-!OCe57)_)sfI>=V|;yqNVdS2~<{sAww-I>9e-r21zCB}hP^UW8}3s%5x> zW+X_6{Z?u4d>j3#3mMK7^hJ{|f+}!`?lZo2&Bhp8F8ST_~lGu|P;Ul+TKQB6{ib0qQs!h}%ZcQ?%Av^mG zkLf+_UO@VjFEQ{5hNdPQBj9h6u5Ch67iL3D4T zZg%pNy_1C~?3Cs222G?>yC2@^W37+vs+WmM#$Td0q&@hAYZ7+!x+(AHXO@qQ4($W{ zZYD#1-Xpdn8`>6}Tgo}g1!_5plXVN^-E!E7G5<`i@?J@HKEZMQCa+`all1#K$K#4v z{OZ|ibX0Z&WjF%hRQ|{BCqf*4@5 z4I&Uo`|+r=u*rzlUo2-!LzA2RU2e#VDlyJ+IQ`fLgm68iDjZ5z6HMx9N`K2kAZYY_ z0e_;{_fWQ5De&}Gn_8{uq9Q&>yGq>dSZ&Vb$?qm3FB9c}vfSUkm1UL0zW<$r@`!2`12*!ctH6pWgtX$ z{ccvVTr&`^cn1$|q&hCGs?w*?=uI~HzKEuP?q0KuggH~^RWYsWs$ zmHbGAHtvHQ4dA`L@edyefSvRFYokk9;SFF=a|hsUSFTEC6U|$(^JI04CIQN5i@^fQ zh$rqJM%IopjhjEn}4a1V~Xeo=YFBC?;Rp92G|5-)q@#A#g6Ic8G`HElL*N^ zFSr=#119F{a_eRknJ5FEc$U9}&I|)HKAc?{XP=_(0SPJBd6hvHnLILEmlw#|w5qvh ze;Wmj88hMxsl_|_yX3SW1!dkheTRDvx2dCXO~oW{Bs=Oan*i3gKbjN+&}0*)7ZKYt zm4nSU@icXB6x@05d5n}1c|wMbdVb9)0>})7*=opi#a6(}^91Q2+Qcx1oP@3@3}(Vu z5x$L1-UN(U?ht7ocCn2}yTx}`&)z3-=ifg#a4ytvQObbyf2VjMjiA(>xZ0naXmV9k zeW_p9PEG+20Q!M*x9#uyBsq24>ma`#zzSD>H^crd%z_Jsmc|u{3gz_;K*6IzjqKm4 z3lw>B>gG=;@ZNO~zIMti1lj_m>jI&g#nJM5Dz~G%i>_5egKegvD$8~(8e31Ei$dj2 zi_=&6=@wJ3wfD?C!5(+{exBy^6(+Dse$kd^0zyXmRA>K~JpRdGT>vj(dvC9Y*DBYV z)0fJ-8IteJd?b!WI!+;7vegadgG4SiHN3{@PIjV5Z&G?<*W;1y>6eEv@0cbvtKlpm z6uU&W=+fv2qW5o3+axKTZ=|CTw79VkhU?oI!Y673>hY@Y!t{Af(y)>i%jIqt)2q^{f~@hzPgNN6r}@N z(3wPQX;X%1m=n5TklOXoE=#Z8kFv2fw@r?aJUtl@?%W$b=JgSN5p{nQbF9jaUW5p@ zF5+1Z(nzZZg25%=+;)#&XyM!(u0Oa?nKfqf>=t*<4C2W?k)UeK7NP>C^Gry@ggZpa zH&|r+#Dr#T7`y+Ob{NIP-{#;mVhZX+V+7DKJ*PGZM{T7v z^Ya6FhBo&`#ev7(RD3!_{N>N)N7~ZIC06EFM2HVQ`RlpSiU_}6;cg50J!BVSm3WO) z5rP`NIVu-OQzLuYh6f~tTwNHc)-=c*!7{n?a7A9UHR6a*Jwgiag%-G!hz@ujFzjy4 znMi$J+1Txxt44HhT)b*-_vEsBymwjQq^={AalCoxo=TrIT~*YJDjQEZIjV)&(8e}| zX@un@Xy!3;Q`Z4lAgah13i7b%8;miIkXK#YKtr>U(7yJ-dy~y-pip5 zP?k~rTP-9GqOJ#F3o?oz1f-j1!KX!O4;vp5hQ~bz9?bxe)|M=>rC7>%F_SNpbcX~P zfdK`d*sD}?i9ERu261T^=nq#!ry+cG9bz7=84^2sk=e+dNYJYuVBReYx0HJLxb-&* zqiL^Nzyv1F68_2f(GE(>59DXEBj+ra>g{q!@mk zY1f@VHlDK$&5IaJ>TU|I?dYGqwV}JGjpKv5I?H)A$As72 zSJ`fbBzb+qdj!T2URzdnL(S6)N;+^n7;p~n>aUU;X z=jEd#u9dhuYQKEbX&c@v$}p0|3rfMR;csgis7=PAHf?=6>1Z$aF3dNxB@)8!!em`w z=F&cyoJ@0it8X<(@H^rGaAC?u`xZDm!lWOvw{0KnMvPClu61EOjIpRc zR)@i1HP|HI$FP3BxOkN*;>R8u@LYUFT>6D#wAX|5SAj-ox+>EL%W{Ve}>l< zd&=5BwUgeyE%nuDRm&Vgo1U%v;4;{#o>02P5iP9b2HXPTGEQ-70(mEep2;8f$*#5m zfVMosMmX`27>MjBKl~6w@u>zuTTBl^$E-zT5$&5ny z-U~m}NysNrCFLeqwhK@ME2xfbX?k!Gl2}rDN&+@41Ub@!o(|dHm-BE+{t56I@k`$v zNHEX=(f~9BpU^V4!c$+yl{j+r>E)7sHWs>kS@D2TCr4J1>;nQLsU!if$R(vOMT4p? zXM#`3u~7};?G{qXx1reA+Ps{rVlB3Xuc_ivd9}=V^t28w)g4!Z^MERYS~A%A!C*#` z#2H@z$C74d(CKpT54_F@7&Gxx;(|qeB0@<-nXRmIla|Kc){^Xm2wOu-Ssx}N0c?vB z!v#6zf!tLY9l`HP#`4&f0umibo{b>LZc<%R?8n1%=Tk1E-i~6p-{^}h0>h2BR~V*n z#as3gUylW;Afv~HXSWg!)`8?fG|a7t%e;(Uu*YeyLL;S%~DYNt7 zcC?#e0-ESB%@)D=5Do3vQ(jDpb23&8H^_I=<|*cf2zJPC0Fe(HOqw9`Xe1l(QIe6Y1RVQFAV z1uq_#F40T9gMo#=a#GDhpFyE&^o0GtnxX&Hp*wd`bM7^BnnX;QnuN;xt8r~s>*Lt* z3_3DX-@`9u!HaN5mSO{XwfFSNL}fwyZl5@)bn!#{-f%wYzxMTT5tn2@!@(cmACO$36N7f;U2KY z5G@BOaqP_YC!vUAx{zE4@xO#~IouF!-?ZkQy>!YYANo0#3Z&R|42e?~ybQAr&^cXD z@rAg3`-crI2=~0*bN3O6Z@qpJxIvt`%3{rzt5!E8YhWDyBjhRGevR45w|Oa$Bta|3 zI&*YPP9P`ENlzr{^Wj0SJ0a0gjm0tb1Dx>}(n|rv((kTY0`!2=aIGsY+P#$+7v_My z`_l@~&DjcP{%wNqI}g-*PtyPjc_I>I+)l zZSE@1V+GC#SBq{J%k~*e6SA~e4aet>vxxA`npzwIr`q8}K2q;Y^ zQnH;JnJCX0POMJ$+0yFs{;!`?X_r0t#y34GOIKXM#ZOz>vMz(&-HQ{qV+@b=P2;i* zx0SzM-4Pu=e>?cC9>=D-e!&3GH8X6F0CU~ncrWw4CzsItjA*tyEmur$G}J=jM6@yQ_x;N0#un#sI_>Eepo046>M{X6{qX zK4FS32+|3N*4i)Us+!sk<(282JGSSQB`&NPhSHKChZf*9vkAJ2BWMo%_C241=;-lGn_{A8cqrHf+^^Q>f|2jFCMgCxMn9^;A{BS7fO~4L zd^_XE9<#o4cZeWB?_VIV#9w>WIXYB9SBvoehXtTv=O%E;@u;`57bZk41$Y7I>~&bz zK@?5tmi?|sHkz1`YkH7?Q%#vJ+Gh!0o>3Z`$vNz*yxWGwY~?#Xa^Me5JoaH-l!F5< zXQUP^D*NWRLZ`d6Owm5MY2eqD{)~#*qc=O2IRp7ZiCz^Z27~k3M~uQj0*AwfopB?P zvoSsi2gB)cu=jz(FovopiA~f=f*CIR{l|<6Cqo%B@K^?(i)9s4vgiACr6V^X@dm|L zfycYDu+&%lv);aSQgt--+;~;PEHWX}C4E2opFUGH^h*~Y?N2X^!<6sJMM9@m_Tsd9 z0!2yGFg&#L+MOS;o(XI(y`K1GOsBPvOJfrEpV{g^<)n3+_O}l&V##*QGm!OgB^!em zvD4Dj)HJ2;4=)O+ekJwX2MC{ZZGSy4*-RCeaq#@-`}>zp?;Gs3cCB+{;JdtOM-(#M z$cuJb?qflIr4bBRmWX0fxah3#sY;Lf{>bZ8e3NW%MEjF(q8~U*xnVVpmAp>`^Cf#! z1YHqtvaHcq-zQT2ZpnXIRT<1P+P(h$1X_c&%1*`&P&O4)aBF6%5zJKVRw{*!X`wVTcfAPI+TgkBY64Hmy3 zRzK$4WgKloK)mi=XV25aK$s7z7?KSYUfDSFk~PM(H%^L#6+wPf8KNO7b5kt5^M0H7 z+KbQMTwFa9qn}&6Dt+(T;Fm-{6+>6?G$q*5zyYQ>p>73AFyBxR`H0Ea=eRKpPkZ|F z#gZ>v3nhds3&6|gl-`v<2?SA*z}S7ls%VYiMyHC8d!sGArHWgOHwky)l=>+Bs?s+J zd*VJP$8Mrhb^^}jjpXAIQ-Ug>L*viZd&6!`;b-RR5jfe#kvYOQ^d~CwD2I>8&~J9- z<%N3h&X8bAD|9Xk{7r6&=NnO4U% zSbGW$^~Ppu4V#*|A{=en`KCgz5uF*cGdzEJ#6>#v+CU`D#&n>rzrgQtBhF|ml3ih- zS(~L0*Tn=sL9Iyj!|M33!j$}wqU^G_{6NmQ9VO|jbvblydmPzCY!KTg7+WilhVWM5 z@_4J8$=r-uin07&1%6#2)n~*r#kpVY4(gn;f!m^7UeTi__!Ck#FJjl_O}UkLhB~*L z-UjO=)66?=xx_{E$c5e-h&Swi(4SZ}QfqYRur|JL_jPDjPFU($a!7pB>)cZ(5ga?N zh#{6ywtUxP1)HC|%74?tMdbUrW1J|5Eo~@q{FAqRp;2bcC%)26%YndAY${iHNjgHu zYi)g`Ti*$BQ5%Z3amX^Gh43F~F$ndyT-C_X%u1K$TgG*S>r=1?2OHCn5OG(O{-HA+o?_4NPhFHsmBf`k+D z8xWd2!LL*}a5(&R$0O|}`{-9xIV{6$H??Z4N7Vi5EtA!ddx{IEhOrmqMZMl+rJ`PV zC}h12L$5fzXO)-{VG1Qen<75kVNKS3G}`W|Qv>D=k$6MDozV$?Pz+Aug^W)yg7T6# z4}SV>_x-bF;X6w^!27%Koi$$50n7+tb$AI-gEfB3wZ$$(p3he!*$E`RkjlYRNtNg( z;ZN?q(z_PoUkl?MQSl|ayuw&h$}l(cGNXr-e%fx@UvIU?%c_$!L$T=cK4hG5P2xcd z!sTlnz9W0S2VjViQZCPqGz^&EcuoP zDuVIOfs4zcEmh+8X&v!5Mw$c(cwYynXI6(X?^joba@|)_`9R-RQd9qi#ZRq7H4VB8 zjd!5WeU{5m047Z04OqUqRv)&8B5Mf!>ejsQDX$zSGCQlYCwUN&%Rm6}{Pc3(l^5&e zrpheXtTRD)8qD?}58!>wA`8Mv8KLvkU5l!IOLN0?E76iWdRK-jI}ut?Q4F;BwP-bK z*|V&*(#cw?NYeR;*i72iDhU*2mCa_i@$9f*#J@3Xj=cGiPwu~E=z;?>Ac0J0zMtHJ zsj=p1Vho8um0GXjtO^4vpV%2O=E}Wb)MW^6Sm7^N?L|zrxBem zKQ4^!*{D|(JNF1u)8vVYOTG>2c?YU|is>Ws?et1Ta1o-G)#2Yl89q}JF>nI^%jtEL zoXsjEtZUy^tbGe53by&UZd@LHik zmWmxdRWH>_AEgsfdT(~`>LJ(3>anmn@|sGdkcb!hkFuYXANqrLRsGIn-Pw+ul2PZi zN!lUDb1$$w6buj+QH0(6XX#ylH&w3BkcXww^_g#{15Lr~SvTylbM>hqw=#klQp z{nh^c;!~S|XvK*+EH%grVKHi0ckxpRK!4vw{mX-m&nt*NDA=8Jq+7;^jSu|hh~=TL zn!{qZ5!XT8rJOjfsBJr94L*VO% zw2|LZQPQPq(HM|sVt5OB5=WNI-)>XnCE@oO7p<7Yz%9*`b+IDy6Vjj^2w6SZ%);<| zmJls5!u}5b5*!rB_El`UThgm!zBlkd5z5^vOU9_O40Jt$7m3klXdZa5Yi_*`*tufB zEeDzmHlx;rrD}xoz2a;auzwfPhETAaENwX=iL{fN9shwL&gM!cr=9#}-_E(EM{bzRov1Q!f+tXS9 zhHQ2~A;?Voa zJ@+^Fg!DJHpU&q^fR#8I!j|p-nDX8V%s;LRPwz;vv?f#P%D0N~rPOFAD~?c=t)ME< zE5|3guok1sEfy`1rl5`m+b9>IYvi>?=HU64%cMYklR$I-$2KF~^&Z`AGW7&)PHATV5l28&FM;@%fDRn<2)f0`h07A*ixz^Ez!Zd_gK#HN#fi!a#uj zFdmfc9_LL1(2TYVIf=HSZ7C(Hz%Jfe`FsaWMm-HPQf{j=+4Nv24{G%HYk}8oL^Q$s zx#)jrJy1)-k98B;fS6DqbZ61$(&@pPp7Q&P)fq+;o}uUm@Zt8n{qyqZh~Q^G-#gYP z>HW(b0OvUjeua|YJI))V4Kow`02K#(QasO#ebb;2^EoD`zGlxr%aZ4|j-n78SO2?@ zAsALT@*FS!Yl(hG^}eMl*akzkf&M(`Zth00@rIrOQvcl6loZf=Y|{?8T>9x1sj8Q2 zRYX|VZt!}ua6&9qkB2JFK-B?%_u6jCg5L=3 zO%ss!xbB0dDfod9y|&8)zO$3FGv);f#lJ<4L?h-7)yb14d@}}ByaD`@Uu{Sk-LGR$ zSD>u-X6W?&^V)@ayAlGuSGWHwgQ{%;CZb-_#No@_KjHIX_yWJQ(&#nFO`d%G^5i?<#bsVnN_H-t!*_T$K;Vo$ zR{^NPvrOrzLg2XEgN(fKukm*-6h>T-G<%#*qn#WWD==#gdcYP-s?la&SE&d1H6XW&D_w zh6AY~QoPn5IYnfo_|Np-I<1y)Q5&`wm*)58zc#uFbr)NhOz)b^XnuZzb06b2xppmo zrZnf+N!NNs&rp?vJGc}~&v259WYhrP_1os}LS`HW7Y~>E$FTr5Y7~sW<^`1#BG2{2 zTS8mdnlbby$pdPZOpMwm?@xIjCzb10%57I-U8veO828cvv8 zCbe++cUw?_BDNsBJ{`nxBX6s^Syon*VI9jAUEQ{YWPp3kf05V2=sNvUn@$z1ftrKkP`c`OeG1SCEBigs1PSR|cq#*&VFLf-8?xbFp1hN+W={;|t* zrsyF9Tj+^NH?{>FXAd6LYPA7fKG(jnmMwT)<6=f*wVLzKJp6U47X24ALwwk%`e4s0 zI6r!j@>9t&i^f+g1NGl&WR;NBz@RI4Ts~SRj4_b}1DHc2v%b2x@Ysw0Jz7Q8mR<@`qnN8sX z2B}9LBlonft~)%9pp%F{(H?a!u`yFdwSNi4veO@?dwn@8l209c9d5qq}tZSbP?v}o{){-xXEi+yaD80ovRs#!2TN|h`sLU{_PUB`ONc#t+B< z(SGO;xhp`ty>EJ|MYtDl2*uy@WMW(k5G8S5JwkmQx?jKYEd%9Jm3?vY&=wU*IDX-T zE5Suju~U4QYxnEq>vP1-bhH)|G>!@n@sbzk%Bs?4m_AkSELoSAT&G8bQ=nisIZn^PXsipT2y9LJP2fW-3p6wc7 z)zR9}M`sQb0`sGjL*Ieu>7uR(&AXeQvefX*S2}$V`^0iY7we5j9JcAy9y7N53p>_^ zw10{}CTTnyAV#oj*NGeVEMQpt6z(BREkr@ z9~IkoK=Gm!@#Cy@Wi66&4`~MD_n*L|)c5N^?R{<3pdfynV9ANRq}_Y&KG@~XPHIaM zUdu)R#}MXVY~Q~wyo#0~m{Tc7hTHQPmxvHMuZy`=KIFmh!|CDh^qah1eQKjgdSm?Y z$&E={rP7#4JUcY(A5FinaYtS7Wr^XKk5*;KvWZAQbLM*yqX*u7mWCmxeImeAdk|Wc z2D!6)ZmgqdK^@+hj8T6&THiu#ps^u0REX9=s8^03B1g6fW*|&raPG$p-$eDcPM* zu)k`DEKE`hXibi7jt?PYvY_a4I7K(xOWnI$XwgJ*T=^mVVdL#lQ(9!X-4M2J&ie!@ zw{O-{syi&2oAKWCWcK%w28;6-WH%!=^k)`fSBxB2Hi!8yUt(}Q5C7kZg9jU+JQ9FX zsR6%BNN>CehX46y;Wgi4t0nBSCf~TZ7mU-rNuR*mUfM9*L)X-GYP=Oe+wNTRS(s0FSv&YOC1$xN*k3kse8R zi$E4Ebq?(5_s6}4KM!NvMN7T+o|B0!2xv|rEjA(V{*AU)EWlvgj_CC5mXuSep5YNyXJ`nZX%O-(zbr*^t%c_0ANquOTGO|S zTh0MDDOBZtB^7sSFqRvGY>&?(vWFJT-P zp%e82%46?R?gM*Q*fPfZbgf!N8gc0=z^#FB_u+4{LLAIwF@`)N8C+`ehM0+2-N3i!<=)ZEqv4qT2q z(E-2td#ZkPh?V#$j#Hqk;}KpqwRcujnmwU_BlSEkH9V`?OT?T`nrXWh`xC{2a6TCn z%NL|MfYt%qJ-BB)SJJc{pz#4qcCyDi3@7v5a2`7mC@eyyIx+N~Q@_7mLgE=>D90)o zAGqR{yVw`V#h>Bk#53AHeAdL13l9Wh<8ETZP1P+gKHi%X7v9$H7`lI+dAvyo;W@;% z-2d2@Qq8>a_5OTWiH(qa7dvXkd!|F!lfCZUcS3AN@<)T)6=_AiAv*&=_ZfE*{}e;} zY}ALxXwP}0KpS@}2&~3Ny(Fz_hI72!rI?P8w+Ct2R~1}xFw>3G%>1%*6dII+$cXrtIh2JqFu&JD$`g^lW7Ol;_Z3;wyL(l{W|}p zM*OEppo(3gj2d`@jY;K!k4qWPulU)dQAJxZcpV8dUAbC26S=)OcO$>uGYp}aQeUq| zX_rZV!ECObv~8)3{ctn7crIhCJ9Y*Zx~g#STtKo3vV#B z^Z6~cFw_g&1ZaqDxUO%=K%nf=mzJI1L(dP%MK6Q^-*tr4*_xxhME}7fI#x{5cS~gF zVEdfb-rhp1kcEo7^m5?aK5EF4)j%ufxm~s#A9?q0@*>+p zf%LU$K!26lW8Cy};MWt9wca=XK&Xx~0<9}B|0p~WYJ@F(Uno zM@g~b6CG^7h1?K!&;vHPztIy)j<2b29;=3YqckVX05ULcPKzA`WW3B`7hzR22j zIhe#z_P6{QeE%8$cQw9<436Fl8z$imnhni6m~W=~uD}a=?3DbJj~3iamJ^>YgG(e= zUXYRA^fThjMGIT2EGCr5^ZeOC2R z`3rTdgxn)LIH$cw>VowRrCPM&HPS&J3)yesGoX(2kE2Da?F#SWYwH#{nK|fk!6)2BHV#^&BkAjluH$XaE41}z$BU^N^O{L8iaML?IE`@ zs;FlZkJR13W7U~FlWY#1*&TP{y5cGuM8gmPMu*^I;4a13Zu=yd$IZ9L^319+R9T)|7<%LYp zi)Ed92`|L1_D+v4bx4D~5H9AsC4UYlR5P8T5s+IvCpr&rI~am`f?#CwxVI%+bWaiB zc{96|a?zcysNy_Ylt34Y{ELj4Qxiy{Jopn%HY4ox>&LOf5&?31=KAgZg-jpTVZrjG zJek+>I|8$mb~Z)}J91XDpi|96jtw+5xc&BBMv`Wr)8IN_6)O3fx@@a2sZmzMFz6Qbp* z`d(-6xn+1WsBlq# zu89*v6$#cX#cC*_q0uxG$`bM4MK?>TSrTd|i}FQT**E0{OAow~;n>?m*f;3H>S>G* zLx{#*cj(K9)MWAO`wtbz(Z=Jl!aH+ywbO_GkdGza;%FL#TN{FW4JX~paU4FC4r1hw z)>9Pa(4x=T7!`I~4p6K$TLv+v3H^D}?C_ihtC%{{5QLf@=! zFF!2nEjIvcsH$=gb8@6plBPqJ4cD7iH91?|Hv4|${@PIt4kc2OnWC|}DyOuD@7L9% z{m=n>cQZ$UD^qZ>n$zou`mj$p<5*hzYa}=?a8~EB>P<3##?sG)8HG|S%R|c>+sfsU zdosCTuE7>6eSFkYEISYbm!P|*74svX>8{;SU; zCWgrk%XEaws)HeB*$lKk1b6dol17Y!wKm`1*bJ3=VUk;5dA$nMMTA0_f#2>XDP5`I zq3X)HZCY=?+8^;M4KM|kGdL9`cw{7{oW`JtGon{%q{1%%l6x9Y>WIZ|f<EJP8*BQ)iwk1%yN6B>HVdR_3C()`LEYN~UZ zW@ITRn@&n)y0{caqBSw2Kr&NH-wdYXkO3KzGHk%#ng^gnRs!4JpyqZ{FMErP=d~$j zB$H${8Jo4z9PTTQ#XR5igT7(!e7OxuIVYW$$UI%3Q+Ot=>433!7AHTf)}U_1YqRpV z$>Zg9O%ygxURJQ=6vluS zhf%R71%uvM=HUJ8Xd2-6_GZk-*u7WpbnF33I*E;~Glb0{=)u-h?ypv@1UF;Djs0G( zXO>at>W>Hm{nV4|(n_zV(G`9HpT z^HcDRohn@dnDCGeY$|*#Il}O~J{vz+0QyyLA8LSC>())`#6T?_sKAXCTkYbiAz*KK zHFPIES>*otL-sqWT2U6Dod`!7k8#&Rm9wI)YyJkws@h_|cZy=N*eXHZRsZpE-|C6) zX}RP$-s8!3dCawH;eHbH*N@$L3kp}=w8a>$ITB86gZ#za$22}I&M(cUWP>r*q$C7q z+Q{f9FyT`5X@qV;iyJ9~GL*ClOCt3CIXtTe^eb(Y9ZiI~er|cuR*y|=h~(SHMN&nX zsl_SM%n6+&B`{&I_``N)t+lF5Vl~XCfT$J(#sAnD1UkMnCLwL^wZ%$r|-YFgx+&G+J(1T#2%| zo1b@WQx=%OP@;ML*|&<=6c<&_>UdK)Fao#A{X{O!p-1uy``y%@vWC;W*99#%3bZ{B59PJhRR3nP#lX%cghU%EL-pq-+NS4Wl;KPl|ANWc=^E2p6xkNK=pi084|$dQt+!m=a-aRD9DI z?eE)}7zvb%H9j@CK^M52ZTl_!d1iYgPQ&N^>adc?iCVTXsVA6J4C@I;(~wqBDaHXU zJ`wcMu;6u(nPGr6&wDCa!-q@&O;obmK-E;JW9umzi|pT1CXJm~%6MfFpc#H=q3R1+ zJpyrGwyaOw+OP0oL4w#8(mQ{mZ!)!3g|I6L&bWP3pT(I$%Td8kwT-1_r%F@a8rgO~ z8REajJ6WC}tiZIt*S{9%lMNB5^7^4JsI5oUdV9MxFSQM)~$?udH+3OW_suo6vP79&qovbNQGYY{!bI7&Hx6hwK9>9d1 z;UVai53o+)?#ceCAG$=*=cZ(VCJlcQ-}1?yj_E&5?BvN;)4n%xXTe&=`R3{E4?dSP z0l#_{A4JY>L&cG7NO<%rEnc3*d1sc>V=yJd&-je4J_P^j!ScUzYnz+W0ZT8aD)k&KMg(X)u?-W(LueJ+TV(R-LGU<9Y$l-66Y@%jbBh6_U1w9E4Rbfdp?TNyp$*PdF z6TxjT3~RryYbQ3W0`DSfZYkXx}Go2)>H67Aid z+%bdr#As=PlOtM6D?2iAgzIQCi*}%?n!mO}@bAJiVK%%b`6bVZ#~_62O?w;u!(~?mD^8|m|Bu9-9v+7z zLE-WL(*`RSnI6MC2~qER$rq>RyztN^Xaqyw{v3dv>5o390yV^r4!Wx^e5 zb-u8$yaI>|3lhzY6rz!?AQh3NRQl0FDhH~H6Ph)FM z4q}c=l_YeFipo?ia2R6;mL>G~2{A61a`W>3QA((lj?M}RVGUv_i5e%1yiq>uZzapq zM{nEW`EL6B>jG$OZ({HS`EN1aEq!@8GYScKCMp;heEw(hsP;NuexlGj?N8=la%cAi zkiFUuVkBM6oh-O%JFW8!)w zcQjRdAAjUgDwySHQ9Mf)S=aG!Usg|6W&D9@9Jm%Tu-$S#uS+d@%(e;ie8v%Fx<}#4 z1iF)5h-un{U+nyQg`|QE@m~ZU&Fxjc*PAK0tkaD4u&4afMEj!@KWN(;rh5;;xv*Eb z96822szM(>JWy6&B4dvutMPVvZb01mb+A!_^YM>yMVF#&eH7nx=_{&+f#!572?h*m% z?(XgyV1S4F-p7CM=L=kK)^VNdT)#9k1Bz;xrqV8WWf;DKqsZrUX$a*nNIydBePN7c zwDV~lwxf_=#wz%>bck=PM<^qw8*@*xfR~j+XQ8>v;CH$u+R+VbOfPmxTN@AGhCxfg zXGJd7)WI@xZaT_2uX)%3rR3<&lTZWv4;MueU{=Awy3qa4u6mwg>z&+-G=j-WeMbFC zbY!gje%@d3Fe6y|1#xU}>+erDO5*>jx;#}lL9&~Q($MhL9Kk+wK+0e5g;1$}zfAHU zZP9qUn_lWDf0#|UGz!W1{1zDmLL;=xVx4_6k7$!Y@*@5tEzhXYb?<4 zWo_^aG%mF48WbgROls0!D3FnnfWw}TxRa*vj#tmyks`k^Dmmz#=T;xUv?R06jV)dX zUPpg%iB2icEnwQ-q^X{0b3%hgKdx>dWkej4v+XVnjoGOs{lOH;Jowh#2Mx^rB$|6+ zr*m^&TN5YqfcYNf<>xyZEw+VQ9l8kkrk(jae&jy$`J1LtRB9?APjp^Gk@TG~RgH-c?qSaaEbD`Fq^(C%DHsj~mT35wqkdnqxgzcHT%El($It zZhh_2TA-wEHLdOKF!bSiCevlsze$aOY2`}+EQoID6FU2}XA=wl%t_K1|D(MCW3y_g z6uwGEV0?0l`uR8YFe?RckC2Ri^)%nH5QXJ|5)}5fOJjE8%~*<;XgwEg;jv=rftU1D z4cJ^eEetvs5FK^+5_*VcheXV0amh5Zem71UZ66!0TIK%}G+C>w#J5&dBp5FG`0=rw zmi!lC#23iuCAg3%z;UsJf-H|~|L)={--e5XOpPm}et!&8QI`mf+(oCi=v-%XzpY*4 zcO%c_0IOpMZrP{VpJ5WTXl~wu+%tb|r|s!k3Y@u!^VkHnIPOG$-3-ht@ogyW*Z^m} zbKQFGc;pZR`BfjLbc@o}1_pgdsG!!sju&bpTaY|V+Z;rdG+9=9acPR(NLele?S0~p zvb^ahnl1@4*}K*6Xh{=hEqHTDKz|a;L1{r)1aVNhhYf27wC>0;lp%hNj*}_-Tkb~N zU>~$697E3;%@qEFqCJWD!<^;f3qa0vAuJj} zAU+*aCbPVicyvYTqGml=kWRq4gON1N4u_!$Re*DY6)26>?*B>G=3E6n-Pved>dn-k zd);jWqNy-J-KW3SlQK?Sj`(2^ZO{ztd*0TpARUE!T)HA_*~4{|h#&u#K=AA)`#po3 zkxEzocvE^(SWoA9zT$tfG|#o{wl-c@vwTsmw7dzg^r{9kJ+$UK9Idyd^%ANV-N5FF zEZBCp&+3l_rcCebZb5xtjJS=Cdx=I-?0)s0jevA_^FLDnN*y)0%pHRz_Iz2O6)wpy|oK$)#w5~`ZCTIKl@ zInnce2$st(lol7J)F(ve z?kRaQ#XEFpfLwCH|4_V8(fZCF!~kc`$|G_fR-Rlg@1FY7+1TcNxbmKr^0v*SN*U-HB!}oi?@uA^Z)e0&c};Fz zk2Q(1f41d2JHBXWb@Ds0GTrERbl)$u$A|@WACo&g>y>y|KNo z^1K@KIqkQ69rM#Z_oh;t53VdVyO5OM7}Q)6iR-A54#`_+u2%|%*dXdMe(JrQ^cLqU z@5&a!^{{#J^-dJH9q4weiFI|WcInkbX2-pe8F_se$5!!b&OEWKFkm< z46&Kywn5RbluL7%jDsT%+Mo|5^Jeo5Qb~h)gra6adTQvBP?@4SMt6J2G$*FMoCDSWD`UpGqN5$qcH{ zU>iCxe_N&@GM&*-9t8001SF?P@B9^dvMhNfru64%Y3961zFPI6_P2ekBb2*+;NKgN z?7wT;RrN;s@VL`$#=};OIunXEhQd!w34prjIK;=gZ02-=xE02B%hM=7tyQT=Se!g0 zbAFeazg|lY7JEz3J^#gY{e|8#!=^L*UTTA6aPb}V$a%o);1geHG_x2Ldsuf z%jjWYhm7>AJiaNETq6q}<_=_$K?}OY)iV(-J=39YDNya2lklHaja#Dyw*%!*HjVud zq@|jvincG|Bp#U%BCfqb9vtN8r(no~^{puwF`kwy!-&|sblw)c|G%k#hd#9Z1dtco zY3D4J_gLS^KRJC~eO@vYybg9ax+!)zSg(62JqTV_24tTn$$e$))qNeuZ`L60iwVWe ztW7qauk|EJYn*jhxp=*V ze^%?$?PbTgrpd$&T!QPf%K_mbx8O^Xr zdey&HpSVyAVs_Kd)F}4Qm#(+#G>j3UmG_??6XB@W8|JBW6yp82i*Hvb6jul%S_xe9 zyth00C-lv!0*A%$8yrUr`qu&}_`XmGzP?)bmZq%zdb8txY~R!h-?r{V?K($HFQW1T zUnl;>Zy<;=Q0YF&+A54{r2|5Oo zL7BZ}ijQDXWDj3ft_#~h476Ht4V`PYOtnvmSzoViCXIew(q+b)iGF>0yRzKqQm9Ox z#@aylu68`EuePYQ&X^KIjd-&d!pUqz5n_$r?&44ZCQ(RZ45K&R<1vae(VO4xt{G?) zxRF85PDjIFK_;=7@NvB`LCH_E{Ves%ws6fcazXo3Oxwq^Za5NUC!y2X^AQ7(yXG}M#?@RSt)wQc716*ve{cR zrhW{{zEJ}m*KiaZd_wuPflNrj9A6p|!4ZLU%Z1x}* z>A7w2b1)}JOtihFtzY}}A|q6Mj0 zm@0X~W69aKZlC!CF1)Xl}otW8(d4Xmc{0H@L30rkKpE1Gf!rFj&Z*ZB{w7;t1|@0mC@dE_)2pMJ7&&NiQmJRh9Bkq2*xi8m;Zd?$}AP9 zwN;3xg#OXP2?5Zl^r!^!9GjkqU_edP4kFQTvV#1rvfiwP;AHAOzs*e0wEiH|Ud_w6 zd2%tLIH>nWP>eh?^P+zf%!iA5n(zWmg%L)1#{w0{UP))SX)b77l!-_klwe5;JJ|>T zetm~ye>;&$4Nl;KNK7K*hSZNgmX?>t9iwm;SPXQNnhLJH#iE+6M{4Vhvt5u_%KjG{ zYJ|hAWsE`Ie*T?*YUCs3LO6I+uYE6f-Hp0EH3=PQ+g;7#loZ$2*Tx%~S9K!a`4Xf9 zG*F(;!vh$*03UC9$9aED-x}4nIiMN{y08SB-M_*71vyv^4IWuvsTEX)DA-1UB2v4a@*(J)Ch zlGxKZHeo6Kq=0?w*9o=;DtyKaU%PWd(Jw?SVJ7`Sm-EB!_t*SpGOclfFRs`K!`*DL zDELliW*B+74{p%nx=kJ5Y3Nr`heDm)xJgi2Zq0u( z?AXP3sT=7(ax>+VVZ+q=Bx;+Dan$!nlHD}ELTSJV#9__yY#|$=N$0yAN64dZLnZVh z{xpeGReRBxHn*%VmM%M9oG))-ql%2D*@pRgNuV#TmCwR(r4jXk9$GOS0IBhE)Hrj$ z{r>5j2MD?sjOES}f#<5^cg=oFZhG5F|V!M$RlQ6gE!3&F+!IVAJ@G4~QNnaq|R%sccJWoyWS?eBMJZucB z6Ciws4}c20Jt_84rrcQxl91ik#?sPzkRP~PA_H7$i!KT zx{w4S3ZzuO05EL-HMN&vJWv+?>n3;n&5zxGPg?f?kk!O!QnvML+jqFBI4sslsw7MU zPLJoI$jgPNWF=CJzv8yqSl@WUco8Whg3W1nO7TPc;s)Xd$7djb*d*`acT&Uui=B`r z`@J*9f2||>T%958L3u_d2<30!?10?eiFrJ4i#@R|cMoQIoT@IL+^C8CQ~G_*4BHAS zGq-Ot7`FDTyZpzgNP-#k?(CD;1yQwPO-N>zBP$zv-26;iOH_Ag&UyE2q&nS~@gD*N znL<#42hIfrO0sa$n4^3kF*MJI1Lv>{pFYF6_PBPU`<&91TeaE>&C({p=IZLux%|(u z>~gNX%Ps|_G)@j?t%07DaxaL@elP>tJA6CtB-tm)EDB)kjGhT@~k`Lefe#)9a?_3L|Ovz$S z^j6O=G~kchfvv(Yy684Wl~1BMXZjfnF=zxCX|Ez#QJn<6bCVIDSNQ8aa)yqfdV9Nk z`!^a($V)AWu{(6lx0;dZ^NDR3@{%m}^+!S74_9?Zfo=Rughbb=5J2=3Vpq(AUM=GY zn^Ls(fX(i5&hez;N$iy)HTu1Zpe7Ez2nXsqr9brYV}V^jO zU&6P4gQPypD50cG7z-Q+#jr0@o1Xz++`ts#Oj#dY$JGV!)^!YiUf8@JQg5%%S9W|y zxRZ-UED==P^SIUb?q8m4W4BP@;b*>OQeW-121lgc0A7o?8*MNu1OV|Wia`{(WE3e; z8dloR;gw~f1Dc}4^Eu3DZLklU`TTR^s(cU4e0)GZb}IMa&`2JDGV+(LKO0K-VRG*i zw{VBPv5GgH${islZhQ?t-V+)-AUYtZ`YYud@X(Ro6e689F6< zXV`i_5;cByEc$ieR&|KP;BVyCjlSJ4+cxIGPh!#8E9Grf%JK&mSH(do{snw3{jcB{ zXuuIs+a%)-0R+W5{~O--WV%Xzv+y_;6Y<6DXHyzsM*51@qhj{OVyv$Vzi)u$W*(`o zR;}Fx!Ef0Id9G+a@y^|);^!5~{Y?u=;@?fRytTe>MX*o9J^pan3z}`3bg~0DMYW7U z^B-jhH^OzyU^_AG1Hluny&d@bC|>nVq5FE9RieSXqX>n{=OhoCqkOV}8y5+SI1~j9 z7d0T}`t@)Um?Tyl-JbcW**V(#fKF6J-URiy3KRQG^Q7ijmzKc(!u-i%X;RPig(hWy z<#iQ;HLppedgUmm&Xc7dyNSR$6E;$2QwCyqR9+s@uvyrXH10*9sM#n#M`E(ht;Nzi z^-5z4d#6+Roy`3!t*k`nmV>w|xPE@n7Pye&-Zk0Yl9jDJ|FL#D?`9Z|fUH^pV9_|?-IK}rg{_0mtZdTrf}4-6W5{nO35#^jP!%7s9%RqLG&ss14#GfYYO zeqkw3Z26R=vk3Bj^+jS>;DOB+K;-?af>9Il9@{fPoaw5qEX)W6&{bMVd*VNIEWY>u zlc0JDm^gBBL`zXr_pQb!{%oH;Mm35N9_;K1BWht2eb&6arhGa*S!n1u-J#4FU9p#I zzzK=VAN=;rU>oW4cuznT7E5|()AA4Dp3dUe7A#^9LNH;!D>YsQO3zPz2 z0|oNDao7GD!3e_v0f2jU!`$IZc0foY`2D%DG!y#!Ixzq~Rmfdp8T0O=1`k!`4UCiH8<2Elau3)--?t4yuWPL2mU(i z&lr7Sqt#!FWO0qio`>}i%D$c-t}z3Y^|2@@EieDkn6&7 zqoDM;DWtXo;5RWogLDS$)81gvZ=WUH)OU{2yxt;8sgt`-mmPyL=H-i7WX6!I+=7@d zjKH?A$UX_LdoorLw!O>$gbbh=6|Rc0ol;)d$?H8)&xcPuy(43v$5fVnVWy(f!g-0V zT=K;~E>K8U&^+uaGSC3nsfFH4Bn8Fr-3#iuq@^fNth?C(Uafva*e}5ZrXSN5q`3mU z7VHM71Kum6; zv;VB^BX?Z{2e|)Hr7IeHa7fce227bUiYwo_Ys_R}NS=Km%=Do7GK9JX?96tCpzOZF zi_@rjJNDF!sONV1lhK|rkt1-9ajYz(Y#u<4os=q0ZYqEz=(isW^5E}3F{%CgYo=cI z>T5zhCK9I5sT_2k@>#LAi|(g#OEATiZH*3AX_<5KQ-;`EZ-K3 z;ducd<%Q88??+wx&2~)^Hs<-!q%#AtlF?rcS+P7T8nMLNVcP$P)R~nG9AAqz63ViQ zZ*TwRNdhFskSf<9Jy{k>T*V`l+U#xLNGWc98hK~?e6pX@0yOP+m1=vZX&plhj9zO+ zpNj=!tO2OPo}+z~p-3MJe54S~-i^<1q2(N)NMri3=(fVQj9*d{SP5hHrpI=Jm) zTcwSU&Y~!bAH2rHCB@VeiL3|}EFOEaQG*eZrxGxHk7U$bDU!a`$ago@IO^jQT|ad* zcgtlhU3$Ln^?;Tqqr&$HBb+er4H>$B$8GQ1rD&5YcW%k^P+z##FxtFM;$q(FwRHo6!hC!*^}egJ zM9sWnMy}3hG!s!E;3h0wm3*}5>L_lH@OmrW(<`(3WWU3qy0X0+C_eYK_4sgQvWCUF zwG6%n_al+J?>4{xnMics+~h_rzStffUBdJuNKy;ZiEI+>{^+JkI8Esn2w1WQdI25x zGSc&G9L*Ax^A-cFV7-1p>U&8_nfO?`I8ehc>ymxa!c) z7c+f^OM|vSN4}TSM9a_T9Z#dn<)_bc$EOu2UqZYzZXo#cRsbmq)Ui460wuGjf8}(w zi=iH$LM;u|uVX$<9=X4(n7=Q!d*D-V_>pT(6j2SFkJ={xf>!vk-Xx)9t=qleUVQkg z3p&1DJ05>cucdp?f9pCPdnG4aXB(?V&ph9hb&Sj~A9~#c&U1|s3KCGt?B*y)AEJ{+ z%5C;y+Ik>2k8wAD4~7&Z`2=I1CquHyqb5?f1p+)Ly)ja)?}YS$|(_oYBPuM&Q9)XJuQ(2}y2^!Qs)j6bw-RGvv;2IR{>5TPNe_^v@of zlyx%u;D36F=gIs_oYe@?)^sWnke$bd2t{)PIHq1Hma-gUnJ~GTxJ3;vpj&FS=QqU) z0tr!Oe zO~RJ|?>4m zOt1nlVF=61;L*@9cA}39n7%+s#maW~-euzcP?LbAIQ1Tcv#BrUWKVYODxxXAEN->H zB_23z2qa6DD{C2uqx=C)3mH4CjdZrD@QJ75T}eruQiJgJ4w3(SidH7jl$iHiw8a|# zd;SJ)M{%`8SuTrRA2u>%eGp=ufrUQH&3yi94aFRi;@KPQY-e}Y&@*3qu4_6L8FaZ1 zvE_0R#kUpd){B!Y#XjgKOHI(qH4|a+p=cKwx4lyE!DsndC6s>uK37>e(Q4C-+Jtp1 zSoXt8lHB8dVQ;?I0Qf^U)^w$?cSbn<(+cSM9B=L-?73j9g$+s_RsGg6L1LTp|Il@K zv4F|*4>n8bn=zSJ?_ALOqP(hOQzpN`2h!ivglh5EE# z;Uc|?FPGfN#)Ds975fAxMgrZDv^j)w$!SmO&oDeS`j!jDN%)nIlEAcHeo|1Hf*-Z8 zA1oOc4RILuk#jS56aihxO6Aw?!caXwi+&}Ma|teGbndr#IyaMr0^=v`t5x#yuy{FJ zrPTHHz@$vWEu2rCI6nw2FE`l5iTk!|fIVlgLZ%v=&lzvWa+poxZ@O_1vq2!dOiSUY z*SP;cYqIG`w|OnCkESV5&Uc03>TXs0d3(nrm$CzfJWIK;TOvv>vGO2UVJ@1x(d z3sAq`+d`9~yvK6EjG-;s^=h{tS|t58vY2u82_r-TNEGb7dK7ctJJdw7gmY3*`KgDYAFPV$RIGyBQ^?l2`3{9c(cTbncayKBD$WJlUW=8;CSvH(@*;X7DD=-tQWB8UIN zyyg<6ba&aTtkN2joyKJ_90B^Jhq#!Bkb-Z&7CaU4L>-lU7wZFO`%AB@c-CuoO_`Wp ztKVmPFGy2@_W3HMdX%z#9^MVF`#EOcui(bJrMHe#VM)|fKB(8KKpc)};Ces7prz}N zQ1uJf%F5ARW#LIjur1`THQOx8J#WxLuO(B;ed~G89NxX?%i5DD2n5_WlJ3tPku;12 z5CTVE3u#(0iC9d!>$UX}bgpXx zoNaS2eDb}eH1`iF<@A*(x(|#T>H`qoXPJaYz_9M6Jy#r^gbxqDW&Bwzpm^DJhFz8q z{BhRZrt)E#DW@4yYSvp@O)yL&oqyLi2l!Zd6YkTq!5|RcEaX59>rEJ)mah8aEP&Yc z)3N4NnOgm})^+yS#4QaTa8jr`xwMYnl~@exuWgI9nI`c-1;=#5AvB2Pb&Ylbb78BG z*ZudM>-3j=n_)Q%!e%BH7vv%1xc-EuNQ(c%h{Um%A_2Y}bk>7|gX#EX_nKw*ZHPg0 z$n{0fiCV|=gTp=|vsrD|!GkIJK9Oc>btp1bz}e39=JV}%c54<6ZYSkuRV!md?}h;} zCAk^8hmVEXqA=dk?hCAsqHDXGWGp!EomY!3x#N-whu@t^Iyn7Gb!!NtoA94N%h~2K zWYczmL^U8x-*yr`_&v3$#@G`JZ?070KnwFhWE@Op7cyZbghyb`@qvlt|9~oLk~MA! z2X5cdRo(se$OOE7pl@F!_KoIv6T?$(dQadFU<^n$ zy{)PoA#E5+M+`Xkgi1aoK-HK8I3nyA*<1~qh#`MBs{}ny%^RNQf`Kt;gj6Er`e3ma zYJjRPv9dnorq>b2J;Jed>9b$uKOiZH1X%^jk+xVc)2V`Uo$UB0L7@=qAcf+f>;8`- zu3{QpQNiolGMmoXf4VSH&#~{uZHsh4^mID1S*Q z;j4a7ZlfA~%U-l!HZiYpRf~7j5XnRDD1&%?p;-?UiCedM^+&Cxqdj~YLrRRq3AJ@R z;x4f^((KpYDSHl+n102tY_Hm6QHI0cF2qn`A>*&rW9a&HczzazMIR38+g>g~NT`3j z{-Ni6HK{lXqOBNptAGKh~Uaa!w^s57ic9L@PP-E^j-yapA*00Bz>9 zl?(yd6mdr-YY80nMr<<&$WHI(Ai4Aq7KllY(oAWOfsSi#4d#^Bk|a-e7R!a!W#8Io zG3@)I$J^%mQGu_O-MhSO#gQEW+4uFNhhh%}WgNuxyRgxCC!fDv(|W0P_nw2P{}>>0 zJVp)S^-mIS6|7o;>(;JFKFNZwc;nb!K{4IHh>r#O<5L4vFn9IT7M;F*Lg5KV?T1q3 zpp9fVsHnX`D++ncZskmT-r?O!Au~JQ?F03^m|BdA)b;+ksN2I#Wo;zq4}inuU;X8y zn!!t(-4e*4W7b3B6Gzq?whNtS#K`0B_!2xx#R|K+F|C_sq^C;g(tvJbXgMv;YIkn5 zTtAw>S?qt!a+BXX5ssrOT>ZYKnH|RUU-X`I*fRDaPM225ilj0zK zWZ5-80({~_RyJskPi#uQwQc~AL)rApfeFM9q`*#RnszX2U)n1Gk}%Ro@|01``n&p! zi%ZR?K#hpl8PLVpm4Y)WFCZrl{kN$lTAp)Eink7ubd~vLaNmvQ{HWpDuadum9{L2E ziwv%Yp-m~x_gGh-oM^pqT{$ckg@zYs^aPv_L{dA9Vlmi}Q8ownjE2^e0D3=Ox*YN6 zx;^mm`Li(Zp>hZ2lvxq&wkkK#yY))&jethxw$N_oVJXpHDei<+n3b(2t9W1Ex7B8y zY()k|Y3@L6mA4%7jl@Zz>cB7KqJmBApGSV5W2t!doANKjucGH8?Jk*((1mFFl3c=g zXC>=&+JR6IkRiLq19t@SrBbGsviPW8E;j7b#&*j&HK_;il+33?==|hidFpBqsBqun zt0u}$xRNlY1a^EaLL*qe)>#szlinA{zBkMx^X3JL3&s(gOIR+8w6ekT7!1)D)y=Q| z_6GEWE;EWhiBo&?`JI@N^0rfP=wmAi@(Na==lN+l=hDHGw!qEtxNmo4A(tl7&<5;=y9-=s`% zdyt2lL1nqpNsJz6Fv!11s=1KM5|KX z6g*tb`v9BZO;AcE^@13HRy?6@d^Mt~Q2^5u1uiLe2Ag?1X6r^y=pjy+qMdlKlt+pZ zXG5idbGFq(@Z{+h5t{31 zl6^ivC)|#)+K3>aVf)x#vLJ)EfNS>ereM(VoR0MJHCEvYaMb-cZExq)u5xl6;P4E# zELNR)I0BKG7dnbWm^lEJ0v{$rQ#E5&AAdib%VD0cSKB!MO!9bM7&s07~_reY#g3CI`7QHwHEYI41>Xm zRpQA)eBU?;)RFkRbne4oVA1EGLdl;vw)7zMkTJp&R&y@dP(*vavsUm`A-ADC3ct8 zUtdM-*;;jl@%z@+VItEvP;>4)c+SBioz~NxdRp_HK9M%%6_xe^OBQY9--(JYW!vJC zh<=bxk^z0%ZnPjEiT}Qx7>ft6w8^j0Oj%RAi1WP~?uXOtbBZVsa@zAGvO6cDtiB}w zZ!n9eH-8i{13r7#RMiopaLW94D4b8ZDma;ptLZiP+9|@$^tZ|N?cw^K*?&~szWnIn zb#GWzD+KnfC>r<*Q;Ge0mtISK__dgtWx0#uPZG#C47@+ZR7FQ*KAp^-0~7cv1)Q=y zEWbimi%eHsOT!09|L1fw)&bA#R-+lm+S=NRTxJfe=;+&{;Kv-N{Xg?3mtqL1+p~*? zSf0PSkuR|yPh89dbu!g@s2)D|ym*zS!zi`P( zxnOpNW-Z^TgrTeVu)VEm!GGvIxT+VECJhn$U>$K}jooq^WJ#h^+omd$iV43u1bGR} zt0ap+Z#b2T8ig*=|9*4tg~YA`a&L3e^Nm*fm~oS2ga&KyfeT64Bj%Lu9)Y22u}BUt z@0btz0;)I|?=3cM(967@2JLiR~jW6@g2SCG!M9w*sXjlUjH zEJZq&^|UPiUH4z6e?Ea6tvnt+S1h(!ovJIf!Rc2`8XT5OM8$+sV_jEc{u5mr6ZAfs zV*`Ml+7G#M1KZDy76MN3)z2VSYc-?on z(IPBSp840CxAzUDY#yA(N?iAdbN;bQA2rp&#aK;J+oy1xpqb{KO$n(-Pnn4C|LYI% z#KKOhOrN`ci8UZ( zm+S`Jx~kq|i38p{8Ix__6qd#(sMV?hNC70GU=YVwQHlvDW+#Bv`~~<``MN?jHfRP1mJ-(B?!GN92qh6rmqpv!c%M2$P{Zk!iQS3n z+CpYEi6FnGue)wloKQiIDQ~&(eaHS<&H8K%e4r+SJXAz!tdxkPeeRFaS;{$KxuHmd zeZSSjbn7RC&{hd9WEKjV%bnUqM~P3o$obvl<`@>0^F0PWX>)sCtC^??=$<<{IDS~( zPF){XB!f9F&ZaM2O5CnkJr_KjO?C{JQnR6n2Ztf&tu#94-lRR;o{m0U9;;iC02R8+ zx&M>osNd~Z&t6vXYBb-rX5Sff;q9<6-u<7e*wY8Qb~tr-Vh}bBqlNYE%SIr`X0faP zMf|7(A$9&=3q`94*&5CMR)6faUFxKLiu_A~_U@$o=~dUn@yQeHv}IY1ya@M^S&oP+bte{#e8*BeXN|zCmwrMK{Hi?elw6NX?uHwS zGDYzafsB>lwT6EwAQ}(H|6U6gXdbEiUN18kL^s)v@Tz|c`)Z4~0999RN@M8TjhS42 zTAxZk0f9CKF#%et*_sL8$8ey{M49rdhE2vMqtY|{bj}Dgo(`4rCz1CJOe9UU1wX{( zR%Is-zy~KO-8SOblw#0|wn>ii*I6F{d7?tRS|go)isD6)*eV2KW9E>*I2umzNG^H% ziMp+CgVElue%^f<0h=K+QxE4XuI#mhI59~#u>oPgGQcA;@gqeqFDw{uHs1o(2-bKK%EMZtHBqjbv%(F={VKGK1@|dJjpUYwYZ%CG}6(9}p zQO49tt2;!EdV#0b2Ib}U=t4sRI^^Mjn>-j61h|0iX}{e?sR)1gX$&xw7ei@%nVx;Y zOdN79zsQD5xT8^#Qch)|L?u_-+G1)JM3`rJY)976yQx;6%}~WVzqhSkOkLn{+n{xhGb^u2)-VDwV=XW%-cM`cDn{)JcgC;vB3art z&}kz>#}hUAI+a9{37wx@Njp>~0gQyXbTK4aHbTXyasKk3CGM2Ol<0;}1eFBWSFq0V z3(Kf2_5_z2dLM0ALrb@AxdBd~3%V@2&Kr!vl?nIk4uKEln7@G_>M*y;)Ke{ivtfrTx5zv(}*zk9OZ#D!!g>ccFdA<3V#AVu{pQFC~$` z1hE$<=k+EaJ^1;PthiQxn|vC-g4mNxZIYNSZHXo6ma}tv#;{Y`Q9UF7uYsRG#Ihzs*<* zy`tLp`cc!ktKSVIinqfowYv~4G4iVCXXRIS%3A9@6++K2z^{Qhcy&BHtX#b)g3(`L zzawMdKjU%k{FibGl|)kf$%!N%Bq_#tXQ0=Ou*ReB(?0}#m>LwzsD=N zj^w~F_#zjbSs{jyWf4l=m7qZCnI)yQ&AtGCzQDf)stJL+vn&Ik4q83hYH9BO@=n6$p4hL z*)9SSjAUr&>M?^`9!8nG%ei zMS2rQ>=na);fVavR7O6a?Y_3rFmp#!Nw4Bm#oo%HVEm=faJObI25klTf_oId zXT1&+d;Q;oRd-rrauLszUtV5T@LPzanwUg6U{TJRsWM$$Uite`siyjZD?Za-Z8lAO z^tF4D$kZFE?1IS7o>4J4e6>Or6r4-zp9M9xr93IgPbu6YgR#&ssAf`4V1b4T;DZ2F?Cv0BT7$DV zI&YMAa7rz%BP9y6wFt|iiI^&G+4|9jc>dyzx-ABbo+oySH|0;{c#i~mG#`4DnJBrJ zZHy&FSMANt{nR45r!=`B+bEFz&e+n{HR+lJs}fnWPV|xfvn|n6$`s*+5ptCSsVMCA zy?aTi3^FDb3s2tE)BSWhEtrm{fJrJ}*;`?uxU*nZz1}PS`KpZ@+lThxzz&$CKc{l6 zl2Z1SrspzO>|@<5uRFWilD)G2b^e!G!K4)LAWgYdT@n!&>T8&RqJJ)trrK$z4pMZP zi?cI_ii(QN-#-Zn355|pr+0>VNb>iFPIvlxeJfkfX!XXVQ})`6hJ=m&$4*thJkSss z%*h=3;EW;2)ZM^~Mn8DjIC*q4c8A9}qoF!HcItETDkpXeJ=?=?+xT0Z!*iW%v5wjL z^t?y8^WQOiJH^a=9qus~m#t3}S~^BIH3>RjZ)Ux~WRG%n0)>1XvmI&^Pr}cpUYXiD zMui>p{k9pc?E~I8r7aitrhW9qr@tR=M!z0nzcApFq81fFh~e3%rhgh>f|oh)pD7{U z<)^t0&5A~Htq9E<&D-?*s1o76LBmglyRmEC&wTPQ9rXEJN`)w0Ouv^?PkN0R7`MkH z50H1MQ9-bi#}P!7($mmzMdEixSD>{cuD^PpeahS(-Fpk4qs_g($?C#i5CF($s#c(| z(=3Z5Rkxw&zQz6%Do8KB;l)#MQH?#3ILVepYw~d}Ut2@E5Ltc#qIn{wP$}o-I~ZL6 zLcYaV>z&ft{&h@_iRm9ZYgiK|195M9!7fVZOdEa-`dB$6o9e%FGORfMxt`&??QygQT4c z>F#Pve(e_>MIIH9uIUVKLraiaInPj|JA=^S;jtkgj~t-|{9r#*C!uR>bJ`Tu+*^^K z{d#s{{oTww)O<{Mhv z*96zPsr#lMBL(;ekq-=|#5Wv|NtUkV9sdNs)bFis_Y*!!K2$$3UXIw^%sS91pPs8z zEIK@VF>3jDT2@GYpC$%BY7iS(Jmm7~c^~q$z{YhCVT*LT43%TB5I}p+wiI`r_~@R6 zt@`C7pVUnl<65}sLLKSxxA2}MDBAvFeaD$7v32IkMJVILxkN@?X+g&C;|p1%Q_fL1 z6PRi)@dkC*V=s((zM@QwGb5?5sk135_lD)NAg5zXrTmh12iE!_-phioCAz%WAI*#T z8P0#30UpyY^t$woim+Qb)~obU{N>xAmct4u*SS}Y!}IA-c$^ox^O?V<@3`Yk1X z)be_{i*Gis)nJq>D1s#{htG5R*2#u-IW>*?=P<9MjTVkaw{BwdqW+}1F}-7mm(YRW zC$nE){^vjXiHwS_8j4IxN{Ux!V{gw=%PA-%)ct)81mO^ESx(*0(p8CNE}5D9cfMcd z6}RS3^=rpW@aKtm>27)0F>%OW9-mWi$Z2}lNDw9^UJG0 z3`ASR?XJ6ts_<}vw;JB^f`tHk6bo+aNxrBQ;gn!&cl!61hUW%1i8c|X3VNLQLiJsC znkb<&1A3Y>yL~=BW5x=^7NyDN^C=B(xu5bb5BGcSL$YpGd~zbPiSxXYNfc7TtRwgW zKBZ5}|(7#Q5IGg3Juv96?_t{B+Ekk!_}f6PORHHoqGL9)KE!HR9Z zrDP5+n4uloSO7F0H2S!>&tpA+WCejp&@C%``X%`io^bg=Imiwa3K2IdKBR~6t2GJbidmjC}z^$v`2#@)X5 z#7Sc}HrlwEiPfYH8aGK}+cqXfV>GsH+qP{R6YI@>_u1#!=R@50TI>JA)sL?jT*BNv z)qQQ|b|33*O1ve3quDijhl1{q*R?)AI~LR*$oF9QtJ+Yut5x(no4*h~k3d`%GCfXb zxH680CdqwFXhF$6Va7}A<8{C{!Gu>MS0;3*SRBVIqHc#rDF7My+4MH_=SHtb389k4 zFY4hp?mP7&9o0O}7zXA5cFK987oZDE6QwK~{J%#`X9L3l-Bs(OxV{$*;?)Xs!42k{!N}WwczOU_`M~GRh(ne~ zQs-)PUDnm-$ZTm^YWM;!W8q-S+w-r8*A z(uO-~?ym3=%uEE*RmCsBPvlHxrG`RSCwle@BF(I;8E!mmAbOBO24s&0r{jN?(T|;_ z#9oL@!rtB<6JFC>+(!+&feL#IH$-!9;jolgIj)s znG`N12_5{;Y_;;ov#8KrEmtIBfW1M!O4m!X_t+h%fFC?J9DSrcQq-ZhtehW1O+NNy zn1ICCI!_y16=#)W3Ni6w2hi5J!MeakI}n#^(^5?ZSbqi3Unn~<;*$lqpSH`gil;Bp zrkR$2KFr1@T20~8+XLU`=*@dwn`v#$_&onQ#k|r_=BsKj8GdaMNY|DPmG&%p?=C62 zKSrM}saTb2Thl!Hun-oI3d%5)Fl_m+B;16DmE7G$06;6e%fA^g;fUn&qxv5VsMD4k zFp=%;$9F(iGG)WqxQYM?Iz5HxBQCUHt(Hta;lYQ~EyZMYjR?;9Wm@xzm4&r9(eSoFlWY7uX^dB>k1VN?wta{CxqR z^z_*c$Fv|v_Gn;ywx*VOp|y06=Y|B$uv>DyNK;J3t(XA4)C99fJgw3Co~4*N;jt$! zpcW>`yX4Y^*2Q=O;;boIc8$1RQbjFWKY5*`+KTJS!I9u<1L_BJvX#U#4T_JuUh;^RY@;6{|=JiI^WhCjsw4 z4hm?fm^)w}jZlXn>?ov2yJo#KcYS=szb{W)C31ITwEKYV{46LvA`~bl6wW3K<_g?< z1zKuvsBC_UVAo%7_0^MSP*Z1!7JIXt(Ka6~*;87mXUTr)I!~CA;zOoYpJ>dGjb*<; zh)m1jC&34#JN{k7*yAbZ+efb@BFim$l+|km{+4C9DX+1NyP7BUNQe*ELx=-)J+X&p zc)KoF6AQ&#cO(<>>N6eXk{+02-=GVR*n>{oIa%|i`%~{(Dx|+ZQ|-GN6J|!9Y_6QX zl&R>?8d+qY*W;Z&r8IB1qb&aIvEcfLz9)~IDes(O@9B4Ig!*RHr?xn>+_+~oSI6FY zaa~^SVST0WpTXMx2?1z&a(w(rw>bv}6$AEh2gvS$RlmvD6x(_`X7cZJw&3>qW(Z&8 zD%Znqq_>7pDDYtM6RidM!TjaF8iLLMnOsoNZ$D9{&vkZ?vz&Kwk+qE!Rpc^4kQt+L z=HaHoq_GJt%T^)*hCcS@kF#VFsa#kvr68yKdzU-I<35!Zc^{7a1fcfijMi5}tHV`* zKpRz|p{hMTbHyH-nfp>v)o8D3;l|nGni9bP<6t;nNA-NiM3J_(%=ae}D)lRHtjq!% z%9?3y;C^ziE8f906DOjR9l#8eg*SPftSX%I_t~MD$;43AJ%mH0{FOR3j;&22TA#(- zYtymGFrz3hZ=>+%2efix&Gdd{#k8>;!nW$7XH&c?W6Z(U`r(PXt7eux{bWAV#G+=%fb5Q|$H?!o4*^}i> zkiB#uR^b0e?{3+YHVJFo)+Nmoay5Qo0hkL2FA|Htt}VDE)&X=n#Y{$gsO!GQFJR;s zY)GS%zEh5|tOYHAQ=c$Dtbf7Fh$-HppE@JIn0q{Nni5jjk@`5n=r)mJCtzj%kVTwA zOq%8Q_AbdxXKF)8F0cv18^JO3usO(pD%OxrD>GJ|J;9VR&e~}>igkadgjHDiV@4F} zBw?*YcbK3IjeN28R#4P-D}{KB$~B>$!vYI)5%vyz$h+M56pXQ~90BE0T_c$R{o=`X zg>Pd1!If~jJ- z?8y)?9zS-n^)4MQ)TXRZ&9eEUuXFTesw z^?bYBz(6&mTx+~U|Bb$xk?b;MyvN2jlG@m(;y8|27?$=(VGki5$7NIf7rTp29&*Ku z$jkjoD)jh%00$s(nX^p?m3?`-Ip5XJdyahL2}gY+?@Vuwkq5y>9!4h4EL}kZt_$wp zL4YeMGZs{+^)_N13o0SD-l}~@7=!i!z7KKc2LJo}@G+1m0rjb|m)|&hBgjoV^u-fm z9KIhHecc{p^@g{q0b81y+iJUSNT;8l@-|mGU&RBHsf5tM^V#{j?5EscMI`FI5_OAD zOLBJ(wb`Dt#>@}A(!3mqg{i_KL#Udy3&Q`;5*`i1vvPiRh8m6mVuvI+5JwrzH8x0) zn1ljrYYW1g=WC-sSnpW9U{aN<4Jrk`@<&JY2Ee3^@!y1Y0Fl|4!M^e>)YmyEyTk@` z`%=ej_cCanql*Etwupwg#E|QU;f$;+&VsqxEBU6{_@qpv?ApqA){JD3tb3SR#n8(% zcU|p2D}6V*O?H&DH2{qn^b!3NJ{JO*HHCX`UQAv_8OLT&S*zZmki@_}SxC$w^pLbO zGWkbzh138=U+6Y&a5nUHjP~*DuPMERP?RWh)tCq0bv1{2Pr&mpmFN|9r zytM(tD}Na!cSjnU(uW0A#r_x#S3g<4UMpRW4cbEc=8JjzoBb;*yTF=4-}6a}2P(|O zF7qzGGXetxKi(IAvcIhS zd5eYN5iyY+_B&{6fEQn^GeFZ8zQ0Pbb=HHg#jESPiw90msKcylmHeAsnTx3G?RA{9 zcjTt+TaK$Ra(_%O<9BcH?1O=$iZ+|NAbp_-2T;Rxug?YxMUao&(nVblKjEmUZto|M zZYl5TcV=SZDSFnO`Ugho^z5D@R3TcKYUPr}zGMXo;&s7;e)bo5g>DKC`(_2A5Yow8 zHBmmo@-9rZ%eCxHoiYh!Ph{P?C5=5jf5EiWC7wJ0%SIXt3YQp$S97XJwP3oq!*Eqc z6+N!V_rFZ@AG#qc!FO%7ezDYxMlNsw-+ zr(kFFbL=~5dU+H$rQuJSM>BLKHgU3gE7`KPFZncR^&TG3gF0)U!ceP_EB(nF2I$u7 z5}PjlGz691KRo^S^?^-aFO6eDOx+i=z9c#JH6W)K_0Ws&-V{Ka19Q>aiut0dong%E zk!+OEuETyAv}gOdP05NXg{w1FY&3+Rzg;MXyf|}R=Yk%;VYpADH<8&~9o*iDBgcG{JqVWNrLF+-KnhY;7h>RUfd$dC1z1991 zeXD|g|5}&;=PjkMpsJh-RBN>SYg!MPwqQfd{yY>wG*RY>ZgcQ^lEJ2~XR%u5op(HU z#@1-V8Ya$)-DqvKXpe*cUDbrQIJ}@<{f}Fc*dHhICa8JA)pH$Ap+late+cs;O&$>% z$mnqjFRK697t;U141e9aL0sBVm2wZeriqIC1#^n|#{~`hQ~CkZCDer1{qd5!)rRAV zlsnc9t>gyJidh(v z6#;*!%wUv$u($IFstuSIx#_n|_6o<+-DzlcRaKAusVCBnxa{$DASjGbL?x&_3c{Y- zAGZ`%%>hW`H=zlL$4=@8nBytK8BI3(Aa+&Cc+>8EmaO+!*{RwSv7+`N0abhKJO3lh zw4`Gv+bMRHhG&UAf4Mki+(-|id*ZdL$eNGG+{jhBw7{^;lo=hF zz;8MXt}T?qSlt`60}I4QR!hiBsEGMhn=-`R5gtR@HX6GVJZc)wW<*QrqpWPS+D5ey zXIjGZXJ19#^vj7hx<;lEqgdBz722X*}mRe6M>N30C& zY;5F%vjO$N=$K|$`?qTd>q|)g<(4{f$ln972I`QIQCT3y$cZBqAd!5a+4 zgyiiL%7Q(Jbriv7`>Ra)g8&f5DcB{Om$%*)U3C+9PLb~+l$}IE3V#MCH|cJL8|iT zuQb7IDWp=b?wvmO0H1YP^rM=Einkp=sRND{j7p=R^(ObrMz+*g9xMvb6liIEMGNn3 zU~BmI=45jBq4-W3*J7wXm9c^(2TO5ILvCjE$y{uy7{7=)n%c%>fcsqKic#r6_<%zP5q?f& zVK;w3em)BV;o>PxiT@j*(!wnIzxAyDU+wy0-|1;|_w7#u!O}=*!4OVNr@~iIgJZ$` zTa_Za;KWUWCn#(6giR8Q(e`~mM>g|2YA(y{C>fA3u5g9s1JEQ2|8n8_*1%9gf znytBdOHxEj%~o*N;|tQ8N`wRoJKpM64id_hrBj|6 z{j^D-u2dHTLCU3_9gpyy_A(sie5-@EsRcafdy>#VrImVWVdLD;wB)=<{zzL7Vv z-#u|Gi?b)b7U@!N=22ZE!;BT-lZl%_uLUxHFQ&bl^A%m6A-FYsg|5f33=zIz`C60l zBCd0}CyBG0RI{N&q|JkRF97xZv9?_+g%q@1tRL|D2Fvo4KBQHqJdB;qLnHXz%3zXcYt_Xnu&g9$gE<-x)F6o zA=SCdHN&udp50vb_+^=Yr~Y#`zXrQ(yB>+?Oe{_Mw05-$A%+l-kY$MLNUzP+T7_5U!SwFuf)w*tal&Z#zCH z2jr?Kd`X!r%a3T+&y|#UU_sD?)v0$OH!3U3y%mz5yQ9lphWE)XJFKuk`Iyq4*9~|? z?r*-=b}hq#Q(i8*+T3L*3||o&k;!LQY;o<$&ymX5i8Q-Pssn$IDm}5yWip8lPHn9> zAi;$S5|2he`mhBoCTX$6)p``=V|)9b&}xOfLgc^*-dhyFYWd*lvYnybMS1&CwgK-e za8n0yMuf;l?ozq6xE(+F654^>q%7G|pxO(nL5lPLXNIAzq$W_ucb|TlJ$kxWDYGfk=o1#53U+iFNmbk{#&Ilu|HGVp;-N;Ko}e zIeI5eQRrO1tI^de68unBN>{GPCk^*}|2Wfmk?QO;`CRxY{28A^865Ao=c-DWyf^O>qqQr5m%PW zTH$B-pqajt4GGDN(flCvi`@48F`!f~(|25u*)1Cc!@lz9+a`sO0kFQkn%!vRo&bM> zn=Ii~ee_twh0dGo*u_6Tj!?A5`;V`1vNx(D2(IuSZ|0x*PjZ3g z^9=O)7ZfC#=4KqrGq8t>5&Qe~EQB-A+FG*CbO#DV>Y2L0T(h6U!4CB`d0zzM zY5iOAUp~>s-SgmWtuL5-XxiSn^Ks-cYVy#WBATlH9CZ=98D6o=cmMk7k0;{GGWy*1PG9 zgz7o$akUXUb4tTgi7{| zFv|0vj#vct802RUey4l(Zm~!09#I!wQEAs)ZmGV7;op&c&DVWWl9$&qVD#|Mb-1F6 z+%tISV5%6MU~`5043xL6f^dxpYmFMaEi>m{v_m#}xr!>R^}-S(pO|9Z;k@8mw12{E z8=2USGOeM-7BhL$M;Aze1n1Li1&&9KHgd#xEE23)9k^5^jGGe8-o8XCd=L2b#Ez@A zVkFpTKx&cz?$@Zya136>U8a4aNlqS-f`@axvyNqNmxz|#Yi`}(^M<842k&izzwh?k zDh}iqp#^z;mxU0dkanV{iQPM|>8xAj^TvWPlYc^^pvGf*Qp>Aq+X@YvxZAbvDLZhp&++PZgUL+k2G-)o{&r0@WgEg4 zj8C{t*lCGTmRHxHDzIm{CM(7C)%RdK7{2L$JoX5A@pbWlMX88dOfB-5>{83 z^LsvyQP6UD#;fcxX}jUS^x^-~wLYPxq9J&s@7Z~Goa@Ub!%u{L^jNkf38*#33Uu5i z&<9oIPf>IQuhkCkC{OPAAu#CbaX?RI6D$(cdI0^faDL~r_1TxdG|omwflHEd_ak(M z_+P#jB|%(lslqYCN9FFH=Q2`~zpC_tHII7TLW1Eu7Xo*rzT4>keKW>mhZUO^C0F82!U_587DysF%Q`&@Ce zT|S(BTR4P(P^i9!Kt~d_1U~p%q}#B-@EY`2W3#_^fP*bhn$df;Ciu-75%mU+58$c< z7g$7V7ad-OhOCR&rw-Qx4QZG^aV`~AFg=y%#4J$jP7UCEZQr<(kX4tZHv@z+u2Wag07v1nzLQG}Nk)yC%kHuZ(phSo@OHiP+;O&Cnxq%j+!w@8 z@08WoM%2G2jacWUT)dP4ogPJxFGOxO+=^Mpa-^LhB>dY-NaJo=D}zLSqz8H<0XcMg2$Ya@0w;meuCshmB9a&$oPy(m*AfV*%~Fuj*ti6 z>>^xKL8n6kH(IX6*17plPA-k&f?Fj3d$!kz0R8h#B`4?cNQ;q}_uJhHk4SEU%}DR^ zn;8K}BMKr3j}zlSL$HO0B+qmFpJ)D*g7_4h`i(^uz1X|Yo$V=a`ZK{3P)FnYgi9IX z<#ivUB;vNF$*jclv9ilv2Q+7SvQt`m_W9H`Mobo`;Xa6X@Mf>W9Y56XzrXwcGK|?A z6U(733C~|)vrOv?I#!$U@GQ?99xeg79oRfYy;JJWp0BVx1A^WjvLh0h*g3I9u#v*k z@O)6C!b$dEi@6_aM;h#XaeZl?o-j|@Ds-ly<%iFqUsAuL9h>$!@AUt9@_>)Er<_RZ zxHBSbdUN98^$6rAenT-=n}K2Ep0{G!Zgj`OD5gcF2SU82HnXYh7sA&Pf(OU z2eqEWzd@%*FTr-s%1qBndrMhXvUX5aEH&W_c}DF&KEj}?!XNr89us_5V(cEJ{%h{ozF{}wxN!MeRRk~wxIW8oKn|7|qK(G*I7tZ)1rS@T+oIa$#4ZSLYEL?a-?ELYUZJk0Euww15C z)03DA*+xl_OYfeXVkX@C1rriIj4Y#dm(^hq`R|R|mEqIikIHJq+gkTBKLd`TTJ#$A z;SENwPuE^D@r;$%v1>}@v`0sl9nxMZjHyC(Ky6=I57-dkUmks6d9(yP}u6T*8 zzz44%tky%HDpy-I>%XZP61^_=y%`zjq+1UsoJHMbZg4!nQ@tTsnFZopSVdUK%!`=4 z>yTBfIgfqqesO!KXLD$gpSc4HMbOhch=M~X47G6yU{t34aVHbT{Lo`9v`#Kee7I4# z0IYg~Mv?cYS|g(?wc3QnmJB4O+M;Bl??z(JkHz=&he4}!11S*WSKOPC4`Nz%1@X=k zqgkQ0KUpEK{&`c#JBz2DVM?CI_O1s{ey0US9Q)};f^^#74s<4HHS=%wye!5yGgNq7 zPpeUVE4=0pDgeG;E07K&*CghcCbay|E#)A?nvT>b3*w8&qnDSiXY_aVw|#x;cYZ!& z+xq`Cui+8m(190hLLr&H@~ddzkn6o=Ie!W_bK_&8_C%hEj%YK|1wAiEPU#8eJoJLe z&)azq0dM&qk6`Em9I48#SV z%wTy>rsZK7DH_k*E^Pc-(0-Qj58LR)%BgW?UHe)8*nqw+n*n~q#2j&&RyA?*YXHRP z!8WbLGI+C~9xdB^nepxZAqAk@WjRFT8S!_sfPn6U)QS&*&%;T{m(q~+*F6YaMEM-z zw{L1({j?V^tG|-9)@4v1_F7%e|IM!R#;K{NOi%Z6ZKC?GbsqydeS0nd2?&F+c7#mAwv;tug^-JxxBRDT5;OQNOAcC(*_r%h}veuh`Q7n zfvA^x8Ctl@4`15kD0@)d9=;9>`Mzj;jo67ujf7WM$2yc&RBk7AhY|STZfY2CITL%d z?y^taRD(JvSsMu(S<5?DD`lBCVDBFe%WB3@2Y~+9t-(xJ^Mn6Y(|>B!qT9!uD)~1N z7wS0iOTEq5&?@z0PpR^=1V)j7Fsk@p2s!rmiqHe9S-uOwWTZ3ty5eQq`L&HTykvO+ za@fh){$uD#tRXZC-d}@%)yN@xv$gv+Nlr0|(=#!Dzw7CzaZR3_|7eR1Myozn#7+iW zcVyVCD2syqCsGzRC5WH}wnQ*x+?QGMZj8NrZ%#%SJG~uE5F0i6f4O>N1PJtigZ@D2 z_;lwjm^3Ku;DQH4v66DMt85iZ3!HFh9Q5k;m(f0MAMS% zzN!(!Z%_R3_akI|HA*J>a1Ae9{56a-hh}O3=5TsYw!1-+om{b7ELKh4{T{(b>47G^ zwgj6Dm=FZ+PW_BhhVDs%l<%1R=2rh6#FzW|CNbS``KzH9nMuU!-cCi!EA&nyP3P1K z`dg}iy6ge`l_54CzfCOINP&v^2fk>KVcWh&u)r)%2r-d5GT)+cqPAqJa-A|r_W)J@ z3uy~pf5GVU{63WM%)4$_S3Z1geB%J3)mq}9=I1AvC(qa>F#~Qdh9gA1;$um#$#OtW zxXbk4_lk==!%@zb$3Sh-_?HEmOy&) z7QoT6GLSvC^y*45z)|0*@}H-5uL9!5&hS71J=mD!l(~7^i*$Z>S>a&2>CVe0hpl~H z)9u$fhm+V4pkTNlm525nVE7PwX!7!Dy-EWVjSuJg_y-j68 z%+3}UF)gYOK5o7IP3Zpke1zga?)Kx`CkK{{u_^l^{arUVJ}(v~E7?E~{|L8a^@^VR z7#0lnGfEQglDZt(T$kmVyvuv~Z~WFNvj8k&1!U7t;S@Pza|>npTjlUVtbmfbG+$WlRUcR7s151oim z`HKuq80R=S3c~l#AdRFlRp7Gk`=gD}nhztZ!u?B?YlBfF54e^rw*}={45qu&V-Dxt zxWJh4Kn{XkMNceo%>n!>Kyikd#K%vAiQkr*gc)6m+$ctk#}`j>jo4O#iEaF~yP2knzrrxogZ! zz3~?BXmZRb6uL<)9qZ3+w>y{O;a&7&?Wcy%ZDyUtM)nV_9y}Ctrk&9S+t3{o99>Z3 zxYFf2rs-1B71CJxDq+M=Z5MZXrkIF&x*SlbDWPN5Gc|B(5}x(2*^-5-GYKit7D|Fm zq$h2@GvX^CU$5@wj6o6vwSS@0fo`TS$rjRMI9wPe2S0h;sC{X>`(};6^Q{bRsJdK1 zBC#52R^#&%h3~1W)b*3x(rFet5vCU}rrpNYh5h6zUbi*$pu7zV8K1kf4tH2R3 zQ2_81-U~Y~R+2LWWVqCw7?DA4>*C=HPXH`lg4i{_Nteohfvg2Dm}hMOusX)5`;q-J zT;ZQvi5t2^&-??or>>EA$H|Q)9u$^j0(;NedtN@2prjzj#cuSTAY25RF`iE2ag#fQ zQxVmQ9h2uzXjS0o^;ZaZ>w9z8stZ*o{qtp`g9&)4>1mEaY94EArC?tWUdWSJzr8Lm zC|GS$O#2kTwfl;z|91tJd874O7GQG%V-S7*=jAF4t#yoxIOOFuEx{zg+o+CiKY$3n z-;8K;?ajAh&}~>{)>vbw)!tlmN%;Y8?)mx!W_*+T+%MPu?`^iiB$)pkQ-+B z*cD_7|2?%qG8zOB&95q0u=t9uYUhVlbmt!{lw6FNKl8#m3{sn3ee%R@re;tW-^2m z8~0Lb!Z|Ao1lk$YBtO!4zYh_$r#dtjmMpJpvc72Nr~LcBjtw~);z`Ky)y}}FI}(*3 z*JlB}^gh{`vCn+$ac1itbWCFt`5CX*n%#M01u4d})X+bKiA0$Qzhi0o_Qs!>qMHw+ zWcq1)BA7zy9X@T9t957J5F@-a-@Cwch!UAz9*0BxI$p^NPBtFB%m!x9b-vcs=58@q zS1!;UpRNf?l5ISbm+dUN)kGvr4)PyNoy+V0n&J3P4T`W!+N1y0*4~$(7fZ+q&0CRf zDY*j>$Ox;*Z&5Hm3b&U&y4I0Q9!R>=uyiIQZcf$mx(%P50n-BWb$+V3?9DlxaKaqD zW^8L2ex!E$(g0Sh$H%tqV|T7-_8e)hvN?}b8-ca}uX^Q-Qc$bP%FLJL9o*c3cp9zb zB(gFZr~>uG2G2_!f=qM2S51U#OmC!GWV9pFA2uc;UJ#UmsnCnu`17pVX4U|~>7m+s zU=S_0e96vFfeL$~WCevHZ0vfUSzcCX6JUHGRKzkb&l399G$5mWt~@}ITQomEfBNCU zC9yt@7Xc&WebY?*zn2v(!c=jO>Yr3MezSkfq+s|-ba?_lA(g1?uc{rawEvsHQ^CiO z>YXG^)>J_Q|9bl*9uAGB$|nJaa@(KX)T=Tz|rlA z59a5Vy;pbAPx`Q?twSTSxtaic*SSB0IzITHcXTCG+)d|@Y?R&EJAVl7M0T*1tlomc zI2B!oeG=;9+h8=GOzEuUR9az(L%%DCnlLOE!2h*Sn96J=h$5i`yFW}fDoFW}Cd!ojDXn4sMHn*fNx zSMF!Cd`tc24E2R&%l-V>`)5L;b!`g@x$D^OlTHWGT15_!P+wK-d48(vX=a&k5HrWW zskq-n0AYK7yW~H&jv^)kPU_0leUXu<0`~rIzKi?&Q97zx0s_O!Q5(W`7pgSZqqJH9 zt?r^**l&NAUw;Vq^ueI_NsVtV>|)Oin7vtq$9ny{SHB`2Bil28{rmTzh~<^BB;UVd z`M2zG(}O1EKwJ8FgeJgZnZfWPGr)8IaYWfQ&N{b+;r>eOnZAs1^omhiriXv*$ikUn zo}eAVGV@b&daoVQh*KNerR{_5$zY1b*;W~$or`+M_aFzGscK31>nw*WRlu1E(12Il zemxuUt#f+ypTqs#Tx|sH%kX@fV$hI^zP)AlFe5*8%qp>07VOk){Ba+YE75p0+#c*A znq;~XW1N>K6=;*{x1e0Jq?rG_s|doQOW+y$eGKjkMcG2BZtM$c{LQ+l?hPhoQ$DB`a_bjtILh<9hbEu*nGpn zdhqx&aZ{7{O$3m|_Eoj6mhNv1sRD_wu?*NG*p>My@NSv;O`ueBT|NBE1#6Q#E z+K!jJ@2V>gw?7P9wx4|9;Rd_f%1jJy9W{G(Uop@vpQVC)4TILIK-{P2FaiJGM*&EQ zk@3UZ#|BR?TiW@N7cZ)bSX$io1tzJjqHq>!lX1h%^bEWakDr84bVh5qF;3!~+6sS4 zj(c!ldT_zbIdf#dG-dc-mcVbM0c*dm&l{W#&xhuykp~-W0B`O}Dn7KhGkuQ2z^tDN zU;c1NUfGkMrRfR4XG2pris9LKt=r>lM`s)9=}e2rde+L@kC$n5)Y-=LlV>9@|B#;EUTuU<_3N$B2yk*~vcsr?$eocDQ#Y12C! zs$c{HwtNOFCba(vD1Ic)lx#CU+$5S!b^*p~R>y~ji|&E^4AH|v$`$RsuVBHoR~s(r z_V3s_(#Ey$N!2EOVxtDppdK-W#PBK1-b_gEmrd-CE!QP3Hne{=>~qY_ zU1&*Iw_Y_&9|oKRCCmI0@5i^i5v9*>)3-iD*O zPN|Onwq^U}Zv7gkIgZAG)M~aBF4YbE<^p>8+Qzq>oNUIX-4irJ=_5}#t|`-&&=Je5 z*$m4C^i6tWiaa%S8HrlG*uzH#e0C7!Q4E4)CQE6pM{mqz6tz#46K>(#6Z2y(c= ze2AS8wI8k}fh6c()q!prH^NJ-^D{P`2IJT(_so!yYb zbH0CtO59}X(>0CsVu>f9qucAS+}~Y)>^ZNPw^waxm+IgX(Pc?idgG}^V!LQmiwu-2 zYC@JycnkhpP&B>M6v_7Q1SyL0WqH^BU~u{r_iajB{Rfsi@R`DX7eDUii}P%DhyxN+ za>Ermuz%x{7_iOyl=@MCf0=Uqww-$Liof%1T%n(nUZMjqtyPUW#oUS2;3#u$GEH$- zocLfh1jHSCLXMS|f>p{%r%|l*s&>%xsx^u^k^keRS$0#)uwPK7Ke8@F z)#$0DvRLkoYJ1!U+@SwNZ8D3wFecmqxUnoE?Z1xCPJUvC7<<7y+ui*MMPHsKOnzry zX+7=YD`~Py!*4j-cn(@`Utf%L-PPuf4Li*s}{$cn(D!2#Q{zWn8j1eD-w2PV4s` z%a*5`f~CdDwuIJtXyaqFmck^7*r2P>FLiqALwCG6l2+x-p86VO)e{TCMo1bkuq1BX zCyQRts_4OtZeuBwB`;Y?s+4r&*T{{^cScMCWmLA#WSKI9NvGrfss=klBR70SrMT-o z5qt6a-TLf4pucslQ73awX&FV!I+%~lJ>yaO#e2JZb98a!=yZ?B6d(}Lf?uXz>;1?t z>pCk8F8P8h^c!^Bk7R;LH{~JnJPwHFTJJh#NbwbuT2ztxF{`?(!Us`@z>U|##n>rC zwecp6F^3Ux)wqYD(?X?cDq$B<4cJk!z${gUM&IEvzi$`6^DAqu*-#cgXmg9;+MHLZgv}=u8k@s#vkh4;oviRsiX6Va30f%aEHDbqJ<2hzd{%lvf3+n% zbfYm@`0?wzJrVETzzvx{S$g%* zFI2?d9e(!fC0i|DP8xFXN?B&egw1N_lJC(XD06;I9GvlpgT3B%4T1>Bi^uYea`3ct zWXf_^?%x?BBoldyZ)1=j#1f7(_hF)HyNnKAC*D-uE^feq)mrw=eAi-J9Pe!7M=bJU z&N!IMa$>l5y?kY-S*?}`D;|p9m7D~Y_lO0=!RzJNlSB>0Ooc{N<8v$HQ0)^s=Z^_l zRRykzH%$97TB%C$u@SSl>Rx zxaVcm77et=Ll-}IBPNs|L{jFCj z(&Hl}anKAM?|AZ(KuUyjUBX)9*{j*<2Gl(gnf(FdqMuosif7ht|ACU$=Wwx(f3)?W zm)ry8Y`vrAcCyUcCBY*sFcl}weJxqNfq8aY`}hjHu>)sfGnJ!hNZPzzoeI0%&;L*> zxsYnZZ;xe8^<*D~VbN&vKYD9G?p9Gz5l1Hzka~T6Eh<1*pwz~c;?L(zKbmeR!A}nX z%_VVTxZ=S{hpI@_{G$}-my+8QZa1=3C6_ph7oAiG|BQAVZOiypM!oTe^Yd%>u&vA$t0o3tcEtgjE>PQ_@L zJ^qZU3&96-cG<>@fA;2U=5nMCcl?5hIK7IMo~XhIjIHf0g=b)8G9U(R29R?;St!J= zhb^S1`-TmmhMgAc=skD&gx~;9Wk!32^1XEQejU}Vir!9_UG!+b3*3)?9XJz^Yj1}r zos7e0<;&N?77YIIAbJo$?~PwXfa2I0{M(-MrqOLdZ&k%062z z8ucs&S(i9puHBJlL{k1Fb=a1Jik4YcP(maB{=X(pGp`UMkBC8KRS753lE5J88=`wX6=9aECQ)X1J38DLDZqef)l$5sg}unj zki8CC2gF%geXl@iQ-Nr`%C1IQ+m{~ZcPZaX_C)g0g!^~rWC>Sl;J4WF5rMd7oAPwP z0yvI&&4U3IJ+d-m^*+=g3OHg#^ZS1D@Db>7%LJ@fo`H-K3pftgz5`YkQ%)++&;djSA!vSHJ|iD&*kez_B0vMCT{JJl8A{d1+vAd%p85-5a~N zcioeMVxF#YaOj%}WWT7U&2q|v$#j0d{+vARDwoq&{3kEAG2?+}^TOYf3A4iO!FlWd zo$x%PkhLCbJ0d9^u$HdH1HMY!LijJUAx|9I_+HlPSwY|cl$BcVmde#5D zh*Pi5i5-4%8=^oXdVvQ|NiDToK6Sa?Ki|B4iB_mkG7c_+Y`^ zcSt+azWx2pG3D?CCA07@;sqApWr0;j-Tm6%LqU`NRJ4e5C^U(VZZ6{TCt^sU;?h}e z6D^|OvIj6;>Fi86-Lmp-{&Z&Ck-N{+8F zE=u8#FKUDT-YrH}?hWWxi+u2VXR_eZuZJdgLpdq$@OL8LH+BU+u(#HfJ{?5*;0&9l zvm?%i_W>K5wHdDf?zl3hJ=}dcD>F3>^?}w>{h5&WI<J-4?rF z5E8~~%Q7b5l}I`vWn;x-$;^^}Catwtr5x-n+Q?#JVk$Xy_G9dfR%KvwY1SDYqo_cz z&dH`cR+hJOS~tB;R;V^}j{eNjn$CM_(8TTXam?X9NBbk(e7B-l17WROV9dAjATDZ~ z`97J8r;+ygN-c?k-EVn64_DqxME!4}nIx}i9-eFYf$%YDm-S3I=SY+1uejQ9}!MSz$iV{<&g?E;a)L$(l#=^LLnxV22b@<&{6W6fl+Pf2;=6 zPF*36R=~!`fIgr|+||-r`D}RXb(Z->F4@~uM(zm98sb=a!N2=w7fsYwJj!QJTA8#Lb!@gKn>1j$3!``7-NX9<*OAGG)X|%2fJjIZ#Ked~+Pl*%sFY(4Lr4XH zhNx^##7n-dZmMS&`z^RKK+}>oQ*bKzRgm1s(lY@;%u=lvGhKcIry1JsOO2zTy(6(< zHeHFi_fK!W%QE-@d+`h6C!+Fk3XUB}myv-?mF=b^qya>1zLD}`{y{{fUS8qP7I>xq zcvR3AYHgr;iY<9Kw=&AVX|ZTSZv&-bTV2Ngyw!!803>)N{7ALa6t!lb%g+0zID;|) z{`DO1T8JIuCwHD){MZSuP8&zIyn=F>LR?XKAg<}_kZ`7Z<9{0yKn05@h{uP!w$ZU|+qP{RJGwdNyXSuIxj*-hwVpZW zv#V;>sIkU~?a2oumh!c=;FK_|h2&PJ`vMs6FSin`Iol#FJMxa?AHN-TG1dc7)_`JL{_BlddE%Dw)xmUFxClbQmnK+Q#3=PKa z?ra#hhLX9a3K4;Jv!m}TauS7KLG*tGc1$9jKgJ!V)O$wpb+I~sxMoeW8uo94PLr|s zzP5!=jiB2>Gbns;pRrSETbjI z(iE?7(WGy@8hHkcfHIR~U$3MPRVpmIcL5ZG+5Jfni?G8}ti21?3(n`!_@E*AZ=CYk ziHL?JL*-&aQEOCi!&6Iy*C=szTKVYnF;)u8E#0VV?d{J(OO3{8kRGJBP!Z$eMK=F$ zEn;i@Z+^~x!0Fz(?QTuFmvPr22VpC3v4=<7{G(b|K=8XeAQJrzrnzbEU~F@ttzN8olMcf_x}3;Gcm#+GK~HyG(8!W{vXZ@EP&04yQCBjh`jbP)^YQKUEK1GIEqrOE znFWgI)JV!~{q1d_ItTNcYybG%8&T{#S{l3TZ`ajQmriDeF64Dvc%Qn&(pZpQS*aWX zlv9k6TP0jeWRGTN2GK)moUpGN(BX^x-e%V_u|&Y#a<+J>Z#cAfk>QCu54XG8g38P$ zv&vHG0XE5eFU1k08$Bq>9JjVs$ugss;(g=d*ybW)wvnb;uxOT-BBm#+r4T!Re_$@9p8@H=`^r3`e`*^ zAN|~RZ^32EN%byJ98<3UCzVyDNk%#Pcs&r=UnSaJcs`GjqjJhlC%lmO7wnn8%^#zO zw%TLjN9%(?HJ#Zpp9n4|)rXB@G$E6}mmpo&ef5HjnnjXi=s_2gio2XLnWY-N0C+y? z0z&2un4&9;yAw_!Bm9OsRl~bQYjVA25^(N!oL_~|WkB|{M~|03Y%)lw$_S;P zVrgERmoI(&+-mdStQ-IQMs*A)H7!j7-}}XY8`*ZNBMmSU>4qqAUp6)6@&shW(RC$v zZnX>)yYvUM9r5pBy&037j`Ym=$}%eu1uJWF0a8~o%3Tt@>@O&XD5TrU`pe-{Kx4|> z#X~*|JF9?Gb65il^AybUXafuTWW6J??hsaCMKr|*Y~^JsB6W1tm7fz-6lIo=tJ8CF zG#yKbyIsVN&?~1BdAgWw=i??zWc9k1uMTp~Czsd5&gR4EK|jk%QvyKqysw}d3a`|! z1K#@}sm{#+IY0l-nYB0;lniYJo2CxJoG-)OrXyRlx+9ye`&|@^%L6YW%$7d%AeGrN z!#gfLJ$>()v;A_xmfE*K$;=`N6(e3$##bD`J-0l=;wufroEq(%%Vgmoz{2@28-}QN zv;=o-U{<1{+E+F8TYv@0wj>PF@7$m~ws6_*O0l`4|a7e=?$zUDE|_j6B&YrWoS2#bd6%NZNYzMSvCq~In9?>Akwg;4-vu1wYL zFPO-!-tZ-dvHSV*qYonV!{eDR{%ZMnT>zkSAIy2;v^@$bsTGz^Y z4H^2S;bW5fJModET<(yb9k`Y^aA}8Y>eg(|)^-zy&yrRqt^UoXr-1CTI@PGSQ8g3# zc3Lr1liwVLwU%>X!aCx$zK(ZtorgG!#<7d%yu6<*dlNBXF5*+6ieqW=9e!G6sx)DnMKSc4JBj>C9)C@QgXoZ6ScF%b0=WU?v5zAM_?kqcxQU+E~Nye@l0kw=fN zsp8fK2b}0z`boepue+RBXFr}lo*xslEab>P{yzlLmo?6suCDCwA>$tA`E7*s7_klz zsdpLStNQQbpopB^-Q64K2RjF@D87yyysgfJCA=+}b{Y8jr6H`i6+5HZK;|n&YE#$E z(tKA2Sda(pjz5%Hm_6hRl)Kt(+^6S@o-zFRfMm-niDq`I`982atxX3@)9rGfVOryG z&8NVm_Uy>w3rfxz0?|%@NCh=ot~HcnYqTG>#mSM}Tw#iylp0-hCxTrS7T}SDWuPmA zC&9z+!$c(_;WG0HM0||%_6;z8ppt%SNne<9D^wu4M*}oRg1EkA_6;pGGqUhwji31g zidIEqbF{Q&UGZeWLmlm%fZCUV!GOrTf{x~PY2NM>?HO|Fgkhb2`(63C+cNo9)-rdV zzT4@Q34AairA1mahT|8Erila=!Dddg1?M;ahMhnooqXVeVsEVnjXj7$`%FA2h=hXr z^C-hU%vo1TDAh-a^Cm0zMds!3oMSZi#2mOQ!y;A%BHn?SJM?rV`7Na~38`=|5DTX# z4Ot+XX1S*%=fobgAbk@h7*lbVM$hmAM=oZTt=;QOp$$6@>=<#Q9{m2OaMlS{CZ$c3 zU%EHRtSVR2X2$^fLv*)|3E&F+;2noI-I-&`K&z~~Wber$D^n1h+|Y5O0eG4E5-=Xj z**kH=dUGZ6-ZAfN-zVAxs`)l;)APleNMriam_U7lfr#TWtrp4`xkPoyqyoKbmZ*mr z2PX0o%_&vc6RM16x5GAP>-wBiw=~RG66*HO_bnJR5mI6SlsCFD=wN*!9|^E~JwhQO zUW;(7xyZm9HB#O;-))WUBin5|_*a`7S)OsU{~?#gWL5eVL0{l^bzU0W6$LD~&^>r~ z>-4spgLrs19Y*o2w&(o7W6M7qF9{?`Q+D$KhU#Oj!L;W|Xy;={_5s*$2O7)}lD(4-4zV!-nXk zM0%p6I8REw2oo_cBV@#`*<`9lL~Y38Jzeg@g~$z;&kM-(!O2scZf0cH`M!g!29`I3 zSHmA~Z6ZxoFA2Dqlhk&LV?nQ!Sw3v+V{z)NELU2FJvnmsmLU$#@y;IR9oBuM`9q#|t9 z%SUVQ5ao}EigY>NW%*Q1o(mXVMKFAja2x_b^zUQD#^R5$cX)d3AGW}xC6Ivwi}Or) z7cN6cXKL$kM;dEEL=d1w{=nb-ix*)zsF0~C?k@nX2AGMRY8_N5BPTJIgV+(HE=xU7 zO$sm`@MnPFlxVaU6E%p{tt$3x19_S7$dLOcJ|VA zj8hLWCf#z3PrLw6?#G?LkF!)V*T{C-tCjs;zayVZA5*XR+fBG}8IbY7UM1k1sU-zZ z#Sada_$wZOtFQ2@I?Zz_zoHC`{-9x-^Oqiky1w2wvaqVB_0p2A(ITQDI)AU*070ph z!-HFB;9GQ$XkU^1iW40vm!TIgalb&IrLGy}tyFKD1{smV_b0MQnR2s*JG~P5kPQ6q zA^>%stmWlk1!O_O*NIkG<`?^m(9wX4JIgcOy*X-4cts_(e=%!1Ci2a5_2FR zvphPEqURjb=+Uv@tD+s5FQECpwyG8Lx5Y)Yhw7~HzeW)|Z#ji>`0p7vJ~Q)S>0JAg zmxT--)G0~Jn$F$5%$n2M%gaH$O+T;Cv35G12Y)aMmPcRs=l;Kp?9Mq8S`;PNFuHH+ z=&(E!o^Yee>KAHmtyn*NISMR6Th)0|Et`n?9vPiAXnQ&7^tMEP+Kb*iKljkzP#cPd zd4&n~{m2wArSk3SI(w1MQxmHUkhXPaX=^@TBYQH)JJv5(wBfUF#;i#HhgS9mv*^Q} z3K0iAU2wh|ty>xCH?-dPFc9Zt3?iP&%3$Z2P)<;6!9Vh|Ro?NN^n}ju%S!m?d`O+6 zwkyWMFP&iCmlb$NKKHP#)O7}W1#+Wd7ZhCLNSsgtNFTRBd6!>+!2FjVgZ?o?NZLxlTVPmp;`?|#Samv}ssKm0B|35MeA7BL*N$yj8ynDMA= z2sg4GVe0v~Q14Wt&RCU6n%un>a-OxICEYwaR7Kk6N7*w#{J(G5&RO8 z^kR1+@``+!TOfR;^_M{Rfk)IAt}rqo>c^5r{{(~j`QeNMYcl26e@WovZOcCLLUqwd!3i10o*|Z!QOM&gR7N_xT<3+F1OWGL7U3nVGo9F}^H~d5$-uN&742 zAh2N=(b<|pul#JY#BU}c1Yl${-`cR|Us!f9`28o9@{MsJHg;3BCP@^J?Ch{6dp8@F zSWvyX14}co#4yE0a?;}H-*j?q8@?u;><{2)^=H#~y-oAFCr~*jKNb_LfDRj**>;D^ z$N}ZR1kZTB58OR-@up>Q!!DWn-d@h(+4X^uP1ak{l}2~1UAxzY58HU^k~yrm!4=hS z-*u)HUDJrdQZ%Qso)CI?2@Tn3-q|*&*>8$R2pnbtZ>RnB1rkkT84w(Om@?+vmW=Gw z1B?36)W$fD^FsTcDV%Vlmpc{-Dj&YC&r@@CxJb!AY5)K%0<}%Sb`7=#Y8XW}gFf<- zheUTwy0pOEWmL9f(#K5r?D#J%)@+qaxPSB^ysr&#-|?O!6W~rap)S3VdA_F6_*C=l zN#Cmchqk({3Wovk^z`ITubwwxO$<-8*Y~3^d&cL6^tCoNJ{Uk;kwI^cIg|q6J!8U*B)m{G$XUu{eh;HWvr=!)D`rwdb6nJqglm?b`Iz*gs&64+ZfE zlzpY=e83%Z^n~sR{JRrEu&Z-GfESFVMXb8xCbK2xus9!3CMD4g+$L9SwIpv}69y5S zBshQt;z>5*if_w>2ysc=j3VpjNSD}Gx+e^^bj4OUpvXg385LlOo60)v9ktW5v<*PJ zdBW{SR4$m9soWc=4IxMICer%x3o70ksw#>6p?1)xFv9L9p=!uD2z({2aD&xVe@8Gl ziSz^&=NV)480U5%^QEE^Uv3VsHjwv|dCx2tzmTI5{oy3g1}5xpWZLRqX*Jk`aT#AA zA|QT+WEXu}(;x>lGo}T{O!!aX+=ZaS5fMi0aiCi1KxHrF2!AayG|i)z!-F#wE9vtv zj=_&i9v^_Qv|%5q|30d~Gax{9aUoj*foU?JUhuD%mw#~c_r5hO&eU%{AWGG~n%5Tv z(MEYqP|_fC(whI(E7y6aQurAreHXkB3wGR{ae#aAo3*KYsoIx=#1d z=w>@{Wd$8~?h~)(jM2jKIN9Q2apE1UcavB(qGULZEw_L9md4~)&eX$S&ZFYYrPXg2Sn~{- z>NjdXl2fZbnyR@C*(mG}eO5$yVa{pvm+hy6t+g1PulD+xbR9qwS8GrT`)7j`GRAqHLw9nBQ+O>7RZWC^oO zVO<@bT@ghf>e(juKoBPns=cNNZm4B6hBB6?M6-FXPOo~JqxsQ6`VA>nbfD|ER{gRk|E@h&>X(zpEWx5eeBH}pm| zC>xK+f$Ni*CY8n&DFGo*2QMt*B`W(cJC%N3C(Lv~?9Ht-3AI86(oAI~mDZXns_o?y zFC41Bv7>{9@9<|!i@3&mwb!0}T&DlnZ{T1V?H6HYl?`cKO!z?NhdW-3JRxrAzz09* zAypLV6r25wJkHk+p)V_+yr~1ju)uKhiK-|bHBq)1qLN`u7NRZzrmey+3SPUK;j*x} zk_^ycXXYnIzv(B5swu@ynJ-8GDtEHc&-9+@y#WWf#J)zZ(d%2o==XiYCK6}X?S4_e z?pcRCpKf?Yez0n@TwV-TRCb>i0+ECK470!Ue=#*?>q(eEQE^-#iw0*tSF(OcdhE(= zgJ;@t`-H2p#*WHXn>$zggfg;@Vk@C_!ORt%!Qm(SL2iT>)#=iAsLS^ZujDg~4dnB_ z=}rcSg^S=t_H7HlF27Rs)Ogedm&Y1w2vuM8)nbCKObA<^cVX;wxy27LRj`UMjW7a= ztvF;94IS&n>E^z39d(8M{)|p}u3H0B8pRw*+|v5gOa{l~(lg9xYmL!$L6v*a`2?X$ z^OFs`SB1adq+uG&s|lkAB&=@yalc@6QXXO&`dZ#uhd~0PPPva9o1jo(GjApVP5prS zKLXX;R&W@NXLE&8w-xs-;%_+eQYOEDM|O&NEe3_8NcI-~&~?A#nwS(EA+-6THpnC8 z?;DQ6FCHD0Xs}&Vf>$_7syYYOcJgIb5Ibe=9Y6 zd3Eq9Ou`WXqXtyD!HOa+;Dg%=~ zBhce|Qze{*WzVyes%%pf{NNe8SI{D?QrnWZ|9!xMxTyQS<77yck2cmNDwNEKhN)m> zVZewe%*8J#5?1U}l7rb;USJhnVhC`oqX%08oA4}`HRBF=d2qtI)RO61P;BJ_;P<9j zuSI!-cr84f$6M6MpFwzX=`ogUBcd;D!xhkw|xG(d|}ccfBXA2 zOIKQHA>J>ndVcN6W8?U_qUWGhyi%ki6LSIYxu%W(1~J>5Z5Pc7I#ht`$0rQ~OjZ^7 z331w&){XmoeM>3nc1I0}P;v%pW_%Wu3T1o)0a#U=0vjF$el$)DH*w*!1XP-Ny3qox zuY=FH8|!CVWqcG$l<0TC zP0>falSIM3bFlG5P4FF8Q0g(~U%$4uThk=j5mdV}`&Cs<(dJ?@l1eVNX`6 zR{~WR{naXmPzRn3sPkpVMKNmsXS!vAqu{n`G66KbiH~vAebL(ffPgUa(f{-38EUbh zk*H7-8Q2S_PDOtHH=NiUS0WmNyHEUL1M~nEmcsTZ%+U$_gyd+ zNd1N2+yKc8t=}xNU1R);c9sUUtJV|E=Wo8^bO-}PD2f+^^?2#`095^cTg279tvepK z(Gb>Y587@#U_oFyYO9A@y7(_w#6p%a8s=&%Z3gzr1>aMGV4cChGKSu#|=GvR}z zYf>8Dxl`K$>Pfk~%e6-k`B(#YBbzXxKfvubZ!6Nz2l7`#cOGuhkzaz-gv#@YI|CCg zXM6G1L^A%QW=5V7W8M499UHYmk^9LA&oP|VQ@@z;g(}C6@jXDCm2y8kzX|!;{z9(8 zl(PYnoPEX&MQ7(bw5skpX-4m-ZOp$2b<5+G)em-u={5zZTlHo>t_3ms&T&YA2?G3l2tkS zu({TG8iyvPXy{#QJJTKAbB*Wd#k4V$uEXlsF7<=zq*Vr0foq4hWmgnyZUa_)NKia#j4jd|isVKV_n4 zV+mhss1g4M?J6GB(sW{Sa*#61#%fQgAQCh^aI@2cUDq@!Iy#sy%MvUh`ix$$q*R4; zQb$MUugatKvgnu_DMN`5$+P_3P>g3;@;ABf`!-3QM+~7NoA^2+-8w^rkpv~Lp&Ar? zEQykr6MHPRfc0$ViZ3QKq6|S=eq8?W(w+y`=#N!%fBDnE!P|nAC}Q*%s|QhfDnW>F zCqgTPK`q<7F=w`Vb#lO2$@zp?nr?}`>Q%Xz5PYw z<#cS|zF+8g>QSvbHU#gy0K|g&56DK@fxxxtl2;ox6GVLNIGTSh>zAuPJr56$#C*3U z2=)^t&%5}HIBfxrG#B@B^L51=S>yGE)Nlv={(%bXqxE-r=pQnH+?nZs_~8W(xUY+* z-Ya-iPIjL2_>Pb3$!XkouO%k=rv>@gjvb*6X1tT0 zy7F8jDf@qJ8ag1Va{>?c&wh=53?KX1zN_gk33%9|n<_X57}Po17vOKxcUTC{11FKz zb#mb0%k9_eOa^{=&3beU1TS&eCr9`C=XrO7G6A60gE->mf(sT`qdaB{Tew0Pefg?Hzu z%84u;^KP6=!YvqHo?A*S1Hmo>doBu=2@#L>z<+FJ8Dtk>o^A1nEu6iW>)YhuUzy-m z%CXb^EZ+UJc^5KR5=9E7QT&fq2ntTgfv+D1Sc2!1n|6U0T%u$l3k!?zf|6&HY8vyA z7gY?e;`(nB6Hq9K$cxfQ;)guVq*`Mcw$K7FP|baYGYsW;pzmH=ol9vHps9kVt!Po!@_;!(UKV@P^TpnYdMa=&|-FiS+N+Q;!35aDSD7M~XtU z+beECuk3@FBJca~rn8?TU#MF*{Pq|2mI>bWeg)|oynz|iYGPZ`%x_68tNjpA|KG+` z4;#Xh4aM6L&a8WOPzX?RVtSC)+MiCXqZjXv%G2d-Ans}3jr<4+J7BbF~!<^$e9SdhmaEq z_XIo^4E}Gotw2H+(6EBw$t%K)P8C;YXX{q_+UpFDK#xBsZ@BG8yNIW?CZfh|W~nCb zFOTOQ_xn|06Yz3e7Q=iR_$JIE>l41OLed;m zQ7H?}uAc+hB49)yh<<7Uz8>C#iH&wL~pBoAF!xcJNtGO2Wh62vZ5#)8TOh`y{ zF<0+P2@oW}hOd?FTs=zaZzY#Rje(~iUeL;ttoFv^gD3EhehQOdW6HxzW%Id^hDsa5 zL^+|55+NXlLN=g*`2B`_j$ewunv@I>nSOuoKx9OaIAV?4aNMpiCbz;x3d7*wKb*ltI*HIHd5u56TFk zWSj3oE!2ofucx||kG);2nG2;ik7}UDFmvD055Li}(Q?VO<-{94cItVo)wfx^jg8He zAqk=~&p_|D03T7@0(3rtN$CGw_P^ixuM_>BU7#Zn+}!o__##T6^FZFi?3@VKh+2xp z!`?-x^G8w(BLy1#ddgpI={V!8fg+*8Xw-Z!5hv+9gB-p?^!xu__3xi{bdU(t!Pe6f zIKvGgK*`iCJOW(}`%^9m;uqZLLrENRm*yIc>_ldc1AqKwAZsS!Y;c;9M$y6w>M_q@ zbRv%2n7*S7r7gFX$H_^vC3W5(CR67i6(b#-4V{HNTJoUbvYRr$qxwx$J%tl8?C0kZf*Z!^T_o@Bw4w=P#Ik*PG2*JlAnQaSXs1MRGU>c`H6xE=DydU;eh7F9l@oEm>^D)H8`Ss!zGN}17bc*y9NFu{K94uGB9N+f}W%3 za_xhQkXT7NWmd|Ouu*F<>JlTzg-nv7p7_f=X+QX-Eq+odqdPvpl+{Nm_HVTu*tQCk z;dz)`MWGT|!m_dVrhks&Oe)Vm78u$Cg}Ab^Vkei)U2E2_)ohbiI0O~Z^|)FiN|X#o zf>gcy`tFIgllN<9N>tFLB2F`og)`!N2nbgLX(aVdF@#Z9+1>rcvY2VL^}TfJ4qMm5 zBG2-O^+nufomB^w4^1G0*vTTDV#e{}h&DMdm)#xy#}34ZSVxRHu2vAn+z7#yr%#-8 z_k9%y&~-$_flLZ(6(?r#)~l(!ruV6t$0BxiiWuC(?F|YGJe4H&W3KQ!&|r10V-y=@ zt@SljgLcC^`)Jy$)uPOdRjYPFnB2|qFOkuV`>f2L(44hrl7)?p?&_Jxk$M%p|J*gd z@XjP50*$*}fTbEzu-WC%a)wa7vsTr40xgLlNxY^x!mk~z_=a!n< zpN)rPrs&(pi9xTX9_^PkCbVp$e0~*sXZ*@K)7_+KH)DpWqG2k{)f4#vXxktiRX9M8 zzyEX|mh&c#bCJZhLBBU{ER`UNm0hyWkQ>XObZU_nOOaZo|3yyYSx07A^*VIm zjk&V+uis0~&M2E%*eM+u88ryh^9&eRTAvmdy+4pn$A^{n5S5}|kwZ!9zfCA{EK6Pv zLR#u2D&mc^(Ow%BsK(Qy^_&!Ke%DNkPoiW_jaqR#%Mu$q&u?hxIUkZgZ$9-!jug(lq0{R);<9f$u2$)Ge-e01!?e%*@r?OrKC_F4 z)W4l#yCsP>S`z~514e?H1OCX{L~;i*8jCQ}Wpo3>&>Bjtg*DocBrza~-;?%o11p%0 zFGJv0>`7T-+qiR=0F$4E1h0MoaEE-DFk-9{f%w&AWC7Q( z{4-wAe`Y>-{}2K5H_nJ?xyzHu8jWjNIM(yH-&OlB@wH;zmU}y0GM3c2ql)oPlUAGw zQZt}z^M(iWu20>*ekOvmp{?uG6m5B9Kz#HFex)NhqENW}BqeroE_rV-K%%!C)v>++xBTDOQQclO&d33R9%o}RAtO#9Iv^2A#K^X4xtbLOf-zi@DSE0;X z*VmiRS)G><-R3Ne*s}s}TME?lIdBU2e zrlYR+J1Ocw5u{%pg9nHNGf!h<;TjeCS%HURRvy980d`f84ABU`)qmewvLdk}NgQ*% zSF5_^BY9~SscN3$h(a4^nX`9BAZ4+ok)0i<>ai+~A0CK>&kTc4{ z3Hi)wqlch&AD#ZIS3{v5#a|{pzIH?%CjqX}j*(0m2V#L}49i!RS)d2oZ#f4Z8bkdS zufy4pnv!qOMPd`3xgB?i2!>@J0__IA1}BJD5_Q{qC+1@f_vnPWY3HM6SZhLyLt+0g zT(mEX2z+&Xy`^2fEX)juhjA1kYaB2iqs&H5R!K%+8buxpbb0YaYgzUf1y%QIQ=>cK)r^W~DlKkJ2PAwg8U3JuuLztkFgjORWk~t<_lqb}{%-erf3Yc)9 zY4G01(p%1B|GwJlb?)t;74fL)k~gNt8wg0Gk(x@{FC!Z#>ve3JBd%(GvfC}mdp;xy z52HmUu@a@n%9+EppeHfUG^|?_>0K^eWomk)p+rW?NA-`~{arTWc8lBSydv~=C5=v^ zKqt~U`#DGxd9Ffb-UVl<*K+{->e1b6;2z0@k zv>D3Z?K>*(*mN>=SKVWU6BE>HKi{Z-BF zN_6%fT0Ysv6=y9Fo>ArSdH{q(R7jj0^4;4(VTFbdTcGyfRm9?}Ky1piyzdn5`S_$T?jAU*HsQMHGuT}!b z;bK-9nKUa%+lbSYTJvO2O#?|2mV~g*i+H4)j% z`9}evBLMO8^12Fu=dC$@&c^$QMN`5B-aD3-mR_l6UDEVZE;BQ~1r;wiJTw`e^5ni3 zqG8-JAa6!P|I!|0_Bz$p`qMb(<{nczh`{evE`W2I+eB-knA1v;TIVUKL5-T$Q)`us z!Xci`faqF~s?e-9#aZ3<7{XRwN6_m|qJS?`nNUXFDQ{g4_Y#+@g~hC?id`1dRwI5! z6N;g5+K?zKj$AJc1r9f&6O`GfDS$?x?q5N9uK;aH`mF?RG#jP9Eo9lzhR z(G=A$(H3FMkfv+I1Q=rY2kAZpb!5V9<0D)b^6d?ogx4FJqPIJ@O5Wym$I^^6mU%z9 z{%TecR7eIJ#kldphzI~lebaN+IurpDw=BIRD#Q5ILTWI_Fa$B4eRvGuR@^RYyXm*~ z|2xUtX*&i8fA!Tub>oDeilSWT<#032;&Rc^lM4|q@VAT4X@+c`Cs}8dzlZdEfv6zS zOo4pDtGQE^|9lJZ8I=M~SdD%@&dn-2P(`=qwx+wBb^yptq^%`y%(iJSZ`!uy)vUAr zB^6Uu6~!*MIo~y_T3Eeo5+U3Du~O<8;PWs|Y9@;&eZOSpp0>GZznVTm0QOs4o&7T0 z&VNVZ7zQAEWp_@LD{Sbe##6f4uoj3yR82an)=wnq*W>KUj?P7#ghdZIQgJ;IE_t5L z>01~F{vpel3~Q}k*rpikW3}ji#y;f1gapmbI|)im(~0t5*J5-OVDX?U8y;?I5&zvm z34Qk{o`J3xo$?&0CAMDYh;MyZFzFPg&E;WnOV)S>Bnes;rOvx_CJ{vTYUjb<8VVa2 ziHlPGABqi!@HAc*CE%uL$clSM$!fHJ7?W|AjVYBAlMqanQLD{t+$Nj2rqA;>{G>Ep zLt2S=(sb?Wq2oJf1);#Jq_!W&xen&UyS1YSKBb~{C~F(!ex|Jss;pO5Qc0zERD{$~ z1+Xe~AK+l0sb$m^@k`jqe_QDgsIV=tLna+I%A6Aew-aiIDg{XF`}^B`osE+>p>p3^ z`4>)&@K?82aR`BrE}7pNf2b0i>mXu+eTX)(g7`=Q7fr&oCc-{Cw-YjQJT$BGrzJy2 z1g=yZ+=oIYMJm2+EnXi2pP%ZnxSO zs*<|WDJzLi+r$uNurAL&t{)G6J~JI1inu*j;iftoT9L25j1~0;fa{v2l}!FYtte%g z%IJH(edvgqZ#%M-HCm!xg;LH!g&Ob#LY6zfkT7(npg$VDcwp~tsA@eh61mmUePkA$ zV0ZD%huhBYrKPZ?Qt@UOw`b6YG~4R zfk*8xuWaCldy5@v#vX?xomw-m^PuY z=rCy%c#Hm-_HaPT%ge7K=(}pXw>v$SVPRo)IDW#N-^|U$dpw-PjUV4%tuLU;1kW0Ln6QqfQ9EC`syu+N;C5N95)Duq>#??`^(E zQ^;`(-v(`4ik^iw&4fs1mkB<0TJv%`l?^s@ra@un&`Uf!&_-(9aRu(ROl34vt&h{Z zE*Z~0=Puoimu=f$$z>*QI(r-zv~{xM8CCD^any3W4l1qNhhlWC?m zWfD)gh)V1A)0+VFZU<~V&x^77=_v8BQ1P*G)|gbemP=IfDv4|X$M3tiN~g!|Kk{AgV-AXl-y? zPgI^t8HcGJ!atDPksoNd8BqB1SA{_-Fy@24<UqYnkGN>Tsqye+Cg0qd@h-8!mI^}W-{t>xhp>QyHb*J0;JyV2yhKbO8nj& z@*q-G9@9q>(nhuIV`BmE$-fC9;Nn4SIdb+zHPo1cszN{jDNU^8rKTB5d(?X)evv=k8-*m16tnJ zE~$f0EeiSQd^GocAD8k=-JS~}C24#JjxhSEiSLYt#>9;RS(oxC^_WUSLRZ3lIxd zkSU_(nQbfHqO!e8@urWPDy0{L;e>=PrUk<>n-Lg{m?4KgyYKzJ!wpGZby#wIX%lv^ zP;0(hKqii?+r+Bg@@~F;p+;;_N$GUj97D1i-IXLq3c)=f$f(gSv+%lW<$hhU-g>K~ z*6qAJB&+ww%@Y?zIyYEb+btX}vuhS6yfD*$MgFO>2uD#A~nsYfAqEBGje;bNBGr{Px5$ z^NMDSe}eZ4jYFYPTMHA;BR5!Xb97yVOVq$+4c+|n^rXbfPfbvb&j5du}@|=^6NOJ zD6*rq;I@^HT|^tUJ%}fDI+P|;GnMh2e1x;2*N!=0h0q2`mD4P~M||J6ae120!9pER;B{r%NhXFwv}v!Ii*0S%l0$A{bl~ z1Nn~a$$vT)J(Bviv1X%xGqMhUqrhBC7_rEDx%|J-)WhOZ@sG*2IWq z34^zcjV12UUXibqX;|PQLVItaOJh*QND=NOnwo~Llh(bGgMpTWX zpVr4)x1#=S4kqT}{jBRD*|O0JB+f6jYhCXD0O#K~k)290pylk_Tl;hJ;qOH43qJiX zAe~oN#w(L z3tgy|phOf(RR^@n(r0SevMi3h;X~l+j~c51Q<{Z{$UL{ElBE_M7p5-WNn|0P8W*od{g0guG_J((nmF1&|je>0Pti4 z61QD$a>F2df?$LLY+(d6_3Ury*BZL_qxc~l`LC1Zc$(jMygBUgk-lp!U7HHqfXIR| z%?bR9CW0$Fc%uvaFnM6l5i*q%MXc`pp}ro@QG8-+OHpY-U)XTpx3x+r$Bc{fp%l)f ziI9dtKTX5f$1i&FZD(iw$)hm&Be<%>-l*FWOyhfv!zN+r9bOLF6gCF^E9uCe zD=S7}$6-ad2@$pWllBSv+c1O< z8lhRdsFHMCzYJEr0EHk!5#>||=}y^CSndA;)=WjmI#%9 z52SDuD#Mfx{lqupNFd$+$FB8-0kEw zwD-!SB5btW%Fd~eyhF6J*st4uqRC*nF8Tad_WcP~Gxq21Ak6s*@OW<8=)mai?MY9F zNk&dSN&54=UK?6Okp`b+!EHb!5;FfIYrz-tHz=EH=;c@+OcrR!v&|lzM z|2n({#psDcu`2TxFq5}A#mJDz+>tshWH&d7Sj0i&J|ZIO=j5T>g#*78e$MOILxh3h zqLC^=?|!r{R&;L24>O|&T zw*gE<(gfOfXL8*`SvzwrOoE9B$7M=>i^G#%Qjepw=PHRdL+M?YCwp)T5T(N{h#T1B znZQwqHnAhTGeR`wIxu(9NeCose!ITw@kJ-(RW9nDh!0*``?rs-)h+9N zKAt7hj~HQ+zeyfNFc|UFNqD?e+34xC7o(*X%Weea|2pkkZXKOn(H$c65vPO-6{(4j zQkX~D;g7>bx8?a;2<@TJ>9|@{dDb@&)?IGDV;byqJ1rk?!Vf)A0P-4(>Z+2$5pelv*IMIKRxVV17dqo z!_DShBoRozJ>ptt!=%w|ie)ncDo)UMfY|hf`@G`rE91YLI4a^<3W$3lWODL5;0R=K zLK-@|$-X+W!h3`tdc0|j{vdY;I*8;g(z``$r5yYSG%a|wN=b+)+RAX+YCCc@nWv{` zPCPu`Qj`3@3E=$dDr3nmdLtB5H>B;vfkwa~3ln6m$KJS<1-~>%Oyh*KER$YXVr9xU z4*hXLjYd-x$Ob*j(o9h4^tZSqq05~n6g6z5iCCLN*f8vaNrP$1%tHojYz7`~PYV>u zS7ZJN#RYqUrYN@^IS>s?Q_77{0g`4#`29<)2t^f37~+Hg?6nn;sZ%g&ypY5oRmZDd z@D~hbh`eqF^P8po7l7!^7Xmv1%t=dOrVdL*B`gm!G@}cSVQyo(o%9vTCK_TShDzQa z4#4M3p_$oN&e-Tj$cGuE41EA{T7UoAqk7Uie8r;APB?;GJA-q@3ZjDnubb0}c#TTu z@w=aQH9%8D_vQhwL`}kILb_yQEM(hilbCVhEvqurct{_Gpa^EmhkdRlzh@w+hZ2Ri z70c-9VG4d-8!wt73b8*P^B+dRk`7DrRUG^PIM{fy5Z@tkm)w_3v6r5Z6eHC^qBCxc z64-_d<!QJLdUvs>~wx@I+M%%4w>Z(FQw>IDY+}Ny~!CXtCKlx2deq)6M zNh26Za^oj`xSHuKznrF1Tr$T#J!>c7YQq(})~~xrFfjHTR%+r&5ijXb;!u3u zbY_;4Cj!*k=>8xC!x=li0^osuwo&rV5FuF>JaW#yj7J$k{sy?QO9$ipVQp|iP5cP` zAc6r)$a9_}c2zHRzC)Ca^>#DC_`tRIU+AV-gLRNd4m$0ryMWO(LK_U5p!~I71SyiU zTs=a>V@c0Qk878$%`PZBC=9~>xa-(xb)Zp08LPRG4EaZ3%$SO!7J88h{U8yb%c*tz zHK`_zCSn{=OPL@SA)V7;WO~8~dL(GeXastwC+Ty#@)rrr%b_MI>4LW$jc%*9rvg?V zeSa(Jh@-P1eigNp!@VN@9ch;g+rLu;=Kq{3?pTWgrDVs&A)XWc>vlJ@ynOtp(aN9T z|1kB9!I^~Hx-+qD+vdbhzE~66wr$(S#LgGnHYT=hdv4CDdv@*rU0q$({q|bx(aOll zarp+GJY}l5=%!C*#3&Q7W{?CdeS9`SY(>xt(wd|qMU^U}MGNsJ5oJ-J3Vk))^b>;D zF>B1hY(sw-AY4ju(WO*IA{XIjpecue)dkY3_drp(mf~EYvR+Q%U3k40)>BohTi>uu z5NeFOgS#_ksnu8ZYC%62W(>?^eEbo;*_;$*1kPJE)Z)N@0#8#Tzi|!SZkk% z>QI1)S#b)`1&H3=bjjun93h${IsAdK^knXGPkjY!a)gNm?iIKnXHI6DWCYXhX9xO- zlnFH}(ei8<_wN5=QZ2{mr_=5;52>X!zuV% z&N`&#Zq<#um80aM&&)Pj?2o)tZ*`Q4y#J^7`EL00eUPAF*k@oc^csCc1>7X-P7Kr- zeHFsYdkrg9=v3wAJr0Iopfj-7kq}SFOtz1BdF4eig)0n9LWeL$s@a+o0G318;qw%3 z*siyR9gb)(7DJYO!r(3BXphq*>)CGWw#MqUHzXcIsysqt;F0=^$(%|7+^FQRfkPX; z#;3wMB`+BT;r%UXfr*`NC98@?8N>Ug9&!9(+Flmdt!;R^@1oY>#CxD~h)_-96B%7^ zAIPu7JV4dH$dMJ$^Y^~&+BdEQJr}2B?>oq+U9iZSyE*A_LE|S;+>oH7DfjavfKc}_ zo9NyaJjd8GjgDkRzAhk@X{L$5V_|<*mCPD*OxSjUu&kB(JvJnOL6~ zN;p-&fvrR;V7(!!rs9@WuNTxsla?fT9tI}&{G_f`4kB8khdr$?&Ejh_;B0^7zaZu{ zsGlIrBB?iY$bl>^2`|SqG^xDbachmgxH)Y%fG>(Cq9xloRug8*Ty;6$>h%~fBkxx{ zVL-Ml?4=g0`c1zNvh{538Mi8|GwnBp!UP2=CWwCL;Hq>~F9MgXSQ@M#Y$p9R zpo^Q$LHs25N-B@QToaFbu>qOU!04nj4InuD109|Mr;7iiGk?@qA%D+R9_UGoKXB#W zXd=rtUSo7KTmYHTw>=u1)ST8hl>UaB&zPR=`v+r!k{UQ{I3m>0`B#!I)|`-29K>}0 zE!^v5zZ^V4kk7O{jgc(yz68P?)Ioz2?AX*62{A(5XrdmBEd90NRIDjAaov2{h3cgZ z0u1ACXEjk`i7D+>l$XhV>0Lb&Hv@8dHZeZ_(uT@cAc(VBLoN~M#=x78cGrksob||r zTCGI7CLG<*!5T|4LR45|}ZABLR63%PxSx`Wt;P@t`UU7aXQYHQ>) z!DM^`SE@PTN@eL})(O*B=H@9`j@!b_SCS!t(T{|z8m=sf@BKLBsF1gy?43cmVfQck`GloI13Hp*6l7t1a6j*V$kI5i_0}xY@ zEIF@R{eW#vOw<|Yq7a*8I&(^|UwGC9=*|)E6P|d>^>v|OFpLLWY>d2c?Cm#PI4Z*6Giag6sovw3hPF)t0?3#yB!usbArntIwRoCb9;U)(7VXxEW(m`ETVzB=mx4g z#Mvx!d}gAAS9q3SBJQ|3JL&zUh&6}{LQ z4iUSuXf4Cm{eytZx%nW5bU8|HBVlWGG@Ke;dL6Hgzd9zXDjqM|w#&TUL@(cax#f#H z;KOsuOb0^Jn52w!)fis1X*2Y%AU4#Pv}{ZPIe$m8UnySa4Ax4}9C3r8 z1T2Rt*oTSxK#3Pi<6u_PKGEq?WItjYJG!#Q*Tg8;4RIPZOaGfZ0<4vezmcP z8SKM>do9)$5=HMbrUxLUM1SWS6pDy5oXHi}OH%OT`xa_JkL6yO=foT;13O1gN#f>X zSdObjj4g;r1KCn>%|AUeQhtm)27^NgWp)k0@;J;b`v=e@f|OfWekyQLUzFGvFjZ`2 z(*Z)!CLr!W5`Ubd0zz`X2=pMBaI%!S(Bzs!Ik6XAyQ zJikv3^Sa7f5Vu~WE`xFIhh9n>WZUnF|g6Y%rzDL9k{uv(yDY5ESvXr z%d$Edv*IQ8_U75C+yujmW-+y4B*l>cx)3OL=8^|f^&l!ib)q)h?@tkhGU_1A+vQom zLJ4VaQNjW~axWtC~g4`3K8bY~ui|IaD}R%mREp6+NZ| zucH+|ZIVPX1_9Aj+b(XDDFyoPM{7OfwRO8WokwEusZGM5H(zk(Te;z=N@1+}JAfl{BEc2$4A$)2dxu_qBnAtMGO zo_SQ?Y>c`%=IfsHxasSP8N_N)`tDI_V}|)PM1P$WWXL9KV4&S<=FTvjr@10aM!q_l zIM2G?pXKVgvcg)`XQs2B!#)TQ3TDBe+Su!Dnmq)-rc?w9_M?_)h&PfEU3Y8R}aZ+0^at8p3X_b9^efMSD} zYvxHqlW8c(r^YGhtfC%Qq9LZOnhI_ch}OX)Kv4cfH@=h)Jb!0sJ7pJHO_ z{^3}m2NrG`#(y_s)xzK0Jc47YmZ#i5&csZv)0Z!ni)`Pe{v%5Fa;H>qe4)fs*X4sa z6o8MjEu^wWZW!7I_#j>zilGCKN{nV!RhCW!mnO0`rFt7vQya+aKd^(@bl2j zgl}Oz4$~`F+5y`?zMbIF>8KYHgFp!|k&I6IJ-E z6>ZJT*V)yWj22Py0xB(;w>xq_)4k~$bt2EX?yvS7QBtpsS^(3lGkCz z7@EQ*A+ki2-aW1Vh^p5x18y!By7_*2jn$oKXlO9k2VCyW4S}4xK2_)V#Ke<{h={nL z!0nM~=KPs<=Yvq=c)%XUNUTm7=0K4&>misf5_dLzZ{n+qB@%~^WD97iixkutpSZLE z9KcI`?w^uB{|*Qx2H0PprJ96NI|xzB76rcbSwa;I2Rfgx{1qz2T(3x#q3T!nb&j;w zcN8JK`Okx9X^-)x~ebki?1^THmG6)~O2fh&$EFM-z7vc1e7H0BYo*yaLW~-PUgumbe%f z=U`(h!XQhewe?%!`dpXFR{PGzuMUmyu=iE^Q7YFA@;ma`R5EB(-SCe{^dEX;I&Oa6 ze*xU-aIJ;q+OiCmTPgRz=BIHSej42HqK(_} zQWpDPW(* z;beEe-(30H(4dlttF^jEV&chmREWv|n~-u_H*I^(8$#x=r*`L^bu;kROaTz4#QR1cP+Gu*N zWi)_4_~?IrWEhJkV-zv8d3p>b#HnW}Kw{u0H_%P32^?v+#+xmgs%bhziiRRBo2eF! z#PNU4ODz{AqZWAp`l2G}?{}+dNeGOGKvIIf(;P%Fg#bUoNvgDuL;)Yw01!u@xogpm z$e{P-lge{k&+_G+PG-B|4l%grq8e1FmRcyfg2$ZCt@${BY7|a;TbLKm)%iXcl*f21 zSZP}?MJ0_*)+q?4AbZFQANMgL{v;am%1;?~!uGVZXVIRC;4J5+RPS0B-^uUcvZIEo z&6%8i)f3Gap9LDTg}m*``__GY(~sM#2!Ba0UZ%4-C2s~SJQwQ}a}G3tEI7`@UT#kL zY~Bwlh1N2tRYc(P;g11q1Qc^b2+DmNZ@loR_S_339fC~^{RulXW$63H(v=Hfkdll)7ELSFw) zz^q7yV({^OJy<^JzT=R09tJ~`<8hqYCx7*}R5H~AAA?NUMmODYLp}kZwO@)uJ=A>^JjR zCS!0B=~jp~{C=EF$I1Rr0>aDst9Fzgg~d~Od4N17DIrYroPzS#Fxr33AX>z|K7RkY zda4Hn)C}rQun7n_&V8YZT}cyxWroLGdq0_nzhkRRbzJ74$$?PJiKeEe(K7Acuk}N5 zmMMgiqzTS7((fv?5+2X0oK&moYSz-o7pSZHTFY5VMVqUls?p)1t>u?f#Ev!8D@>}? z;$-Bais2apwkAeOtwo5q5o}fJ!r6ZzYpc4GN9#3_ut7B(WyiT6Z6X?l5=O>Bhq`|J zDHNA25n0n$B%1ja-r6cIfguT`h%%2TfvE|cB{n423f-&>DIIzmqdQ_N0jO!8d(bZ% z(QSYAfdp-CI<9uUxD7Q_1Q#xry?u7jaFusU{tS)qn;I{Z3#S9*x+VJBsPW#* zxf%xpKv+!Y&V9J|s=tYyuf0Q$LmRP?dr`d{kW29>j=^W|CCz` zE$S*_5B)&OQ`Zq<6DASroaOh)z^Z9=Lz~R`(2p6yzI+MBC3MN-4{7rRk~wjbr|}sg z9DbV|atJ?jC#q@W)?UzEgo@(nty3x{?DHYAL!C~P)d51&G&uwRflAr2NR2sX%VLbIat+UrII1%iCp8686btaLmZ8rwr_>S5kPMNKfFM@SX=}TUVRZzfZhvjtR;=#<~WQSIMb2Gri6O!UH4ti_!K{r8Vb6 z_O>XP(i))zji7<0xXiXTb~~a6J86fQrDIb%TgB(mR=PK146jjlyejBr_EBL&+!T!C z&83g%J>n^Bu3#-3GI`BG`R0z1!vo)D+Kfs^mkIpP9)JG z`s%FA(c7G2jjp5bp$wBgK|+Lqhh^6h$($tlPG?hGxL8cn!*E4f@*hzd5h86S&vV{% z>%aY4-y@1c$Jd#!ldLO&x2ty!4t3qc=cRVCHxn`nOsw=l}{XeY;pu%oGmJ!sGrN|qai6M}nVJo}4`OoEf zM*VVGSgr?N4BLh_uCK5CT&x?vTr1y*TWAn}oPt&jrAf|_oSGBVapXwwQ-}ubp`lTQlWr6z&+REHBi4G#V2$UpgtQYEhTn_C zsIS^P3?xD*{Ujl(-s!-m4h%Rjw+`(jqmu^K6AS440KN8=s0D5jUm$5cW%x!Kd#@HC5rSooL0KU7)Y_c_h2pGn=d~ zTRI9x@90H^#E%vG=Hy!~aXijb0xIHx+2ba}m1QH>WUXW#qnuP}99v0$8n1nf-rv#B zV6J~MZlJN^llX~>UycXDf<$~R^(=1$9Mt8kgCStZtAp<J=Py#RXG6`q9(EF97UIm#cNm8 z&D_uwbityVa1LzdyHl25t>g9r;G?nSu*XqY%O$@|OYOCc@aS8c&&DEl%Eu!Cpk!|B z)e;)yCO{&TEeis;6d;YE%>1bNmCaaH1Irq^d`FWI8rP7l@bFoGW95mMcTqA_SygyF z9dm}LVR6O!T8_e#)c7)hWYMG;A9|FR-rSprHA6()(o)8zeqQe|iu%8dS5G4Tgt)}g zfQuXSq-D-oT=(f?6K+9V{&ONu?&wpuL;>E)5oYn_8(b8j8Uvdp%m4ANtr?~YSL9C? z!n`-S9S9W8(x`!QB^!J1%CeJ1Qo7_6TZX{J>q1FxB9UG&jj+eeXk*28`~vFpLLW^i zAkFtD3kDaH6l10cGYEU{gHo50M^RzWw`V6y`99tZfD1t5@Y^*Nc^fO>0wQu2nll9`XRCCa(BIuC>;eSiLFSA{X$OM zx8JIHA}=K>Q`2suLF^TKwf}`fU;8!dEB8`FjOq1o{)gMnO7kVs(TRGJ)Tm=bVYunI)-#3aLf+fWka)2iqLKQ4(5?!~_-yo^bX=^;?W!u}4`eE*L>o^^gn|ztq8Emd2>1FB11IV2ndNQJpe}0O zDY_9RUFd;rI?wq!4?#!aX-_#&4Ut}S2!le@#SjdJIUIqA`ki26@Tfnqp{;Hs{uA5hNBlj-Tyr{!{hCIl+v@VKE>WHRroG}7IJNqD+ z`%C`>gbsMJ<1ujpk?;Fj7dZ)V9$@-kyOr|pUjDs*OE=Jh38Q$X&bh!BeJgUlFwmfK z1h3U~42vyJnTU1Em~;P2-j|P#yzGsFGrm`gyN{B|oAx{X+xNa_Ujn-i8G zn)ItBz5I72>l-OLpC$S)*@ln|1}7@3rfA4bDd)7T9p{|y@>XNvoyG-^?i>@ZqDDSu zdl|E^QIpOR>R^*ptxQS5vsB%=o7|iN^?HU-zcYdH%bjK_3rE*@_xr^tAOhz z;tM4%d#0=>S0DJ)v|N;V&ug;MV+>lW#KBfhT}!qHNl}&?ykC=!F@O1qMv_P8{RDLAMc);L~0)Y$OXNtTgEodHJGBa(Dt6|H^ zMc2cqZya(eRW13jv3-MF7rKVAVzatBNtQn~kf=kx=qdLl!N`kGWn}Glep(sjg#L z0}LoE09KXz8ak$P4bs0bhh`=+JD3FwYfSI{%EiI&=dX+bBp=5R`P1SCc>Zm@OjojU zrI#2Fe1y@cGwt_0{$+SLIzGKC4Jf>s2;TOYWR)3toIJi$rMzgz;Li&RuNx2Vl_u`Z zmO1T9+__2%^+J#C&i`c=xl6W}wE0PtVEEqzbQiKe(dx#Av$S+Qe{Oc;3s;NnrXwgQ z=;dY|YdpVx0}!bH0xZO2tWsU0Mz%xegNwpNX_xLuG&GChNe4RNTK(upnqa-*F7F%A zm~aAi1IX}2$$Y!*p@N5?u)%g6-ww(%R7n$0F#Bd=PAarya)53n<@>e==#KkKa_VfR zQBc{OAXV%o=5m9Kb@6Xr+%cmj(+v%z_f3mS`yNIp=l>Di^sa~gc)k-i$rPLnuyfCv5=X2bC=>Ay94D!vJBj~q zGROZypbO(pxeAgTbeKYHqFLkn+E-$wn%5h|(wrWOBl8wNBG zb)#?JN2V4c88im=_e!t#(Ii-KfQT8fzP(p}L6{m)VXrF(MRa9nM}Ko3&Jm8~w15&y zc^Czj_sBQ=+rVV}-5xN!(ZziJ=Oz44e#rkNVbF*EFdjQQtI*QYVzoj~w$$FlskYaY zIasDZP~}0gIX^!SMI^&j)w&cXL)Ok>hF1l|1FA(+JDY#n%prONtw3>0hS}(*^ixYs%GE8DtO`j;yLeC>%6i)2>@<*P3G$aDA7P; zL;_%V#Q*_8?LA*~PwV#Mvb+db2_;r1Jb|xl6bd?vY;?D}&n+%>HQTi^bG%1rY$igZAQ5+sQ z-hk3M%uUiMj+~Hm%_J+XKfz`9Z+(uvONh*PDHO?jk z^QgAG#kZ?jp}mfO{W0x<3{e6w|?`KfYxabqa|*o1yqH?gxVGaPlssL1XQHIeLH{9IpT#aiAy(5yW7toDVs z2C?9EfdNZ5+TaOcSE5JhH=|G?#=Qlu9-uASMFUWqjfL;I z29=UXFptny^veU+39oF_t-eXtRWF_Ze|GdiWTT0$v}aec!D$?)T#t<7pNPX*%;IU* zDgVR4V@GhLf);m;KLs!1gM+L)tjespxE|=JMjl(JD|>;V1tg;++yut1iof`en6~Xn z8Lpf{4l4HkQ6FuaHWb1-6?(axwb1i0?uE!!QUdcIJnEyiD%O@w`OSt{nHjd88Md&X zINBjBm&O%ao=9tL?Xc*ZpU8%rGTZiF`2%jnSLk1NW(DEbKw zu_MgU+YU)RTw}Fo^2{>o+8=8k$~bf*4fI&tVviOTjlXz=_M19JN_dQCcz!9&kM|R3 zd{=z$6)^XX+hZaEf_Qb!&CSc!+Ve^*P9$;-s|7XB;EUDQ=T4uqmV1~ZB)z=LYZ$AY z0y(ywPia2u{Ke6oH_P5NK;*0jTm!!pAR%RIDLGh<&l1fsf2JdOhsLWDQZ{oJ49K^g zrV_}<`|QjwVTB$?RU?N4aB^~bIy*ma5Il&7zf1;};9y|^33n54E3*J(ugGAKxrKef zgsTI`s@U8A``d5)gBO6TGbd*4=(|c^ZQD9E-;8^4?&@Zz?bw!^ovfyj=6-oSN>BhM z1R}#&rOp@BwRtsfpGTjb)+EPO-{M}uxS%W}>o?>mR@5rOG9H6PJt!c_4 z=U+K>cbY`^rTE)6s*)~rUnKNIamb>Vt;1pWI@OF7zuJc2Gdw59%OBdK_sgSDmmd}5 zv7<57&7^SLs`;B!?I>#MfB?zdOphA`zo zH*UyH4Pu&5`r6&he2Sq)XwF~S#4)T>H%o~E=m{S`(~pbhZC^^JLw05F$({UI*!241 zq{(s==~~`+nU!e&rX6}{p3J>Pizu8PPuBKYrjVFu8Dr*_o+_698C8eW>x4zV@~e%y zosZCJ2-Ad|3pH?WJvn`4<}H0bWaO6e&t*@qJhoTH2rPDNuvGao*}?n|$Pq zxW{ET5(Brf*f8P3+46n$Ki@wdtKW;s?6~aBe3hpEg*$Dbbo)FO3Zk-xc@Zt|U)ff> zQgjDK}2^`|%p9j~V8)fO5n>j> z9c`X`Ti$)e$Urg}h!TuVpOwfI68)ZD|Is9tC?AXmpU;6Zs`6Fh&TNk8eMz^F6z?N; z-wHt0K8K{(!q!jQ*B2p8ltS*$Y}yfi>e-s!qLMmpwS7?5-<=_^sxd{uLJ?8;sfbRA zdrb4?eL_B;2KiO@#s$^X9*3Tv_EcimIAF9rtnT~mcYEJmv*F#HW3KKKR@G@rNkb=l zrJkKF1U-jw{wv2t`)Hf1G};hFi|ZSdmxi0m#k1&-E~6q*nCCInIh0JdR;NgSC&x4G^WT}T;)x>5tF@Qw zl?{R*G!-rC>ID0|Un$wl4aE9Zf<}Rh%d9$P922l5y@fzC7OqeC-rAe?T3zFKho<>Q zdy~)d_o<+Nf2(%6VLnONUWCBiq(#cU)F7F2kbb`Z5q?>E2pS%LL;a?8XgOOxGTN~H zl3!Vux4qV7>A%)K5B~mJeRR`vx!!&$B}weD6^J!f0`E|Te2`x;o}>UA4M}q zv*K@AuQ5Jmk4K^(jN4lcdv+ayAc3x=(SCUO>0xXRt@af(I z;VK}OiK_t3&7$#W-|4s|%1QXeVXn_Do9emE=1yv_Kq-xFx{lXFc23gDqVGs7{>ssc zSwuhK9(Dpu(OFoSb>Y!Y5l#GLhf$Y(|3ZZklX0cU{J#><)DSQ#&nN74#vPYv8!#<`C zV!`G+sQgBqHr0x3Ru|rrR0X)N{@I@lGuMz7{5ML$f8D z?BI?J0qg(1-34ReBb|~9kVHdaQMc0K%E$cJQ~R*jDGyIpUrk3QW~IJ}5Xk@N2K8LV zQSJA!l#P74TT@EJ5$=SJI0s;YNp8k)CYh@S7 zjjkoxPl(#`Bdui5slR#H_X2*49{Co(nUc@6cTiZB5hE+>3=v0F3tRuW zmnyM_;>p_Iv506Rb(}uBkyPvKdCIu6-Y>YFU2(50ilqLVrG<`sgF88CykUMl^wuS_ zFp_?iHa7DYbHcq1@$1p$;&JNvoXy1@8ws^sq{Hz;xY>C6^}1E=jugZraB_6tVb@#T zXaV~T;JP}p*i$IlVC*RP75-~;MHd75Q#uXv^cVL{K$CfW_=?P;IgfTjQ0Kc9+bJ2X z&q)&aR?6-*)VU7lhMUAP(Uk042&~Ng-aBoBe0LlHt)&3hGyx=yVjD#Cvr+Owsqf`T zs^f9iM-$MX%0h0188bFhg5#Lz%XxEmjiu|MJQuq}Cv54#AL@H8=`GBia-Ir-+=bWk zFL3K;h++GB53Ft;?~C*E{GZ1@F)NS^T8|NlD z9WN=L?m!)CNeDWAjeov_as3^-%Mowr_Lf_B`X)%R%4S_CIbcSuEm*GTPOFj_+p$CJS2x?1rOOfGzo1hutZ_}jc zYmwKbLjE4#>bhm9*w0RGS$I1$-J2Z`H!rVsSKh+!<+2BM>}*86!1{n+1p@Jg)rJoD zC3PUEhJf99@lLW03@wW<36fLUnRwE*V^><22R0tp{($8Qyj|W)JZ5wN3&>CAS?Ny^d!v|${9#jiKd;k^hhJgMRJ6qrv>_y`*BN& zJHy6T5;rQY2I$NImL2-73N%K6&rd}VyIBC%Q_hB4$PI(j`B+J$;w6!!Q`O=_3n=jm zNx4zTn4-@fLmDK|tUqW*6SH|#EMoXx)l5Rz8d@tL>+2_k1VK>Bbu$n?{QSOp_3Dd7 zrw-93u7A0v4|{A`5_LHV}YhmTeR0wuhyx&6-W@D$@~W%7eEupO%DRlIMqj4Zt# zYi+s%Xj(ldKj98`vsmlF=4*>TG;JuW?j;-GTTKo)l%&%(hfAtrk9-*_^MsUlvgv*T zlZh`MWo;Cf#+{b@L>94Fo?La@(!zQu#olYqQ?qn9bkKGi4hUpFQ@nnCrd@sA6Hc6+ zm|a<2e_&oAwtso4#it}v?=6rO|6~EbYfDdi+%FbLIhm*Ti_1Zdq6rDZCBbC0@e_G$nUJ+~K?9R@e^`hQF`(--zZ(5|} z{J)b-rh9U3Y8|ycOX;8t%6~b3FadQn9f$BY&XNet&Qug{k>}MrMxH11D;V2F+8>2= z#ymN!gWx*DmWdxPcw($KhuQ^G98ZP!>^Pdq~3l^&qM&%}#0f9mqUc*xDD6Wlr+ES>jQ*svV3Tnpi zfnL}!IAb8_6BR-4??SYwqOhs#jdR66=D`<5x;s~t_e6*W3^2Xk1w3*Fctp>6BeZVdyJVyelleF$%6P^oCYNvI&iO`#pLrs(t!C*nV7=b7f8N zgD8atTb$$u%W03(gv`w?3}k@iD3fQ(cM;eotkv%_QAPaWZNLjngAlM1u}%;iKb*gc zcXKbFs$~(=F#NLs{+WX=L7_E1_E*=Siok3 z4KjleWp>!#Q&*y1&t?k5z|V@}5}8GCO(Atf3=C2Hbl9h~=Qy=P_JuS&1MO#rDYV9wLiE&BbS73+0>$0t_iDu6Qe>@AD zk%LvZnuyK zVP&i=@=+zo`t89nf_o)G(Tc*Mb{*Y7u!_nzL!7`+ad%j4^7*VwdNmjm3;PyWD)YBn zr?#45%IR=bF;`YY&tP?t`a88n+s!-*Kggcix*jjl_GFDwIxE?rq?t@v%;yNxv5E_@ zBa?sCX-qPgq>tVIN1lLmJtDmQp_1owP4yoTE@-jq}qqthi~7E9wDox>@p^#Xzqwj-KjX2si*h+C!4F+8&tp(scS87xM7h`7MLnqT-L z!B>~SRyBk8kQ504*34__Z#O-)v=9>smc6*qc0?uWeu?jr=;X_S_2ko;z^CH=+YcVe zki`Nzu5hQpU$b`FCK`nNu`=dErbUdP-Q!uSbA0~H^X;YxhB09(CMtJEuZrk%@DP&; zOuApIr}{mkg|eV*h!%OS9?L&kE6$qS?fWCn@tqRV(x&uagMvqA9&rtqXJT$--AnP4 zsItuy8L1hut>KwR*y1!QAL=xX2=?xHif+>a%igT{7OaemKATebi!D77k}j&`vqpHp zvnD1<84J@QT)t=N5tCZ(B}f4ff^A04ql}rF?@gqh$jA7L4G;&v3i#?NR%bWvF#D64o=KV*mu-nblx_OS6)}uylU$^y56>QU4oxW zOk^TN7@G7eHxthni_!&lU{UC!8~}(LixxCPGUlbex01~_d)+(X{CZZx1Dx9BV%F?N zf%byo1xqL6PTuY|$MO3mC)jP!*M7ZwI_SSy7lR^##>E z)fd-d76x{o$XjT5S_=DTWK&x)B*Jm%J2VDW3#v{T4(gP0F8*5c5JBO~dl)&NTp7_p zvdXM_+3rf(2>dYZKa;Tr>S7;h7ez#}-?G~d2wW!;t&%}O3E)(_8A-MBCWj~-)e)2)e;>7h}Yt20=X`wLzSJR7Uk=-H>nCLdoCl^ zT3?%Su^KJoF+us#DxMW=fOLidcY#(n#+PS&Z}^YX!6pzd;7~CTlVr@SL_=EN#wm)6 zYADWsPaVZTO#Fh9wBTV=P(FCB=4kWCuC^yp96^V_jOC*vQ60z{ZsGD>m zG_aXf?KResLrpAONQs4TV1*QQ$tpfRo{g%^_qzB-ACYQ=vfWCjgc*PCDi9bDT|_@x zmmpJ1OM#fq(foUYh@C7+?>^s`ESq?!tXhSb!@^4 zyJk6r-{&)$*ICkqU#xB6bpgWcJ$C>M<7Z>=hhH8)5Upr_<5}_+@v#rNK8ARwB^UY* zS>dSiVppFB?r~Dr3=WQ7pS3USOX+3OlX2Q8mo<) zrUm*SjyBblo+nb;rl%_T+UU#?N~5gQ;QCH4xjx^Z)@^_316SLSGSv=a~2d68~^c0o)y^yD3AZcAa zzZ5vS_;Zu;pgRR;ksVMJH@5g2&qwUW+qFTv*?~UNU)aOAODIyGi=*c*C8iEs>bmXf z?M2rHb%s`(l*m2$A%QnXM@fpDEmFW%jw&Qx(+CuNigKf{cn3Q{bcRg?WuqdczD>Wt zT%_z0@Mm&9)_9}h?5$#j2hXfhtmz#3s^G|2ZZLk=(rCE#w4#_Rj09#lWxV9A#6R8Q zsIWy zgpCC6Vi1TATfkSrN!DTND)^O;eh+D<@cSyivlTRIA#XA$^4f(7mS4pF zWdhM1vKYfMR_0odH-U74a7|uK2SyCCj2~-9SOJM%+>9EXxTC*aixzaGJld9SJ{nBj zMTT*Rtv(+LCAH#He0hnoS8T!veZ$QhiEdbk6jNWzG07mUti_g*=-62^s*b*QwrTXU zf~!~fqWHC4tGEV70T1Ewh$UO?9BXORRN23rp72I}Qon-mmwy0!&RF+EoZ?=m@qS7( z(J^gX^Y-&DIU7tzAqBUZ)CkSa`n`>~U}CFNR>49|;#WhvGEPRo$vPpb5ORp&%+NBi z?~fk~4Uyxf&^_^~;K}R^#Qi|m!9I+>c>x7F~-!_yv+&wc^^>FCr-T<{wn?ESH=pA^LX^|;vcH!^3@42pq21G@cC z+Bn>{eeS0d#o!=OVx?aLowwlNIR;7@VLczv@we=;DpMvrVQ!JjrR-r;rN?P zQ<1w&8LM59l(2!A0y!G9F?9nVdS*Oq3*36C&}7WvE94Ve4JSvmAV2F(O$}D*r9H$W zhBNnHFA~u@7~C%B<8l8YSdIpe#|^P5<$7F3he)Fg{-)TsK*?EDHd{p;`(6-c3v$}fcst3IqT2es5BpFAj}N;!TUtx< zlX5UPB}Zz(j0ZVY7NUCw(4prfF`-Dxv`mi5QS?IbVkGo)e`b3RmoAEXF!g=IY2@(u!|bpX^AiQl!>Jx zaL?v2E_JFxV3!aLKKWUe`h+S!DsnwI5N=EfnK(cgu!ngywlPAINYN-~muU!7o}92Z zC}$NdfPqG3YJv3C9B03RrxG-6tjXQs4A!(|HQPLq_%THEOj-!Mw(zg zBU&NwJEy^wL0!_InuYzJ*3{|5)%eNvD7Q@C6gx!>bTI*Km`coj!I|cgl}aRLB=tDW zXF;qjI4j{vTLqR$+T=W4Dc3H#vL8OnAxyEJ@z(xn-nr(JD1+oS+e8kz0Y}$O`GI%A zZ6+)4{VG9}inGTBPxCzi4E<-(3>b2*s^EGB+Ny7Y>?FBiqC1Jj;t{$-r-Dq_`&Nof znvErs+!jvNQECWu2fE|3Z~{d>#K9 ztjVITyuD~+&o#6lLa2vb2o)%QP%(kPnqc6j$lJVju2fkx#hBfswlqe}_q_Un(!Z(k z-;i}4-$A;}YfK?02YRk4W|3ur}blj+9VMFf=FoQqpc4S9n5## z!G-^4!gxsJ^f%n)(QirSZksyt3(^jxEkT(faj>51>%Odf>YqsT5E!36 zvL-hbSB~8nR~564wF+6XC6Z-Za}n9+mL8b(<+ygt9O=JpePaIk{%o2%@9g5Iwno66 zdAuv@za7o1srg^(9=~p~BOljQ@VYkkeVd!i6D%Yd53I!|=YOq!J$K{7AGzz7zHbEP zu1M1GT(d3gA-W*U3H;A}0gKP>+$G&?{11Ov*Hx5{-9ovZLC|omDdUn@nsSRGNtbmD zj^H^f5@)XOQ(Y52(7Jt3{p)P7y3@`@$xfZjpEZbVSu>fHm16MCEq$4g_16qzh#<32 zVuh=}p19L^2v^8dt`}UZA!&DNC9{&`A~7eKp3i+^V}5z~>ynrFX8Vb|uo58L%o5Hl zzaZ4;H_z`7jB>*g;SHX&e{ju*I^z4u5a9mE^D8eB^DsPfovenpHy1eY;&~ba1fGxE z5gTG85kMOGao>t$p`WwN_2AkT2qfNHDYcd~t%S^*ki{NQyy9Nos+xlR%g-O%V`rBX z(!pW4WEbNZ?mp zetq(`Ah)zj)`)go>*$eUS@9P+;52d7N0eIRfP{KHy5BhQF!v@VH~j z{dYgO>ArdAn#}q&{lW4)5*K%*OD^)KeUMOB@jbDAJ$N?j?rF>>=T?mPfutc0732g2 z3>Ig@iUstwTbK2^B5NS^OJh+_jGO8zi6nWX-OL_7*8{WOA=)4sAu_-gH$aFaT=M?$ zckUYDrmMPtFa#@&`TeKU?(cqmU+tFlpvJ>{&UJPAUy!j-gAl|*iQUq^b@#fFki^i+ zsC{JE(RYkHW5gU_9+hOR1DVDAL5^n{#|4p9B*7;IDys*&4n5Eg>j6){{1ya`EK=;D zR(+$rdmz(jrgGF&Bog5P$Lj&DdNSia`RbYVuR{T7nAhaN z+G#_0QRfFYFB?HM)F%rCEZp?HznM#s6F~|A%DU(gk;FQ{0t>4zuz^&fE=ap~ua3Fz zfBm*F?DKO)_1KS?Mk3J@UVCr;!x4Y2h{jz+lFhw_toDnw9tEPHC)r-Pq7wcl+vp;y%3f&*h~ga;&x}!6cUmv5Mk5 z%`W{$*TtW>FP{FjNX^G03`8Ckdfa;12X&no`nJXZl5D|XKxy>JqiV{x`nw_J?ZM>V zxJT1}C)s{s1jkVCb(4W{-z;kP?n`;{TdsGD$S7^ex@2|I=J3_gkED?Qz}->ba@5mc9b)rBb#1AxPaph0 zvJ(4|``!nCX5`@~5B`mI4*YY?&6=*yeX3Ji0Lxt3Ov; ziXkJSp;Shpl+vfQz;0l(vI1iVH*a;aZe$Q-7DOHdoLNap`DXrlYp+S+{3Bgg|5mtb zT!bHrEJ2{}T>B?R9%eK8F$Tpy8Lcp8rl@L;Zi|R~IQ2vKaPmir5B5uq&yt9;31Qo< z>(QiSUDoBeAadlE`<>f=rhfhBl12TI)rVPOXdR(kPtN_qtk|&d@`|#4oYC6EvII9U zNIXv@^orE@reSpR@*jI~q!gc3vTLixye9H*e&q|1{Xcb|Klw{x`<1Sv!dz@eSQuqE&!+cYCF=jK1#6f$kYqj!f z_CgY2>GtlGKUJS`$CbObgGln^4pu^7`Dp6L?$`JKT=VPqMW(!~=a8@5zyJCFC_?6* zh`JHY|Elf@vm#h;YhCo}Sz^El6WmK#7h4MA|J#TERy)o9t^4i=|CQGD`}*-r7IHk7 za4pn$K(aj2+L>PX#Ma}r@gL}3{h`aV%M#-zc^&Zr5V?AtdF!wiOA63KK$2eXI6xoT#&UUcC`j|$5CH@ z=%yBaVe1;qu;5}>Oo*Lf5mS(LyIMPtnGiLBa;06 z4lAOmgMW4^MEA5H&Ief**3H{&Ow zT?+yN8`nkF%tyEXOE>VfcjaO)S4R=FM%ZYMU?uj8FaI~a|4P-%tyycPt2n0qr}Vt= zggc4I>2D}L;`iOnOMfJpzNUUbkcmiA{kZaD;df8FGd`aFnbwE`k!w8-_g~jDZ-?3! zVZJf1x%gPtU+=qr^o{>k^QhZMO>>D03)ilVy3+`It~IPFzrjNwl6Ga)hy^*ZUOdq! z(PDZ91nfsg?dpa56A8tdSEH5ED^%asMZpHwCw@rhg{4ZcOlqf&5onwEPX9l9Z~j|X zlI8hb?)$z=qSoBAGPA0)vMQ^pySlrkm+EeK;2GF67z6hFV!#;x>OaDOe=;yI{EH34 z81%G%@EBe&z07pYRCRZC?Mqfx?#e~GL`f7$Q42*%^5=8n#^ZY=pGYdDOfvI^^!V<( zH*Um<6DQ)v`QCF*nBwI#x<@=i_ERGs1FiC4bd(*C;g81xZSrR?@rI{emG&A36c80y zCy1+9Hrx>a0x*atZuu}X_72h%ixn3vK8($O4~0;duz4(!v}V$RLf{9fi;vblu{#=y zx&V+sl9m7}i@jKV^zf!kU_liv(0>Itl*V-~c}<1id{I92TJB9i0bPu=c2Sup6LqfN zmyc8gwQ$j*L-(HncG?n1QkfT6xPRw9bZKTRDxCl--Qc`W%akb6HjzTfrOjIF2#%bc z(-Htz63QJO=;7@+WmB$T!yHy1*J$?{00%*)ad!7IR!z9lWhFWj`6+)#mjTQOK%AYM z4{$}E$`g0R3yTlo)#8g0u*6oMY)ax!42In?N4KHZJIn01Lar1s2@PfSX(PM zaU%mL6&MrP34x)ktx~vQLcsN8{QIq2S$J;(y$@uB_^2%HjY(NSSX! zda9Sdw#4hpx=VZ# zlrP_Qub{Z}0n}-olH5j0z%DpV@yZN}g03koD2yr%EdoW9T>vC3^Va*?_4kp9C3quP z>U=3b5>J{FxXUZ>%y>;IsO@ci&^&`Y4x(5cWWFLmBFb`C2QhnEdg#|V#_}%#N5`<* z{5rw12WWrnd>%nIuJXAS7FzPil-`)fVfsUT zAn*WB^8iRcLe8*EVCS)Z=o;xi0d2e#fDg+Z^8w=82WS5_BLgkK9;I5hbR+cY1kC#XDjF2{9Ugr$%CS!%gJ+kFQ8#dyO206+jqL_t)J zF}6qP%fypA%3;rexF%qmHp*ouC28{m9=yRa(-T1KXM)#;IZT85hmv@0Tn^ub927} zJotOY_2uB>K<{h-8OBCa5no+(&2yD;!eTj?l*cIF$fSY(rg~gDThW*Tot_@iNMju>Rct4iKqr}~t0uS_?Ys zv3{)r``CrlaU@;GreEcIikAtqF~7w8?);jlss8Sg16ta+^|?TaNnK|zZGXPY<~+9S z&I{{v)MKmWu@tPnsSB6ZHUdiOAyrBouuqkF0i?ayzZHQP_y>XOKwuwYE}8v8f7Tht?%zSJ~FuV+yMY zceffJ?qks;sNh23Znz)mT674DCNyMjPK!I0Y^^oi8Eaywm&Gk_9I5l%Swvcr!l6!nq3&BB8ee0Vhw-p}hFUb|HX)=Z+u5D&QcC75Kp-VlO1u zVD_;wcC=uzFE3ra9z1yd*fejqrxI|(B&!96K-25*pW`jtm2fpYgEf!q!f{*x4UFAN zIl2pKZKI{n2LMT0r#ugjW&xf0kh3l=m?%vUo#MmOxB1+` z^k2&*$`dVYlH|GaqQzcUS8MU|OTEQ!eGMy|9zd1bTt%*(FLd+x>a+hM`f4!Z>;TwO z<_NAjUw-DFqsZMZCdZC)uAED@s4u;B#VQLWum@o3^1_=a>_5hJaGblaAVLXCfQTjg z*(yn|Jkr{$2g?K8b%{L{2{*Uv3n&cdzgL{Q^fP2}jh!I1>Np-?fJ(fIMlL$q&N;0P zYGssF-o1Ms<@KjnU;Y&`cs3Lr10N$ie3C4eSwF@XZ7e|`fx79@FQHhxz&U^;0Qnx2 zlThg3zY6^g+yUSE21`2j~7{fJlym<1^pFYUIDBKW6C*1NG}F zdkOc!?_q87Zy0}XhvI19X+cG0)BlmU-`{;b{m4%N2>CPGbcgxio5gcS{t3$NNQ@l; zSlz=!mX-a~_GBFtWzy%mbUAJH_Z(!5y~14fHGu77@S>CF+K0t2-~Zp4Q~nkp>F=03 zmx>pT|L4WihyRFnBR^P>&5XWO^gTPneA36lwVQtVznBBwgExc4iG$zfnfM~~07L*F zsKo1nInY1GoW#7!9F~9~flMrq06GmAKhSfem>vHneE7eakN*Nd<2v^d^F`1g^Ppsf zUx_!m!eG@v9GrPv8P8qOa%Hi2_Q*fv8TQR^)3Lvp0patx;#VL1DZKtJ))c=+2L6&~ z@NXBh6JMdf5|HE=(P~Mn-#fT64Fg2IbmE^Qn_oo60ByOhTjWIm;GewxFR`2;=r#ON z!4I%DQH2;Z8^3i8*vTQh(sJj+3qJviXb zK*)gHCcj&d5d-Sm4qq$?5-B@BeDi;VSC{DfZy-Zo<2f)*8~SOxiar}I^bE0nAPyA# zNQLEbZ)X5dGv>NGyNHE%s`%O4{~P1uE5+BI|8JPLwf0GUthJQms~4GV2XwLAn0G>! zb@w$E7cWE@$1Q-K8`+kgjjThEgAQnGE8T8`9?CM_*KNkBpr1gS0LN2@{unE%-;6mg z0+7Q)!5;yXaOY&K(&6A4T4(r-V&Zh7X&KaLj$ z=2vzFP1>P-WMG=-7h_GJ$fTvOyx|(}KA;)7ic~KT979ub-}J>qSIAC)tnj7A1CBi> znC>$%035*3so^iOM*C;Trl9sTyvAw`D^gv9wR9RAg8$qX7KF%xvF2WT@6Uoq9e`*j z4*Ws!^z1)GhMs{8d1#!*MV?1*7r*}S&sg965+LWB!84yTk)YSU6Rf=o2tCsObn)Wx z|0;mkF07FvwiD|eUtS1~ceHmBO5#AwC-MxYmCd4kj91&`nD6cwFQ!Mn!nJ$Mp$C~( zna{?)SaiMc-%&<4>xW6^IxMTmU%9xYu6UTg8Ax(xvb^^5T$PYc0tYaLB;?&bqWjYL7u@UPDESw5&4geRONkXX(@K(%OIOO+}YRm{a#4r(_=4?Qqdp> z6dHk+RvzHGWeEfsc-Y{|O8_he>NPE4)o=r#K`VqV5nS^6u-4j0P(?*rAVrHD%S$OM zaVR#_qlJZvs(_X5Zz{e0mXB~6n`dmWKNNXQNVy>%rk11dx{Fm8Ry``qD)=t&j7=0f zRh6~H7q`_X+vokd&j}O^5v%AfNVLG0&uh>d!03Eu{>hnTukHv zh~)Q*cHZ_MWdw+bj45-i^#2q5v zGX$!P4`y(B@7T0GZH9QnvGPw=8Zmul^Ue#JDDZ-eD<2rE= z7b45?yeVBv$Nmn5mv7&|3*;CNXL1C$BLkjB0B2%Npa7`wrXKO$_4_R&B}$3~wZV}u z$HKq0qpKLkdP6167$R3ukS|{O8SS~l8|5cZ_yIp~zc4;b$e{v8Eh_WvfQzdr>*uiG zQ7O8|IYEdPKG#uLE-$0S@-};fFCgjT}JUgSWJ(-6yR1DO8Y(B@KlZj3U06BdUU2Ju2bJNtYM4; z)n5W+S=ORX%5-cn^g}|7K{G~drsD^{5j;%0HEE5jEAkSbq|~X92Z+f1DG4a_)23%p zkiQBD@G{na^e^CKKNeT>*WUxAo@Z`+9RU3&0k%Y6L9bRI{oUh5e;3aPtP0S{i{W|Q zehh$*JW`I<49dCSif;n3Vd1*gjVdpR-)SB~?M7Mc!Hu%DZxk@;0CM+waRz`?fJg;0 zjUUIu#(qlU;m(?tiSI${8_4Z>?rCA(c#gjRZFn-ooXH$5SatI-E_rXzws&z&(gN%k z!ROh@S80P5D7vLtTl(j*Zi@nlhsfje&+#V+=kw%^kH(6di{c7@G%e}4d;{6Ic3+)edKznd;gU)05yrn$vBrfbQX`FEdv> z#d9phE%O$~@9MoJ$^evCSRz@L>xpF1odERmQo~o`xnN@SBXdPc5J0~QBpGK;mn&oU zC@<4v&lgY7_r~SYo!As}FJr>*qSavTiC>m$-R)Rx4fOgVaF#M~<09Wx+&8rd)0#_n zNaa8o2rd8^C7DjVZe?8L6w(77t}QLQyraCF+8ue|h4d=_woQK2QmQ1*38q`ljavle zZkYq*F`m-b!?H1USLA;A?c67j)QMZLbQW5u(lLuzYn_?=Qw9OvPMo&?-3)4T>`IoUc`GH*@m65)fg=-9;d18LJb6PhZ9oqE}^^r`y{fo-f`xWst+pu%3A0 z&=iX);%Hz2qU((y(I^VJ!G04^fZ)~1o&bT0wu^d|-9yBY>7u@Pazcprbaew5@o;rw z@(rgC)LN28?zPNVj6u`=-ArDQHF*ytzn<;L9hkB8cXLi zu6m~~5J!LsM8K|}ct)LFEO=;l1R9q&0FIs?2H;r&a59j(0m%~|MIll^YvU5>awTwQ ze~(}h)Wdy|oRs+A+J+k$b;lf4_D_6~DJe}XwvopqgD{SJ1n&eo1(TF1gLnIuP#`BB zXxw`jYZ+Gl7Lw|tUsF31eVWdDrMw#b9$SJVcdY;``L=ZP=D#7A(Cs}y3}snaaj2Er zaHI8G?tSD(x5pcdFL|!p;GwCJ0PQ+h(83iy2ITETwgzJy>W+7dc1JLG9)5yfQC?Yt zK%7bIzDam(!1{QwE9d|@ZR8gqR(1u5bz9Z)uMdl&@iE65)=Y! z+eLYzT{*Np#;@sNm$xMX693?h|GD`2d;dDld))`E_4HwpFf;ZWfC69S^8(}INGJ-% zEqhccD-{A=emVh^+*186iuujk7XUNTmh0}eWdm1vh%<*o>SR|kcP0}LwQsdvOXDZLq3Id{ch6sa2S9^o#Q0YOjB0HIP$>+M%Zmtt z9QU>ogdKVf>TIl*IRY5F>skjcZXEz2*bGp%%DhRiT2Q}0hF$_J`8G5?jpZ$u+T5j6 zUNGtyC!GQ8xFviE#*}^U+kcgNV~ZU|Aw4?toA8vmtVOqnJ6K?7_4pge8RHuME-Zft z&}Cp}tS~GCSu=)~ieh0~P`vL^Z;~u3=eVU65D0}`r9H(J^BH6twvZiYWxz$=mbRpT zruL>1xwvHtnMRJ4ku#Tm99CcV=x2kl52Fcz!l`m>m-4Z^vr6~XXMcxNtaaPSUrFApGHvW-X2ha7Od%7*8#&;Xz92| ztT3#0;<*$&F6CegaR>r7{n?n2K4XqDp3~g?n}A6t8DnhXfQ8QJ;2~bpjFxpV?y1jb zbL<2d<+^OAT`^&Mg0f{%oRcrs#W)HOEnRn9Dbw}^StH5gB-8%PXV(umQXDsJ@uw8I zEv|UV@s`$(g2#Qh*o^`*j}M>VIf7fSU^w$g#Iz%9K^rog$NN3z5?y^4mvtSy9w6=W zNB=Y8Wd1(eaWXa{!3!+COe4(i{k`Kc=NY*F;{5ByG|kctH{3R(LFqYjk)VA5*WI=cs&$ccaYKw=j7>&ET)YMvl=cS0!g|B+x|EW zgrJ0~{Z1sLJ1tv-u3cHG^~_&zEWFpQUI^2p{}Zh6SB`u?`B{Jlng`4<34@Y3C*bVV^v zfF|<>{=?FN-%%ESR90eD7Ij#U$%)T&J zo^@_Cln~W>W{vh53#WDU7UU#XTCItIB2lP%0Fs{Ja}*_S7J!1e$<=xtU?NN8IaSkl zxaZtS))*>>y2p@v4dgxCPv(}Gi|znY16tswq$QBC zLEMo@iwwaaqy$SWEKXu;c!C1+c3eeLdC>yLb{Ka=g|Le@_wuH^jSoTNV3-Oe3KyWd zF{&o9n3==@{0Ykd^g{mtira>L!V{I*X% zrV{zlBsst7zRQkVSfwnmlT;i0(v`2L8&_ZfKS8AEPXT(sX52{KwQUY1dFj^K0LVf~ zE^A10k8?p=^oKFgIC(wnrX@ic`{z7{F& zTsJZF0WyW<=e5_OeeSr`iAAJw>N>DWX%FyAS3~AZ_>wG?a*6WSkG!>C>@SJ*oX;BH z)_UISp7u9i59otrEl7ZOz?)fiFPk1Cpu1=XV5D`GPo*eMa0ABip-_`muYaW<+y2c@ z1~XC)V^7yPE%=PJ}Xi%!%XR-8@MC5vd5AwrYnHOcA z;EiR~-oVq)ZDf%~cP*zXC`ij&Un(elp&gK5M-ad?>T{9FE_S*S^}~ z-uiN?<83*h?5jNcoRyz~4jxwt`GX&>y;s?(*r40;YCp=H08)0%i)Vh-xm$8zFY=}W zlD6!sPx++J9TY>AS1lxN5M<6>m9FCcqwAWM4q813l(;+DJb;5BfysPYo{ST}Mt3!1 z4J9DS#c+D)S-j_ThwB4SFqp9qQI-Ar326K0pC!56%Kxlk=gGR_ZoWO<__z&P%4@mQ zdiq>2-GvW=XM!kBrn*$=<`uhyg@u!KBpQ9V-@_b25RPKY#KHPgKcD<`zoEyz)!N7Y zG-$Ye9*f+dJMk-9R%rdBrH~WMO{{|idQ$u7Qx;`{I=a0H@MyKw4ImW{VKc$MNb3v< ze$@Z;dIY5wFaV_c=$tW)=CM3jLXK1jdxN&j#?%itQA|>dAEhoWCUcHdr9?(OWo7qS zy!+3T&xc%;*UP&$eR)UP9dBK}H)Yyz0#-$4ck**LHhFCl;OW7YvlY}I#-RtLabtd! z3Bgt0(O6)% zpuWs>tZRmP!sx&uKm@l_KMHU(1|J0GE*GcfzJ~(vF3Q3?e6Vccjd6b;U<%eLNl8qN z5Oi80AaOS?L{H8BF+e);GAMY3z}nXUO%|2`GYCw4>f#Rpk$!_d`#O^zeN^h1Qu8QM zDC4v#c00O$rg-brpA+lkd-2;iNh%}8e9_WFCNH2g8f&Vv1(zw5Y~@>vP6068{RE%F za)$g7-%I7$|K<}=U{32MxbPs#ukLQ9^w!*?=tRb}-~&NlduG_V>W`TZe?(l6H+e(< zM)B+S{|d{~VU%aV(rIFqJ;gKPS*%)rqv#<9kXw@92iR~YFt;SP9K(nSY^daRV+9oT z2uMU%r2Yt3LzFK2QTMj%xamZkqoApzOA<-SyjDTR?9r0RSRzqa$q)0yc<{hmW3c+8 zO!RZOj4>WVFBaHB-s9OIfPliUqB?|CPd7WBd3yp4_2KJ(;}bBs^FlDL#hIz5WhNDo#y#({F)ew0aGv|tscoVtUW@u<#U{b2-MJ~Q`v z#Fy#mX5$FP_b7ni_|P-B*?x(gE{QV+5ZnR?8=$5gM3P+lDN_EjBvvd&**8&lGbG z^SyHj01l%v=*+h|CzwJ}g1lA0^RT$YlCFn1Oba*OFTV4O|DNX~*5=$FyfMH3n0wX$ z<>!~)#sV53iQV>e?VI6wWDX~OWl0%3ap2pyk-ml%;144nU3(kB^_eI7dyREAi`C^b z%!$wOe0rWo1}??SgW*z0DH~JB(1w%I2aN4Tes9QB{oEA?48*M4Q~j=O!^6C25g`AZ zrwk$)j9eZWs9N6ax1`mX@sW)GQ-Oqyy=UKEyLJuzBLyONj@(ODNfnUP*k60(7f(@? zOOQ<9 z1%+?Wllzf7WYtf`TRj*b%#^ekaamG6IUk&T<5h0V3xfELf}*gL)9dMpyz$QX?LAr@ z`7vK8n1TXvj~ww+7RsR$l<|GE^wNqau0NpssN`|{k*o(K+m*pWX%5~H|TNm)%C#Lx0iuB80VKh0m4cN1;(Mbsa2Q^-&JO@&!|qM$xwKK6=WtUC@S zODcb7Uc;yaB$d30w$L7V?LSXzl_b!&k>9z7`r2(r4M-wGytRY`&)3?>n7>Vc>>D?+ z`eK(oEs|_N=Hq&~bw@VUa=2I8`uhO(uxj!JI!)V<5??l);I6vOG$Tp0nQwBHan7GK&kc{v;r!M{g1?A@`CIeWu9Y=W{U@WjZT`N{q$`3Jt zqR#=sl9C+Vrz8gOBKQ)rVk<*ywiA6a9m9;zlwGZ-|Ycl_jDNaZJ5ZFBzQa_XnMz#Vp(c!)Q80OZf`GAP9ea(6l1XVbjsxX=6#=Em>(Gc}sa%kh;n)Glsy zkZw>$y0DBYu*!4SNaN(Be(|0r1x1(2!Q8h^@ZhKt1K91U2P4>u$}5%4a^__`T_Z)Z{kSXLh`;gE{qA8jlkl&bh{~vTvQc zY@1eNipY9u_`x+To!MjtFxzLFc@4;Y6RRYxFze-2S@GH3+llN94Dym=5&#pRL5oRS z>-gfxKYqE7!KoJtvTA z3@PPaz-_O|e+49M?z2z*jL(f01jd0iNKBid8@&M}>3*Z-(GZp%?zZLj^%*#DlGU7D zg-e$Yfh4zeH||lyZ1EXbH@7&?=mOA+6-1hJ>MlME2T)|=N&PVQa!)L6q_I}W&ggL= zC!Y+mEvR9jW_R7vg2&?GAzat?Ji*GJ$x+{okqJQHai&B^szJWT#9!YTTU4IfrQ)gk zj*HC{gQ&3+(~WNPo?OX5sj--G$C<>(U?}xrX~V-MEYrNAfce=?BEJBov8dd-ewc0T zpE``|5b`N6wbZd50lnJ<*FNnIXY4ko%U1O51L_L?*jxO!-FC^trrfmc>P-9rSY*6N zd*Y3YRqo7%Y(Yd^;=Fz6eYRcs+d4JpajhI~6m+9|id8_Q4^G`F-v8(p-z)6e#;#Qq zT?@9oMJ1La!wk0FMXaMQf2ptd?QgJ{Al_2!+SZ(eElS(jDcuZIA`GTHGyX;9rc-?W zJ?D&3g)0j?-ndZTs_Nh^)u_C6cDnNfmM;Ah%v;2b!UgAvdw)PXMgyEO$g_c~kFzsX zr@~d2Y`0377Fh0z<8DvZ*@LBx!F>CO(c^aMXXd`o4rxCteg%`CKJuRwM<^=-{9?J3 zWT3I!x+=CkjJX7BqkFihoxb!VcH{dC`eB7&u>T;et$Z79Kwx>|u1c+dH1D4Iw*Xsj zqr~WnmpCL(1Y$c0BKLj+jbDhF;6z-t(+0I9N=7rpG&W3M6BXriuhqYnn1J@HcV{PkmZZ5;Kq`IFC zq}G*}GVc6u>bnHWgpAZz(ojtFCTFxx)Jjx$E`p!q{*~G%ahX8!LSO(s^o5d;T!G|X zJ%4k4E8u2WgW8!Ab=|;al~${ zoX5S3?cp!}DB7qMpVlA-nb*xN$y|LyQX6;Dwr$BSS&Vn>E)zE@BZ-u_x1K^}Gyc*3 z=(8A;WNVNs)_BOjE}98kv44FDrtFl2?UJfmc9;{-vf7E=9wc}&#wIj2G?ZVK?MoQj zW?P#juhiyz<~4zxcp>J6I^Jz{zw@AcVU`EjSqV4RFF*Y+c(L$wxUj8hIa%YAa{;p` z_Xu`(;u_ckI5<2o6Jwyo_*;}@VC|`~=i>R`ZhvV$yoNjybpG~Pti#UoLg1HJxx1Sx zLAYtVu6WMN&f{rr-HO_ecTOI;j%Z;$5sQH+jyCb4>b~pK9q{YpQLwk~HZF|uk+fJb z$tx`~s$_(UWThfMhRzT8& z`Sa;r^})h}Q9#7FEG9vuh2@+4?hd!K18h+)GP%MoPh^iBx$b~m`am@RurXSAs>Z{p z_9XIw6p1AQ`O-o+pL>XJ$_qu9iQTtGIYqfUTnnXa(Bl2jloAD(_j}6rZA$U*#npp$ z3L^P2zRk02Z9nCXZ1CnDz(y_QqT>WVVu3`q`aHGFAMc<>Zpq_l14V|F5t^Awa3sm# zApeYWT#}dFSPgVyJtcr+-zN)|x<-^`uL3gNX7@8EUja2O)>1AC;+(*e=WpF^!yQOB ziu87lO3I3B)Ryd50ZHfVM~~gb&p_I`5NZWCi`(Q2PaKM%-O?9*Po=kRuZGoU!|e!Kf~x!z)(fD-DcMc=Y-+`L;{nO`l=o?j{Et{7W~_NdP{OBTkx ztE=}>(prn-#|c~=c{$1d@<+3~cX{3dZE9Ljx3Ew*aH`vGE7w}K7#enWU3EW5xOZ_7dXc<@+ z(kC4SE@5p$oa+utrCTePadCKuSUKN~-Ixwfe}iG47g?!2HS%_KP%<7jK4+NAi|N4UkF!1Nj86d%F2!kmBDBGadDd_=(szy84Kt7&2yo+pS$u?06>EuzYo}k z^%rm8-SWO4YcXRVt;f64W%_zpcip~oq!=s?P^lJ>rR9cn!Fv^c`^PvWeOQ##tO4qY zYdBU-rELzp-6U8q-?VNM+(PMlUr3b9gxM_6Jx20S02Qv5+%ljA_kiquZ~$@0o_;!7(e`<`IsD66*tIB(Iv1|(0=o~6&+)2!?;r?Ur#FrAp>|42l zWzuO}S>NROwZQj#ynJ{)DehRuIj_GSxW2%nN4+S+5*kYfBn`do2=R1h!Mf=>jOlzIH_t@}(amK18+8yp~ zIja&>5}TgQ51^Nf(&kw7miWk*IP!!?KCiseSdM*_#)>Zj%mXa~#2m-{wwI37zvkgczz}D->4o(0zbQTL(iTJ=jf8`oV!E)qPq4wbvPosthZIe15=5#Cs(wh`P zE5R#CcgZ1Y%43j5aJ_7gPe&gDaodLaR3gJ_Mw-dsWTKMKlY?~&pL`Sd2MA-C@T8r5 z#{F4Z?g+5uI$%dT{`3W|B`O%LpxjZRb5rJr<;oMSe9Cx2UV1Evf?W)_&=Lq*1)#V? zoq|`b?v}-Z&-fQEe(mERmSg!b2#DBE|1Vs}wUPK(S|!00naFNt+zXHGV|U-v{ZUX+ z^XzUwqtqOE#Evu6-w%%#0U-@aK7bWbi|B+~J-hQ#)JxSeLEiA?u5f}XmM>KTS@Q%n zH(0r>@1(yCB#nR;9e`MIpKlUwFX2M79C4@wm?Cylw5nW)a=bBFhwU|4m;D_vuH+l- zOfEgq?2w+30M^|&W8GlwKH@S1$Z8-g1-lJU^{qR%v1#7 zdfh$ihq*vlcxY5Dl#L)Q0k&@mWXv^z+{1yLz7=Ygea;|#%@ z7r9qnB1c#yrMF+i9JzG{u<7Rj0SB-`dogH& z5YXYgm}MbTzmsprj9|+h0=5@-Qo!C8`7B5X7&E{4L2+*GXT>7@zQ$Ovj->d*i%luX zfMVfT(|t_qF+W;nwP3E|9BunTvEFol;|bd>XrgPSpwAlC5V7+f=ZzWEQd;zd?9mSM z5A+^n4jLzx)p`2&5;6iv2S{n2mU}!)=|@HAHbBDyE{SW%btmy#bQ_Cu8cEnwzXT*h z4(%J{p_P7X*9xa&f;n`Qwof5nSS8_->aK-z*K|WYLX5{R(;@ViotL_(Y!^SAgq0nh z6#;lM7ddZ+wIP>U;jwbZC?jjb%?-S1ex)JXh93~bv$GA$sQ_!3bdn{ELCujH!3M?y zo4f_tHvuXF{DU6ns#=5SW5>8)#kH=FcxJwVTh&GK(2jP1XRHoe!$Juv1#m(hSubcA z7_Ot~nYT_{*%|<>mUiA=`+P4)SGXI;Z*wOwBmlQ8(Zl6iU2y~MYTM|Gl z{T4yOkw1gjUn5AovD6$}scn|uJf+bt4<3|khZdE@&jQr$!9vyN>n(xD8z+m&m7}x| zrOKTe9VhTL+6fKpPLOz|wGv=uUT^v>eQgFYZ=((J1#qexKDD)vu#6$D4Dpd%hs-a& z$2fE2gZXfC8y|Wql_}pIPGBC?*jQsxadDgV{d=b`;g-D+8*u1#@XQed!4ta<+Kun| zc}-fXT)cR(SY9@6+Wm+Fbl|{&`2M`|B)3~jo|`ubeX_I^{4rql^z?KDuFiS(!*_pw ze=##N6P9L4f7}mLKYkjWx4Mbj>bD@FppyLA$$b0xsY^Ae?N@1!aUegd6q+8;#{>+t z@L;>cZY@i^nIRbO05L|!2D#o*TmozmWcY}!;$z1)7Q^kvoj~y7fw^V-eA~o@{MjEF*nLo3HfSgUjA4C8-0g!0nk+)J$yJ@WygYY0M zaWW~C5dfJL3zBf(7=gtFQWkj-yF1xcVo8mS@}`>WZaps-%xJ2SCu zV`AHwNiwl*XC}67n;lJTbnKho`R=*robTS-fA#ZpKX1LeYSpf)y=$#~MG6*>Pt8y# zL$y&V_bIGOWbSB?Lu)t$k>CN6%`$=7bicYrs#lPwgOlx0&dls4zFUA$KClwS0Bs&Q z(}(|9c+i)NS!(Y4cFo1FxKgD6ssif}-%0xjFL5ooX5NR(!51)~7kaXraSF=k>|r)n zGor_?!^C70gR7)@*0F)3qV3o5&~IzQt>fX9*%lsi{kt<|4ZW<<1(`_b@fPT=zy8g5 zG5Mjb;b>N2X|wjOEU9 zdh`roVsG05vZR5aP3hnaArc^?VcW9=L(hh|Je0${ry@5Mwzfi&xmR&r-`DPLb4=ON z{o>jL3e z$V(_%JYHQesl3U#->Y=)iiIC}uD?>sK8^2QT=)e~Zt0roxrP)pwmHK+p;0`tWUNQj zl_m$1{s4r?6gX_XmEFP(wNk!pWS0Jjojdko)IeOZ<4*yzONTFWA?HbWBwulib(U*O zl*(LaLv_-9{G0q^V1&(ANUzQZ4(D{3Q}=Hq-^*BHk%Yp1bI4hqPid-C!QiH`9_;n=4AqW~ zeV$cVcJO;v1lm=l-;}oE0T79UQuxB0YZIug))rhuJl~tf>yJzHa{&}Ek1g%)yk_*B z+jXf%{O6ZT=11>pEvooQx{+DxplYbV1bSKWO#WaG(DajTo>-pFs$QE@&|#MIgs)tC zAN+)uYHMuz7wZWqE`OM6d_~R=^Y7zs9WUS0cw?T=2t-MV@{$tW%~Z>?32xtz+}YHS zfim8ztHJ0tV32L)dpmda0v-mtPhMzwS8}W_hw#f@>Gh$qDd(i(sJ`Q|&vu$SrgYU8 z)q(I0jR*+?gkc46vG!oL@H%`rMNam!QwXEaBi?(zccpqZQ{Xol0N6;ix~8_^A_m88 zLALGlS?zpOpo|$6a(Y%TYPM8MQ+Y6!GcD(slgk`L|D1MEp_T<7N->Ylls0(iYkwy{ z(X4N)DR3`2;FuZKLa;Yd<9V!=rINgJ$VXD-Q=iGBxIv+^zhkNk-gocr$#SAjyf@sBV&ux{aCFUix+^`}J zW+b(>MtJlE)|mN0`p6FLR@^G3VBBCbt9)cFNd5Qw{IfTILd>RFPbI+(B7uQRal#xA z1${9mJXol$7bKdB{X`A!Sq^^$A)+k8Aa0w%RoP{hXhR17>w~aC&I*(7zk)c#7)YMV z7GqGXdjr{1wLJSuF~X=K4`VRG!0@5eh-GDvimKac)r|5HNw4o|cfYQGA)YYPI@ z&wU0H**Rx?GvSY&C<-jZv7Ap1A-KIX$L7uM#i*_ZShu*W!B`xEckr?ZNxg>GuKb?f z$I!7R#q7K~y}HGX>{DOs68T`j6!w36+nL)R-06W7==&q_K1LEG6V&s&HouG-+xS+6 z84w+`1*H#p-w?-#KtergQ$Z?99#rMxGD@ews|BGYgW|`)(lQ*PuhNvvFLRF~4Ul}# zYWHPiNIx5Ib18B4Dts#DrLeOoeS+iu}t+B!pd-x%tg+Sm_OxC`Yk6W~(h zhmU%#7l4n1ThmgU2h<0QgdqWxsFcZ%wJ>h%D2PW?c_=Y$egerHCow z8#-By3xih~4;Be2Z|+7cWk30n`;jycwbblyOe*5X4kT3oGY)~1$s85{p1QIs#tKH# z&FAX50MoOm`+Y}@$E!XM<2Zb-2aeV%6R;* zbdS&PIw2btHoC^Y^-6CLbfS4z0a31Y3 zh)pJ7ncCcHqi)(?756a$u3X6C#`M&!v>6JV<71~Y0Lz|cn(ztZVEK?n;-NS3EbD=L zpz@jZ^P(fTU_Hjl)s0?Z0=}SmNU(4g`-mqFwh*`!_4GSuoqHvZ`*x-=V}VZwX%75% zm@3vOQ(bh1M^SWKo(Xpl-P)Y9)%&EQ4`G3A@+K3eTM(n^Zdsr1#o0nRV1vE`@tHV< z_b}pN>K#hG0&9K%Ud6bp-uF|D{-IC&W@5d1#qJXB0%)jqua)G(FVyX zdzKa3l!z2%Vwoof-yXP+;ze`=9gyl+EZ}u7eC3zZ<;nW|yW9_8V5$Dyw}}1fh_g$P zeG27z-9O!35DS!ikmlW%gj136NKFDID=ac}-b{`BP7L<}6gR-3rvxtOpXjVNoX<#7*+jac3yK`58r@YB@T8WukxI;ieO;MD>XIYVw8#dO2~ zk$G~lKciRqMGl7HbdNv2m;FQgu>llaaPjgvMpLx2P~?nh3GAS#d@fP>0R@D=a8n? zWH+n8(hs(wmwL&MpS9Oe2b#wN&~h`#n^~q0N?x>`Fe_j)tlzDnNL^GRE9s`|XoHzL z=1mQ^L%?fKw-@y*5?v>kHXi00n|E_ItEBg?S&VW6J)3+ZWMl7RZNGBq!L+%*$GPR4 zYA?4(c3|E?)!9&B>_C(3Ub);!dgRQ`&eoJP{%w0Y8|D#-Eu4qMUH>f)_-JEP#G2)L7hvXL2maaVK=)5E%2w#?&`2kmQ{Wcaf2qH7+z{^JmEz3hcuZ< zZC=l5XrX#Yt+m?Fno=l~kpzHRLwgdo;8BiG>=j=>y+csY%@_0t2@*}(9H7u7+Zw3z zW!dVdfLu?g7guRf7o8{CB+lv_(d7w*^N_sxQuY#S^wWzUj5xtZ=;5%GOs3ObssD_P zy38>%?9gg#rdXy}-&?%{Vgg`0=O-DWJg8eU>T591c8H0xL!foFp03}E^n-i0K^gS| zy4LSgGa%AKs0~-Nu=upqbl@eh1fWkG3Ip6wZBkrAR*Z#S*^jXRO+G`#Hr%6$&C4r4 zP-uuVpw~9^kqwEj-DYe3_4SK@~e6aaCmcG3FcpKvW*&;wNpKZ(gA)e5E2zTj_COpQH-e&;rJOX$d^~xmiDw>GDGE1uGrA%& zcp3(f0S#&hvgD6_(-cci9?yk@wBxAw&Uq6f%$41nFqSvX&gMk^q0s1Qn0?b^Q|?4L z_{Gdsc?+;V8}b-XAVt2f9{#;)4=h}c%bo4@4`ixH-tHgT(@H=N+Vb3OW&M^^F%lmZ z>$SjaZ5l@cMm6LYF06}r9jH2cqZg3o)6Ari$EQaeJgkHFTPjgcuQ0CcGxkK<7Jc_y zHh?4ot3ToXZvmPYJ7WV5YR}(;hZK0;NIxuO4`+V9H+}$?ChJ`Q#_C{2HVCM7TYf8} z$S)JhMA;ZdyC_odlM>&Hd`q@xA^b0fZ;UaLE4=vI<<~EK?pGdPCWj4aBR8AM)iz-| z!3*em3Tf_SAs>Z8d~!)6u+h8rm-_Z^i*xfP-WWlC2)sGQ46b2hCF=4tT1^P2p*i;M z@LyNwL3<2U+Bzj=SAQB>6K4dLr06@;xnaFLsIloslq!YU-e#-7b*9)2U$NGuZq6QO z$OKN2xjTyY>#y#XA4O-$|5FR#Y5)Xg^b*Uy*$wE1tOnZwBy>2K?D0OsIi`qgXojm7 z?+^3#P@7V%cJVS4Zt~t7(o9}Li*Tq@Tc$aah%)Z;hGLKszD|>69&a7! zw$@&9YIJaXe7Wrls7N@IvFZ-i&CH7gbFrZA0_(X7128K}LxCbDmP+G{84#@^HAL@Y zua(a*Dx2)lvh>Dl?M2|Qj%(<^qQ=jamz-tV{jet3VIq8XC-Ah=vRYp;JX10CUdZ** zTZG19{6V5{wlJu8XF0UUMvWZuq}$1h<;HL3gcM*u<9iFi2o3u7PsbV`JgZF3fj&Q* z{E~T}XeeN10_mU`$9^oa#jtPO#FazirEZG3L|K8-xkqQ1_=6KNsHht_a3PXjj{3H) zD!ii)hrk4!tN%SgikyQt^99M{Q52$lC|)o#eDndZ(4j<<-7Q6`E zWsG`dA``$GwvF^U5N#+(7pB1y^23kn9^j6Glc>cQ{r4;7k$XlqNj1ArGUP^6*&%=aa0*Q`gJy_M}iAvOG+GPvZWwjFUr*Eg!z&AKKCGF7HT$y(q zq$O!T{|!EW8ajXfae`ZWe4TL<%&4>1a}HdYUVgaD*%^m)F`IqMysO>KhnQYA*gCeO zXPYj(Ae(VQvaN%U+IpWt*tPuDfB!QgppOH_;lSRFd`oB)?Z{y^AyikrH?ynoS;~wV zGQob63>k}g&$stbwz?r_WgQrsS<tZof(ugg&hvZVus+={xG=3W*h+gCpx{#D!uIx<{0m|bfX zmP71oSwq!Kr5u#DN9MOcxHh&Blgc^+pLa_7(wZ{>E~;6XV4hykBFIjB0HBowp zLxX`?n4JLr6~6rSMk~!yl#(WYa1J-3b&0hT0CU~t+zB9Fm|N1B0B>5?<}70mqP98} ze;ricgo@WonlRC3hjzqt3d<7)mne`M&Be(T5F_ej$Vh4RU<^bym00Bf!viDe-k)7D zatH*gZD9aI(#0K**$^ln@3@n<+D_t7>3YlWZufJIAewY_;)hU5skHe{rRE9ah;z;$};neqfk?pKV8jqlF>xyPhcjKZynrB3I#`5@evjITB4QSFMCvLO;2R3Xm||f}8}b#7 zvDfsmJaUCyvQ$f|x~8msV4(CR?jokck23dkB!TWiI?@PC!iwL?kt|8MdU}!>6a;PA zEEp_5Z`uzA?RG`g2<3r!=^*vKmle4F`4FenBUlef(_i{9MjT6N+evjhk9ZtIs$SEm zB3p@JZn4rq&F8tQf!5$|OXQAJW&109$k{rcR-@v(_)>8OjJe*w+qopTXRLBFB~Lpn zNe~K((n{`?#L}%{o(7t>2>z^beL%2l;eG&hm6^2Cw(Qy=HX!l`2K{K{nT8q>agArs zGk1k55>wiz!ger$%wapEu>k6Vjknt$q`%rDE#Wk$On)CIr7|Qx*HAfoWRD{_fbG0d zTr8sxXAe%S{V>^*^(`R|euSq=CQrR&ljeudt|FQ*WX5iGU^XoV5v}(!{bbv}u+BcW zmoxHzPr5qrrZzj%!>7Mye*c5%aF9D}#9mI;W&U6PLtL;wfWaVaS4ob3O^TG|K}6x3hYj z=)|$N5{#(H-aP%D)eQ<7llOa0Yt$Z;jmW4*U(-r!& zpb8ZcLPp?|c|zK+SRnUKhN&8af_0^ybeO*)Di=8szwH5F=3s1Q8cTAZs4RAU5D<8x z6YWl~zT_G1lGk=1MajP1sGnfBvS|PH1FIMoSssg-@Pio;mP7VYtrRtFI(kENFe!@Z zrPt-?k}w5fTFtg)8qHDR7S7~zr6Cfv=jp5m2ZG~rXET7PVYG^rTd)w)$M?alW-SRH zE+RbKtG_o^i6&9b&CA!w>zqJfUvP;yT!sXL{(*yN@#Q@pvY;kBB4DR*X ze2e3HYH%&K7Ek`S+=EO#9P?`i0(>5=4#%_H*#||HCB-y;-}~2y!=;dsOMCma(njSH zw$1MWU~*RBs=Hn#sU5+QL$p5Ej{$I3H#P@=rp9hJEXU_kr`#$}@HKF{{y>%4jTk7; zQ+6x&9hnoZR0hGh0;^Ostd|ZqrW&-a$qgY*8+29`X@{kpJ-RxflYG~FF=-aQ$_CEtda{D7Fw`{z za%N8z)Wdv2A%u*y&TJUjf+8m)E~H5l{UD#zS5YWaMDlcmEuY9|iuNOg7Qx|}U_B#- zzLE#e%&{aJi)JUQH&vF{RtkWsRFM#x^x1mDT8%`QQA8`b6>{$6cagB?%E4e;Yi5JV zljxRR2s{u)ow!>u^qK7ib&9h3oh6KRm&Rluf7_cXn;_;n!Ua=P;ix}cq7lI42q1W} zaL%Cwvcmjo#MKaJ(}j!ED)vPmK}X~Pkg`FspvlTs9YPL!42QOS+3I}azl62;xCIpL z!EMuZ%)ZIW(k3n!v!f-gg>dzgRgeRafp1GNH~{Vg6X)A838HRISC(A6MVe{ZE zI^=|8Zv$2*(ZkHnPx9zzzyw47+cgh6oGOY zQiZ^D0bVLR-6;SUtq2eQh&BhBWnM*^o^s>`i>A2#P9K!& zS`lx6OPP3%Q$@uxtMvx00X)uVW_@X<&|r6M+_Ik<&TRioJN8&uL}@|%`p<3N$eSvz z;A9glwixHrK7EfsOvhF3UsrKWZKRrCad`n{Gd`8^f zPvz;K3TYwG_He)v^;T{hu4S83p$H<0OEgbjAM@V?t#ZGm{rQq8tL5E+Jyr~_6|@t7 zO5Q)EvYPxo<{GM|7+eU6bbiZ)Cy63i$U|a&XLwg3X|G?k`_n9vJjc7xIsR@}^qz1W z0F7q8Rx*5DUhg%;2f!w9H!Ab$|yGYR@4})I+V5 zRL_8SdhG_EFbr+^!%VMI9MqTfW9D<2#xzX1KiB}Y9+?W!72mR@_^dNSN1nieERW4V}xZ_}6#InTm{mr#ip8^T18 z4}YCGWPQFM@|OOQ~7 z1IG)UExK^IubZokm0vF2J)7$bK!2+=RRdegZU1(8-XJokDnPGbxb@OkCVnc2jPQKW zfu0dvCt1En%_at(JZC_7iXU@*&oH$vyVM%__)Ii|FX;i~_%N3Er?d-OhVg&MdHq(L zS_b!g`$GN6lxKI0cvllTVdvtFsRg?t?S|BYZgUI(?|9knGi0AS_))ssKmGId?yruO zK7tmyfwpgCZScgfmi+V^r0sKpX^W@zeQZ4N@LSHb{u8UIKQijkx#f=@U21!8@_`|_ zK*SrWL-6KRkvqR2ii34gcN^dPE8!jSd6$++KbUm?%)_^@BlRx7LDW+CE|Lw&OE%%K z-6fLUzjU|7uAcV1Dtv$4ad0aNuo>XWeQxo`(=Y_eslT~5K%*zYD>Wq59|gofWj9>l z4X#bM7&moyB9f7SR(=lWq#%E8gpqgYfFXpm_28?e%zm+)eLm7LBc=Q;pCl@IMB&ML zn%1Qfuh~9A@|Girg;TEM*Y?lSgFo7-S+;PfI%e>_kyDqpO5d234mq65YzPzDL_>=% zm5a4xLPOn$Ki}ezrvh; zZ!4l2mLkL*Adhs|3(*w_fThc00P=z_O;pCC;q($-HD>C*Za15hV*OlV4u;QR&s{>R zpxY5`hr$EqjJd&5A>KY<+45i&;W4an(@NtVo1x8Qo;bT1?Vco*%IPFVL2s1#CzV2_ zkp2`2j_ib6!vp4nj7A@{QXtBaG_j*{JTY2_$VwA^SO=+c;oCcuq|s4l$jmt~-}s1o zvl_1CUC&OcCxgH>jsOBccjhTFT~*2#^bIjS~=!Q&(71hPfc9hozC@C z^|D3^GA22$qOB=vOg@jQOGcPlo&IeXZN66*%$$@@7~M194{+-weJqb6IDY7`Q@tRk zeW0ppeDR|ZRZyx$Cc`tHq@)QyqEio|R|>%cOWcTJFCovwpVY(9SI zp!z`fs`;MCndp#w2}W9us$0Ds=8TERBpWd(Z0>5TzRP4TiSo*|HLRqOi};Lru?D=( zX3GKD%D(;}`F)uvULu;kF)N2ayR}(9fd4U)-8iIdKNK3}5&X#qiwG&I+lr*m1jMu2 zM-db!tS;pz2uByZa;^!00lot!8}nLGmg`yd?w)-l0n!HH_pz~rr3(ACu0VKmh3(BW z#_GOPa%Bm|n&wVP!Ibk%k565q{Lv@T99y%HZ4U=-pkXs%@rDjIveM?)&n4|0oOwtN zpNhugEDerGDB~ni*o{${RTj|W)DvO>;W+WcwUjyS@SO5-Wgzr$XFd7^NqT$fB!Hov z%&WiA5T!EZEV740kvv#ZKSiHCh4%v{a<$zMY$OwDn^n&nK$t8T#GNcZY?}}ypEL+( z-VbW{Lo1O3o(A0jI71&vy8PC1atpC}^xaZhI@DJJ%!J(mxpsHU*o0~|Ku6k4T$5fX zdG9(OVRRc`n;adB<`Xy1UlRfratt_m8RR9&8Ee*kmtoKoF5)!{g);+%<8r<=Y` z7|cY7WnY&J#m-56^D_VnlpNASDl?=!q?7U|t!zATcKP#!83g z(KCI}JTTSg@rgh74@_O_5ap%q$~y0h&R%g2TkeXH9wrU0w>$G&Hg$mBjv* zJ4W)v1$znARDfg3w8z(>!sHi}|9O#Mdwy2aM$3rOzg0+vnQwi;IPR%={u(t{s|w9s zD$x8J@n>#VVH-ptxSs}}0Wo`1}&D*C6 zES09cYfoXFTT3VRgNgKCA)K6?jfUcPwKI%?e8SV?Vz-zbO9u7Hg zxoP1az>N)!{r4iCygb;SmR&TUGl~=55UHd&)9Fg1-$jEVdC|*RdTL{*iNa83QO0KJ zC0Zh>Pb7;!WZ2bCYnbvOtQv@|628ZhBo+1F>%_RWt-CgE8v+l$4X`;AIBPGF$L{^; z#c=UqBcS2JhDLL2Eh3*U;Edc4!DAgxX|y9V#Dp^kM*?+e*YTV-%QrEu?N6rkh?nVoY-7W^`rgRO0 z1fFTfuI*eSqW|tNl**-oO#Qo|6s+$Yt3S?2a9C)E(#|3kCI%f*xX5&7jIYgdapQn> zr#B%{bP#*VyD504?qTPD)bGZGox<9p z(vQ*Dj$ShILR`BSfF@UcqE59-_qZq$>7$#{4!tPg0X83;Ud;VrTU0ib5K4KNYls-H za04=D12X9>%kyW>9Xt8~?YcFRsH8mvC*0=VZsTslqo_PBanBpuYgQNRxz4n2Q?XNc z*+kL1n*!1j31=$gl)Q<5I>51Asd7qqNz9|~UD>I><2$!A_H*efO5s!KQnt>BYs+bZ z!qZ#aPjZbt{Ew#564lrJrr?^axid((>447Q(p3Gm8M;o|ID)X{Z-}W10?r#wjhY7rrE&DhLy)@i? z#M*5iEPFyCx)hdeDx)pG=eNCdl`vZM;1G_<>Ug!=5d?I-xA zM$mUVA+$L@hS<2~bH-hXjoyxgy&!=^Bvr*(!g6=!tMu@K>$LUf-hwLiUS17Z7y>v+6V&sLUe{$bbF;pa|ia3Kv~_+;O84{=sm z81M#2S3f`pwDt;69S-<9cr$rX8b&k3p2xcCY9jaw`n=0rmj+z_*eSe8jr_RvFl6%o zD4)LBIbpSjy;;#|{1N=Obvhoq;#>#Nn|TfF()7Q1Y{)$$f4RHK*^^$PPqG_MHTt|1 zr+9p~Y{=~YJfDjlEM@rdyd3%0*rZvV*6e-Zx9VUB0**Fv_7Q%uJ8El=Epa7g`5X#yYc70bYk1ivz6yBrUV2p`+jAs)LA3 zAqhUQFq=bWf*QCHNanguC6NfU{2ZL8FfZ~p882FD(#>`()%$B(*FL7j^8S+A!Gf>! zHdTJI-^n=jSEXXNi>^}|(CIGdB}GMi_g{(zV{c3PY>iPjeipd$VP`a`#)+;KI2jaF zB&oC#SkW4J$VkH+ z#eAtWdUjChJZoh^DQ+t{Wo1@tPn2V-pPpc{d{V)nSpixE@R2XhOHi3L>E zP_-&<(0#M<*l0YnXt8_;0HR1kJ0_bxNRrez)an}Hol_Yrza^+k>(qF4Gx26$$lz_q zGL@hb|4>7qPhK#4vcRn4c$Oh<&10hE0I@)~>8#+@^w(Cp{j%6^1;oJ@^YiQA2>f!M zmxZodOz07D=9a#7#Mm!N2=3m*d7x^u=C*et4Wz+JS;hjhrN!uyNoJC|FLoM#-C)oz z^_!jFP~^H)eBd14I_nT%sc~yzShW}fcP?(!l$j%CZ1tP8qI3AAF_H-j9s?EbpYS_i zLX@b1akPB@g5kHN&2>rC&p8=5XW>!bl(1-XT3{5$^SgiLWpVMD&#?UHVO%9ra~J}q zi)Mk~-QAF<^Zkz}S8c=qC7?uW<&{Hn-qax+x}Vd#Tj1KKk6Xe^awubblQUHxSLR|r z!iPr~I=)V=*~ut*#SEKp`fFclWmmAi2O=bC&3$dEZn~_l`+k5qO!%r!b?T>EDJqoQ zA@j8S0iEl~hjpd?Zk0Q&xKN`((hIcp+dYyBc ze+jiyM8CF!Paz2GbkK*U;odWb@huojgE5g$^167C&|yP52EJpnq^2+}C%1M7=kEIw znAd*_4JIG_a<81nbe7=NKLaeA`y}bVxNG_miVx>H!X+^=gndf)MKvlW$+S)(DBnNp zCHG|HM#QW}l{oz{>$H!vV}y%WhC z+BoU&GVYV*>&~NdTZjOKhwpDPlY|zw0Wolz`G}>TJz?YzWeS_kF$%=LTKi8J6Ap~`dIW8 zX(1HCFy`2002bI-vm}1Waw^!Cq^CPA`(*f4VyFiceYI+*Fv022c3Ac%k<2_U9sN>@8Is zPFoZZ6c#R5FOt}8U8q2xIIN8iGgNsDeZve{n(O>iCD`a6V!>!@)dgj9>g>SNZ~=hC zpQa8G3$dn_hJP#@c@#x!9S&YNFEad;TJ1xUUrswe5Z@S&`pqigi@J*vjavQ9BWZwu z27X^DkTm*3r@emLOy%3^>PYCFoOYMmgo^fQt**0hkpO_z_HsjD^*~QzY%!_iAS8*~ zyOhMUOSF14NpiIjJ_E~=gkx@j!D9Y~rMJsDM#^*vg(Lz z)Y)tWoo?*KPb9o|pVAW6g~Yp7aGAN3dKof|GLZ=4j1j+Z)Fa)!7`s~v`%oNkm0`^< z3iwvKoSX}>cUto2W$Ewl>YYFGh}r&uf|zD%M4m?7+<4VM3|&p&vvIU0czQ~0xifJP zp^ABQ==lMsr42>(b!w#!bGdCSwe%PCW0uBxfz4S>%s+^r0GH&+$n7o;V#3SJ!`o+q zW_s@Ehi87`ixw`S*3Pa8`~cfsckwVnK8t7R-|z48hoJD`E5^#&DPx7ju`zvzk9(ZU z0fb2h|Krh}@oC~{7Vx=cvb9@Y1sxIsh%%v-rW9AYrQC-i&IG{R`nsOe6KKCiZPvuR z8%$b<%sjOT=GY*&`8@?^sB?@p!FRT3{gRScz0i?F+PrgnA!(EE`R1;&YSCO41^9X! z3-#@7QPL&L>Nm3;f7{88_v?)LK=4klY=pOtZ#k{w-u-=8se}^4YW{e0DUd%+0BUAC$%@pj*)t&d}7w3<~TwuW; z`O=KpCZIEVc;-`MqBGlh<}V;v^-U+rAL_%}SD1aE+DECYN$Ks*N%->Fo2Pa=0zI!I1LM%cWQaqQ`8VKwGnwit$qC*dsNxw>u1zI@sl3hx0BuO%<^E z*L>(oi|~&4OE3`TjbfMJn4<8>Q;i*7=1`f~0=ys1DzMFuKXrHQ4aL$YIvokd5mJPl z!4P)F_G;so6BfYW&DHFM>!v-w%+7zovSM6w`k`$eNb*%-x8BTWE#t29YlCQ1Q$@GL z2Lx#O5H7yQTdG{tRFsG?083NU#0APh-6Ru2dX;ZTe`dYWspSLGZ!abVfNH)aWmegR zKS*MT?V_AHzL_p?@Iu_?zz&wVw!AXTxb72-f5DSXdfXiXHj^ZT$OxlGzT2ibY}mAiAb zm##ilj$P?Z1(1YjFSo3YR%4_+7ISQR><9(QJt}I(8?4v6KFOxwN7u_&QP0l$c5od4 z<=m3i9DVv@Ta1BhO-Z|x`?gh`Pw7K^pL@C8x5-oOZWE%f^liW*yHg~d3aF@{iTN10 zHvfJ8e-gn}-5!d&5Pt%*B;dS6HbHX=&+nqeXFuOV1=vvHI4;GwMq64CU>i&ooC8{f zci1dmbL@jTw_=HUmVVooRl9o+b1q!0oEV+fHIJ=&UKn56Fi^gED{0S;xq)q*+=se+ z)?2&kkWm+2f76|>Q@FTao_uk1t6rY-Y-pXzPVIi$lvFe%Z=4K}j{ORP{~JYYf_(Ez!QikwW?%RhY7Eu)17P}6W6gA8NHXblsFx9 z{Cf&_u1b7Qp=55_%jvOT=#YK+2pv1_T~XLKog|~1XT2C4Bx7&D16l1QNAE04AM7;k z-8%7L)Pfryov%Ng67UyE`ulgnc3ExF^*;@re)Z^oZutL-Xw0mmRh7D^#gyjPCT7}r zomFsfHN`-vjcXC95ti{NIrnguwcp1e6EsgGj?OiDw^2azK4@CmAoDmZ!>p%hz9{3M zQA}J!s0UO(G|LyY)H*jG05;`lXeAZWUiTWEB_R{f2^m zp{e4~n-1>{XWSc8g%d3+ncHrQ)E$o6+mS;IN94pvGZ<+Z{oQmoBt1sc72r_&@Om-s zAlcdZ>9b)E++^XR+jnR_D+dJ#@ses0D<;9#y+7^sx>suCPXWHYZ(ig~_q&Nwc=Rta z$~}}CbdcYZt<>ln;tFh#{#8f4lTe^N9qO1fSFVvq!4>wgLR1q`G0j!)7CIz^)l3f4 z`nVY82ISsURb?1D0a$karfv6i%j9yvxY{|sxK2C*SBYv6w{6eQLDRdSz*NyO&pYrk+`jBr8P8R?V;=rxE^H==6=m&< zCcUM2>x(`VnV`zOgz;jF=dD1cOa)I35zHT_X3j839JMUfb(&qw3&zN(%HQv1Q{OjX zS=P-Tgu5TmM`~1=aAA%LV}h-ijPH^UT4p*@ zyhZZgTb_t~E+hQ1D(&uV2#9es_a)yBofdCu{vo;dJ6E+;?}!Wr{p%5GgpUKfN}a7A z1KI}aTAwX_{-PKHp+km8F2OQNk+;`Mho+0LOwAa-w6xR*B_r}(xGmJm++}AV{OGU~ zTkd&2h23KxL#oc!iIP?K`4(NJiwN&$goZP+QV^a3 zjMS>{NRr{0DX95kBU&Gct)XFR=cMevTlKGQ``--;$r7@xTs7J(M`Ut4NYppT;ggV5 zS0rqQ=EaL7kY3-NO2Du)D8}_OE-pZ?Gu>F~lkk*hVuO zznhyqwsdY+a-VK^KF{UjNzB0^Pd?R+<_=)e0P%-(nkptH91EsN9Gq$f>}Cy(2#m0j z2xcL_7R%E!8Xa)E4IA5vqu>7ljDKtJKgLgT5X|)8Gbfj91HVNoLDR3v9Ylu5OA0vw;gwfq8;5ZJQE#s>dZX2pSXmD zNe-#0sSCf}ATWK_4G^l#gk+ZUNBH`5t+g#XdU)SJ12bA!FF5 zbJ|%x*kI14PENEkOge?0F}K((b1(ZGv@&fV4jfG+`!f4!={)(edDi3nP^Nc(dBpKhEg?Imxe9w*&JAdp|Q8h`wg08PgJ{?#uAK zsk+VfSQb-Rv{N~P1hp>6ggi9mE|Z|;I0FN%kmE)gPW|{ z6iEN|FN#LA5Xvb5w@0&I$BUacz3Bm^hq(blJp~?He$8421~DRAIuTa06w!y%xv`fP zxqe*kD;RriuE#2@0s@32LFB>*&6};Qtu#O$#irgaY`H%$iULzs^gU(7M!%;sFITI# zng-v|f8KajH`*zJNjEA$+a?+(lK+7GR0I_g@F;%VZ!hItRuOPB<1?Xbgr!)tFGK$S znEvVC>ifWg$ic#?LLFXIORsqpr;FSQZFY@kc=Px~w%F zE&!&aq(nsGFiX4ztLuoaBHze{b=MasnadEZGpPwvAq{e?Pt5v}IM# zoo1UzeM-N4XnvAgN*|BJ*eAIhw<3?*ZA85C%nHa9KHNBY_q`DHV(5QQtU`R3-ueUt zh7_GCrF<+3mSGS0@la**&fMtDpj%fV^QqEk=fFfn%73mW{|4&M1?G_um1l-;$RSZ= zpr@zDf=faLnjBIb(xE{FR2UK;7`ANpA(xK52lkWs@qIRl&HIrCxPi@U@Mqs&a%}XfkoQk^D|?u zf54tTYRnF4;a08y=}X!Y^m_vP5tK4{D0io$IQC zj!F(H7QblGT&7Oh(R8Z%CGear&E`fL3z?<}rl(>pRy;Z_j7x~gCLH4%E#$3|$*f8m zSlS#ypRTT94(}2!3>Ex%6)oDm-H?SdVfx!xs(#Cs%l?lZKn?mK-y_>tmVkEs7Yg_( z8YMjdXcfhdLlXnH5|tS-16R)9_(p|F`pvl_`p2_%m^;$7=%1)2tK6nI+9}#^_-JA= zxdHD_nQxVL)!pyUb6aa?e0|}NDod=$7kBfy@2=rY=ze>j300Gk73HC{FLo5me-fRi zkZ8Fnrhh@ID)=nO6Ge+oJ2OA1#nh-M;H!*XYn6|=nZ+^2A9VQH=wYWM z2>U-n{jX7nNe}c8VZMU=iDNSyCQT79P619Uni2;qhQ7KjxXg<8m8w#l_;topWm;KN z_l-J0lgz?##(#b;H0_I)7_J@F+@bsND0Oa!*BX!0nRngD+atK?ue{#nE>g;waGd^WV-3w~$^DS;H66>Q9pg{ka?WKz5YbE%x{b&DJPIg4L;lQ-Y! z{FZqqxC=A6#y8p=80@LQWtKwYxyjSkbq&W$8l))_*q%3Aa8H@gqE4!_e*JcGAhVKJ zaoQlWau>s#LyG2*&keZVza8xs((l@FpG@8OKCk(2FNqq|FWwW`eP`Rz5GhedRIs1d zpynDbm53trXcP}+))Ob$^b;&U&n{+}0{;F-ZFRXeyVS?~9<{JG&)Klc!!ust*3kXS zg(63|z?}P6Whw%=;#%k=R^y7OYvcCG&hE=rpEqEe_}scqmE~x?!t+VF&Adf@<;`8w z>fJeK_ zF6kP>jmvS{?zeM?)MEqe!YP2%0 zX)Ehn)ot%HE*i`U@28Oy795%jdP8LK{$-{wg8y=%|LIM8L@Bq6fnNvq;&F~SKhIhV z3#kI=c4UcQ1yK}VqexNR*a|9!9AFv)?h7^{;e*6#h`5M{2Bb>Z6C_Hi<0FT{ok-~= zGZG?ApEojwpDpJQ(tS2*%v8)80Ii&8l0|AM~~+7;;SZx{&^ z^D^^i)WRAl0a;ewXa`H-#+4k6`z20H^=T)~l3+fgh*KhM9id0a?qkiSnh}lHY|L1? zsDPFA8F(v!FpN=X7T=i9P#{k%Jx27u+B?s;CbM;oBW5J(s3agLH6)>^lo2UXLQNo{ zN^t}%p^6M3(kEhw2%2P&k|0HC3K2vd!GI_o6+?gn3;`(u(utu2*@PN`C+^w%!^A(} zd^!30Ue}XVp69pjb+5Moa8*O*^D>zIeVcALY%XnT#q_PZ@K!X08R2ETg^o4Y zf4l#8CM)a{|GdsTqVqWz*CrEy1T)tH-dQNQxD}<^=z)F42lWh&CGa{Q9n&RVc>7aj zVjd$0B7;&QTd>rE6*p7RRsEC@M~xmdOq>I$j+=qDJ!S7$ggqzmzAh^}mpT{ZIGOD* zqsF$CJGQ4BJ7Og#&ap-K2HG&#r88odV3x#sZNIQ|)CJ}@_sIitA993GqkMt&R>4;< zWdMe;Uc-paEtjw+!5UnbvoKUC5tBme4EMf!p(Y}LY=)1p|D2E5R*PBJdt@dWfkyE_T9KNv=zHm5DS&f_zgWGfvBwB-Z61i9tp&5ls)w`HZQt|y8=tm=NFM|zXEGbV*> z{^SD(I7Jb;YuWOaKuC0$F@gUZJBr@<;s8PMe&rjcf~DLgr=ugzQ<+1C8g8sExlWds;o_TqLUPIIxuilh(Az-&- zf~$zk0V)`aPp3p*u20Xfi@VR<PKd`M;Oy>+fMA{ro&50=aw@3KQ8A#1Ov^SB~-FT~s)yN{zZ@&hlJ!RQ@*K%gHbz ztZjbeydlBqGos1;lwI2-V3qO;^b8=AC0Bxz99ogIX_>fmDw!|IV$BBxo9cH zKm{u1ai(LaiGg!a-Ok5XocC$p5Dk&b#hl*mrkn!PWhRd&wes%lA;rSk%<59Vb4!cu zf&&jkCI|GU4uDLkUuMX6B-bUR3HR$$}T@k1QU)oZz=MFVydJQ;J#0vRDCPLo<*{r^ncW8dccOp z>m!y2-}j!-F+G)!h>p1awROd6xKd>yGBnY3Sfo9~jAjy)Wz3JIGR1n-rj(!#nh&+a z+l+k4^+$q7NoLdm-G(`*PuI_M%qa_eye8t2@toKxEBew9l9i6>%CMQ9%%Q98YpW4F zDP$|ts*Y3q+vVJN8^YS%Ipyr6u1vU6orrZ1suKU5;sV*B9hTJq;^QXH; z9a;_tsctb>{HLsxTzdw8vAJ_iR~vaOv(eG}tUF^IC*1H^8L&%dgF*+`sSRd2jrfqR zV(X^SyImp}AvOD`jH||LRtv0kR;+<@-+7eSsJ&wC5N%FyWcfC(%_NJuHbcF z{q(4*o`0xDeUkV;LmksQ1*f?7(2L)hj$|#-*&U2*V`+R9{?KzfIKKGPiiL5^LL&+i zo)PPLCVr|%@rQqIfPGzVRdyz9Pbp{1?qIhkWjnI_uAY;psQJx32qwW?Z)#rO;*@6F zBNZv=u3>ed{CHhlW6owOinqS|Sd3Lz3eb)2to0~(+@ynTG!?8Ae!IZ<5ZR4*jV@lU zcTpBPRf-8`|Dd7d@S&;Pt2c*Z(vKe&6*zp`W@*~L<&{{8d1wi!$4YF&v1%_FDU;K_ z*5~G|g>w*oFLcezJ*2->c!vGURv7Y+#oL4OmAQRL_XlMujNRc^6plDgQ}I1PMQL^> z>J?6us+F~YMHjO&Ipkc$OUQnH6Q5qvpo3$rG4-uN`Y(zF8lS9yf~TGFfn*hR3mw(0X>dv z>JTOyO-&yeXmU(Z1%iO(mb_uThaX)TuDDXhT7ZJVOBcjt)2Z4CatT%lV=&o`;p2OxB;Mayw zW|F8zh7IIr6-z798So6$_;y}88DM2e*TE0G95d)J$znvW5%Vf^Mz5F42lCg6z%M$< zA;Skj8~ENs;r(Ou9m!0Ck_wzmxW;^>5Zxu2T&0{tA&8+zvWpvHVN%n=`6K7uli~vIG$hvdWCCRTTius1}%BSl!_1-G`L&b?`0^+vgig=oOQELMG+`3+=i7s z%p1FN2BLW5LrR_`W!iA&g8jeVGzFvHYdlkU>qkIJ*JRHFm2P2vm2Is~ZC&cBak|$j zR9IR3>q@~IJJMoez$+M}rTYYLHaARr;P>jtb_&)%bKY@yMRUesDB?LcUY$ugv3k~U z0GBo2=NaRK%!-)mh>rW~r`ScAIfwePXJ+uEzKg8#PgY+i=3{5V-{+yJw!^EcF^>hR zlFNdb(O%KiAC~}10+RLx>9Rva_Oqq8IgEaf#l}sQvB=f*;wpB7!f(06T|j$ zn;Dt!76#$HZW4rUeJqcEWtx0X=%}(-X7m6cY<>Ac^f`63l&*{4nbH=pn+N-+Z zf6^Meo|T3W^3LxY`9SE)N3$-kDmc7sy-j*=SyL!j2sA`x7|&TRDuvRu7R`m}zH@4c zEGl-tA^>*=-&_Pm_@6RrT+JH`OL2FZjbR%s>C-YIXdQzErnm@@9su~(a_24|WY~3} z$7gF;BaqLs*U?Io)Yq5Ej!DrtF_5CQJiQ5xW*9Y@7}EVFZ5#6Bqal8G(m@|r)`r7$ zK#nb$aVcNaZaE+j)}z{&>fazOH*Y6lc#pz)|W%N&lm>m zky?$sS`pmZYPoK*gDnM0XW8 zpnRMDT7G4@0%vNlucB#ZwLJMbZYJ|wmWI+TMwO8#ZZWa}trmd#txTQFBbsmf;P&83 zMyp%8&3Bg5RE?Q0(M3YnJeUkIj^&QHn!7Tq5H2oIVqa@yl{L-^J&X*+aSL~NrJU{h z%ZD$0k1sqFCq_UiBYwbBiy3>^GY<*eqr&MzSdS%&(#bm%a8>;TGrKdYPB&heVqN?^ zV!F#92uO2@ahQ-}wc^pLt|&aCa$CFZv(fo^t56%Bq|uy}($H3?KCOGGGEg>X%8QLs z0C}w;!bajq#|PpXx6+XXrFY{@%&Vbk4pUM;Jsu(HLLY0oa}5VlpQtZJH?XPb+!52t zDkb64u*yT}lSLOHpeEpsw~EU&DAUiRrTw(?J2dicneTT)&$VFO#l|0G1h)sI(xF)n z4f1s6mRmd6+1jio9<6yk;Nx3&QxFg)GMi~Idp$! zaP@`iE4z8sbhKC4n0uutW==3B{MQAsie;6KU<5o332p;>BGrbBZJ{93 z;!8z$NXijckX9-57qM8^ppjN#(7V7Bs|KhuG-~8j%?qtRbA=om6}T;mmOn(C?{xFa zQHo`COM0X=`~XHT)pkgwnlZp*s_C{Z(W7Y&4H=A@@Wn<$x&)UbjAA=jtMN~J!vYXv z`!G5zrge#nMbQ2lylNH>q=j?)b*;fEW36lb8c)I-7tJJdP~!^ktLG)~r+&RU@Opla zXtn;mHJpc4#F1%^DEOb{fzMn-WXOArH<^@^8zXoYRJ*pRpm0*>=?wux{4ZkV+gHcM zPpDj5uw9XUzq#a;#Tc7RDK^2LfL3 z>xM7-yF!BkztkQ7p^J9S9Sg6XnKabS3&$`YJeU|w1!GKZk4202qrzG|64}DN1&!Gl z(N1o5QLo^e*CSaj%GR&U{~5Izp;Ez@{S@cAs3eyFb9z`_t+>4F%!+7Td^hBqtAz{a zF!kvLkrDZK!knHIUbMX-*0(L1Ki`+x3N2y1Rnw0D;EsK7ck`apKVIjLh?Vm=7j?VWG<0L6G{QtZ9e>bb+ a-*y>1LXL87C}xQXe;AY#@`aW6t^Wlz4L8^T diff --git a/mkdocs.yml b/mkdocs.yml index 203bad85..a0af5426 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -32,30 +32,29 @@ plugins: show_source: true nav: - - HOME: - - README.md + - Home: README.md - User Guide: - - Structure Generation: - - Bulk: User_Guide/Structure_Generation/bulk.md - - Surfaces: User_Guide/Structure_Generation/surface.md - - Single Atom Alloys: User_Guide/Structure_Generation/saa.md - - Adsorption: User_Guide/Structure_Generation/adsorption.md - Sequential Learning: - Featurizers: User_Guide/Learning/featurizers.md - Predictors: User_Guide/Learning/predictors.md - Sequential: User_Guide/Learning/sequential.md + - Structure Generation: + - Bulk: User_Guide/Structure_Generation/bulk.md + - Surfaces: User_Guide/Structure_Generation/surface.md + - Adsorption: User_Guide/Structure_Generation/adsorption.md + - Single Atom Alloys: User_Guide/Structure_Generation/saa.md - Data: - HHI: User_Guide/Data/hhi.md - Segregation Energies: User_Guide/Data/segregation_energies.md - Lattice Parameters: User_Guide/Data/lattice_parameters.md - Intermediates: User_Guide/Data/intermediates.md - API: - - Structure Generation: - - autocat.bulk: API/Structure_Generation/bulk.md - - autocat.surface: API/Structure_Generation/surface.md - - autocat.saa: API/Structure_Generation/saa.md - - autocat.adsorption: API/Structure_Generation/adsorption.md - Sequential Learning: - autocat.learning.featurizers: API/Learning/featurizers.md - autocat.learning.predictors: API/Learning/predictors.md - autocat.learning.sequential: API/Learning/sequential.md + - Structure Generation: + - autocat.bulk: API/Structure_Generation/bulk.md + - autocat.surface: API/Structure_Generation/surface.md + - autocat.adsorption: API/Structure_Generation/adsorption.md + - autocat.saa: API/Structure_Generation/saa.md From a9e901f06bb7e61c2c334e31bb36f99d9482614b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 18 Jan 2022 11:14:24 -0500 Subject: [PATCH 187/239] add logo and ads img to home --- docs/README.md | 5 +++++ docs/img/autocat_logo.png | Bin 111373 -> 27285 bytes 2 files changed, 5 insertions(+) diff --git a/docs/README.md b/docs/README.md index 2ad2080b..76cc92fd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,7 @@ # AutoCat Documentation + +![AutoCat Logo](img/autocat_logo.png){ align=right } + AutoCat is a suite of python tools for sequential learning with arbitrary candidate metrics for materials applications. It additionally has tools for automated structure generation related to catalysis. @@ -28,6 +31,8 @@ key objects intended for each of these purposes are: ## Structure Generation +![Adsorption Figure](img/struct_gen_figs/adsorption.png){ align=right } + This submodule contains functions for automating atomic structure generation within the context of a catalysis study using density functional theory. Specifically, this includes generating bulk structures, surfaces, and diff --git a/docs/img/autocat_logo.png b/docs/img/autocat_logo.png index 131058a8b51c9ac80c406745cc7a4030fbb03dea..49ec457c3ba2a51d2a0a796a331be5bc61be4709 100644 GIT binary patch literal 27285 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 OZMfWYS+<_*-v0+Ww<&@E From cd4669dfe52d1ab5f3cb686f05020a65ee7deab0 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 18 Jan 2022 13:56:12 -0500 Subject: [PATCH 188/239] fix sine, coulomb mat multi feat shape --- src/autocat/learning/featurizers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 3c157222..5651e903 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -206,7 +206,7 @@ def featurize_single(self, structure: Atoms): structure, positions=adsorbate_indices, ) elif feat_class in [SineMatrix, CoulombMatrix]: - return self.featurization_object.create(structure) + return self.featurization_object.create(structure).reshape(-1,) elif feat_class in SUPPORTED_MATMINER_CLASSES: conv = AseAtomsAdaptor() pym_struct = conv.get_structure(structure) From 44eb1f4ed9ffb3a56c9316701b348e40bb8c9fca Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 18 Jan 2022 13:57:26 -0500 Subject: [PATCH 189/239] extend feat multi tests --- tests/learning/test_featurizers.py | 72 +++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 7bb5ded5..95f68741 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -134,14 +134,14 @@ def test_featurizer_featurize_single(): f.featurizer_class = SineMatrix acf = f.featurize_single(saa) sm = SineMatrix(n_atoms_max=len(saa), permutation="none") - manual_sm = sm.create(saa) + manual_sm = sm.create(saa).reshape(-1,) assert np.array_equal(acf, manual_sm) # test CoulombMatrix f.featurizer_class = CoulombMatrix acf = f.featurize_single(saa) cm = CoulombMatrix(n_atoms_max=len(saa), permutation="none") - manual_cm = cm.create(saa) + manual_cm = cm.create(saa).reshape(-1,) assert np.array_equal(acf, manual_cm) # TEST SITE FEATURIZERS @@ -209,6 +209,8 @@ def test_featurizer_featurize_multiple(): # tests featurizing multiple structures at a time # TEST STRUCTURE FEATURIZER + + # test ElementProperty saas = extract_structures( generate_saa_structures( ["Au", "Cu"], ["Pd", "Pt"], facets={"Au": ["111"], "Cu": ["111"]} @@ -222,12 +224,38 @@ def test_featurizer_featurize_multiple(): manual_mat = np.array(manual_mat) assert np.array_equal(acf, manual_mat) + # test SineMatrix + f.featurizer_class = SineMatrix + acf = f.featurize_multiple(saas) + manual_mat = [] + for i in range(len(saas)): + manual_mat.append(f.featurize_single(saas[i])) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) + + # test CoulombMatrix + f.featurizer_class = CoulombMatrix + acf = f.featurize_multiple(saas) + manual_mat = [] + for i in range(len(saas)): + manual_mat.append(f.featurize_single(saas[i])) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) + # TEST SITE FEATURIZER ads_structs = [] for struct in saas: ads_structs.append( extract_structures(place_adsorbate(struct, "NNH", position=(0.0, 0.0)))[0] ) + species_list = [] + for s in ads_structs: + # get all unique species + found_species = np.unique(s.get_chemical_symbols()).tolist() + new_species = [spec for spec in found_species if spec not in species_list] + species_list.extend(new_species) + + # test SOAP f.featurizer_class = SOAP f.design_space_structures = ads_structs f.kwargs = {"rcut": 6.0, "lmax": 6, "nmax": 6} @@ -237,3 +265,43 @@ def test_featurizer_featurize_multiple(): manual_mat.append(f.featurize_single(ads_structs[i]).flatten()) manual_mat = np.array(manual_mat) assert np.array_equal(acf, manual_mat) + + # test ACSF + f.featurizer_class = ACSF + f.kwargs = {"rcut": 6.0} + acf = f.featurize_multiple(ads_structs) + manual_mat = [] + for i in range(len(ads_structs)): + manual_mat.append(f.featurize_single(ads_structs[i]).flatten()) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) + + # test ChemicalSRO + f.featurizer_class = ChemicalSRO + vnn = VoronoiNN(cutoff=10.0, allow_pathological=True) + f.kwargs = {"nn": vnn, "includes": species_list} + acf = f.featurize_multiple(ads_structs) + manual_mat = [] + for i in range(len(ads_structs)): + manual_mat.append(f.featurize_single(ads_structs[i]).flatten()) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) + + # test OPSiteFingerprint + f.featurizer_class = OPSiteFingerprint + acf = f.featurize_multiple(ads_structs) + manual_mat = [] + for i in range(len(ads_structs)): + manual_mat.append(f.featurize_single(ads_structs[i]).flatten()) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) + + # test CrystalNNFingerprint + f.featurizer_class = CrystalNNFingerprint + f.preset = "cn" + acf = f.featurize_multiple(ads_structs) + manual_mat = [] + for i in range(len(ads_structs)): + manual_mat.append(f.featurize_single(ads_structs[i]).flatten()) + manual_mat = np.array(manual_mat) + assert np.array_equal(acf, manual_mat) From 83becc52b93ba2c8d78e0a85a6d54977315a5daa Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 18 Jan 2022 18:00:28 -0500 Subject: [PATCH 190/239] start featurizers doc --- docs/User_Guide/Learning/featurizers.md | 91 +++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/docs/User_Guide/Learning/featurizers.md b/docs/User_Guide/Learning/featurizers.md index e69de29b..de743a9f 100644 --- a/docs/User_Guide/Learning/featurizers.md +++ b/docs/User_Guide/Learning/featurizers.md @@ -0,0 +1,91 @@ +The `Featurizer` object allows for the featurization of +systems into a format that can be fed into machine learning +models. Specified within this object are all the desired +settings for when featurizing systems. More specifically this +includes: + +- `featurizer_class`: the desired class for featurization + +- `preset`: if the featurizer class can be instantiated by +a preset, that preset can be specified here. (e.g. the `magpie` feature +set for the `ElementProperty` featurizer class) + +- `design_space_structures`: if the design space is already known, +the structures can be specified here to extract the `max_size` and +`species_list` parameters. supercedes `max_size` and `species_list` +upon instantiation + +- `max_size`: the largest structure size that the featurizer can +encounter + +- `species_list`: all possible species that the featurizer can +encounter + +Applying the `Featurizer` there are two main methods: +`featurize_single` and `featurize_multiple`. The former is intended +for featurizing a single structure. On the other hand, the latter +can take multiple structures and returns them in a single feature +matrix. + +Below are three examples using structure, site, and compositional +featurization methods: + +```py +>>> from autocat.learning.featurizers import Featurizer +>>> from autocat.utils import extract_structures +>>> from autocat.surface import generate_surface_structures +>>> from dscribe.descriptors import SineMatrix +>>> surfs = extract_structures(generate_surface_structures(["Li", "Na"])) +>>> f = Featurizer(SineMatrix, design_space_structures=surfs) +>>> f.max_size +36 +>>> f.species_list +['Li', 'Na'] +>>> X = f.featurize_multiple(surfs) +``` + +```py +>>> from autocat.learning.featurizers import Featurizer +>>> from autocat.utils import extract_structures +>>> from autocat.surface import generate_surface_structures +>>> from autocat.adsorption import place_adsorbate +>>> from dscribe.descriptors import SOAP +>>> surf = extract_structures(generate_surface_structures(["Cu"]))[0] +>>> ads_struct = extract_structures(place_adsorbate(surf, "OH", position=(0.0, 0.0)))[0] +>>> f = Featurizer( +... SOAP, +... max_size=36, +... species_list=["Cu", "O", "H"] +... kwargs={"rcut": 6., "lmax": 8, "nmax": 8} +... ) +>>> X = f.featurize_single(ads_struct) +``` + +```py +>>> from autocat.learning.featurizers import Featurizer +>>> from autocat.utils import extract_structures +>>> from autocat.surface import generate_saa_structures +>>> from matminer.featurizers.composition import ElementProperty +>>> saas = extract_structures(generate_saa_structures(["Cu", "Au"],["Pt", "Pd"])) +>>> f = Featurizer(ElementProperty, preset="magpie", design_space_structures=saas) +>>> f.species_list +['Cu', 'Pt', 'Pd', 'Au'] +>>> X = f.featurize_multiple(saas) +``` + +The goal of this `Featurizer` object is to provide a unified class across different +featurization techniques. + +At present the following featurizer classes are supported: + +- [`dscribe`](https://singroup.github.io/dscribe/latest/): + - `SineMatrix` + - `CoulombMatrix` + - `ACSF` + - `SOAP` + +- [`matminer`](https://hackingmaterials.lbl.gov/matminer/): + - `ElementProperty` + - `ChemicalSRO` + - `OPSiteFingerprint` + - `CrystalNNFingerprint` \ No newline at end of file From 736e51294d1548a6fd6f33dac19542546204530c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 18 Jan 2022 19:05:01 -0500 Subject: [PATCH 191/239] update pred and seq docs for new feat obj --- docs/User_Guide/Learning/predictors.md | 34 ++++++++++++++++---------- docs/User_Guide/Learning/sequential.md | 28 +++++++++++++++------ 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/docs/User_Guide/Learning/predictors.md b/docs/User_Guide/Learning/predictors.md index 3696e22d..65ad67e1 100644 --- a/docs/User_Guide/Learning/predictors.md +++ b/docs/User_Guide/Learning/predictors.md @@ -6,10 +6,9 @@ object class. This contains two key attributes: - a regressor that can be fit to data and used for predictions (the class provided must have `fit` and `predict` methods) -- specified featurization methods that are available in -[`autocat.learning.featurizers`](../../API/Learning/featurizers.md). +- featurizer class and kwargs to instantiate a [`Featurizer`](featurizers.md). In particular there are two currently implemented approaches, -structure methods that featurize the entire structure (e.g. `sine matrix`) +structure methods that featurize the entire structure (e.g. `SineMatrix`, `ElementProperty`) and adsorbate methods that featurize locally (e.g. `SOAP`). Generally, this predictor object behaves similarly to regressors found in @@ -29,41 +28,50 @@ single atom alloys. >>> from autocat.learning.predictors import Predictor >>> from autocat.saa import generate_saa_structures >>> from autocat.utils import extract_structures +>>> from dscribe.descriptors import SineMatrix >>> from sklearn.ensemble import RandomForestRegressor >>> saa_dict = generate_saa_structures(["Cu", "Au", "Fe"], ["Pt", "Ru", "Ni"]) >>> saa_structs = extract_structures(saa_dict) >>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) >>> acp = Predictor( -... structure_featurizer="sine_matrix", model_class=RandomForestRegressor +... model_class=RandomForestRegressor, +... featurizer_class=SineMatrix, ... ) >>> acp.fit(saa_structs[:-1], labels) >>> pred, _ = acp.predict([saa_structs[-1]]) >>> pred array([0.]) ``` -Here we have chosen to featurize the structures as a `sine matrix`. +Here we have chosen to featurize the structures as a `SineMatrix`. + Note as well that the `predict` method will return uncertainty estimates if available. To see this, let's train a gaussian process regressor with an RBF - kernel. + kernel. Let's also featurize using `SOAP` to see how featurization kwargs are passed ```py >>> import numpy as np >>> from autocat.learning.predictors import Predictor ->>> from autocat.saa import generate_saa_structures +>>> from autocat.surface import generate_surface_structures >>> from autocat.utils import extract_structures +>>> from autocat.adsorption import place_adsorbate +>>> from dscribe.descriptors import SOAP >>> from sklearn.gaussian_process import GaussianProcessRegressor >>> from sklearn.gaussian_process.kernels import RBF ->>> saa_dict = generate_saa_structures(["Cu", "Au", "Fe"], ["Pt", "Ru", "Ni"]) ->>> saa_structs = extract_structures(saa_dict) ->>> labels = np.random.randint(1, size=(len(saa_structs) - 1)) +>>> subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) +>>> structs = [extract_structures(place_adsorbate(s, "OH"))[0] for s in subs] +>>> labels = np.random.randint(1, size=(len(structs) - 1)) >>> kernel = RBF() >>> acp = Predictor( -... structure_featurizer="sine_matrix", ... model_class=GaussianProcessRegressor, ... model_kwargs={"kernel": kernel}, +... featurizer_class=SOAP, +... featurization_kwargs={ +... "design_space_structures": structs, +... "kwargs": {"rcut": 6.0, "nmax": 6, "lmax": 6}, +... } ... ) ->>> acp.fit(saa_structs[:-1], labels) ->>> pred, unc = acp.predict([saa_structs[-1]]) +>>> acp.fit(structs[:-1], labels) +>>> pred, unc = acp.predict([structs[-1]]) >>> pred array([0.]) >>> unc diff --git a/docs/User_Guide/Learning/sequential.md b/docs/User_Guide/Learning/sequential.md index c81f9983..cbdd4569 100644 --- a/docs/User_Guide/Learning/sequential.md +++ b/docs/User_Guide/Learning/sequential.md @@ -60,7 +60,7 @@ The object stores information regarding the latest iteration of the sequential learning loop including: -1. A [`Predictor`](../../API/Learning/predictors.md#autocat.learning.predictors.Predictor) +1. A [`Predictor`](predictors.md) (and its kwargs for both the regressor and featurizer) 2. Candidate selection kwargs for score calculation (e.g. acquisition functions) 3. Iteration number 4. Latest `DesignSpace` @@ -69,29 +69,35 @@ iteration of the sequential learning loop including: This object can be thought of as a central hub for the sequential learning workflow, with an external driver -(either automated or manual) triggering iteration. +(either automated or manual) triggering iteration. The first +`iterate` trains the model and identifies candidate(s) to +start the loop. ```py >>> import numpy as np >>> from autocat.surface import generate_surface_structures >>> from autocat.utils import extract_structures +>>> from autocat.adsorption import place_adsorbate >>> from autocat.learning.sequential import DesignSpace >>> from autocat.learning.sequential import SequentialLearner +>>> from dscribe.descriptors import SOAP >>> from sklearn.gaussian_process import GaussianProcessRegressor >>> from sklearn.gaussian_process.kernels import RBF ->>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) ->>> surf_structs = extract_structures(surf_dict) +>>> subs_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) +>>> subs = extract_structures(subs_dict) +>>> ads_structs =[extract_structures(place_adsorbate(s, "H"))[0] for s in subs] >>> labels = np.array([0.95395024, 0.63504885, np.nan, 0.08320879, np.nan, ... 0.32423194, 0.55570785, np.nan, np.nan, np.nan, ... 0.18884186, np.nan]) ->>> acds = DesignSpace(surf_structs, labels) +>>> acds = DesignSpace(ads_structs, labels) >>> kernel = RBF() >>> acsl = SequentialLearner( ... acds, ... predictor_kwargs={ -... "structure_featurizer": "sine_matrix", ... "model_class": GaussianProcessRegressor, ... "model_kwargs": {"kernel": kernel}, +... "featurizer_class": SOAP, +... "featurization_kwargs": {"kwargs": {"rcut": 5.0, "lmax": 6, "nmax": 6}} ... }, ... candidate_selection_kwargs={ ... "aq": "MLI", @@ -104,6 +110,9 @@ sequential learning workflow, with an external driver ... ) >>> acsl.iteration_count 0 +>>> acsl.iterate() +>>> acsl.iteration_count +1 ``` ## Simulated Sequential Learning @@ -126,6 +135,7 @@ each iteration are kept. >>> from autocat.utils import extract_structures >>> from autocat.learning.sequential import DesignSpace >>> from autocat.learning.sequential import simulated_sequential_learning +>>> from dscribe.descriptors import SineMatrix >>> from sklearn.gaussian_process import GaussianProcessRegressor >>> from sklearn.gaussian_process.kernels import RBF >>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) @@ -138,9 +148,9 @@ each iteration are kept. >>> sim_sl = simulated_sequential_learning( ... full_design_space=acds, ... predictor_kwargs={ -... "structure_featurizer": "sine_matrix", ... "model_class": GaussianProcessRegressor, ... "model_kwargs": {"kernel": kernel}, +... "featurizer_class": SineMatrix, ... }, ... candidate_selection_kwargs={ ... "aq": "MLI", @@ -172,6 +182,7 @@ this function can also initiate the multiple runs across parallel processes via >>> from autocat.utils import extract_structures >>> from autocat.learning.sequential import DesignSpace >>> from autocat.learning.sequential import multiple_simulated_sequential_learning_runs +>>> from matminer.featurizers.composition import ElementProperty >>> from sklearn.gaussian_process import GaussianProcessRegressor >>> from sklearn.gaussian_process.kernels import RBF >>> surf_dict = generate_surface_structures(["Pt", "Pd", "Cu", "Ni"]) @@ -184,9 +195,10 @@ this function can also initiate the multiple runs across parallel processes via >>> multi_sim_sl = multiple_simulated_sequential_learning_runs( ... full_design_space=acds, ... predictor_kwargs={ -... "structure_featurizer": "sine_matrix", ... "model_class": GaussianProcessRegressor, ... "model_kwargs": {"kernel": kernel}, +... "featurizer_class": ElementProperty, +... "featurization_kwargs": {"preset": "matminer"}, ... }, ... candidate_selection_kwargs={ ... "aq": "MLI", From 6becacb5450530b9ffecc13810c2edcc1af073de Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 19 Jan 2022 14:41:08 -0500 Subject: [PATCH 192/239] fix featurizer aliasing --- src/autocat/learning/featurizers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 5651e903..ba9e3f9c 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -117,7 +117,7 @@ def kwargs(self): @kwargs.setter def kwargs(self, kwargs): if kwargs is not None: - self._kwargs = kwargs + self._kwargs = kwargs.copy() @property def design_space_structures(self): @@ -126,7 +126,9 @@ def design_space_structures(self): @design_space_structures.setter def design_space_structures(self, design_space_structures: List[Atoms]): if design_space_structures is not None: - self._design_space_structures = design_space_structures + self._design_space_structures = [ + struct.copy() for struct in design_space_structures + ] # analyze new design space ds_structs = design_space_structures species_list = [] @@ -157,7 +159,7 @@ def species_list(self): @species_list.setter def species_list(self, species_list: List[str]): if species_list is not None: - self._species_list = species_list + self._species_list = species_list.copy() @property def featurization_object(self): From 50acd69731065bc52fe6a4b9162e7d252c5e9333 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 19 Jan 2022 15:08:51 -0500 Subject: [PATCH 193/239] fix predictor aliasing --- src/autocat/learning/predictors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 656f505c..58d210fb 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -131,7 +131,7 @@ def model_kwargs(self): @model_kwargs.setter def model_kwargs(self, model_kwargs): if model_kwargs is not None: - self._model_kwargs = model_kwargs + self._model_kwargs = copy.deepcopy(model_kwargs) if self.is_fit: self.is_fit = False self.X_ = None @@ -168,7 +168,7 @@ def featurization_kwargs(self): def featurization_kwargs(self, featurization_kwargs): if featurization_kwargs is not None: assert isinstance(featurization_kwargs, dict) - self._featurization_kwargs = featurization_kwargs + self._featurization_kwargs = featurization_kwargs.copy() self.featurizer = Featurizer(self.featurizer_class, **featurization_kwargs) if self.is_fit: self.is_fit = False From 988342001dd0c5dcbf6bb3d7cb664a97ba076c9c Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 20 Jan 2022 11:48:07 -0500 Subject: [PATCH 194/239] rm multiple_separate_models --- src/autocat/learning/predictors.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 58d210fb..d169a9f8 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -183,9 +183,7 @@ def copy(self): Returns a copy """ acp = self.__class__( - model_class=self.model_class, - featurizer_class=self.featurizer_class, - multiple_separate_models=self.multiple_separate_models, + model_class=self.model_class, featurizer_class=self.featurizer_class, ) acp.regressor = copy.deepcopy(self.regressor) acp.is_fit = self.is_fit From 8f4baed01195b4565c9f94160188e89c4444c1b3 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 20 Jan 2022 13:36:42 -0500 Subject: [PATCH 195/239] clean feat and get feat obj, add default class --- src/autocat/learning/featurizers.py | 117 +++++++++++++--------------- 1 file changed, 52 insertions(+), 65 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index ba9e3f9c..3e496a6e 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -33,7 +33,7 @@ class FeaturizerError(Exception): class Featurizer: def __init__( self, - featurizer_class, + featurizer_class=None, design_space_structures: List[Atoms] = None, species_list: List[str] = None, max_size: int = None, @@ -41,7 +41,7 @@ def __init__( kwargs: Dict = None, ): - self._featurizer_class = None + self._featurizer_class = SineMatrix self.featurizer_class = featurizer_class self._preset = None @@ -167,22 +167,15 @@ def featurization_object(self): def _get_featurization_object(self): # instantiate featurizer object - if self.preset is not None: - try: - return self.featurizer_class.from_preset(self.preset) - except: - msg = f"{self.featurizer_class} cannot be initialized from the preset {self.preset}" - raise FeaturizerError(msg) - else: - if self.featurizer_class in [SineMatrix, CoulombMatrix]: - return self.featurizer_class( - n_atoms_max=self.max_size, permutation="none", **self.kwargs or {}, - ) - elif self.featurizer_class in [SOAP, ACSF]: - return self.featurizer_class( - species=self.species_list, **self.kwargs or {} - ) - return self.featurizer_class(**self.kwargs or {}) + if hasattr(self.featurizer_class, "from_preset") and self.preset is not None: + return self.featurizer_class.from_preset(self.preset) + if self.featurizer_class in [SineMatrix, CoulombMatrix]: + return self.featurizer_class( + n_atoms_max=self.max_size, permutation="none", **self.kwargs or {}, + ) + if self.featurizer_class in [SOAP, ACSF]: + return self.featurizer_class(species=self.species_list, **self.kwargs or {}) + return self.featurizer_class(**self.kwargs or {}) def featurize_single(self, structure: Atoms): """ @@ -201,53 +194,47 @@ def featurize_single(self, structure: Atoms): Numpy array of feature vector (not flattened) """ feat_class = self.featurizer_class - if feat_class in SUPPORTED_DSCRIBE_CLASSES: - if feat_class in [SOAP, ACSF]: - adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() - return self.featurization_object.create( - structure, positions=adsorbate_indices, - ) - elif feat_class in [SineMatrix, CoulombMatrix]: - return self.featurization_object.create(structure).reshape(-1,) - elif feat_class in SUPPORTED_MATMINER_CLASSES: - conv = AseAtomsAdaptor() - pym_struct = conv.get_structure(structure) - if feat_class == ElementProperty: - return np.array( - self.featurization_object.featurize(pym_struct.composition) - ) - elif feat_class in [CrystalNNFingerprint, OPSiteFingerprint]: - representation = np.array([]) - adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() - for idx in adsorbate_indices: - feat = self.featurization_object.featurize(pym_struct, idx) - representation = np.concatenate((representation, feat)) - return representation - elif feat_class == ChemicalSRO: - species_list = self.species_list - adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() - formatted_list = [[pym_struct, idx] for idx in adsorbate_indices] - featurization_object = self.featurization_object - featurization_object.fit(formatted_list) - # concatenate representation for each adsorbate atom - representation = np.array([]) - # TODO: order species_list so that this is no longer needed - for idx in adsorbate_indices: - raw_feat = featurization_object.featurize(pym_struct, idx) - # csro only generates for species observed in fit - # as well as includes, so to be generalizable - # we use full species list of the design space and place values - # in the appropriate species location relative to this list - labels = featurization_object.feature_labels() - feat = np.zeros(len(species_list)) - for i, label in enumerate(labels): - # finds where corresponding species is in full species list - lbl_idx = np.where( - np.array(species_list) == label.split("_")[1] - ) - feat[lbl_idx] = raw_feat[i] - representation = np.concatenate((representation, feat)) - return representation + featurization_object = self.featurization_object + # dscribe classes + if feat_class in [SOAP, ACSF]: + adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() + return featurization_object.create(structure, positions=adsorbate_indices,) + if feat_class in [SineMatrix, CoulombMatrix]: + return featurization_object.create(structure).reshape(-1,) + + # matminer classes + pym_struct = AseAtomsAdaptor().get_structure(structure) + if feat_class == ElementProperty: + return np.array(featurization_object.featurize(pym_struct.composition)) + representation = np.array([]) + if feat_class in [CrystalNNFingerprint, OPSiteFingerprint]: + adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() + for idx in adsorbate_indices: + feat = featurization_object.featurize(pym_struct, idx) + representation = np.concatenate((representation, feat)) + return representation + if feat_class == ChemicalSRO: + adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() + formatted_list = [[pym_struct, idx] for idx in adsorbate_indices] + featurization_object.fit(formatted_list) + # TODO: order species_list so that this is no longer needed + for idx in adsorbate_indices: + raw_feat = featurization_object.featurize(pym_struct, idx) + # csro only generates for species observed in fit + # as well as includes, so to be generalizable + # we use full species list of the design space and place values + # in the appropriate species location relative to this list + labels = featurization_object.feature_labels() + feat = np.zeros(len(self.species_list)) + for i, label in enumerate(labels): + # finds where corresponding species is in full species list + lbl_idx = np.where( + np.array(self.species_list) == label.split("_")[1] + ) + feat[lbl_idx] = raw_feat[i] + representation = np.concatenate((representation, feat)) + return representation + return None def featurize_multiple(self, structures: List[Atoms]): """ From daf49b33f158431687e880262f3d3300e7cd86bb Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 20 Jan 2022 14:27:44 -0500 Subject: [PATCH 196/239] order species by mendeleev number --- src/autocat/learning/featurizers.py | 37 +++++++++++++---------------- tests/learning/test_featurizers.py | 21 ++++++---------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 3e496a6e..b46928ea 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -15,6 +15,7 @@ from ase import Atoms from pymatgen.io.ase import AseAtomsAdaptor +from pymatgen.core.periodic_table import Element SUPPORTED_MATMINER_CLASSES = [ ElementProperty, @@ -53,7 +54,7 @@ def __init__( self._max_size = 100 self.max_size = max_size - self._species_list = ["Pt", "Pd", "Cu", "Fe", "Ni", "H", "O", "C", "N"] + self._species_list = ["Fe", "Ni", "Pt", "Pd", "Cu", "C", "N", "O", "H"] self.species_list = species_list # overrides max_size and species_list if given @@ -131,17 +132,21 @@ def design_space_structures(self, design_space_structures: List[Atoms]): ] # analyze new design space ds_structs = design_space_structures - species_list = [] + _species_list = [] for s in ds_structs: # get all unique species found_species = np.unique(s.get_chemical_symbols()).tolist() new_species = [ - spec for spec in found_species if spec not in species_list + spec for spec in found_species if spec not in _species_list ] - species_list.extend(new_species) + _species_list.extend(new_species) + # sort species list + sorted_species_list = sorted( + _species_list, key=lambda el: Element(el).mendeleev_no + ) self._max_size = max([len(s) for s in ds_structs]) - self._species_list = species_list + self._species_list = sorted_species_list @property def max_size(self): @@ -159,7 +164,12 @@ def species_list(self): @species_list.setter def species_list(self, species_list: List[str]): if species_list is not None: - self._species_list = species_list.copy() + _species_list = species_list.copy() + # sort species list by mendeleev number + sorted_species_list = sorted( + _species_list, key=lambda el: Element(el).mendeleev_no + ) + self._species_list = sorted_species_list @property def featurization_object(self): @@ -217,21 +227,8 @@ def featurize_single(self, structure: Atoms): adsorbate_indices = np.where(structure.get_tags() <= 0)[0].tolist() formatted_list = [[pym_struct, idx] for idx in adsorbate_indices] featurization_object.fit(formatted_list) - # TODO: order species_list so that this is no longer needed for idx in adsorbate_indices: - raw_feat = featurization_object.featurize(pym_struct, idx) - # csro only generates for species observed in fit - # as well as includes, so to be generalizable - # we use full species list of the design space and place values - # in the appropriate species location relative to this list - labels = featurization_object.feature_labels() - feat = np.zeros(len(self.species_list)) - for i, label in enumerate(labels): - # finds where corresponding species is in full species list - lbl_idx = np.where( - np.array(self.species_list) == label.split("_")[1] - ) - feat[lbl_idx] = raw_feat[i] + feat = featurization_object.featurize(pym_struct, idx) representation = np.concatenate((representation, feat)) return representation return None diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 95f68741..e863e30e 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -25,18 +25,18 @@ def test_featurizer_species_list(): # test default species list f = Featurizer(SineMatrix) - assert f.species_list == ["Pt", "Pd", "Cu", "Fe", "Ni", "H", "O", "C", "N"] + assert f.species_list == ["Fe", "Ni", "Pt", "Pd", "Cu", "C", "N", "O", "H"] - # test updating species list manually + # test updating species list manually and sorting f.species_list = ["Li", "Na", "K"] - assert f.species_list == ["Li", "Na", "K"] + assert f.species_list == ["K", "Na", "Li"] # test getting species list from design space structures surfs = extract_structures(generate_surface_structures(["Fe", "V", "Ti"])) saas = extract_structures(generate_saa_structures(["Cu", "Au"], ["Fe", "Pt"])) surfs.extend(saas) f.design_space_structures = surfs - assert f.species_list == ["Fe", "V", "Ti", "Cu", "Pt", "Au"] + assert f.species_list == ["Ti", "V", "Fe", "Pt", "Au", "Cu"] def test_featurizer_max_size(): @@ -73,7 +73,7 @@ def test_featurizer_design_space_structures(): assert f.design_space_structures == surfs # make sure design space is prioritized over max size and species list assert f.max_size == 36 - assert f.species_list == ["Li", "Na", "Cu", "Ni"] + assert f.species_list == ["Na", "Li", "Ni", "Cu"] def test_featurizer_featurizer_kwargs(): @@ -174,15 +174,8 @@ def test_featurizer_featurize_single(): csro = ChemicalSRO(vnn, includes=species) pym_struct = conv.get_structure(ads_struct) csro.fit([[pym_struct, 36], [pym_struct, 37]]) - manual_csro = np.array([]) - for idx in [36, 37]: - raw_feat = csro.featurize(pym_struct, idx) - labels = csro.feature_labels() - feat = np.zeros(len(species)) - for i, label in enumerate(labels): - lbl_idx = np.where(np.array(species) == label.split("_")[1]) - feat[lbl_idx] = raw_feat[i] - manual_csro = np.concatenate((manual_csro, feat)) + manual_csro = csro.featurize(pym_struct, -2) + manual_csro = np.concatenate((manual_csro, csro.featurize(pym_struct, -1))) assert np.array_equal(acf, manual_csro) # test OPSiteFingerprint From b4d27d2d7e32b33f74b29f658394690068df753f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 21 Jan 2022 13:20:24 -0500 Subject: [PATCH 197/239] use ase jsonio for atoms io --- src/autocat/learning/sequential.py | 70 +++++++++++------------------- tests/learning/test_sequential.py | 31 +++---------- 2 files changed, 30 insertions(+), 71 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 1f4d4b74..52fab3dc 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -4,14 +4,14 @@ import json import importlib from joblib import Parallel, delayed -import tempfile from typing import List from typing import Dict from typing import Union from prettytable import PrettyTable from ase import Atoms -from ase.io import read as ase_read +from ase.io.jsonio import encode as atoms_encoder +from ase.io.jsonio import decode as atoms_decoder from scipy import stats from sklearn.gaussian_process import GaussianProcessRegressor @@ -179,46 +179,32 @@ def write_json( write_to_disk: bool = True, return_jsonified_list: bool = False, ): - with tempfile.TemporaryDirectory() as _tmp_dir: - # write out all individual structure jsons - for i, struct in enumerate(self.design_space_structures): - tmp_filename = os.path.join(_tmp_dir, f"{i}.json") - struct.write(tmp_filename) - # load individual jsons and collect in list - collected_jsons = [] - for i in range(len(self.design_space_structures)): - tmp_filename = os.path.join(_tmp_dir, f"{i}.json") - with open(tmp_filename, "r") as f: - collected_jsons.append(json.load(f)) - # append labels to list of collected jsons - jsonified_labels = [float(x) for x in self.design_space_labels] - collected_jsons.append(jsonified_labels) - # set default json name if needed - if json_name is None: - json_name = "acds.json" - # write out single json - if write_to_disk: - json_path = os.path.join(write_location, json_name) - with open(json_path, "w") as f: - json.dump(collected_jsons, f) - # write jsonified list to memory - if return_jsonified_list: - return collected_jsons + collected_jsons = [] + for struct in self.design_space_structures: + collected_jsons.append(atoms_encoder(struct)) + # append labels to list of collected jsons + jsonified_labels = [float(x) for x in self.design_space_labels] + collected_jsons.append(jsonified_labels) + # set default json name if needed + if json_name is None: + json_name = "acds.json" + # write out single json + if write_to_disk: + json_path = os.path.join(write_location, json_name) + with open(json_path, "w") as f: + json.dump(collected_jsons, f) + # write jsonified list to memory + if return_jsonified_list: + return collected_jsons @staticmethod def from_json(json_name: str): with open(json_name, "r") as f: all_data = json.load(f) structures = [] - with tempfile.TemporaryDirectory() as _tmp_dir: - for i in range(len(all_data) - 1): - # write temp json for each individual structure - _tmp_json = os.path.join(_tmp_dir, "tmp.json") - with open(_tmp_json, "w") as tmp: - json.dump(all_data[i], tmp) - # read individual tmp json using ase - atoms = ase_read(_tmp_json, format="json") - structures.append(atoms) + for i in range(len(all_data) - 1): + atoms = atoms_decoder(all_data[i]) + structures.append(atoms) labels = np.array(all_data[-1]) return DesignSpace( design_space_structures=structures, design_space_labels=labels, @@ -557,15 +543,9 @@ def from_json(json_name: str): with open(json_name, "r") as f: all_data = json.load(f) structures = [] - with tempfile.TemporaryDirectory() as _tmp_dir: - for i in range(len(all_data) - 4): - # write temp json for each individual structure - _tmp_json = os.path.join(_tmp_dir, "tmp.json") - with open(_tmp_json, "w") as tmp: - json.dump(all_data[i], tmp) - # read individual tmp json using ase - atoms = ase_read(_tmp_json, format="json") - structures.append(atoms) + for i in range(len(all_data) - 4): + atoms = atoms_decoder(all_data[i]) + structures.append(atoms) labels = np.array(all_data[-4]) acds = DesignSpace( design_space_structures=structures, design_space_labels=labels, diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 1b2c562b..e18b72c4 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -14,7 +14,7 @@ from matminer.featurizers.composition import ElementProperty from scipy import stats -from ase.io import read as ase_read +from ase.io.jsonio import decode as ase_decoder from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES from autocat.data.segregation_energies import SEGREGATION_ENERGIES @@ -139,14 +139,7 @@ def test_sequential_learner_write_json(): acsl.write_json(_tmp_dir, "testing_acsl.json") with open(os.path.join(_tmp_dir, "testing_acsl.json"), "r") as f: sl = json.load(f) - # collects structs by writing each json individually - # and reading with ase - written_structs = [] - for i in range(3): - _tmp_json = os.path.join(_tmp_dir, "tmp.json") - with open(_tmp_json, "w") as tmp: - json.dump(sl[i], tmp) - written_structs.append(ase_read(_tmp_json)) + written_structs = [ase_decoder(sl[i]) for i in range(3)] assert structs == written_structs assert np.array_equal(labels, sl[3], equal_nan=True) # check predictor kwargs kept @@ -181,14 +174,7 @@ def test_sequential_learner_write_json(): acsl.write_json(_tmp_dir, "testing_acsl.json") with open(os.path.join(_tmp_dir, "testing_acsl.json"), "r") as f: sl = json.load(f) - # collects structs by writing each json individually - # and reading with ase - written_structs = [] - for i in range(3): - _tmp_json = os.path.join(_tmp_dir, "tmp.json") - with open(_tmp_json, "w") as tmp: - json.dump(sl[i], tmp) - written_structs.append(ase_read(_tmp_json)) + written_structs = [ase_decoder(sl[i]) for i in range(3)] assert structs == written_structs assert np.array_equal(labels, sl[3], equal_nan=True) # check predictor kwargs kept @@ -518,16 +504,9 @@ def test_write_design_space_as_json(): # loads back written json with open(os.path.join(_tmp_dir, "acds.json"), "r") as f: ds = json.load(f) - # collects structs by writing each json individually - # and reading with ase - written_structs = [] - for i in range(2): - _tmp_json = os.path.join(_tmp_dir, "tmp.json") - with open(_tmp_json, "w") as tmp: - json.dump(ds[i], tmp) - written_structs.append(ase_read(_tmp_json)) + written_structs = [ase_decoder(ds[i]) for i in range(2)] assert structs == written_structs - assert (labels == ds[-1]).all() + assert np.array_equal(labels, ds[-1]) def test_get_design_space_from_json(): From 5a620c2b4502e72ef306a010a5b3baced839cd4b Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 21 Jan 2022 18:14:43 -0500 Subject: [PATCH 198/239] add eq, copy methods to featurizer --- src/autocat/learning/featurizers.py | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index b46928ea..6f40778e 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -9,6 +9,7 @@ from matminer.featurizers.site import CrystalNNFingerprint import numpy as np +import copy from prettytable import PrettyTable from typing import List, Dict @@ -61,6 +62,20 @@ def __init__( self._design_space_structures = None self.design_space_structures = design_space_structures + def __eq__(self, other: object) -> bool: + if isinstance(other, Featurizer): + for attr in [ + "featurizer_class", + "species_list", + "max_size", + "preset", + "kwargs", + ]: + if getattr(self, attr) != getattr(other, attr): + return False + return True + return False + def __repr__(self) -> str: pt = PrettyTable() pt.field_names = ["", "Featurizer"] @@ -80,6 +95,24 @@ def __repr__(self) -> str: ) return str(pt) + def copy(self): + """ + Returns a copy of the featurizer + """ + ds_structs_copy = ( + [struct.copy() for struct in self.design_space_structures] + if self.design_space_structures + else None + ) + feat = self.__class__( + featurizer_class=self.featurizer_class, + design_space_structures=ds_structs_copy, + species_list=self.species_list.copy(), + max_size=self.max_size, + kwargs=copy.deepcopy(self.kwargs) if self.kwargs else None, + ) + return feat + @property def featurizer_class(self): return self._featurizer_class From 98838c20eea89e4ecc73afd40df146e68e3f4f43 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 21 Jan 2022 18:26:41 -0500 Subject: [PATCH 199/239] add test eq for featurizer --- tests/learning/test_featurizers.py | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index e863e30e..d1a7493f 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -22,6 +22,38 @@ from pymatgen.analysis.local_env import VoronoiNN +def test_eq_featurizer(): + # test comparing featurizers + + f = Featurizer( + SOAP, + max_size=5, + species_list=["Fe", "O", "H"], + kwargs={"rcut": 12, "nmax": 8, "lmax": 8}, + ) + f1 = Featurizer( + SOAP, + max_size=5, + species_list=["Fe", "O", "H"], + kwargs={"rcut": 12, "nmax": 8, "lmax": 8}, + ) + assert f == f1 + + f1.kwargs.update({"rcut": 13}) + assert f != f1 + + surfs = extract_structures(generate_surface_structures(["Fe", "V"])) + surfs.extend( + extract_structures( + generate_surface_structures(["Au", "Ag"], supercell_dim=(1, 1, 5)) + ) + ) + f = Featurizer(SineMatrix, design_space_structures=surfs,) + + f1 = Featurizer(SineMatrix, species_list=["Fe", "V", "Au", "Ag"], max_size=36) + assert f == f1 + + def test_featurizer_species_list(): # test default species list f = Featurizer(SineMatrix) From 4c24ddc745bd8713ed92c8810e55a90857ca6adc Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 25 Jan 2022 17:33:31 -0500 Subject: [PATCH 200/239] add Rao 2020 saa stability dataset --- .../data/segregation_energies/__init__.py | 25 +- ...gregation_energies.json => raban1999.json} | 0 .../data/segregation_energies/rao2020.json | 842 ++++++++++++++++++ src/autocat/learning/sequential.py | 45 +- tests/learning/test_sequential.py | 26 +- 5 files changed, 907 insertions(+), 31 deletions(-) rename src/autocat/data/segregation_energies/{segregation_energies.json => raban1999.json} (100%) create mode 100644 src/autocat/data/segregation_energies/rao2020.json diff --git a/src/autocat/data/segregation_energies/__init__.py b/src/autocat/data/segregation_energies/__init__.py index 63e0b849..4c3265a3 100644 --- a/src/autocat/data/segregation_energies/__init__.py +++ b/src/autocat/data/segregation_energies/__init__.py @@ -1,18 +1,29 @@ import json import pkg_resources -__all__ = ["SEGREGATION_ENERGIES"] +__all__ = ["RABAN1999_SEGREGATION_ENERGIES", "RAO2020_SEGREGATION_ENERGIES"] """ -Values obtained from https://doi.org/10.1103/PhysRevB.59.15990 -SEGREGATION_ENERGIES: +RABAN1999_SEGREGATION_ENERGIES: + Values obtained from https://doi.org/10.1103/PhysRevB.59.15990 Segregation energies for different host/dopant combinations For hosts used fcc: 111, bcc:110 (Fe100 also available), hcp:0001 + +RAO2020_SEGREGATION_ENERGIES: + Values obtained from https://doi.org/10.1007/s11244-020-01267-2 + Segregation energies for different host/dopant combinations """ -raw_seg_ener = pkg_resources.resource_filename( - "autocat.data.segregation_energies", "segregation_energies.json" +raw_raban_seg_ener = pkg_resources.resource_filename( + "autocat.data.segregation_energies", "raban1999.json" +) + +with open(raw_raban_seg_ener) as fr: + RABAN1999_SEGREGATION_ENERGIES = json.load(fr) + +raw_rao_seg_ener = pkg_resources.resource_filename( + "autocat.data.segregation_energies", "rao2020.json" ) -with open(raw_seg_ener) as fr: - SEGREGATION_ENERGIES = json.load(fr) +with open(raw_rao_seg_ener) as fr: + RAO2020_SEGREGATION_ENERGIES = json.load(fr) diff --git a/src/autocat/data/segregation_energies/segregation_energies.json b/src/autocat/data/segregation_energies/raban1999.json similarity index 100% rename from src/autocat/data/segregation_energies/segregation_energies.json rename to src/autocat/data/segregation_energies/raban1999.json diff --git a/src/autocat/data/segregation_energies/rao2020.json b/src/autocat/data/segregation_energies/rao2020.json new file mode 100644 index 00000000..d2f0236f --- /dev/null +++ b/src/autocat/data/segregation_energies/rao2020.json @@ -0,0 +1,842 @@ +{ + "Al": { + "Al": 0, + "Sc": 1, + "Ti": 0.5, + "V": 0.5, + "Cr": 0, + "Fe": 0.5, + "Co": 0.5, + "Ni": 0.8, + "Cu": 0.9, + "Zn": 0.9, + "Y": 1, + "Zr": 0.8, + "Nb": 0.5, + "Mo": 0, + "Ru": 0.5, + "Rh": 0.8, + "Pd": 0.9, + "Ag": 0.9, + "Cd": 0.9, + "Hf": 0.5, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0.5, + "Ir": 0.5, + "Pt": 0.9, + "Au": 0.9, + "Pb": 1 + }, + "Sc": { + "Al": 1, + "Sc": 0, + "Ti": 0.5, + "V": 0.5, + "Cr": 0.5, + "Fe": 0, + "Co": 0, + "Ni": 0.5, + "Cu": 0.9, + "Zn": 1, + "Y": 1, + "Zr": 0.5, + "Nb": 0.5, + "Mo": 0.5, + "Ru": 0, + "Rh": 0.5, + "Pd": 0.8, + "Ag": 0.9, + "Cd": 1, + "Hf": 0.5, + "Ta": 0, + "W": 0.5, + "Re": 0.5, + "Os": 0, + "Ir": 0, + "Pt": 0.5, + "Au": 0.9, + "Pb": 1 + }, + "Ti": { + "Al": 1, + "Sc": 1, + "Ti": 0, + "V": 0.9, + "Cr": 0.9, + "Fe": 0.8, + "Co": 0.5, + "Ni": 0.8, + "Cu": 1, + "Zn": 1, + "Y": 1, + "Zr": 1, + "Nb": 1, + "Mo": 1, + "Ru": 1, + "Rh": 0.8, + "Pd": 0.9, + "Ag": 0.9, + "Cd": 1, + "Hf": 1, + "Ta": 0.8, + "W": 1, + "Re": 1, + "Os": 0.9, + "Ir": 0.8, + "Pt": 0.9, + "Au": 1, + "Pb": 1 + }, + "V": { + "Al": 1, + "Sc": 1, + "Ti": 1, + "V": 0, + "Cr": 0.8, + "Fe": 1, + "Co": 1, + "Ni": 1, + "Cu": 1, + "Zn": 1, + "Y": 0.9, + "Zr": 1, + "Nb": 1, + "Mo": 1, + "Ru": 1, + "Rh": 1, + "Pd": 1, + "Ag": 0.8, + "Cd": 0.8, + "Hf": 1, + "Ta": 1, + "W": 0.9, + "Re": 0.5, + "Os": 0.8, + "Ir": 1, + "Pt": 1, + "Au": 1, + "Pb": 1 + }, + "Cr": { + "Al": 1, + "Sc": 1, + "Ti": 1, + "V": 1, + "Cr": 0, + "Fe": 0.8, + "Co": 0, + "Ni": 0.8, + "Cu": 0.8, + "Zn": 0.9, + "Y": 0.8, + "Zr": 1, + "Nb": 1, + "Mo": 1, + "Ru": 1, + "Rh": 0.9, + "Pd": 0.5, + "Ag": 0, + "Cd": 0.5, + "Hf": 1, + "Ta": 1, + "W": 1, + "Re": 1, + "Os": 1, + "Ir": 1, + "Pt": 1, + "Au": 0.5, + "Pb": 1 + }, + "Fe": { + "Al": 1, + "Sc": 1, + "Ti": 1, + "V": 0.8, + "Cr": 1, + "Fe": 0, + "Co": 1, + "Ni": 1, + "Cu": 0.9, + "Zn": 0.9, + "Y": 0.9, + "Zr": 1, + "Nb": 1, + "Mo": 0.9, + "Ru": 1, + "Rh": 1, + "Pd": 1, + "Ag": 0.5, + "Cd": 0.5, + "Hf": 1, + "Ta": 1, + "W": 0.5, + "Re": 0.5, + "Os": 1, + "Ir": 1, + "Pt": 1, + "Au": 1, + "Pb": 1 + }, + "Co": { + "Al": 1, + "Sc": 1, + "Ti": 1, + "V": 0.5, + "Cr": 0.9, + "Fe": 0.9, + "Co": 0, + "Ni": 0.9, + "Cu": 0.9, + "Zn": 1, + "Y": 1, + "Zr": 1, + "Nb": 1, + "Mo": 0.5, + "Ru": 1, + "Rh": 1, + "Pd": 1, + "Ag": 0.5, + "Cd": 0.8, + "Hf": 1, + "Ta": 0.8, + "W": 0.5, + "Re": 0.5, + "Os": 0.9, + "Ir": 1, + "Pt": 1, + "Au": 1, + "Pb": 1 + }, + "Ni": { + "Al": 1, + "Sc": 1, + "Ti": 0.5, + "V": 0, + "Cr": 0, + "Fe": 0.5, + "Co": 0.8, + "Ni": 0, + "Cu": 0.9, + "Zn": 1, + "Y": 1, + "Zr": 1, + "Nb": 0.5, + "Mo": 0, + "Ru": 0.9, + "Rh": 1, + "Pd": 1, + "Ag": 1, + "Cd": 1, + "Hf": 1, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0.5, + "Ir": 1, + "Pt": 1, + "Au": 1, + "Pb": 1 + }, + "Cu": { + "Al": 1, + "Sc": 1, + "Ti": 0.5, + "V": 0, + "Cr": 0, + "Fe": 0, + "Co": 0.5, + "Ni": 0.8, + "Cu": 0, + "Zn": 1, + "Y": 1, + "Zr": 1, + "Nb": 0.5, + "Mo": 0, + "Ru": 0.5, + "Rh": 0.9, + "Pd": 1, + "Ag": 1, + "Cd": 1, + "Hf": 0.8, + "Ta": 0.5, + "W": 0, + "Re": 0, + "Os": 0.5, + "Ir": 0.9, + "Pt": 1, + "Au": 1, + "Pb": 1 + }, + "Zn": { + "Al": 0.9, + "Sc": 0.8, + "Ti": 0.5, + "V": 0.5, + "Cr": 0, + "Fe": 0.5, + "Co": 0.5, + "Ni": 0.5, + "Cu": 0.5, + "Zn": 0, + "Y": 1, + "Zr": 0.5, + "Nb": 0, + "Mo": 0, + "Ru": 0, + "Rh": 0.5, + "Pd": 0.5, + "Ag": 1, + "Cd": 1, + "Hf": 0.5, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0.5, + "Pt": 0.5, + "Au": 1, + "Pb": 1 + }, + "Y": { + "Al": 0.9, + "Sc": 0.8, + "Ti": 0.5, + "V": 0.5, + "Cr": 0, + "Fe": 0.5, + "Co": 0, + "Ni": 0, + "Cu": 0.5, + "Zn": 0.9, + "Y": 0, + "Zr": 0.5, + "Nb": 0.5, + "Mo": 0, + "Ru": 0, + "Rh": 0, + "Pd": 0.5, + "Ag": 0.9, + "Cd": 1, + "Hf": 0.5, + "Ta": 0.5, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0, + "Pt": 0, + "Au": 0.5, + "Pb": 1 + }, + "Zr": { + "Al": 1, + "Sc": 1, + "Ti": 0.8, + "V": 0.8, + "Cr": 0.5, + "Fe": 0, + "Co": 0, + "Ni": 0.5, + "Cu": 0.9, + "Zn": 1, + "Y": 1, + "Zr": 0, + "Nb": 0.9, + "Mo": 0.5, + "Ru": 0.5, + "Rh": 0.5, + "Pd": 0.9, + "Ag": 0.9, + "Cd": 1, + "Hf": 0.8, + "Ta": 0.5, + "W": 0.5, + "Re": 0.5, + "Os": 0, + "Ir": 0, + "Pt": 0.5, + "Au": 0.9, + "Pb": 1 + }, + "Nb": { + "Al": 1, + "Sc": 0.9, + "Ti": 0.9, + "V": 0.8, + "Cr": 0.8, + "Fe": 1, + "Co": 1, + "Ni": 1, + "Cu": 0.9, + "Zn": 0.9, + "Y": 1, + "Zr": 1, + "Nb": 0, + "Mo": 0.5, + "Ru": 1, + "Rh": 1, + "Pd": 1, + "Ag": 0.9, + "Cd": 0.9, + "Hf": 1, + "Ta": 0.8, + "W": 0.5, + "Re": 0.5, + "Os": 0.5, + "Ir": 1, + "Pt": 1, + "Au": 1, + "Pb": 1 + }, + "Mo": { + "Al": 1, + "Sc": 1, + "Ti": 1, + "V": 0.5, + "Cr": 0.9, + "Fe": 0.9, + "Co": 0.9, + "Ni": 0.8, + "Cu": 0.8, + "Zn": 0.9, + "Y": 1, + "Zr": 1, + "Nb": 1, + "Mo": 0, + "Ru": 1, + "Rh": 0.9, + "Pd": 0.5, + "Ag": 0.5, + "Cd": 0.8, + "Hf": 1, + "Ta": 1, + "W": 0.8, + "Re": 1, + "Os": 1, + "Ir": 1, + "Pt": 0.9, + "Au": 0.5, + "Pb": 1 + }, + "Ru": { + "Al": 1, + "Sc": 1, + "Ti": 1, + "V": 0.5, + "Cr": 0.5, + "Fe": 1, + "Co": 0.9, + "Ni": 0.9, + "Cu": 0.9, + "Zn": 0.9, + "Y": 1, + "Zr": 1, + "Nb": 1, + "Mo": 0.5, + "Ru": 0, + "Rh": 0.9, + "Pd": 0.9, + "Ag": 0.5, + "Cd": 0.9, + "Hf": 1, + "Ta": 0.9, + "W": 0.5, + "Re": 0.5, + "Os": 0.8, + "Ir": 1, + "Pt": 0.9, + "Au": 0.9, + "Pb": 1 + }, + "Rh": { + "Al": 1, + "Sc": 1, + "Ti": 0, + "V": 0, + "Cr": 0, + "Fe": 0, + "Co": 0.8, + "Ni": 1, + "Cu": 1, + "Zn": 1, + "Y": 1, + "Zr": 0.9, + "Nb": 0, + "Mo": 0, + "Ru": 0.5, + "Rh": 0, + "Pd": 0.9, + "Ag": 0.9, + "Cd": 1, + "Hf": 0.5, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0.8, + "Pt": 1, + "Au": 0.9, + "Pb": 1 + }, + "Pd": { + "Al": 0.5, + "Sc": 0, + "Ti": 0, + "V": 0, + "Cr": 0, + "Fe": 1, + "Co": 0.5, + "Ni": 0.5, + "Cu": 0.8, + "Zn": 0.8, + "Y": 0.8, + "Zr": 0, + "Nb": 0, + "Mo": 0, + "Ru": 0, + "Rh": 0.5, + "Pd": 0, + "Ag": 0.9, + "Cd": 1, + "Hf": 0, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0.5, + "Pt": 0.9, + "Au": 1, + "Pb": 1 + }, + "Ag": { + "Al": 0.9, + "Sc": 0.5, + "Ti": 0, + "V": 0, + "Cr": 0, + "Fe": 0.5, + "Co": 0.9, + "Ni": 0.5, + "Cu": 0.8, + "Zn": 1, + "Y": 0.5, + "Zr": 0, + "Nb": 0, + "Mo": 0, + "Ru": 0, + "Rh": 0.5, + "Pd": 0.5, + "Ag": 0, + "Cd": 1, + "Hf": 0, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0.5, + "Pt": 0.8, + "Au": 1, + "Pb": 1 + }, + "Cd": { + "Al": 0.8, + "Sc": 0.5, + "Ti": 0.5, + "V": 0, + "Cr": 0, + "Fe": 0.8, + "Co": 0.8, + "Ni": 0.8, + "Cu": 0.8, + "Zn": 0.8, + "Y": 0.5, + "Zr": 0.5, + "Nb": 0, + "Mo": 0, + "Ru": 0.5, + "Rh": 0.5, + "Pd": 0.8, + "Ag": 0.9, + "Cd": 0, + "Hf": 0.5, + "Ta": 0.5, + "W": 0, + "Re": 0, + "Os": 0.5, + "Ir": 0.5, + "Pt": 0.5, + "Au": 0.8, + "Pb": 0.9 + }, + "Hf": { + "Al": 1, + "Sc": 0.9, + "Ti": 0.9, + "V": 0.9, + "Cr": 0.8, + "Fe": 0.5, + "Co": 0.5, + "Ni": 0.8, + "Cu": 0.9, + "Zn": 1, + "Y": 1, + "Zr": 1, + "Nb": 0.9, + "Mo": 0.9, + "Ru": 0.5, + "Rh": 0.5, + "Pd": 0.8, + "Ag": 0.9, + "Cd": 1, + "Hf": 0, + "Ta": 0.8, + "W": 0.9, + "Re": 0.5, + "Os": 0.5, + "Ir": 0.5, + "Pt": 0.5, + "Au": 0.9, + "Pb": 1 + }, + "Ta": { + "Al": 0.9, + "Sc": 0.9, + "Ti": 0.9, + "V": 0.9, + "Cr": 0.8, + "Fe": 1, + "Co": 1, + "Ni": 1, + "Cu": 0.8, + "Zn": 0.9, + "Y": 1, + "Zr": 1, + "Nb": 1, + "Mo": 0.9, + "Ru": 1, + "Rh": 1, + "Pd": 1, + "Ag": 0.5, + "Cd": 0.5, + "Hf": 1, + "Ta": 0, + "W": 0.5, + "Re": 0.5, + "Os": 0.9, + "Ir": 1, + "Pt": 1, + "Au": 0.9, + "Pb": 1 + }, + "W": { + "Al": 1, + "Sc": 1, + "Ti": 1, + "V": 0.8, + "Cr": 0.9, + "Fe": 0.8, + "Co": 0.8, + "Ni": 0, + "Cu": 0.5, + "Zn": 0.5, + "Y": 0.9, + "Zr": 1, + "Nb": 1, + "Mo": 1, + "Ru": 1, + "Rh": 0.5, + "Pd": 0, + "Ag": 0, + "Cd": 0.5, + "Hf": 1, + "Ta": 1, + "W": 0, + "Re": 1, + "Os": 1, + "Ir": 1, + "Pt": 0.5, + "Au": 0, + "Pb": 0.5 + }, + "Re": { + "Al": 1, + "Sc": 0.5, + "Ti": 1, + "V": 1, + "Cr": 0.9, + "Fe": 0.9, + "Co": 1, + "Ni": 1, + "Cu": 0.8, + "Zn": 0.5, + "Y": 0.5, + "Zr": 1, + "Nb": 1, + "Mo": 1, + "Ru": 1, + "Rh": 1, + "Pd": 1, + "Ag": 0, + "Cd": 0, + "Hf": 1, + "Ta": 1, + "W": 1, + "Re": 0, + "Os": 1, + "Ir": 1, + "Pt": 1, + "Au": 0.9, + "Pb": 0 + }, + "Os": { + "Al": 0.9, + "Sc": 1, + "Ti": 1, + "V": 0.5, + "Cr": 0.5, + "Fe": 1, + "Co": 0.9, + "Ni": 0.9, + "Cu": 0.5, + "Zn": 0.8, + "Y": 1, + "Zr": 1, + "Nb": 1, + "Mo": 0.8, + "Ru": 0.9, + "Rh": 0.9, + "Pd": 0.8, + "Ag": 0, + "Cd": 0.5, + "Hf": 1, + "Ta": 1, + "W": 0.8, + "Re": 0.5, + "Os": 0, + "Ir": 1, + "Pt": 0.9, + "Au": 0.5, + "Pb": 0 + }, + "Ir": { + "Al": 1, + "Sc": 1, + "Ti": 0, + "V": 0, + "Cr": 0, + "Fe": 0, + "Co": 0.5, + "Ni": 0.9, + "Cu": 0.9, + "Zn": 1, + "Y": 1, + "Zr": 1, + "Nb": 0, + "Mo": 0, + "Ru": 0.5, + "Rh": 0.9, + "Pd": 0.9, + "Ag": 0.5, + "Cd": 1, + "Hf": 0.9, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0.5, + "Ir": 0, + "Pt": 0.9, + "Au": 0.9, + "Pb": 0.5 + }, + "Pt": { + "Al": 0, + "Sc": 0, + "Ti": 0, + "V": 0, + "Cr": 0, + "Fe": 0, + "Co": 0, + "Ni": 0, + "Cu": 0.5, + "Zn": 0.5, + "Y": 0.9, + "Zr": 0, + "Nb": 0, + "Mo": 0, + "Ru": 0, + "Rh": 0.5, + "Pd": 0.9, + "Ag": 0.9, + "Cd": 1, + "Hf": 0, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0.5, + "Pt": 0, + "Au": 0.9, + "Pb": 0.5 + }, + "Au": { + "Al": 0.9, + "Sc": 0, + "Ti": 0, + "V": 0.5, + "Cr": 0, + "Fe": 0.5, + "Co": 0.9, + "Ni": 0.5, + "Cu": 0.5, + "Zn": 0.8, + "Y": 0.5, + "Zr": 0, + "Nb": 0, + "Mo": 0, + "Ru": 0.5, + "Rh": 0.5, + "Pd": 0.5, + "Ag": 0.8, + "Cd": 1, + "Hf": 0, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0.5, + "Pt": 0.5, + "Au": 0, + "Pb": 1 + }, + "Pb": { + "Al": 0.9, + "Sc": 0.5, + "Ti": 0.5, + "V": 0, + "Cr": 0, + "Fe": 0, + "Co": 0, + "Ni": 0.5, + "Cu": 0.5, + "Zn": 0.9, + "Y": 0.5, + "Zr": 0.5, + "Nb": 0, + "Mo": 0, + "Ru": 0, + "Rh": 0, + "Pd": 0.5, + "Ag": 0.9, + "Cd": 0.9, + "Hf": 0.5, + "Ta": 0, + "W": 0, + "Re": 0, + "Os": 0, + "Ir": 0, + "Pt": 0.5, + "Au": 0.8, + "Pb": 0 + } +} \ No newline at end of file diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 390e5729..8fa58da7 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -15,7 +15,8 @@ from autocat.learning.predictors import Predictor from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES -from autocat.data.segregation_energies import SEGREGATION_ENERGIES +from autocat.data.segregation_energies import RABAN1999_SEGREGATION_ENERGIES +from autocat.data.segregation_energies import RAO2020_SEGREGATION_ENERGIES Array = List[float] @@ -977,7 +978,9 @@ def calculate_hhi_scores(structures: List[Atoms], hhi_type: str = "production"): return hhi_scores -def calculate_segregation_energy_scores(structures: List[Atoms]): +def calculate_segregation_energy_scores( + structures: List[Atoms], data_source: str = "raban1999" +): """ Calculates HHI scores for structures weighted by their composition. The scores are normalized and inverted such that these should @@ -989,11 +992,11 @@ def calculate_segregation_energy_scores(structures: List[Atoms]): structures: List of Atoms objects for which to calculate the scores - hhi_type: - Type of HHI index to be used for the score - Options - - production (default) - - reserves + data_source: + Which tabulated data should the segregation energies be pulled from. + Options: + - "raban1999": A.V. Raban, et. al. Phys. Rev. B 59, 15990 + - "rao2020": K. K. Rao, et. al. Topics in Catalysis volume 63, pages728-741 (2020) Returns ------- @@ -1006,17 +1009,23 @@ def calculate_segregation_energy_scores(structures: List[Atoms]): msg = "To include segregation energies, the structures must be provided" raise SequentialLearnerError(msg) - # won't consider surface energies (ie. dop == host) for normalization - max_seg_ener = SEGREGATION_ENERGIES["Pd"]["W"] - min_seg_ener = SEGREGATION_ENERGIES["Fe_100"]["Ag"] - # normalize and invert (so that this score is to be maximized) - norm_seg_ener_data = {} - for hsp in SEGREGATION_ENERGIES: - norm_seg_ener_data[hsp] = {} - for dsp in SEGREGATION_ENERGIES[hsp]: - norm_seg_ener_data[hsp][dsp] = 1.0 - ( - SEGREGATION_ENERGIES[hsp][dsp] - min_seg_ener - ) / (max_seg_ener - min_seg_ener) + if data_source == "raban1999": + # won't consider surface energies (ie. dop == host) for normalization + max_seg_ener = RABAN1999_SEGREGATION_ENERGIES["Pd"]["W"] + min_seg_ener = RABAN1999_SEGREGATION_ENERGIES["Fe_100"]["Ag"] + # normalize and invert (so that this score is to be maximized) + norm_seg_ener_data = {} + for hsp in RABAN1999_SEGREGATION_ENERGIES: + norm_seg_ener_data[hsp] = {} + for dsp in RABAN1999_SEGREGATION_ENERGIES[hsp]: + norm_seg_ener_data[hsp][dsp] = 1.0 - ( + RABAN1999_SEGREGATION_ENERGIES[hsp][dsp] - min_seg_ener + ) / (max_seg_ener - min_seg_ener) + elif data_source == "rao2020": + norm_seg_ener_data = RAO2020_SEGREGATION_ENERGIES + else: + msg = f"Unknown data source {data_source}" + raise SequentialLearnerError(msg) seg_ener_scores = np.zeros(len(structures)) for idx, struct in enumerate(structures): diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 7e8b401b..8223de8b 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -11,7 +11,10 @@ from ase.io import read as ase_read from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES -from autocat.data.segregation_energies import SEGREGATION_ENERGIES +from autocat.data.segregation_energies import ( + RABAN1999_SEGREGATION_ENERGIES, + RAO2020_SEGREGATION_ENERGIES, +) from autocat.learning.predictors import Predictor from autocat.learning.sequential import ( DesignSpace, @@ -1061,16 +1064,27 @@ def test_calculate_segregation_energy_scores(): generate_saa_structures(["Pd"], ["W"], facets={"Pd": ["111"]}) ) ) - # saa_structs = [saa_dict[host]["Pt"]["fcc111"]["structure"] for host in saa_dict] + # test calculating scores from RABAN1999 se_scores = calculate_segregation_energy_scores(saa_structs) assert np.isclose(se_scores[-1], 0.0) - min_seg = SEGREGATION_ENERGIES["Fe_100"]["Ag"] - max_seg = SEGREGATION_ENERGIES["Pd"]["W"] + min_seg = RABAN1999_SEGREGATION_ENERGIES["Fe_100"]["Ag"] + max_seg = RABAN1999_SEGREGATION_ENERGIES["Pd"]["W"] assert np.isclose( se_scores[0], - 1.0 - (SEGREGATION_ENERGIES["Ag"]["Pt"] - min_seg) / (max_seg - min_seg), + 1.0 + - (RABAN1999_SEGREGATION_ENERGIES["Ag"]["Pt"] - min_seg) / (max_seg - min_seg), ) assert np.isclose( se_scores[1], - 1.0 - (SEGREGATION_ENERGIES["Ni"]["Pt"] - min_seg) / (max_seg - min_seg), + 1.0 + - (RABAN1999_SEGREGATION_ENERGIES["Ni"]["Pt"] - min_seg) / (max_seg - min_seg), ) + + # test getting scores from RAO2020 + se_scores = calculate_segregation_energy_scores(saa_structs, data_source="rao2020") + assert np.isclose(se_scores[0], RAO2020_SEGREGATION_ENERGIES["Ag"]["Pt"]) + assert np.isclose(se_scores[0], 0.8) + assert np.isclose(se_scores[1], RAO2020_SEGREGATION_ENERGIES["Ni"]["Pt"]) + assert np.isclose(se_scores[1], 1.0) + assert np.isclose(se_scores[-1], RAO2020_SEGREGATION_ENERGIES["Pd"]["W"]) + assert np.isclose(se_scores[-1], 0.0) From 87851ae3be8d8273a8d9c68a5a63c3a3abbd3b72 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 26 Jan 2022 10:56:34 -0500 Subject: [PATCH 201/239] update readme --- README.md | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7b82ca84..b6ac00ef 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,36 @@ -# autocat -Tools for automated structure generation of catalyst systems. +# AutoCat -Currently writes out all structures as ASE trajectory files which may be read using ASE as follows: -```python -from ase.io import read +AutoCat is a suite of python tools for sequential learning with +candidate selection metrics for materials applications. It additionally has tools +for automated structure generation related to catalysis. -sys = read('name_of_traj.traj') +Development of this package stems from [ACED](https://www.cmu.edu/aced/), as part of the +ARPA-E DIFFERENTIATE program. + +## Installation + +There are two options for installation, either via `pip` or from the repo directly. + +### `pip` (recommended) + +If you are planning on strictly using AutoCat rather than contributing to development, + we recommend using `pip` within a virtual environment (e.g. + [`conda`](https://www.anaconda.com/products/individual) + ). This can be done +as follows: + +``` +pip install autocat +``` + +### Github (for developers) + +Alternatively, if you would like to contribute to the development of this software, +AutoCat can be installed via a clone from Github. First, you'll need to clone the +github repo to your local machine (or wherever you'd like to use AutoCat) using +`git clone`. Once the repo has been cloned, you can install AutoCat as an editable +package by changing into the created directory (the one with `setup.py`) and installing +via: ``` +pip install -e . +``` \ No newline at end of file From 3be598dd2796d3b8b79a85e330bdb87ea2891591 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 27 Jan 2022 17:01:11 -0500 Subject: [PATCH 202/239] add deploy pages workflow --- .github/workflows/deploy-pages.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/deploy-pages.yml diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml new file mode 100644 index 00000000..52f825e9 --- /dev/null +++ b/.github/workflows/deploy-pages.yml @@ -0,0 +1,17 @@ +name: deploy-pages +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.x + - run: pip install mkdocs-material + - run: pip install mkdocstrings + - run: mkdocs gh-deploy --force \ No newline at end of file From af20b94c8e03c64879c591868d07cea0b6bb8179 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 3 Feb 2022 10:08:50 -0500 Subject: [PATCH 203/239] add discussion on Rao2020 data --- docs/User_Guide/Data/segregation_energies.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/User_Guide/Data/segregation_energies.md b/docs/User_Guide/Data/segregation_energies.md index d0d92d3c..e9933fb1 100644 --- a/docs/User_Guide/Data/segregation_energies.md +++ b/docs/User_Guide/Data/segregation_energies.md @@ -11,3 +11,15 @@ By definition more negative values indicate more stability towards keeping the dopant at the surface. Values where the host is the same as the dopant is the surface energy for that species. + +In addition, for specifically SAAs, [K. K. Rao, et. al.](https://doi.org/10.1007/s11244-020-01267-2) +studied the stability of various different host and dopant combinations. The different configurations +included SAA, subsurface, dimers, adatoms, and adatom + SAA. Here for most preferential configuration +we attributed the following scores as per the results shown in figure 3 of the above reference: + +- SAA is the most stable: 1 +- SAA is not the most stable but is within: + - <0.1 eV: 0.9 + - <0.2 eV: 0.8 + - <0.5 eV: 0.5 +- SAA is not the most stable by >0.5 eV: 0 \ No newline at end of file From f918ba71c89e8cd4fd86406e3c2db0f3f55cd6b5 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 10 Feb 2022 10:19:48 -0500 Subject: [PATCH 204/239] start making predictor tutorial --- docs/Tutorials/pred_h.md | 69 ++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 2 ++ 2 files changed, 71 insertions(+) create mode 100644 docs/Tutorials/pred_h.md diff --git a/docs/Tutorials/pred_h.md b/docs/Tutorials/pred_h.md new file mode 100644 index 00000000..7071acd5 --- /dev/null +++ b/docs/Tutorials/pred_h.md @@ -0,0 +1,69 @@ +In this tutorial we are going to show how to use the learning tools within +AutoCat to train a regressor that can predict adsorption energies of hydrogen +on a set of single-atom alloys. + +## Creating a DesignSpace + +Let's start by creating a DesignSpace. Normally each of these +structures would be optimized via DFT, but for demo purposes +we'll use the generated structures directly. First we need to generate the single-atom +alloys. Here, we can use AutoCat's +[`generate_saa_structures`](../API/Structure_Generation/saa.md#autocat.saa.generate_saa_structures) +function. + +```py +>>> # Generate the clean single-atom alloy structures +>>> from autocat.saa import generate_saa_structures +>>> from autocat.utils import extract_structures +>>> saa_struct_dict = generate_saa_structures( +... ["Fe", "Cu", "Au"], +... ["Pt", "Pd", "Ni"], +... facets={"Fe":["110"], "Cu":["111"], "Au":["111"]}, +... n_fixed_layers=2, +... ) +>>> saa_structs = extract_structures(saa_struct_dict) +``` + +Now that we have the clean structures, let's adsorb hydrogen on the surface. +For convenience let's place H at the origin instead of considering all symmetry sites. +To accomplish this we can make use of AutoCat's +[`place_adsorbate`](../API/Structure_Generation/adsorption.md#autocat.adsorption.place_adsorbate) +function. + +```py +>>> # Adsorb hydrogen onto each of the generated SAA surfaces +>>> from autocat.adsorption import place_adsorbate +>>> ads_structs = [] +>>> for clean_struct in saa_structs: +... ads_dict = place_adsorbate( +... clean_struct, +... "H", +... (0.,0.) +... ) +... ads_struct = extract_structures(ads_dict)[0] +... ads_structs.append(ads_struct) +``` + +This has collected all of the single-atom alloys with hydrogen adsorbed into +a single list of `ase.Atoms` objects, `ads_structs`. Ideally at this stage we'd have +adsorption energies for each of the generated structures after relaxation. As a proxy +in this demo we'll create random labels, but this should be adsorption energies if you +want to train a meaningful Predictor! + +```py +>>> # Generate the labels for each structure +>>> import numpy as np +>>> labels = np.random.uniform(-1.5,1.5,size=len(ads_structs)) +``` + +Finally, using both our structures and labels we can define a DesignSpace. In practice, +if any of the labels for a structure are unknown, it can be included as a `numpy.nan` + +```py +>>> from autocat.learning.sequential import DesignSpace +>>> design_space = DesignSpace(ads_structs, labels) +``` + +## Training the Predictor + +## Making predictions \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index a0af5426..44a54037 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,6 +48,8 @@ nav: - Segregation Energies: User_Guide/Data/segregation_energies.md - Lattice Parameters: User_Guide/Data/lattice_parameters.md - Intermediates: User_Guide/Data/intermediates.md + - Tutorials: + - Training a Predictor on hydrogen adsorption energies: Tutorials/pred_h.md - API: - Sequential Learning: - autocat.learning.featurizers: API/Learning/featurizers.md From ad9b0b6197cded163a44a714c1a0fdae89a40640 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 14 Feb 2022 18:02:22 -0500 Subject: [PATCH 205/239] extend predictor tutorial --- docs/Tutorials/pred_h.md | 72 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/docs/Tutorials/pred_h.md b/docs/Tutorials/pred_h.md index 7071acd5..95850397 100644 --- a/docs/Tutorials/pred_h.md +++ b/docs/Tutorials/pred_h.md @@ -2,9 +2,9 @@ In this tutorial we are going to show how to use the learning tools within AutoCat to train a regressor that can predict adsorption energies of hydrogen on a set of single-atom alloys. -## Creating a DesignSpace +## Creating a `DesignSpace` -Let's start by creating a DesignSpace. Normally each of these +Let's start by creating a `DesignSpace`. Normally each of these structures would be optimized via DFT, but for demo purposes we'll use the generated structures directly. First we need to generate the single-atom alloys. Here, we can use AutoCat's @@ -56,7 +56,7 @@ want to train a meaningful Predictor! >>> labels = np.random.uniform(-1.5,1.5,size=len(ads_structs)) ``` -Finally, using both our structures and labels we can define a DesignSpace. In practice, +Finally, using both our structures and labels we can define a `DesignSpace`. In practice, if any of the labels for a structure are unknown, it can be included as a `numpy.nan` ```py @@ -64,6 +64,68 @@ if any of the labels for a structure are unknown, it can be included as a `numpy >>> design_space = DesignSpace(ads_structs, labels) ``` -## Training the Predictor +## Setting up a `Predictor` -## Making predictions \ No newline at end of file +When setting up our `Predictor` we now have two choices to make: + +1. The technique to be used for featurizing the systems +2. The regression model to be used for training and predictions + +Internally, the `Predictor` will contain a `Featurizer` object which contains all of +our choices for how to featurize the systems. Our choice of featurizer class and +the associated kwargs are specified via the `featurizer_class` and +`featurization_kwargs` arguments, respectively. By providing the design space structures +some of the kwargs related to the featurization (e.g. maximum structure size) can be +automatically obtained. + +Similarly, we can specify the regressor to be used within the `model_class` and +`model_kwargs` arguments. The class should be "`sklearn`-like" with `fit` and +`predict` methods. + +Let's featurize the hydrogen environment via `dscribe`'s `SOAP` class with +`sklearn`'s `GaussianProcessRegressor` for regression. + +```py +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from sklearn.gaussian_process.kernels import RBF +>>> from dscribe import SOAP +>>> from autocat.learning.predictors import Predictor +>>> kernel = RBF(1.5) +>>> model_kwargs={"kernel": kernel} +>>> featurization_kwargs={ +... "design_space_structures": design_space.design_space_structures, +... "kwargs": {"rcut": 7.0, "nmax": 8, "lmax": 8} +... } +>>> predictor = Predictor( +... model_class=GaussianProcessRegressor, +... model_kwargs=model_kwargs, +... featurizer_class=SOAP, +... featurization_kwargs=featurization_kwargs, +... ) +``` + +## Training and making predictions + +With our newly defined `Predictor` we can train it using data from our +`DesignSpace` and the `fit` method. + +```py +>>> train_structures = design_space.design_space_structures[:5] +>>> train_labels = design_space.design_space_labels[:5] +>>> predictor.fit(train_structures, train_labels) +``` + +Making predictions is a similar process except using the `predict` method. + +```py +>>> test_structures = design_space.design_space_structures[5:] +>>> predicted_labels = predictor.predict(test_structures) +``` + +In this example, since we already have the labels for the test structures, we can +also use the `score` method to calculate a prediction score. + +```py +>>> test_labels = design_space.design_space_labels[5:] +>>> mae = predictor.score(test_structures, test_labels) +``` \ No newline at end of file From ba6ab1a05db36b0fa3fa70dac5ba2be0466354ec Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 16 Feb 2022 17:00:43 -0500 Subject: [PATCH 206/239] add a tutorial for simulated sequential learning --- docs/Tutorials/sl.md | 112 +++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 113 insertions(+) create mode 100644 docs/Tutorials/sl.md diff --git a/docs/Tutorials/sl.md b/docs/Tutorials/sl.md new file mode 100644 index 00000000..d3169349 --- /dev/null +++ b/docs/Tutorials/sl.md @@ -0,0 +1,112 @@ +In this tutorial we will show how to conduct a simulated sequential learning +run over a fully explored design space. + +## Creating a fully explored `DesignSpace` +Following a similar procedure as in the previous tutorial, we will create +a fully explored `DesignSpace` (ie. no unknown labels). This time +the structures will be clean mono-elemental surfaces which we can generate via +`generate_surface_structures`. + +```py +>>> # Generate the clean surfaces +>>> from autocat.surface import generate_surface_structures +>>> from autocat.utils import extract_structures +>>> surfs_dict = generate_surface_structures( +... ["Pt", "Cu", "Li", "Ti"], +... n_fixed_layers=2, +... default_lat_param_lib="pbe_fd" +... ) +>>> surfs = extract_structures(surfs_dict) +``` + +In this case we specified that the default lattice parameters +from the library calculated with the PBE XC functional and +a finite difference basis set. + +As before, we will create random labels for all structures. But if you +want meaningful sequential learning runs these must be actual labels relevant +to your design space! + +```py +>>> # Generate the labels for each structure +>>> import numpy as np +>>> labels = np.random.uniform(-1.5,1.5,size=len(ads_structs)) +``` + +Taking the structures and labels we can define our `DesignSpace`. + +```py +>>> from autocat.learning.sequential import DesignSpace +>>> design_space = DesignSpace(surfs, labels) +``` + +## Doing a single simulated sequential learning run + +Given our fully explored `DesignSpace`, we can simulate a sequential learning +search over it to gain insights into guided searches within this context. +To do this simulated run we can make use of the `simulated_sequential_learning` +function. This will internally drive a `SequentialLearner` object which will be +returned at the end of the run. + +As before, we will need to make choices with regard to the `Predictor` settings. +In this case we will use a `SineMatrix` featurizer alongside a `GaussianProcessRegressor`. + +We also need to select parameters with regard to candidate selection. +This includes the acquisition function to be used, +target window (if applicable), and number of candidates to pick at each iteration. +Let's use a maximum uncertainty acquisition function to pick candidates based on their +associated uncertainty values. We'll also restrict the run to conduct 5 iterations. + +```py +>>> from sklearn.gaussian_process import GaussianProcessRegressor +>>> from dscribe import SineMatrix +>>> from autocat.learning.sequential import simulated_sequential_learning +>>> kernel = RBF(1.5) +>>> model_kwargs = {"kernel": kernel} +>>> featurization_kwargs = { +... "design_space_structures": design_space.design_space_structures, +... } +>>> predictor_kwargs = { +... "model_class": GaussianProcessRegressor, +... "model_kwargs": model_kwargs, +... "featurizer_class": SineMatrix, +... "featurization_kwargs": featurization_kwargs +... } +>>> candidate_selection_kwargs = {"aq": "MU"} +>>> sim_seq_learn = simulated_sequential_learning( +... full_design_space=design_space, +... init_training_size=1, +... number_of_sl_loops=5, +... candidate_selection_kwargs=candidate_selection_kwargs, +... predictor_kwargs=predictor_kwargs, +... ) +``` + +Within the returned `SequentialLearner` object we now have information we can use +for further analysis including prediction and uncertainty histories as well as the candidate +selection history. + +## Doing multiple simulated sequential learning runs + +It is often useful to consider the statistics of multiple independent simulated +sequential learning runs. For this purpose we can make use of the +`multiple_simulated_sequential_learning_runs` function. This acts in the same manner +as for the single run verion, but will return a `SequentialLearner` object for each of the +independent runs in a list. Moreover, the inputs remain the same except with the added option +of running in parallel (since this is an embarrassingly parallel operation). Here we will conduct +three independent runs in serial. + +```py +>>> runs_history = multiple_simulated_sequential_learning_runs( +... full_design_space=design_space, +... init_training_size=1, +... number_of_sl_loops=5, +... candidate_selection_kwargs=candidate_selection_kwargs, +... predictor_kwargs=predictor_kwargs, +... number_of_runs=3, +... # number_of_parallel_jobs=N if you wanted to run in parallel +... ) +``` + +Taking the `SequentialLearner`s from within `runs_history`, their histories +may be used to calculate more robust statistics into the simulated searches. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 44a54037..b29c1aae 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -50,6 +50,7 @@ nav: - Intermediates: User_Guide/Data/intermediates.md - Tutorials: - Training a Predictor on hydrogen adsorption energies: Tutorials/pred_h.md + - Conducting a simulated sequential learning run: Tutorials/sl.md - API: - Sequential Learning: - autocat.learning.featurizers: API/Learning/featurizers.md From 73346669a572d165d6900a54fca40d0516e91bb4 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 21 Mar 2022 17:04:56 -0400 Subject: [PATCH 207/239] update ads user guide to new syntax --- .../Structure_Generation/adsorption.md | 112 +++++++++--------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/docs/User_Guide/Structure_Generation/adsorption.md b/docs/User_Guide/Structure_Generation/adsorption.md index a6bc4b67..a20bfd57 100644 --- a/docs/User_Guide/Structure_Generation/adsorption.md +++ b/docs/User_Guide/Structure_Generation/adsorption.md @@ -5,7 +5,7 @@ are geared towards generating structures with adsorbates placed on a candidate catalyst surface. The core function of this module is -[`generate_rxn_structures`](../../API/Structure_Generation/adsorption.md#autocat.adsorption.generate_rxn_structures) +[`generate_adsorbed_structures`](../../API/Structure_Generation/adsorption.md#autocat.adsorption.generate_adsorbed_structures) for generating multiple adsorbed structures with a single function call. For the oxygen reduction (ORR) and nitrogen reduction (NRR) reactions, @@ -15,35 +15,38 @@ which can be found in [`autocat.data.intermediates`](../Data/intermediates.md). In addition, by default initial heights of the adsorbates are guessed based upon the vdW radii of the nearest neighbors to the anchoring atom. +In the example below we are generating adsorption structures for all ORR intermediates +on all of the identified unique symmetry sites on a Pt111 slab. The unique sites are +identified using the Delaunay triangulation, as implemented in `pymatgen`. +Additionally, by default initial heights of the adsorbates are guessed based +upon the vdW radii of the nearest neighbors to the anchoring atom. + ```py >>> from autocat.surface import generate_surface_structures >>> from autocat.data.intermediates import ORR_INTERMEDIATE_NAMES ->>> from autocat.adsorption import generate_rxn_structures +>>> from autocat.adsorption import generate_adsorbed_structures >>> surface_dict = generate_surface_structures( -... ["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 +... species_list=["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 ... ) >>> surface = surface_dict["Pt"]["fcc111"]["structure"] ->>> ads_dict = generate_rxn_structures( -... surface, -... all_sym_sites=True, +>>> ads_dict = generate_adsorbed_structures( +... surface=surface, +... use_all_sites=True, ... ads=ORR_INTERMEDIATE_NAMES, -... refs=["H2", "H2O"], ... write_to_disk=True, ... ) -OOH at (0.0,0.0) written to ./adsorbates/OOH/ontop/0.0_0.0/input.traj -OOH at (7.623,6.001) written to ./adsorbates/OOH/bridge/7.623_6.001/input.traj -OOH at (6.93,5.601) written to ./adsorbates/OOH/hollow/6.93_5.601/input.traj -OOH at (9.702,4.001) written to ./adsorbates/OOH/hollow/9.702_4.001/input.traj -O at (0.0,0.0) written to ./adsorbates/O/ontop/0.0_0.0/input.traj -O at (7.623,6.001) written to ./adsorbates/O/bridge/7.623_6.001/input.traj -O at (6.93,5.601) written to ./adsorbates/O/hollow/6.93_5.601/input.traj -O at (9.702,4.001) written to ./adsorbates/O/hollow/9.702_4.001/input.traj -OH at (0.0,0.0) written to ./adsorbates/OH/ontop/0.0_0.0/input.traj -OH at (7.623,6.001) written to ./adsorbates/OH/bridge/7.623_6.001/input.traj -OH at (6.93,5.601) written to ./adsorbates/OH/hollow/6.93_5.601/input.traj -OH at (9.702,4.001) written to ./adsorbates/OH/hollow/9.702_4.001/input.traj -H2 molecule structure written to ./references/H2/input.traj -H2O molecule structure written to ./references/H2O/input.traj +Structure with OOH adsorbed at ontop/0.0_0.0 written to ./adsorbates/OOH/ontop/0.0_0.0/input.traj +Structure with OOH adsorbed at bridge/8.316_2.4 written to ./adsorbates/OOH/bridge/8.316_2.4/input.traj +Structure with OOH adsorbed at hollow/8.316_1.6 written to ./adsorbates/OOH/hollow/8.316_1.6/input.traj +Structure with OOH adsorbed at hollow/5.544_3.201 written to ./adsorbates/OOH/hollow/5.544_3.201/input.traj +Structure with O adsorbed at ontop/0.0_0.0 written to ./adsorbates/O/ontop/0.0_0.0/input.traj +Structure with O adsorbed at bridge/8.316_2.4 written to ./adsorbates/O/bridge/8.316_2.4/input.traj +Structure with O adsorbed at hollow/8.316_1.6 written to ./adsorbates/O/hollow/8.316_1.6/input.traj +Structure with O adsorbed at hollow/5.544_3.201 written to ./adsorbates/O/hollow/5.544_3.201/input.traj +Structure with OH adsorbed at ontop/0.0_0.0 written to ./adsorbates/OH/ontop/0.0_0.0/input.traj +Structure with OH adsorbed at bridge/8.316_2.4 written to ./adsorbates/OH/bridge/8.316_2.4/input.traj +Structure with OH adsorbed at hollow/8.316_1.6 written to ./adsorbates/OH/hollow/8.316_1.6/input.traj +Structure with OH adsorbed at hollow/5.544_3.201 written to ./adsorbates/OH/hollow/5.544_3.201/input.traj >>> ads_dict {'OOH': {'ontop': {'0.0_0.0': {'structure': Atoms(...), 'traj_file_path': './adsorbates/OOH/ontop/0.0_0.0/input.traj'}}, @@ -69,22 +72,7 @@ H2O molecule structure written to ./references/H2O/input.traj 'traj_file_path': './adsorbates/OH/hollow/6.93_5.601/input.traj'}, '9.702_4.001': {'structure': Atoms(...), 'traj_file_path': './adsorbates/OH/hollow/9.702_4.001/input.traj'}}}, - 'references': {'H2': {'structure': Atoms(...), - 'traj_file_path': './references/H2/input.traj'}, - 'H2O': {'structure': Atoms(...), - 'traj_file_path': './references/H2O/input.traj'}}} ``` -In the example above we are generating adsorption structures for all ORR intermediates -on all of the identified unique symmetry sites on a Pt111 slab. The unique sites are -identified using the Delaunay triangulation, as implemented in `pymatgen`. -Additionally, by default initial heights of the adsorbates are guessed based -upon the vdW radii of the nearest neighbors to the anchoring atom. - -One such use of these structures is generating free energy landscapes. -With this in mind, there is also the `refs` parameter -which allows specifying the reference states that will be used. This generates a separate -directory containing the isolated molecule structures so that their energies may also -be calculated. In general the dictionary generated has the following organization: @@ -92,8 +80,6 @@ In general the dictionary generated has the following organization: {ADSORBATE_SPECIES: {SITE_LABEL: {XY: {"structure": Atoms, "traj_file_path": TRAJFILEPATH}}}, - "references": - {REFERENCE_SPECIES: {"structure": Atoms, "traj_file_path": TRAJFILEPATH}}} ``` When writing these adsorbed structures to disk it is done with the following subdirectory format (mimicing the organization of the dictionary). @@ -137,22 +123,18 @@ format (mimicing the organization of the dictionary). │   └── ontop │   └── 0.0_0.0 │   └── input.traj -├── references -│   ├── H2 -│   │   └── input.traj -│   └── H2O -│   └── input.traj ``` Instead of generating the adsorption structures for all unique sites, -the xy coordinates of individual sites may be specified using the `sites` - parameter. +the xy coordinates of individual sites may be specified using the `adsorption_sites` + parameter. Here we can give each of these sites custom labels to be used for referencing + and writing to disk. ```py >>> from autocat.surface import generate_surface_structures ->>> from autocat.adsorption import generate_rxn_structures +>>> from autocat.adsorption import generate_adsorbed_structures >>> surface_dict = generate_surface_structures( -... ["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 +... species_list=["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 ... ) >>> surface = surface_dict["Pt"]["fcc111"]["structure"] >>> x = surface[15].x @@ -161,16 +143,38 @@ the xy coordinates of individual sites may be specified using the `sites` >>> y = surface[15].y >>> y 5.6011665451642 ->>> site = {"custom": [(x,y)]} ->>> ads_dict = generate_rxn_structures( -... surface, -... ads=["Li"], -... all_sym_sites=False, -... sites=site, +>>> sites = {"Li": {"custom": [(x,y)]}} +>>> ads_dict = generate_adsorbed_structures( +... surface=surface, +... adsorbates=["Li"], +... use_all_sites=False, +... adsorption_sites=site, ... write_to_disk=True, ... ) -Li at (4.158,5.601) written to ./adsorbates/Li/custom/4.158_5.601/input.traj +Structure with Li adsorbed at custom/4.158_5.601 written to ./adsorbates/Li/custom/4.158_5.601/input.traj >>> ads_dict {'Li': {'custom': {'4.158_5.601': {'structure': Atoms(...), 'traj_file_path': './adsorbates/Li/custom/4.158_5.601/input.traj'}}}} ``` + +If we are dealing with multiple adsorbates, adsorption sites, heights, etc.. that we want to +treat differently depending on the combination, we can leverage the `dict` option for each of these +inputs. The example below illustrates this capability, where can be used to specify adsorbates. + +```py +>>> from autocat.surface import generate_surface_structures +>>> from autocat.adsorption import generate_adsorbed_structures +>>> surface_dict = generate_surface_structures( +... species_list=["Pt"], facets={"Pt": ["111"]}, n_fixed_layers=2 +... ) +>>> surface = surface_dict["Pt"]["fcc111"]["structure"] +>>> sites = {"Li": {"origin": [(0.,0.)]}, "H": {"custom": [(0.5, 0.5)]}} +>>> ads_dict = generate_adsorbed_structures( +... surface=surface, +... adsorbates=["Li", "H", "N"], +... use_all_sites={"Li": False, "H": False, "N": True}, +... heights={"H": 1.2} +... adsorption_sites=sites, +... write_to_disk=True, +... ) +``` \ No newline at end of file From 6d986175dc59ae6ce0eb7ffcdbb796d233d757b8 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Mon, 21 Mar 2022 18:26:19 -0400 Subject: [PATCH 208/239] update saa user guide to new syntax --- docs/User_Guide/Structure_Generation/saa.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/User_Guide/Structure_Generation/saa.md b/docs/User_Guide/Structure_Generation/saa.md index dedbd658..aec3e32b 100644 --- a/docs/User_Guide/Structure_Generation/saa.md +++ b/docs/User_Guide/Structure_Generation/saa.md @@ -13,8 +13,8 @@ be generated simultaneously. ```py >>> from autocat.saa import generate_saa_structures >>> saa_dict = generate_saa_structures( -... ["Fe", "Cu"], -... ["Pt", "Au"], +... host_species=["Fe", "Cu"], +... dopant_species=["Pt", "Au"], ... facets={"Fe": ["110"], "Cu": ["111"]}, ... n_fixed_layers=2, ... write_to_disk=True, From 51f4f83b95a4910e666506995740acb15cfa840d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Mar 2022 15:22:04 -0400 Subject: [PATCH 209/239] update tests w new struct gen syntax from ec4535e --- tests/learning/test_featurizers.py | 20 +++++-- tests/learning/test_predictors.py | 40 ++++++++++++-- tests/learning/test_sequential.py | 83 +++++++++++++++--------------- tests/test_utils.py | 9 ++-- 4 files changed, 101 insertions(+), 51 deletions(-) diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index d1a7493f..370bd6da 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -12,7 +12,7 @@ from matminer.featurizers.site import OPSiteFingerprint from matminer.featurizers.site import CrystalNNFingerprint -from autocat.adsorption import place_adsorbate +from autocat.adsorption import generate_adsorbed_structures from autocat.surface import generate_surface_structures from autocat.saa import generate_saa_structures from autocat.learning.featurizers import Featurizer @@ -177,7 +177,14 @@ def test_featurizer_featurize_single(): assert np.array_equal(acf, manual_cm) # TEST SITE FEATURIZERS - ads_struct = extract_structures(place_adsorbate(saa, "OH", position=(0.0, 0.0)))[0] + ads_struct = extract_structures( + generate_adsorbed_structures( + surface=saa, + adsorbates=["OH"], + adsorption_sites={"custom": [(0.0, 0.0)]}, + use_all_sites=False, + ) + )[0] f.max_size = len(ads_struct) species = np.unique(ads_struct.get_chemical_symbols()).tolist() f.species_list = species @@ -271,7 +278,14 @@ def test_featurizer_featurize_multiple(): ads_structs = [] for struct in saas: ads_structs.append( - extract_structures(place_adsorbate(struct, "NNH", position=(0.0, 0.0)))[0] + extract_structures( + generate_adsorbed_structures( + surface=struct, + adsorbates=["NNH"], + adsorption_sites={"custom": [(0.0, 0.0)]}, + use_all_sites=False, + ) + )[0] ) species_list = [] for s in ads_structs: diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index 5259a0a4..e3619616 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -14,7 +14,9 @@ from matminer.featurizers.composition import ElementProperty -from autocat.adsorption import place_adsorbate +from ase import Atoms + +from autocat.adsorption import generate_adsorbed_structures, place_adsorbate from autocat.surface import generate_surface_structures from autocat.learning.predictors import Predictor from autocat.learning.predictors import PredictorError @@ -24,7 +26,17 @@ def test_fit(): # Test returns a fit model subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) - structs = [extract_structures(place_adsorbate(s, "OH"))[0] for s in subs] + structs = [] + for sub in subs: + ads_struct = extract_structures( + generate_adsorbed_structures( + surface=sub, + adsorbates=["OH"], + adsorption_sites={"origin": [(0.0, 0.0)]}, + use_all_sites=False, + ) + )[0] + structs.append(ads_struct) labels = np.random.rand(len(structs)) acsc = Predictor( featurizer_class=SOAP, @@ -84,7 +96,17 @@ def test_fit(): def test_predict(): # Test outputs are returned as expected subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) - structs = [extract_structures(place_adsorbate(s, "OH"))[0] for s in subs] + structs = [] + for sub in subs: + ads_struct = extract_structures( + generate_adsorbed_structures( + surface=sub, + adsorbates=["OH"], + adsorption_sites={"origin": [(0.0, 0.0)]}, + use_all_sites=False, + ) + )[0] + structs.append(ads_struct) labels = np.random.rand(len(structs)) acsc = Predictor( featurizer_class=SOAP, @@ -120,7 +142,17 @@ def test_predict(): def test_score(): # Tests the score method subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) - structs = [extract_structures(place_adsorbate(s, "OH"))[0] for s in subs] + structs = [] + for sub in subs: + ads_struct = extract_structures( + generate_adsorbed_structures( + surface=sub, + adsorbates=["OH"], + adsorption_sites={"origin": [(0.0, 0.0)]}, + use_all_sites=False, + ) + )[0] + structs.append(ads_struct) labels = np.random.rand(len(structs)) acsc = Predictor( featurizer_class=SOAP, diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index ee08cc2f..b05d8476 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -15,6 +15,7 @@ from scipy import stats from ase.io.jsonio import decode as ase_decoder +from ase import Atoms from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES from autocat.data.segregation_energies import ( @@ -45,15 +46,15 @@ def test_sequential_learner_from_json(): sub1 = generate_surface_structures(["Au"], facets={"Au": ["110"]})["Au"]["fcc110"][ "structure" ] - sub1 = place_adsorbate(sub1, "C")["custom"]["structure"] + sub1 = place_adsorbate(sub1, Atoms("C")) sub2 = generate_surface_structures(["Li"], facets={"Li": ["100"]})["Li"]["bcc100"][ "structure" ] - sub2 = place_adsorbate(sub2, "Mg")["custom"]["structure"] + sub2 = place_adsorbate(sub2, Atoms("Mg")) sub3 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ "hcp0001" ]["structure"] - sub3 = place_adsorbate(sub3, "N")["custom"]["structure"] + sub3 = place_adsorbate(sub3, Atoms("N")) structs = [sub1, sub2, sub3] labels = np.array([0.1, np.nan, 0.3]) acds = DesignSpace(structs, labels) @@ -114,15 +115,15 @@ def test_sequential_learner_write_json(): sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["110"]})["Ag"]["fcc110"][ "structure" ] - sub1 = place_adsorbate(sub1, "B")["custom"]["structure"] + sub1 = place_adsorbate(sub1, Atoms("B")) sub2 = generate_surface_structures(["Li"], facets={"Li": ["100"]})["Li"]["bcc100"][ "structure" ] - sub2 = place_adsorbate(sub2, "Al")["custom"]["structure"] + sub2 = place_adsorbate(sub2, Atoms("Al")) sub3 = generate_surface_structures(["Ti"], facets={"Ti": ["0001"]})["Ti"][ "hcp0001" ]["structure"] - sub3 = place_adsorbate(sub3, "H")["custom"]["structure"] + sub3 = place_adsorbate(sub3, Atoms("H")) structs = [sub1, sub2, sub3] labels = np.array([0.1, 0.2, np.nan]) featurization_kwargs = {"preset": "magpie"} @@ -152,7 +153,7 @@ def test_sequential_learner_write_json(): "GaussianProcessRegressor", ] predictor_kwargs["featurizer_class"] = [ - "matminer.featurizers.composition", + "matminer.featurizers.composition.composite", "ElementProperty", ] del predictor_kwargs["featurization_kwargs"]["design_space_structures"] @@ -187,7 +188,7 @@ def test_sequential_learner_write_json(): "GaussianProcessRegressor", ] predictor_kwargs["featurizer_class"] = [ - "matminer.featurizers.composition", + "matminer.featurizers.composition.composite", "ElementProperty", ] assert sl[4] == predictor_kwargs @@ -219,19 +220,19 @@ def test_sequential_learner_iterate(): sub1 = generate_surface_structures(["Ca"], facets={"Ca": ["111"]})["Ca"]["fcc111"][ "structure" ] - sub1 = place_adsorbate(sub1, "Na")["custom"]["structure"] + sub1 = place_adsorbate(sub1, Atoms("Na")) sub2 = generate_surface_structures(["Nb"], facets={"Nb": ["110"]})["Nb"]["bcc110"][ "structure" ] - sub2 = place_adsorbate(sub2, "K")["custom"]["structure"] + sub2 = place_adsorbate(sub2, Atoms("K")) sub3 = generate_surface_structures(["Ta"], facets={"Ta": ["110"]})["Ta"]["bcc110"][ "structure" ] - sub3 = place_adsorbate(sub3, "H")["custom"]["structure"] + sub3 = place_adsorbate(sub3, Atoms("H")) sub4 = generate_surface_structures(["Sr"], facets={"Sr": ["110"]})["Sr"]["fcc110"][ "structure" ] - sub4 = place_adsorbate(sub4, "Fe")["custom"]["structure"] + sub4 = place_adsorbate(sub4, Atoms("Fe")) structs = [sub1, sub2, sub3, sub4] labels = np.array([11.0, 25.0, np.nan, np.nan]) acds = DesignSpace(structs, labels) @@ -286,19 +287,19 @@ def test_sequential_learner_setup(): sub1 = generate_surface_structures(["Ir"], facets={"Ir": ["100"]})["Ir"]["fcc100"][ "structure" ] - sub1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + sub1 = place_adsorbate(sub1, Atoms("S")) sub2 = generate_surface_structures(["Mo"], facets={"Mo": ["110"]})["Mo"]["bcc110"][ "structure" ] - sub2 = place_adsorbate(sub2, "H")["custom"]["structure"] + sub2 = place_adsorbate(sub2, Atoms("H")) sub3 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ "structure" ] - sub3 = place_adsorbate(sub3, "O")["custom"]["structure"] + sub3 = place_adsorbate(sub3, Atoms("O")) sub4 = generate_surface_structures(["Re"], facets={"Re": ["0001"]})["Re"][ "hcp0001" ]["structure"] - sub4 = place_adsorbate(sub4, "N")["custom"]["structure"] + sub4 = place_adsorbate(sub4, Atoms("N")) structs = [sub1, sub2, sub3, sub4] labels = np.array([4.0, np.nan, 6.0, np.nan]) acds = DesignSpace(structs, labels) @@ -341,11 +342,11 @@ def test_design_space_setup(): sub1 = generate_surface_structures( ["Pt"], supercell_dim=[2, 2, 5], facets={"Pt": ["100"]} )["Pt"]["fcc100"]["structure"] - sub1 = place_adsorbate(sub1, "H2")["custom"]["structure"] + sub1 = place_adsorbate(sub1, Atoms("H")) sub2 = generate_surface_structures(["Na"], facets={"Na": ["110"]})["Na"]["bcc110"][ "structure" ] - sub2 = place_adsorbate(sub2, "F")["custom"]["structure"] + sub2 = place_adsorbate(sub2, Atoms("F")) structs = [sub1, sub2] labels = np.array([3.0, 7.0]) acds = DesignSpace(structs, labels) @@ -364,19 +365,19 @@ def test_delitem_design_space(): sub0 = generate_surface_structures(["Pd"], facets={"Pd": ["100"]})["Pd"]["fcc100"][ "structure" ] - sub0 = place_adsorbate(sub0, "O")["custom"]["structure"] + sub0 = place_adsorbate(sub0, Atoms("O")) sub1 = generate_surface_structures(["V"], facets={"V": ["110"]})["V"]["bcc110"][ "structure" ] - sub1 = place_adsorbate(sub1, "H")["custom"]["structure"] + sub1 = place_adsorbate(sub1, Atoms("H")) sub2 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ "structure" ] - sub2 = place_adsorbate(sub2, "S")["custom"]["structure"] + sub2 = place_adsorbate(sub2, Atoms("S")) sub3 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ "hcp0001" ]["structure"] - sub3 = place_adsorbate(sub3, "P")["custom"]["structure"] + sub3 = place_adsorbate(sub3, Atoms("P")) structs = [sub0, sub1, sub2] labels = np.array([-2.5, np.nan, 600.0]) # test deleting by single idx @@ -420,19 +421,19 @@ def test_eq_design_space(): sub0 = generate_surface_structures(["Pd"], facets={"Pd": ["100"]})["Pd"]["fcc100"][ "structure" ] - sub0 = place_adsorbate(sub0, "O")["custom"]["structure"] + sub0 = place_adsorbate(sub0, Atoms("O")) sub1 = generate_surface_structures(["V"], facets={"V": ["110"]})["V"]["bcc110"][ "structure" ] - sub1 = place_adsorbate(sub1, "H")["custom"]["structure"] + sub1 = place_adsorbate(sub1, Atoms("H")) sub2 = generate_surface_structures(["Fe"], facets={"Fe": ["110"]})["Fe"]["bcc110"][ "structure" ] - sub2 = place_adsorbate(sub2, "S")["custom"]["structure"] + sub2 = place_adsorbate(sub2, Atoms("S")) sub3 = generate_surface_structures(["Ru"], facets={"Ru": ["0001"]})["Ru"][ "hcp0001" ]["structure"] - sub3 = place_adsorbate(sub3, "P")["custom"]["structure"] + sub3 = place_adsorbate(sub3, Atoms("P")) structs = [sub0, sub1, sub2] labels = np.array([-2.5, np.nan, 600.0]) @@ -546,9 +547,9 @@ def test_simulated_sequential_histories(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - base_struct3 = place_adsorbate(sub2, "H")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("O")) + base_struct2 = place_adsorbate(sub2, Atoms("N")) + base_struct3 = place_adsorbate(sub2, Atoms("H")) ds_structs = [ base_struct1, base_struct2, @@ -595,8 +596,8 @@ def test_simulated_sequential_batch_added(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("O")) + base_struct2 = place_adsorbate(sub2, Atoms("N")) candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} predictor_kwargs = {"featurizer_class": SineMatrix} num_loops = 2 @@ -624,8 +625,8 @@ def test_simulated_sequential_num_loops(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "H")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "N")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("H")) + base_struct2 = place_adsorbate(sub2, Atoms("N")) predictor_kwargs = {"featurizer_class": SineMatrix} candidate_selection_kwargs = {"num_candidates_to_pick": 3, "aq": "Random"} ds_structs = [base_struct1, base_struct2, sub1, sub2] @@ -676,9 +677,9 @@ def test_simulated_sequential_write_to_disk(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"][ "fcc100" ]["structure"] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] - base_struct3 = place_adsorbate(sub2, "N")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("O")) + base_struct2 = place_adsorbate(sub2, Atoms("S")) + base_struct3 = place_adsorbate(sub2, Atoms("N")) predictor_kwargs = {"featurizer_class": SineMatrix} candidate_selection_kwargs = {"num_candidates_to_pick": 2, "aq": "Random"} ds_structs = [base_struct1, base_struct2, base_struct3] @@ -727,8 +728,8 @@ def test_simulated_sequential_learning_fully_explored(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("OH")) + base_struct2 = place_adsorbate(sub2, Atoms("NH")) predictor_kwargs = {"structure_featurizer": "elemental_property"} ds_structs = [base_struct1, base_struct2, sub2] ds_labels = np.array([0.0, np.nan, 4.0]) @@ -749,7 +750,7 @@ def test_multiple_sequential_learning_serial(): sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("O")) predictor_kwargs = {"featurizer_class": SineMatrix} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) @@ -773,7 +774,7 @@ def test_multiple_sequential_learning_parallel(): sub1 = generate_surface_structures(["Cu"], facets={"Cu": ["111"]})["Cu"]["fcc111"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "Li")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("Li")) predictor_kwargs = {"featurizer_class": SineMatrix} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) @@ -799,7 +800,7 @@ def test_multiple_sequential_learning_write_to_disk(): sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "N")["custom"]["structure"] + base_struct1 = place_adsorbate(sub1, Atoms("N")) predictor_kwargs = {"featurizer_class": SineMatrix} ds_structs = [base_struct1, sub1] ds_labels = np.array([0.0, 0.0]) diff --git a/tests/test_utils.py b/tests/test_utils.py index ca6713d1..a4ee1ea0 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -7,7 +7,7 @@ from autocat.surface import generate_surface_structures from autocat.saa import generate_saa_structures -from autocat.adsorption import generate_rxn_structures +from autocat.adsorption import generate_adsorbed_structures from autocat.utils import extract_structures @@ -48,8 +48,11 @@ def test_extract_adsorption(): saa = generate_saa_structures(["Ru"], ["Pd"], supercell_dim=(2, 2, 5),)["Ru"]["Pd"][ "hcp0001" ]["structure"] - ads_dict = generate_rxn_structures( - saa, ads=["NH2", "Li"], sites={"custom": [(0.0, 0.0)]}, all_sym_sites=False + ads_dict = generate_adsorbed_structures( + saa, + adsorbates=["NH2", "Li"], + adsorption_sites={"custom": [(0.0, 0.0)]}, + use_all_sites=False, ) ex_structures = extract_structures(ads_dict) assert all(isinstance(struct, Atoms) for struct in ex_structures) From 9d4d7322c23dd09608b3612447da81b98787a052 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Tue, 22 Mar 2022 15:24:38 -0400 Subject: [PATCH 210/239] add matminer, dscribe, prettytable to reqs --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 6d717763..8e1889d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,6 @@ numpy==1.22.0 ase==3.21.1 pymatgen==2022.0.17 fire==0.4.0 +matminer==0.7.3 +dscribe==0.4.0 +prettytable==3.2.0 \ No newline at end of file From a295ca05f19bb61603e1fa31d791d2675bb04c4e Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 23 Mar 2022 10:34:22 -0400 Subject: [PATCH 211/239] rm currently unused qml io submodule --- src/autocat/io/__init__.py | 0 src/autocat/io/qml.py | 27 --------------------------- 2 files changed, 27 deletions(-) delete mode 100644 src/autocat/io/__init__.py delete mode 100644 src/autocat/io/qml.py diff --git a/src/autocat/io/__init__.py b/src/autocat/io/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/autocat/io/qml.py b/src/autocat/io/qml.py deleted file mode 100644 index eb5ea0d6..00000000 --- a/src/autocat/io/qml.py +++ /dev/null @@ -1,27 +0,0 @@ -import qml -import os -from ase import Atoms - -import tempfile - - -def ase_atoms_to_qml_compound(ase_atoms: Atoms): - """ - Converter from `ase.Atoms` to `qml.Compound` - - Parameters - ---------- - - ase_atoms: - Atoms object to be converted to a qml.Compound - - Returns - ------- - - qml_compound: - qml.Compound object corresponding to give Atoms object - """ - with tempfile.TemporaryDirectory() as _tmp_dir: - ase_atoms.write(os.path.join(_tmp_dir, "tmp.xyz")) - qml_compound = qml.Compound(xyz=os.path.join(_tmp_dir, "tmp.xyz")) - return qml_compound From 2ed1a41964fe15b4a1f83f940ea9653a4564f86f Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 23 Mar 2022 13:29:25 -0400 Subject: [PATCH 212/239] intermediates -> rxn intermediates, update syntax --- docs/User_Guide/Data/intermediates.md | 18 +++++++++++++----- mkdocs.yml | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/User_Guide/Data/intermediates.md b/docs/User_Guide/Data/intermediates.md index 90ed656e..49a3df06 100644 --- a/docs/User_Guide/Data/intermediates.md +++ b/docs/User_Guide/Data/intermediates.md @@ -1,6 +1,6 @@ When characterizing a surface in the context of a specific reaction, calculating adsorption energies -for all of the intermediates is often important. +for all of the reaction intermediates is often important. Here, AutoCat has default structures for adsorbates of both the oxygen reduction reaction (ORR) and @@ -14,13 +14,21 @@ AutoCat functions: >>> from autocat.data.intermediates import NRR_INTERMEDIATE_NAMES >>> from autocat.surface import generate_surface_structures >>> from autocat.utils import extract_structures ->>> from autocat.adsorption import generate_rxn_structures +>>> from autocat.adsorption import generate_adsorbed_structures >>> pt_dict = generate_surface_structures(["Pt"]) >>> pt_struct = extract_structures(pt_dict)[0] ->>> orr_dict = generate_rxn_structures(pt_struct, ads=ORR_INTERMEDIATE_NAMES) ->>> nrr_dict = generate_rxn_structures(pt_struct, ads=NRR_INTERMEDIATE_NAMES) +>>> orr_structs = generate_adsorbed_structures( +... surface=pt_struct, +... adsorbates=ORR_INTERMEDIATE_NAMES, +... use_all_sites=True +... ) +>>> nrr_structs = generate_adsorbed_structures( +... surface=pt_struct, +... ads=NRR_INTERMEDIATE_NAMES, +... use_all_sites=True +... ) ``` -In the above example, `orr_dict` and `nrr_dict` have all of the corresponding +In the above example, `orr_structs` and `nrr_structs` have all of the corresponding intermediates at every identified unique surface site. Alternatively, if you would like to access the diff --git a/mkdocs.yml b/mkdocs.yml index b29c1aae..03259b57 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,7 +47,7 @@ nav: - HHI: User_Guide/Data/hhi.md - Segregation Energies: User_Guide/Data/segregation_energies.md - Lattice Parameters: User_Guide/Data/lattice_parameters.md - - Intermediates: User_Guide/Data/intermediates.md + - Reaction Intermediates: User_Guide/Data/intermediates.md - Tutorials: - Training a Predictor on hydrogen adsorption energies: Tutorials/pred_h.md - Conducting a simulated sequential learning run: Tutorials/sl.md From 6f3eae3972c7888494c49cbd17332e698dc744bd Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 23 Mar 2022 14:08:22 -0400 Subject: [PATCH 213/239] update the readme --- README.md | 7 +++---- docs/README.md | 23 +++++++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index b6ac00ef..6523293d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # AutoCat -AutoCat is a suite of python tools for sequential learning with -candidate selection metrics for materials applications. It additionally has tools -for automated structure generation related to catalysis. +AutoCat is a suite of python tools for **sequential learning for materials applications** +and **automating structure generation for DFT catalysis studies.** Development of this package stems from [ACED](https://www.cmu.edu/aced/), as part of the ARPA-E DIFFERENTIATE program. @@ -15,7 +14,7 @@ There are two options for installation, either via `pip` or from the repo direct If you are planning on strictly using AutoCat rather than contributing to development, we recommend using `pip` within a virtual environment (e.g. - [`conda`](https://www.anaconda.com/products/individual) + [`conda`](https://docs.conda.io/en/latest/) ). This can be done as follows: diff --git a/docs/README.md b/docs/README.md index 76cc92fd..50c418ef 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,27 +2,26 @@ ![AutoCat Logo](img/autocat_logo.png){ align=right } -AutoCat is a suite of python tools for sequential learning with arbitrary -candidate metrics for materials applications. It additionally has tools -for automated structure generation related to catalysis. +AutoCat is a suite of python tools for **sequential learning for materials applications** +and **automating structure generation for DFT catalysis studies.** Development of this package stems from [ACED](https://www.cmu.edu/aced/), as part of the ARPA-E DIFFERENTIATE program. Below we provide an overview of the key functionalities of AutoCat. -For additional details please see the User Guide and API sections. +For additional details please see the User Guide, Tutorials, and API sections. ## Sequential Learning One of the core philosophies of AutoCat is to provide modular and extensible tooling to -facilitate accelerated computational materials discovery workflows. Within this submodule -are object types for defining a design space, featurizing materials systems, -training a regression model, and defining a closed-loop sequential learning iterator. The -key objects intended for each of these purposes are: +facilitate closed-loop computational materials discovery workflows. Within this submodule +are classes for defining a design space, featurization, +regression, and defining a closed-loop sequential learning iterator. The +key classes intended for each of these purposes are: - [**`DesignSpace`**](User_Guide/Learning/sequential#designspace): define a design space to explore -- [**`Featurizer`**](User_Guide/Learning/featurizers): specify how to featurize the materials for regression +- [**`Featurizer`**](User_Guide/Learning/featurizers): featurize the systems for regression - [**`Predictor`**](User_Guide/Learning/predictors): a regressor for predicting materials properties @@ -52,8 +51,8 @@ Structures generated or read with this package are typically of the form of [`ase.Atoms`](https://wiki.fysik.dtu.dk/ase/ase/atoms.html#module-ase.atoms) objects. -When opting to write to -disk using these functions, they are automatically organized into a clean, scalable directory structure. +When opting to write structures to +disk using these functions, they are automatically organized into a clean, scalable directory organization. All structures are written in the [`ase.io.Trajectory`](https://wiki.fysik.dtu.dk/ase/ase/io/trajectory.html#trajectory) file format. @@ -67,7 +66,7 @@ There are two options for installation, either via `pip` or from the repo direct If you are planning on strictly using AutoCat rather than contributing to development, we recommend using `pip` within a virtual environment (e.g. - [`conda`](https://www.anaconda.com/products/individual) + [`conda`](https://docs.conda.io/en/latest/) ). This can be done as follows: From a64f4f66803477e8df337f3e82c40a9a5b43eb49 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 23 Mar 2022 16:15:14 -0400 Subject: [PATCH 214/239] update example with new syntax per ec4535e --- .../adsorbing_molecules_on_surfaces.ipynb | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/examples/adsorbing_molecules_on_surfaces.ipynb b/examples/adsorbing_molecules_on_surfaces.ipynb index 1e3f5541..9e295aab 100644 --- a/examples/adsorbing_molecules_on_surfaces.ipynb +++ b/examples/adsorbing_molecules_on_surfaces.ipynb @@ -9,8 +9,8 @@ "source": [ "from autocat.surface import generate_surface_structures\n", "\n", - "from autocat.adsorption import generate_rxn_structures\n", - "from autocat.adsorption import generate_molecule_object\n", + "from autocat.adsorption import generate_adsorbed_structures\n", + "from autocat.adsorption import generate_molecule\n", "\n", "from autocat.data.intermediates import ORR_INTERMEDIATE_NAMES" ] @@ -78,12 +78,11 @@ "metadata": {}, "outputs": [], "source": [ - "h_adsorption_structure_dictionary = generate_rxn_structures(\n", - " clean_slab,\n", - " all_sym_sites = True, # to consider all identified sites\n", - " ads = [\"H\"],\n", - " height = {\"H\" : 1.5}, # manually specify height. default guess based on covalent radii of nearest neighbors\n", - " refs = [\"H2\"], # reference molecule for the reaction\n", + "h_adsorption_structure_dictionary = generate_adsorbed_structures(\n", + " surface=clean_slab,\n", + " use_all_sites=True, # to consider all identified sites\n", + " adsorbates=[\"H\"],\n", + " height={\"H\" : 1.5}, # manually specify height. default guess based on covalent radii of nearest neighbors\n", " write_to_disk = False\n", ")" ] @@ -99,11 +98,7 @@ " - Symmetry Site types (ie. hollow, ontop, bridge) or Custom Label\n", " - `x-y` coordinate of each site\n", " - `ase.Atoms` structure\n", - " - Path to structure file (in the `ase.traj` format)\n", - "- references\n", - " - Reference Molecule Name\n", - " - `ase.Atoms` structure\n", - " - Path to structure file (in the `ase.traj` format)" + " - Path to structure file (in the `ase.traj` format)" ] }, { @@ -163,9 +158,9 @@ "id": "5c3bc7ac", "metadata": {}, "source": [ - "Instead of exhaustively considering all sites, it can be restricted to specific types via `site_type`. \n", + "Instead of exhaustively considering all sites, it can be restricted to specific types via `site_types`. \n", "\n", - "Or alternatively, if we want to consider only manually specified sites, that can be done via `sites`. When specifying the sites manually in this way, we need to provide them as a dictionary with keys as to how we'd like the site labelled. This is solely used for organizing the output dictionary" + "Or alternatively, if we want to consider only manually specified sites, that can be done via `adsorption_sites`. When specifying the sites manually in this way, we need to provide them as a dictionary with keys as to how we'd like the site labelled. This is solely used for organizing the output dictionary" ] }, { @@ -175,11 +170,11 @@ "metadata": {}, "outputs": [], "source": [ - "h_manual_adsorption_structure_dictionary = generate_rxn_structures(\n", - " clean_slab,\n", - " all_sym_sites = False,\n", - " ads = [\"H\"],\n", - " sites = {\"custom\": [(0.,0.)]},\n", + "h_manual_adsorption_structure_dictionary = generate_adsorbed_structures(\n", + " surface=clean_slab,\n", + " use_all_sites=False,\n", + " adsorbates=[\"H\"],\n", + " adsorption_sites={\"custom\": [(0.,0.)]},\n", " write_to_disk = False\n", ")" ] @@ -219,11 +214,10 @@ "metadata": {}, "outputs": [], "source": [ - "orr_adsorption_structure_dictionary = generate_rxn_structures(\n", - " clean_slab,\n", - " all_sym_sites = True, # to consider all identified sites (can also manually specify via `sites`)\n", - " ads = ORR_INTERMEDIATE_NAMES,\n", - " refs = [\"H2\", \"H2O\"], # reference molecules for the reaction\n", + "orr_adsorption_structure_dictionary = generate_adsorbed_structures(\n", + " surface=clean_slab,\n", + " use_all_sites = True, # to consider all identified sites (can also manually specify via `sites`)\n", + " adsorbates=ORR_INTERMEDIATE_NAMES,\n", " write_to_disk = False\n", ")" ] @@ -259,7 +253,7 @@ "id": "51561b67", "metadata": {}, "source": [ - "It's important to note that if you already have the adsorbate molecule you'd like to consider as an `ase.Atoms` object, that can be supplied as well. We are going to use `autocat.adsorption.generate_molecule_object` to generate an example, but this can be anything (e.g. an `*.sdf` read by `ase.io.read`)" + "It's important to note that if you already have the adsorbate molecule you'd like to consider as an `ase.Atoms` object, that can be supplied as well via a `dict`. We are going to use `autocat.adsorption.generate_molecule_object` to generate an example, but this can be anything (e.g. an `*.sdf` read by `ase.io.read`)" ] }, { @@ -269,12 +263,12 @@ "metadata": {}, "outputs": [], "source": [ - "nh2_mol = generate_molecule_object(\"NH2\").get(\"structure\")\n", + "nh2_mol = generate_molecule(\"NH2\")[\"NH2\"].get(\"structure\")\n", "\n", - "nh2_adsorption_structure_dictionary = generate_rxn_structures(\n", - " clean_slab,\n", - " all_sym_sites = True,\n", - " ads = [nh2_mol],\n", + "nh2_adsorption_structure_dictionary = generate_adsorbed_structures(\n", + " surface=clean_slab,\n", + " use_all_sites = True,\n", + " adsorbates = {\"NH2\": nh2_mol},\n", ")" ] }, From 79e411516749b04f957e2e2dbaa51b94d1252a03 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 25 Mar 2022 12:41:11 -0400 Subject: [PATCH 215/239] rm extremely out of date learning examples --- .../learning/featurization_tutorial.ipynb | 434 ------------------ examples/learning/predictor_tutorial.ipynb | 371 --------------- .../sequential_learning_tutorial.ipynb | 291 ------------ 3 files changed, 1096 deletions(-) delete mode 100644 examples/learning/featurization_tutorial.ipynb delete mode 100644 examples/learning/predictor_tutorial.ipynb delete mode 100644 examples/learning/sequential_learning_tutorial.ipynb diff --git a/examples/learning/featurization_tutorial.ipynb b/examples/learning/featurization_tutorial.ipynb deleted file mode 100644 index 8492d5ef..00000000 --- a/examples/learning/featurization_tutorial.ipynb +++ /dev/null @@ -1,434 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "from autocat.adsorption import generate_rxn_structures\n", - "from autocat.surface import generate_surface_structures\n", - "from autocat.learning.featurizers import full_structure_featurization\n", - "from autocat.learning.featurizers import adsorbate_featurization\n", - "from autocat.learning.featurizers import catalyst_featurization\n", - "from autocat.learning.featurizers import _get_number_of_features\n", - "from autocat.learning.featurizers import get_X" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Adsorbate Featurization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example we are going to show how to featurize a catalyst structure solely based on local features of the adsorbate. First, let's start with making our adsorbate+slab structure" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# generate the surface structure to place an adsorbate on\n", - "surf = generate_surface_structures([\"Fe\"])[\"Fe\"][\"bcc100\"][\"structure\"]\n", - "\n", - "# place an adsorbate onto the surface\n", - "ads_struct = generate_rxn_structures(surf, ads=[\"Li\"])[\"Li\"][\"ontop\"][\"0.0_0.0\"][\n", - " \"structure\"\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have our structure, we can now featurize the OOH adsorbate using a multitude of techniques. Let's use SOAP:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Adsorbate featurized into a vector of shape: (1950,)\n" - ] - } - ], - "source": [ - "soap_feat = adsorbate_featurization(\n", - " ads_struct,\n", - " featurizer=\"soap\",\n", - " species_list = [\"Fe\", \"Li\", \"H\"], # species that should be accounted for by the representation\n", - " rcut = 6, # cutoff radius\n", - " nmax=4,\n", - " lmax=4, # maximum order of spherical harmonic\n", - " maximum_adsorbate_size = 5, # maximum adsorbate size that can be handled by the representation\n", - " refine_structure = True # this refines the structure to only include surface and adsorbate atoms\n", - ")\n", - "print(f\"Adsorbate featurized into a vector of shape: {soap_feat.shape}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Similarly, we can use the same function with a different featurization technique. For example we can use the order parameter site fingerprint:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Adsorbate featurized into a vector of shape: (185,)\n" - ] - } - ], - "source": [ - "opsf_feat = adsorbate_featurization(\n", - " ads_struct,\n", - " featurizer=\"op_sitefingerprint\",\n", - " maximum_adsorbate_size=5,\n", - " refine_structure=False,\n", - ")\n", - "print(f\"Adsorbate featurized into a vector of shape: {opsf_feat.shape}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that different featurization techniques will result in varying representation sizes, but will also have varying degrees of learning success. We encourage the user to try different approaches for their specific application" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Long-range full structure featurization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the previous section we looked at featurizing the adsorbate by its local environment. Now we can take a look at how we might want to capture longer-range phenomena via full structure featurization. Again, we start with making an example structure" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# generate the surface structure to place an adsorbate on\n", - "surf = generate_surface_structures([\"Cu\"])[\"Cu\"][\"fcc111\"][\"structure\"]\n", - "\n", - "# place an adsorbate onto the surface\n", - "ads_struct = generate_rxn_structures(surf, ads=[\"OH\"])[\"OH\"][\"ontop\"][\"0.0_0.0\"][\n", - " \"structure\"\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One of the simpler forms of larger scale featurization is just taking elemental properties based upon the structure composition:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1.00000000e+00 4.40000000e+01 4.30000000e+01 4.08461538e+01\n", - " 5.82248521e+00 4.40000000e+01 5.60000000e+01 9.20000000e+01\n", - " 3.60000000e+01 5.85128205e+01 4.63905325e+00 5.60000000e+01\n", - " 1.00794000e+00 1.01070000e+02 1.00062060e+02 9.37062200e+01\n", - " 1.35946708e+01 1.01070000e+02 1.40100000e+01 2.60700000e+03\n", - " 2.59299000e+03 2.40879667e+03 3.65913846e+02 2.60700000e+03\n", - " 1.00000000e+00 1.50000000e+01 1.40000000e+01 7.82051282e+00\n", - " 6.99539776e-01 8.00000000e+00 1.00000000e+00 5.00000000e+00\n", - " 4.00000000e+00 4.71794872e+00 5.20710059e-01 5.00000000e+00\n", - " 3.10000000e+01 1.46000000e+02 1.15000000e+02 1.38179487e+02\n", - " 1.44378698e+01 1.46000000e+02 2.20000000e+00 3.04000000e+00\n", - " 8.40000000e-01 2.22153846e+00 4.19723866e-02 2.20000000e+00\n", - " 1.00000000e+00 2.00000000e+00 1.00000000e+00 1.02564103e+00\n", - " 4.99671269e-02 1.00000000e+00 0.00000000e+00 3.00000000e+00\n", - " 3.00000000e+00 7.69230769e-02 1.49901381e-01 0.00000000e+00\n", - " 0.00000000e+00 7.00000000e+00 7.00000000e+00 6.46153846e+00\n", - " 9.94082840e-01 7.00000000e+00 0.00000000e+00 0.00000000e+00\n", - " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", - " 1.00000000e+00 8.00000000e+00 7.00000000e+00 7.56410256e+00\n", - " 8.04733728e-01 8.00000000e+00 0.00000000e+00 1.00000000e+00\n", - " 1.00000000e+00 9.74358974e-01 4.99671269e-02 1.00000000e+00\n", - " 0.00000000e+00 3.00000000e+00 3.00000000e+00 7.69230769e-02\n", - " 1.49901381e-01 0.00000000e+00 0.00000000e+00 3.00000000e+00\n", - " 3.00000000e+00 2.76923077e+00 4.26035503e-01 3.00000000e+00\n", - " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", - " 0.00000000e+00 0.00000000e+00 1.00000000e+00 4.00000000e+00\n", - " 3.00000000e+00 3.82051282e+00 3.31360947e-01 4.00000000e+00\n", - " 6.61500000e+00 1.47687500e+01 8.15375000e+00 1.31886859e+01\n", - " 6.74224195e-01 1.35100000e+01 0.00000000e+00 7.85300000e+00\n", - " 7.85300000e+00 5.67769231e-01 1.04818935e+00 0.00000000e+00\n", - " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", - " 0.00000000e+00 0.00000000e+00 1.94000000e+02 1.94000000e+02\n", - " 0.00000000e+00 1.94000000e+02 0.00000000e+00 1.94000000e+02]\n" - ] - } - ], - "source": [ - "elem_prop = full_structure_featurization(\n", - " ads_struct,\n", - " featurizer=\"elemental_property\",\n", - " elementalproperty_preset=\"magpie\", # here we can choose other presets as implemented within `matminer`\n", - " refine_structure = False,\n", - " maximum_structure_size = None, # by default we will just take the max size as the size of the slab+adsorbate\n", - ")\n", - "print(f\"Full structure featurized into representation of shape {elem_prop.shape}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also featurize the full structure through use of a sine matrix, an extension of the coulomb matrix:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Full structure featurized into representation of shape (1600,)\n" - ] - } - ], - "source": [ - "sine_matrix = full_structure_featurization(\n", - " ads_struct,\n", - " maximum_structure_size = None,\n", - " refine_structure = True # this helps reduce the number of features to only\n", - ")\n", - "print(f\"Full structure featurized into representation of shape {sine_matrix.shape}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We note here the use of refine_structure for the sine matrix. The main motivation for this is that since the sine matrix consists of pairwise interactions, and we are most interested in the adsorbate + slab interactions, only considering the surface and adsorbate atoms is an approach to reduce the representation length " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Catalyst Featurization (Adsorbate + Full Structure)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a means to incorporate both the short-range and longer-range features into a single representation, we can combine them in a consistent, generalizable manner using autocat. First, let's make our structure:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# generate the surface structure to place an adsorbate on\n", - "surf = generate_surface_structures([\"Ru\"])[\"Ru\"][\"hcp0001\"][\"structure\"]\n", - "\n", - "# place an adsorbate onto the surface\n", - "ads_struct = generate_rxn_structures(surf, ads=[\"NH2\"])[\"NH2\"][\"ontop\"][\"0.0_0.0\"][\n", - " \"structure\"\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we have the freedom to combine any of autocat's local featurization techniques with any of its longer range features. As an example, let's combine the sine matrix with SOAP" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Catalyst featurized into a representation of shape (3160,)\n" - ] - } - ], - "source": [ - "cat = catalyst_featurization(\n", - " ads_struct,\n", - " maximum_structure_size=40,\n", - " maximum_adsorbate_size=4,\n", - " structure_featurizer = \"sine_matrix\",\n", - " adsorbate_featurizer = \"soap\",\n", - " adsorbate_featurization_kwargs={\"rcut\": 6.0, \"nmax\": 4, \"lmax\": 4},\n", - " refine_structure=False,\n", - ")\n", - "print(f\"Catalyst featurized into a representation of shape {cat.shape}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is where the use of maximum structure size and maximum adsorbate size becomes most useful, as it allows for consistently sized vectors when incorporating structures and adsorbates of different sizes into a single representation. This is done through the use of zero-padding for both full-structure and short-range components of the representation. This leads into our next section of featurizing into an input matrix" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Featurizing many structures at once" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As is often the case, we are generally most interested in featurizing multiple structures at once rather than just a single structure in isolation. While if the user opts to use AutoCats structure corrector predictor this is done under-the-hood, but we showcase this feature here to show how it can be used if one then wants to feed this into an alternate ML software" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# generate multiple slab+adsorbate structures\n", - "structs = []\n", - "surf1 = generate_surface_structures([\"Pt\"])[\"Pt\"][\"fcc111\"][\"structure\"]\n", - "ads1 = generate_rxn_structures(\n", - " surf1,\n", - " ads=[\"NH3\", \"CO\"],\n", - " all_sym_sites=False,\n", - " sites={\"origin\": [(0.0, 0.0)]},\n", - " height={\"CO\": 1.5},\n", - " rots={\"NH3\": [[180.0, \"x\"], [90.0, \"z\"]], \"CO\": [[180.0, \"y\"]]},\n", - ")\n", - "structs.append(ads1[\"NH3\"][\"origin\"][\"0.0_0.0\"][\"structure\"])\n", - "structs.append(ads1[\"CO\"][\"origin\"][\"0.0_0.0\"][\"structure\"])\n", - "surf2 = generate_surface_structures([\"Ru\"])[\"Ru\"][\"hcp0001\"][\"structure\"]\n", - "ads2 = generate_rxn_structures(\n", - " surf2, ads=[\"N\"], all_sym_sites=False, sites={\"origin\": [(0.0, 0.0)]},\n", - ")\n", - "structs.append(ads2[\"N\"][\"origin\"][\"0.0_0.0\"][\"structure\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Input matrix, X of shape (3, 10000) generated\n" - ] - } - ], - "source": [ - "X = get_X(\n", - " structs,\n", - " maximum_structure_size=50,\n", - " maximum_adsorbate_size=5,\n", - " structure_featurizer = \"sine_matrix\",\n", - " adsorbate_featurizer = \"soap\",\n", - " refine_structures = True,\n", - " adsorbate_featurization_kwargs={\"rcut\": 5.0, \"nmax\": 4, \"lmax\": 4},\n", - " write_to_disk = False, # we can write the matrix to disk as a json\n", - " write_location = \".\",\n", - ")\n", - "print(f\"Input matrix, X of shape {X.shape} generated\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We note that the shape of the matrix X generated is (# of structures, full_structure_feat(max_structure_size) + adsorbate_feat(max_adsorbate_size))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.9" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/learning/predictor_tutorial.ipynb b/examples/learning/predictor_tutorial.ipynb deleted file mode 100644 index aa47e9e0..00000000 --- a/examples/learning/predictor_tutorial.ipynb +++ /dev/null @@ -1,371 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "cdcaeeef", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "from sklearn.gaussian_process import GaussianProcessRegressor\n", - "\n", - "from autocat.adsorption import place_adsorbate\n", - "from autocat.surface import generate_surface_structures\n", - "from autocat.perturbations import generate_perturbed_dataset\n", - "from autocat.learning.predictors import AutoCatStructureCorrector" - ] - }, - { - "cell_type": "markdown", - "id": "42ad423a", - "metadata": {}, - "source": [ - "In this tutorial we show how to use the AutoCatStructureCorrector for both training on relaxed structures and predicting corrections to initial structure guesses" - ] - }, - { - "cell_type": "markdown", - "id": "37fb7a41", - "metadata": {}, - "source": [ - "# Creating an AutoCatStructureCorrector object" - ] - }, - { - "cell_type": "markdown", - "id": "053de3bb", - "metadata": {}, - "source": [ - "Let's start with creating our AutoCatStructureCorrector object" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "78ebf3b3", - "metadata": {}, - "outputs": [], - "source": [ - "acsc = AutoCatStructureCorrector(\n", - " model_class = GaussianProcessRegressor, # regressor model class\n", - " structure_featurizer=\"sine_matrix\",\n", - " adsorbate_featurizer=\"soap\",\n", - " adsorbate_featurization_kwargs={\"rcut\": 5.0, \"nmax\": 8, \"lmax\": 6},\n", - " refine_structures = True,\n", - " maximum_structure_size = None,\n", - " maximum_adsorbate_size = None,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "baa10924", - "metadata": {}, - "source": [ - "The model class provided should have fit and predict (with return_std) methods. The AutoCatStructureCorrector class behaves similarly to sci-kit learn regressor objects with fit, predict and score methods." - ] - }, - { - "cell_type": "markdown", - "id": "33d69603", - "metadata": {}, - "source": [ - "# Generating Perturbed Structure Datasets" - ] - }, - { - "cell_type": "markdown", - "id": "9930927d", - "metadata": {}, - "source": [ - "Before we can demonstrate the capabilities of the AutoCatStructureCorrector class, we need to create a perturbed dataset from given base structures. These base structures would typically be already relaxed with DFT. For simplicity let's consider a single base structure, which would correspond to an interpolative prediction on the surface, generated directly by AutoCat (in practice this would ideally be relaxed before use)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "225e7954", - "metadata": {}, - "outputs": [], - "source": [ - "sub = generate_surface_structures([\"Pd\"], facets={\"Pd\": [\"100\"]})[\"Pd\"][\"fcc100\"][\n", - " \"structure\"\n", - "]\n", - "base_struct = place_adsorbate(sub, \"CO\")[\"custom\"][\"structure\"]" - ] - }, - { - "cell_type": "markdown", - "id": "4ea5e3f3", - "metadata": {}, - "source": [ - "Now that we have the base structure, we can now perturb it to create the dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "42d1ffe5", - "metadata": {}, - "outputs": [], - "source": [ - "train_set = generate_perturbed_dataset(\n", - " [base_struct],\n", - " num_of_perturbations=20,\n", - " minimum_perturbation_distance = 0.01,\n", - " maximum_perturbation_distance = 0.75,\n", - " maximum_adsorbate_size = None,\n", - ")\n", - "\n", - "# separating out the corrected structures and corresponding correction matrix\n", - "train_structures = train_set[\"collected_structures\"]\n", - "train_correction_matrix = train_set[\"correction_matrix\"] # matrix which has padding\n", - "train_corrections_list = train_set[\"corrections_list\"] # list of corrections (which will have variable length)" - ] - }, - { - "cell_type": "markdown", - "id": "44df37cc", - "metadata": {}, - "source": [ - "**A note on directionality:** Perturbations are controlled via the tags associated with each atom in the Atoms object. By default, slabs generated with ASE/AutoCat will have the tags going from 1 (top-layer) to # of layers (bottom layer) and adsorbates will have a tag of 0. Atoms given a tag of 0 will be free to move and atoms with tags >=1 will be fixed. Constraints in directionality may be specified via the following specially assigned tag-values (see documentation for further details)" - ] - }, - { - "cell_type": "markdown", - "id": "2e8e7e28", - "metadata": {}, - "source": [ - "For future use let's also make a test set" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "234bd52a", - "metadata": {}, - "outputs": [], - "source": [ - "test_set = generate_perturbed_dataset(\n", - " [base_struct],\n", - " num_of_perturbations=20,\n", - " minimum_perturbation_distance = 0.01,\n", - " maximum_perturbation_distance = 0.75,\n", - " maximum_adsorbate_size = None,\n", - ")\n", - "\n", - "# separating out the corrected structures and corresponding correction matrix\n", - "test_structures = test_set[\"collected_structures\"]\n", - "test_corrections_list = test_set[\"corrections_list\"]" - ] - }, - { - "cell_type": "markdown", - "id": "364c6359", - "metadata": {}, - "source": [ - "# Fitting to perturbation data" - ] - }, - { - "cell_type": "markdown", - "id": "20312914", - "metadata": {}, - "source": [ - "To fit our model to the generated perturbation data, we can use the fit method" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "b8803318", - "metadata": {}, - "outputs": [], - "source": [ - "acsc.fit(\n", - " train_structures,\n", - " correction_matrix = train_correction_matrix\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "c2c69d9e", - "metadata": {}, - "source": [ - "Alternatively, we can also provide the corrections as a list" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "6bd60f27", - "metadata": {}, - "outputs": [], - "source": [ - "acsc.fit(\n", - " train_structures,\n", - " corrections_list = train_corrections_list\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "0d67ceb0", - "metadata": {}, - "source": [ - "If we want to check whether our model has been fit to data, we can use the is_fit method. Note that changing any of the model or featurizer settings will automatically change this value back to False" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "977af7d1", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Has the model been fit? True\n", - "Is the model still been fit? False\n" - ] - } - ], - "source": [ - "print(f\"Has the model been fit? {acsc.is_fit}\")\n", - "\n", - "acsc.structure_featurizer = \"elemental_property\"\n", - "print(f\"Is the model still been fit? {acsc.is_fit}\")" - ] - }, - { - "cell_type": "markdown", - "id": "f113fce5", - "metadata": {}, - "source": [ - "Let's refit the model to be used for the next section" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "021a19e7", - "metadata": {}, - "outputs": [], - "source": [ - "acsc.structure_featurizer = \"sine_matrix\"\n", - "\n", - "acsc.fit(\n", - " train_structures,\n", - " corrections_list = train_corrections_list\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "158eefad", - "metadata": {}, - "source": [ - "# Structure Correction Predictions and Performance Metrics" - ] - }, - { - "cell_type": "markdown", - "id": "7ab75c93", - "metadata": {}, - "source": [ - "Now that we have a fit model, we can now make predictions on the test set we made earlier. To do this we can make use of the predict method" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "1a1c2303", - "metadata": {}, - "outputs": [], - "source": [ - "predicted_corrections, corrected_structures, unc = (\n", - " acsc.predict(\n", - " test_structures\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "adb13e4d", - "metadata": {}, - "source": [ - "When we make predictions we are given three quantities: a list of the predicted corrections for each structure, Atoms objects of the corrected structures, and the uncertainties attributed to each prediction." - ] - }, - { - "cell_type": "markdown", - "id": "3226da93", - "metadata": {}, - "source": [ - "Since we know what the corrections need to be for each of the test structures, we can evaluate performance metrics on each of the predictions. At present, both MAE and RMSE are implemented within the score method" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "9d1b78e5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MAE = 0.7263375790848622\n", - "RMSE = 0.5777037271424246\n" - ] - } - ], - "source": [ - "MAE = acsc.score(test_structures, test_corrections_list)\n", - "print(f\"MAE = {MAE}\")\n", - "\n", - "RMSE = acsc.score(test_structures, test_corrections_list, metric=\"rmse\")\n", - "print(f\"RMSE = {RMSE}\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.9" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/learning/sequential_learning_tutorial.ipynb b/examples/learning/sequential_learning_tutorial.ipynb deleted file mode 100644 index 1e98871c..00000000 --- a/examples/learning/sequential_learning_tutorial.ipynb +++ /dev/null @@ -1,291 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "b2f4b043", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "from autocat.adsorption import place_adsorbate\n", - "from autocat.surface import generate_surface_structures\n", - "from autocat.learning.predictors import AutoCatStructureCorrector\n", - "from autocat.learning.sequential import simulated_sequential_learning\n", - "from autocat.learning.sequential import multiple_sequential_learning_runs" - ] - }, - { - "cell_type": "markdown", - "id": "2ceb554b", - "metadata": {}, - "source": [ - "In this tutorial we show how to conduct sequential learning runs for training an `AutoCatStructureCorrector`" - ] - }, - { - "cell_type": "markdown", - "id": "c2d963c5", - "metadata": {}, - "source": [ - "# Sequential Learning" - ] - }, - { - "cell_type": "markdown", - "id": "016adce8", - "metadata": {}, - "source": [ - "In order to conduct a sequential learning run, we need three things: i) an `AutoCatStructureCorrector` object with our desired featurizer settings, ii) base structures to be used for training, iii) base_structures to be used for testing (this one is optional)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "c77eca10", - "metadata": {}, - "outputs": [], - "source": [ - "acsc = AutoCatStructureCorrector(\n", - " structure_featurizer=\"sine_matrix\",\n", - " adsorbate_featurizer=\"soap\",\n", - " adsorbate_featurization_kwargs={\"rcut\": 5.0, \"nmax\": 4, \"lmax\": 4},\n", - " refine_structures = True,\n", - " maximum_structure_size = None, # will default to max structure encountered\n", - " maximum_adsorbate_size = None, # will default to max adsorbate encountered\n", - " species_list = [\"Pd\", \"C\", \"O\", \"Cu\"] # This is important to include!\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "b86e5552", - "metadata": {}, - "outputs": [], - "source": [ - "sub = generate_surface_structures([\"Pd\"], facets={\"Pd\": [\"100\"]})[\"Pd\"][\"fcc100\"][\n", - " \"structure\"\n", - "]\n", - "train_base_struct = place_adsorbate(sub, \"CO\")[\"custom\"][\"structure\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cd42d052", - "metadata": {}, - "outputs": [], - "source": [ - "sub2 = generate_surface_structures([\"Cu\"], facets={\"Cu\": [\"100\"]})[\"Cu\"][\"fcc100\"][\n", - " \"structure\"\n", - "]\n", - "test_base_struct = place_adsorbate(sub2, \"O\")[\"custom\"][\"structure\"]" - ] - }, - { - "cell_type": "markdown", - "id": "e185c3d8", - "metadata": {}, - "source": [ - "We're now ready to conduct a sequential learning run" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "764fc7bb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sequential Learning Iteration #1\n", - "Sequential Learning Iteration #2\n", - "Sequential Learning Iteration #3\n", - "Sequential Learning Iteration #4\n" - ] - } - ], - "source": [ - "sl_dict = simulated_sequential_learning(\n", - " acsc,\n", - " [train_base_struct],\n", - " testing_base_structures = [test_base_struct],\n", - " initial_num_of_perturbations_per_base_structure = 4, # for generating the initial training set\n", - " batch_num_of_perturbations_per_base_structure = 3, # how large of a pool to predict on for each loop\n", - " batch_size_to_add = 3, # how many structures to add to training on each loop\n", - " number_of_sl_loops = 4,\n", - " write_to_disk = False # if we want to save the history info to disk\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "164c2d27", - "metadata": {}, - "source": [ - "Contained within the returned disk is multiple histories as a function of sequential learning iteration including MAE & RMSE on the validation and testing sets" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "49a10d8f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MAE training history: [0.6085485197361659, 0.6085485197361659, 0.6085485197361659, 0.6085485197361659]\n", - "RMSE training history: [0.40434634537932845, 0.40434634537932845, 0.40434634537932845, 0.40434634537932845]\n", - "\n", - "\n", - "MAE test history: [0.6661825694029918, 0.6661825694029918, 0.6661825694029918, 0.6661825694029918]\n", - "RMSE test history: [0.46687185319246804, 0.46687185319246804, 0.46687185319246804, 0.46687185319246804]\n" - ] - } - ], - "source": [ - "print(f\"MAE training history: {sl_dict['mae_train_history']}\")\n", - "print(f\"RMSE training history: {sl_dict['rmse_train_history']}\")\n", - "print('\\n')\n", - "print(f\"MAE test history: {sl_dict['mae_test_history']}\")\n", - "print(f\"RMSE test history: {sl_dict['rmse_test_history']}\")" - ] - }, - { - "cell_type": "markdown", - "id": "493fde74", - "metadata": {}, - "source": [ - "Moreover, the uncertainties of each batch that is added to the training set is also kept (note here it will be very large as the training set was kept small for easier demonstration purposes)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "0e005797", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Full Maximum Uncertainty History: [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]\n" - ] - } - ], - "source": [ - "print(f\"Full Maximum Uncertainty History: {sl_dict['max_unc_history']}\")" - ] - }, - { - "cell_type": "markdown", - "id": "817d0951", - "metadata": {}, - "source": [ - "For additional information about the other quantities that are tracked, we refer the reader to the documentation" - ] - }, - { - "cell_type": "markdown", - "id": "a94d5c80", - "metadata": {}, - "source": [ - "# Running Multiple Sequential Learning Runs" - ] - }, - { - "cell_type": "markdown", - "id": "8ee96e20", - "metadata": {}, - "source": [ - "In most cases we will want to run multiple sequential learning runs in order to remove any effects from data initialization of the first training set. Through the use of `joblib`, `AutoCat` can parallelize this process across multiple cores where each core runs a separate sl run. The example below runs 2 sequential learning runs on 2 cores (1 on each core)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "bbf1ab53", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of runs information stored: 2\n" - ] - } - ], - "source": [ - "runs_history = multiple_sequential_learning_runs(\n", - " acsc,\n", - " [train_base_struct],\n", - " testing_base_structures = [test_base_struct],\n", - " number_of_runs = 2,\n", - " number_parallel_jobs = 2,\n", - " initial_num_of_perturbations_per_base_structure = 3, # for generating the initial training set\n", - " batch_num_of_perturbations_per_base_structure = 3, # how large of a pool to predict on for each loop\n", - " batch_size_to_add = 1, # how many structures to add to training on each loop\n", - " number_of_sl_loops = 4,\n", - " write_to_disk = False # if we want to save the runs history to disk\n", - ")\n", - "print(f\"Number of runs information stored: {len(runs_history)}\")" - ] - }, - { - "cell_type": "markdown", - "id": "74b194b9", - "metadata": {}, - "source": [ - "The returned list contains the sequential learning dictionary of each separate run, thus allowing for averaging of any of the quantities across runs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f401661", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.9" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From dc21a7cc202fe6f7d437e664467c0761db0d9aff Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 25 Mar 2022 14:09:03 -0400 Subject: [PATCH 216/239] add contributions guidelines --- CONTRIBUTING.MD | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 CONTRIBUTING.MD diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD new file mode 100644 index 00000000..e20c3aa3 --- /dev/null +++ b/CONTRIBUTING.MD @@ -0,0 +1,108 @@ +# Contributing + + + +- [Installation and Development](#install) + - [Running Tests](#tests) + - [Test Coverage](#test-coverage) +- [Coding Style](#codestyle) +- [PR Submission](#pr) +- [Documentation](#documentation) + +## Installation and Development + +Clone from github: +```bash +git clone https://github.com/aced-differentiate/auto_cat.git +``` + +Create a virtual environment; +one option is to use conda, but it is not required: +```bash +conda create -n python=3.9 +conda activate +``` + +Then install requirements from within the cloned `AutoCat` directory: +```bash +pip install -U -r requirements.txt +pip install -U -r test_requirements.txt +pip install --no-deps -e . +``` + +### Running tests +We use [pytest](https://docs.pytest.org/en/stable/contents.html) to run tests. +To run all tests: +```bash +pytest -svv +``` + +### Test coverage + +We use [pytest-cov](https://pytest-cov.readthedocs.io/en/latest) to check +code coverage. +To run all tests and output a report of the coverage of the `src` directory: +```bash +pytest --cov=src/ --cov-report term-missing -svv +``` + +## Coding Style + +`AutoCat` follows [PEP8](https://www.python.org/dev/peps/pep-0008/), with +several docstring rules relaxed. +See `tox.ini` for a list of the ignored rules. +Docstrings must follow the +[Numpy style](https://numpydoc.readthedocs.io/en/latest/format.html). + +We use [flake8](https://flake8.pycqa.org/en/latest/) as a linter. +To run the linter on the `src` directory: +```bash +flake8 src +``` + +A pre-commit hook is available to auto-format code with +[black](https://black.readthedocs.io/en/stable) (recommended): + +1. Make sure you are using a Python version >=3.9 +2. Install black: ``$ pip install black`` +3. Install pre-commit: ``$ pip install pre-commit`` +4. Intall git hooks in your ``.git`` directory: ``$ pre-commit install`` + +Names for functions, arguments, classes, and methods should be as descriptive as possible, +even if it means making them a little longer. For example, `generate_surface_structures` is +a preferred function name to `gen_surfs`. +All class names should adhere to [upper CamelCase](https://en.wikipedia.org/wiki/Camel_case). + +## PR Submission + +All PRs must be submitted to the `develop` branch (either fork or clone). +Releases will be from the `master` branch. + +In order to be merged, a PR must be approved by one authorized user and the +build must pass. +A passing build requires the following: +* All tests pass +* The linter finds no violations of PEP8 style (not including the exceptions in `tox.ini`) +* Every line of code is executed by a test (100% coverage) +* Documentation has been updated or extended (as needed) and builds + +PR descriptions should describe the motivation and context of the code changes in the PR, +both for the reviewer and also for future developers. If there's a Github issue, the PR should +be linked to the issue to provide that context. + +## Documentation +`AutoCat` documentation is built using `mkdocs` via +[`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/) +and +[`mkdocstrings`](https://mkdocstrings.github.io/). +All custom documentation should be written as `.md` files, appropriately placed within +`docs/`, and referenced within the `mkdocs.yml` file. + +With `mkdocs` the docs webpage can be hosted locally with the command: +```bash +mkdocs serve +``` +which will give an `html` link that can be pasted in a web-browser. + +API documentation is automatically generated with `mkdocstrings` which parses the docstrings. +Please ensure that all docstrings follow the Numpy style. \ No newline at end of file From 511b1be8d7e30d3386a1b084e35d75624a33a754 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Fri, 25 Mar 2022 14:11:30 -0400 Subject: [PATCH 217/239] make ref to contribution guidelines in readme --- README.md | 5 ++++- docs/README.md | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6523293d..9721f801 100644 --- a/README.md +++ b/README.md @@ -32,4 +32,7 @@ package by changing into the created directory (the one with `setup.py`) and ins via: ``` pip install -e . -``` \ No newline at end of file +``` +## Contributing +Contributions through issues, feature requests, and pull requests are welcome. +Guidelines are provided [here](CONTRIBUTING.MD). \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 50c418ef..b8cd9c30 100644 --- a/docs/README.md +++ b/docs/README.md @@ -86,3 +86,6 @@ via: pip install -e . ``` +## Contributing +Contributions through issues, feature requests, and pull requests are welcome. +Guidelines are provided here. From 468b05e57db54c3e386db69a6d5d7bdde8226d88 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Sun, 27 Mar 2022 17:26:51 -0400 Subject: [PATCH 218/239] add updated examples --- .../conducting_simulated_sl_search.ipynb | 185 ++++++++++++++ .../learning/defining_a_design_space.ipynb | 138 ++++++++++ .../learning/featurizing_structures.ipynb | 236 ++++++++++++++++++ examples/learning/making_predictions.ipynb | 199 +++++++++++++++ 4 files changed, 758 insertions(+) create mode 100644 examples/learning/conducting_simulated_sl_search.ipynb create mode 100644 examples/learning/defining_a_design_space.ipynb create mode 100644 examples/learning/featurizing_structures.ipynb create mode 100644 examples/learning/making_predictions.ipynb diff --git a/examples/learning/conducting_simulated_sl_search.ipynb b/examples/learning/conducting_simulated_sl_search.ipynb new file mode 100644 index 00000000..c96507e4 --- /dev/null +++ b/examples/learning/conducting_simulated_sl_search.ipynb @@ -0,0 +1,185 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from sklearn.gaussian_process import GaussianProcessRegressor\n", + "\n", + "from matminer.featurizers.composition import ElementProperty\n", + "\n", + "from autocat.saa import generate_saa_structures\n", + "\n", + "from autocat.utils import extract_structures\n", + "\n", + "from autocat.learning.sequential import DesignSpace\n", + "from autocat.learning.sequential import simulated_sequential_learning" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example we show how to conduct a simulated sequential learning run." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "saa_dictionary = generate_saa_structures(\n", + " host_species=[\"Cu\", \"Au\", \"Fe\", \"Ag\", \"Ti\"],\n", + " dopant_species=[\"Pt\", \"Pd\", \"Co\", \"Ni\"],\n", + " facets={\"Cu\": [\"111\"], \"Au\": [\"111\"], \"Fe\": [\"110\"], \"Ag\": [\"111\"], \"Ti\": [\"0001\"]}\n", + ")\n", + "\n", + "saa_structures = extract_structures(saa_dictionary)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "labels = np.random.randint(-15,15,size=len(saa_structures))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "saa_design_space = DesignSpace(\n", + " design_space_structures=saa_structures,\n", + " design_space_labels=labels\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sequential Learning Iteration #1\n", + "Sequential Learning Iteration #2\n", + "Sequential Learning Iteration #3\n", + "Sequential Learning Iteration #4\n", + "Sequential Learning Iteration #5\n" + ] + } + ], + "source": [ + "sl_history = simulated_sequential_learning(\n", + " full_design_space=saa_design_space,\n", + " init_training_size=2,\n", + " predictor_kwargs={\n", + " \"featurizer_class\": ElementProperty, \n", + " \"featurization_kwargs\":{\"preset\": \"magpie\"}, \n", + " \"model_class\": GaussianProcessRegressor\n", + " },\n", + " candidate_selection_kwargs={\"aq\": \"MU\", \"include_hhi\": True},\n", + " number_of_sl_loops=5\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "+----------------------------------+--------------------+\n", + "| | Sequential Learner |\n", + "+----------------------------------+--------------------+\n", + "| iteration count | 6 |\n", + "| next candidate system structures | ['PdTi35'] |\n", + "| next candidate system indices | [17] |\n", + "| acquisition function | MU |\n", + "| # of candidates to pick | 1 |\n", + "| target maximum | None |\n", + "| target minimum | None |\n", + "| include hhi? | True |\n", + "| include segregation energies? | False |\n", + "+----------------------------------+--------------------+\n", + "+-------------------------+--------------------------------------------------------+\n", + "| | DesignSpace |\n", + "+-------------------------+--------------------------------------------------------+\n", + "| total # of systems | 20 |\n", + "| # of unlabelled systems | 13 |\n", + "| unique species present | ['Cu', 'Pt', 'Pd', 'Co', 'Ni', 'Au', 'Fe', 'Ag', 'Ti'] |\n", + "| maximum label | nan |\n", + "| minimum label | nan |\n", + "+-------------------------+--------------------------------------------------------+\n", + "+---------+--------------------------------------------------------+\n", + "| | Predictor |\n", + "+---------+--------------------------------------------------------+\n", + "| class | sklearn.gaussian_process._gpr.GaussianProcessRegressor |\n", + "| kwargs | None |\n", + "| is fit? | True |\n", + "+---------+--------------------------------------------------------+\n", + "+-----------------------------------+------------------------------------------------------------+\n", + "| | Featurizer |\n", + "+-----------------------------------+------------------------------------------------------------+\n", + "| class | matminer.featurizers.composition.composite.ElementProperty |\n", + "| kwargs | None |\n", + "| species list | ['Ti', 'Fe', 'Co', 'Ni', 'Pt', 'Pd', 'Au', 'Ag', 'Cu'] |\n", + "| maximum structure size | 36 |\n", + "| preset | magpie |\n", + "| design space structures provided? | True |\n", + "+-----------------------------------+------------------------------------------------------------+\n" + ] + } + ], + "source": [ + "print(sl_history)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "interpreter": { + "hash": "bbcedd833a666dedd7a02add1ace8ad982a80fa78e4f715016af545a1fb42dd5" + }, + "kernelspec": { + "display_name": "Python 3.9.10 ('autocat39')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/learning/defining_a_design_space.ipynb b/examples/learning/defining_a_design_space.ipynb new file mode 100644 index 00000000..c96d9f2b --- /dev/null +++ b/examples/learning/defining_a_design_space.ipynb @@ -0,0 +1,138 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from autocat.saa import generate_saa_structures\n", + "\n", + "from autocat.utils import extract_structures\n", + "\n", + "from autocat.learning.sequential import DesignSpace" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example we show how to define a `DesignSpace` containing single-atom alloys and corresponding labels.\n", + "\n", + "The data is as follows:\n", + "\n", + "| SAA | Label |\n", + "| --- | --- |\n", + "| Ni1/Cu111 | -0.3 |\n", + "| Ni1/Au111 | Unknown |\n", + "| Pd1/Cu111 | 0.2 |\n", + "| Pd1/Au111 | -0.1 |" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Create single-atom alloy structures\n", + "saa_dictionary = generate_saa_structures(\n", + " host_species=[\"Cu\", \"Au\"],\n", + " dopant_species=[\"Ni\", \"Pd\"],\n", + " n_fixed_layers=2,\n", + " facets={\"Cu\":[\"111\"], \"Au\":[\"111\"]}\n", + ")\n", + "\n", + "saa_structures = extract_structures(saa_dictionary)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 Cu35Ni\n", + "1 Cu35Pd\n", + "2 Au35Ni\n", + "3 Au35Pd\n" + ] + } + ], + "source": [ + "# Get indices of each structure\n", + "for idx, struct in enumerate(saa_structures):\n", + " print(idx, struct.get_chemical_formula())" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate labels in the correct ordering as per above\n", + " # Ni1Cu Pd1Cu Ni1Au Pd1Au\n", + "labels = np.array([-0.3, 0.2, np.nan, -0.1])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "+-------------------------+--------------------------+\n", + "| | DesignSpace |\n", + "+-------------------------+--------------------------+\n", + "| total # of systems | 4 |\n", + "| # of unlabelled systems | 1 |\n", + "| unique species present | ['Cu', 'Ni', 'Pd', 'Au'] |\n", + "| maximum label | 0.2 |\n", + "| minimum label | -0.3 |\n", + "+-------------------------+--------------------------+\n" + ] + } + ], + "source": [ + "# Define the design space\n", + "saa_design_space = DesignSpace(design_space_structures=saa_structures, design_space_labels=labels)\n", + "\n", + "print(saa_design_space)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "bbcedd833a666dedd7a02add1ace8ad982a80fa78e4f715016af545a1fb42dd5" + }, + "kernelspec": { + "display_name": "Python 3.9.10 ('autocat39')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/learning/featurizing_structures.ipynb b/examples/learning/featurizing_structures.ipynb new file mode 100644 index 00000000..c0674baf --- /dev/null +++ b/examples/learning/featurizing_structures.ipynb @@ -0,0 +1,236 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from autocat.surface import generate_surface_structures\n", + "from autocat.saa import generate_saa_structures\n", + "\n", + "from autocat.utils import extract_structures\n", + "\n", + "from autocat.learning.featurizers import Featurizer\n", + "\n", + "from dscribe.descriptors import CoulombMatrix\n", + "from matminer.featurizers.composition import ElementProperty" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example we show how to use `AutoCat` to featurize structures with the `Featurizer` class.\n", + "\n", + "Here we will be featurizing mono-elemental surfaces." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate structures to be featurized\n", + "mono_surfaces_dictionary = generate_surface_structures(\n", + " species_list=[\"Fe\", \"Ru\", \"Cu\", \"Pd\"],\n", + " facets={\"Fe\": [\"110\"], \"Ru\":[\"0001\"], \"Cu\":[\"111\"], \"Pd\":[\"111\"]}\n", + ")\n", + "mono_surfaces_structures = extract_structures(mono_surfaces_dictionary)\n", + "\n", + "saa_surfaces_dictionary = generate_saa_structures(\n", + " host_species=[\"Cu\", \"Au\"],\n", + " dopant_species=[\"Pt\", \"Pd\"],\n", + " facets={\"Cu\":[\"111\"], \"Au\":[\"111\"]}\n", + ")\n", + "saa_surfaces_structures = extract_structures(saa_surfaces_dictionary)\n", + "\n", + "all_structures = mono_surfaces_structures.copy()\n", + "all_structures.extend(saa_surfaces_structures)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fe36\n" + ] + } + ], + "source": [ + "print(all_structures[0].get_chemical_formula())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "+-----------------------------------+-------------------------------------------------+\n", + "| | Featurizer |\n", + "+-----------------------------------+-------------------------------------------------+\n", + "| class | dscribe.descriptors.coulombmatrix.CoulombMatrix |\n", + "| kwargs | None |\n", + "| species list | ['Fe', 'Ru', 'Pt', 'Pd', 'Au', 'Cu'] |\n", + "| maximum structure size | 36 |\n", + "| preset | None |\n", + "| design space structures provided? | True |\n", + "+-----------------------------------+-------------------------------------------------+\n" + ] + } + ], + "source": [ + "# Instantiate featurizer based on Coulomb Matrix\n", + "coulomb_featurizer = Featurizer(\n", + " featurizer_class=CoulombMatrix, \n", + " design_space_structures=all_structures\n", + ")\n", + "print(coulomb_featurizer)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1296,)\n" + ] + } + ], + "source": [ + "# Featurize just Fe\n", + "fe_feature_vector = coulomb_featurizer.featurize_single(all_structures[0])\n", + "print(fe_feature_vector.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(8, 1296)\n" + ] + } + ], + "source": [ + "# Featurize all structures into a single matrix\n", + "feature_matrix = coulomb_featurizer.featurize_multiple(all_structures)\n", + "print(feature_matrix.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "+-----------------------------------+------------------------------------------------------------+\n", + "| | Featurizer |\n", + "+-----------------------------------+------------------------------------------------------------+\n", + "| class | matminer.featurizers.composition.composite.ElementProperty |\n", + "| kwargs | None |\n", + "| species list | ['Fe', 'Ru', 'Pt', 'Pd', 'Au', 'Cu'] |\n", + "| maximum structure size | 36 |\n", + "| preset | matminer |\n", + "| design space structures provided? | True |\n", + "+-----------------------------------+------------------------------------------------------------+\n" + ] + } + ], + "source": [ + "# Instantiate element property featurizer\n", + "element_featurizer = Featurizer(\n", + " featurizer_class=ElementProperty,\n", + " design_space_structures=all_structures,\n", + " preset=\"matminer\"\n", + ")\n", + "\n", + "print(element_featurizer)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(65,)\n" + ] + } + ], + "source": [ + "# Featurize just Fe\n", + "fe_feature_vector = element_featurizer.featurize_single(all_structures[0])\n", + "print(fe_feature_vector.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(8, 65)\n" + ] + } + ], + "source": [ + "# Featurize all structures at once\n", + "feature_matrix = element_featurizer.featurize_multiple(all_structures)\n", + "print(feature_matrix.shape)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "bbcedd833a666dedd7a02add1ace8ad982a80fa78e4f715016af545a1fb42dd5" + }, + "kernelspec": { + "display_name": "Python 3.9.10 ('autocat39')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/learning/making_predictions.ipynb b/examples/learning/making_predictions.ipynb new file mode 100644 index 00000000..cc668ae3 --- /dev/null +++ b/examples/learning/making_predictions.ipynb @@ -0,0 +1,199 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from sklearn.gaussian_process import GaussianProcessRegressor\n", + "from sklearn.gaussian_process.kernels import RBF\n", + "\n", + "from dscribe.descriptors import SineMatrix\n", + "\n", + "from autocat.surface import generate_surface_structures\n", + "from autocat.adsorption import generate_adsorbed_structures\n", + "\n", + "from autocat.utils import extract_structures\n", + "\n", + "from autocat.learning.predictors import Predictor" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example we show how to train a `Predictor` and use it to make predictions for adsorbates on Pt." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate adsorption structures\n", + "substrates_dictionary = generate_surface_structures(\n", + " species_list=[\"Pt\"],\n", + " facets={\"Pt\":[\"100\"]}\n", + ")\n", + "\n", + "substrate = extract_structures(substrates_dictionary)[0]\n", + "\n", + "adsorbed_dictionary = generate_adsorbed_structures(\n", + " surface=substrate,\n", + " adsorbates=[\"H\", \"O\", \"N\", \"C\", \"Na\"],\n", + " use_all_sites=False,\n", + ")\n", + "\n", + "adsorbed_structures = extract_structures(adsorbed_dictionary)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate labels\n", + "# N.B. here they are random for convenience, but should be actual values to train a meaningful `Predictor`\n", + "\n", + "labels = np.random.randint(-10,10,size=len(adsorbed_structures))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "+---------+--------------------------------------------------------+\n", + "| | Predictor |\n", + "+---------+--------------------------------------------------------+\n", + "| class | sklearn.gaussian_process._gpr.GaussianProcessRegressor |\n", + "| kwargs | {'kernel': RBF(length_scale=0.5)} |\n", + "| is fit? | False |\n", + "+---------+--------------------------------------------------------+\n", + "+-----------------------------------+-------------------------------------------+\n", + "| | Featurizer |\n", + "+-----------------------------------+-------------------------------------------+\n", + "| class | dscribe.descriptors.sinematrix.SineMatrix |\n", + "| kwargs | None |\n", + "| species list | ['Na', 'Pt', 'C', 'N', 'O', 'H'] |\n", + "| maximum structure size | 37 |\n", + "| preset | None |\n", + "| design space structures provided? | True |\n", + "+-----------------------------------+-------------------------------------------+\n" + ] + } + ], + "source": [ + "kernel = RBF(0.5)\n", + "\n", + "predictor = Predictor(\n", + " model_class=GaussianProcessRegressor,\n", + " model_kwargs={\"kernel\": kernel},\n", + " featurizer_class=SineMatrix,\n", + " featurization_kwargs={\"design_space_structures\": adsorbed_structures}\n", + ")\n", + "\n", + "print(predictor)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "predictor.fit(\n", + " training_structures=adsorbed_structures,\n", + " y=labels\n", + ")\n", + "\n", + "print(predictor.is_fit)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "test_dictionary = generate_adsorbed_structures(\n", + " surface=substrate,\n", + " adsorbates=[\"S\", \"Li\", \"P\"],\n", + " use_all_sites=False\n", + ")\n", + "\n", + "test_structures = extract_structures(test_dictionary)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(3,)\n", + "(3,)\n" + ] + } + ], + "source": [ + "# Make predictions on unseen data\n", + "predictions, uncertainties = predictor.predict(testing_structures=test_structures)\n", + "print(predictions.shape)\n", + "print(uncertainties.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "interpreter": { + "hash": "bbcedd833a666dedd7a02add1ace8ad982a80fa78e4f715016af545a1fb42dd5" + }, + "kernelspec": { + "display_name": "Python 3.9.10 ('autocat39')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} From eac7e9e87e62022d8b9497093e9665294fd10679 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Apr 2022 16:28:24 -0400 Subject: [PATCH 219/239] rm pymatgen substitution -substitution now done with the Atoms object -pymatgen still used to check single unique site to dope --- src/autocat/saa.py | 59 +++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/autocat/saa.py b/src/autocat/saa.py index 9aa6350f..b405be95 100644 --- a/src/autocat/saa.py +++ b/src/autocat/saa.py @@ -10,6 +10,7 @@ from ase.data import ground_state_magnetic_moments from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.analysis.adsorption import AdsorbateSiteFinder +from pymatgen.analysis.structure_matcher import StructureMatcher from autocat.surface import generate_surface_structures @@ -34,6 +35,21 @@ def _find_dopant_index(structure, dopant_element): return dopant_index[0][0] +def _find_all_surface_atom_indices(structure, tol=None) -> List[int]: + """Helper function to find all surface atom indices + within a tolerance distance of the highest atom""" + if tol is None: + tol = 0.5 + all_heights = structure.positions[:, 2] + highest_atom_idx = np.argmax(all_heights) + height_of_highest_atom = structure[highest_atom_idx].z + surface_atom_indices = [] + for idx, atom in enumerate(structure): + if height_of_highest_atom - atom.z < tol: + surface_atom_indices.append(idx) + return surface_atom_indices + + def generate_saa_structures( host_species: List[str], dopant_species: List[str], @@ -310,33 +326,34 @@ def substitute_single_atom_on_surface( substitution functionality is folded into a more general form. """ - tags = host_structure.get_tags() - constraints = host_structure.constraints - host_magmoms = host_structure.get_initial_magnetic_moments() - # convert ase substrate to pymatgen structure - converter = AseAtomsAdaptor() - pmg_structure = converter.get_structure(host_structure) + all_surface_indices = _find_all_surface_atom_indices(host_structure) - # find all symmetrically unique site to substitute on - finder = AdsorbateSiteFinder(pmg_structure) + ase_all_doped_structures = [] + for idx in all_surface_indices: + dop_struct = host_structure.copy() + dop_struct[idx].symbol = dopant_element + dop_struct[idx].magmom = dopant_magnetic_moment + ase_all_doped_structures.append(dop_struct) - # collect all substitution structures and convert them back into ase.Atoms - pmg_substituted_structures = finder.generate_substitution_structures(dopant_element) - if len(pmg_substituted_structures) > 1: + # convert ase substrate to pymatgen structure + converter = AseAtomsAdaptor() + pmg_doped_structures = [ + converter.get_structure(struct) for struct in ase_all_doped_structures + ] + + # check that only one unique surface doped structure + matcher = StructureMatcher() + pmg_symm_equiv_doped_structure = [ + s[0] for s in matcher.group_structures(pmg_doped_structures) + ] + if len(pmg_symm_equiv_doped_structure) > 1: msg = "Multiple symmetrically unique sites to dope found." raise NotImplementedError(msg) - ase_substituted_structure = converter.get_atoms(pmg_substituted_structures[0]) - ase_substituted_structure.set_tags(tags) - # ensure pbc in xy only - ase_substituted_structure.pbc = (1, 1, 0) - # propagate constraints and host magnetization - ase_substituted_structure.constraints = constraints - ase_substituted_structure.set_initial_magnetic_moments(host_magmoms) - # set initial magnetic moment for the dopant atom - dopant_idx = _find_dopant_index(ase_substituted_structure, dopant_element) - ase_substituted_structure[dopant_idx].magmom = dopant_magnetic_moment + # assumes only a single unique doped structure + ase_substituted_structure = ase_all_doped_structures[0] + # center the single-atom dopant if place_dopant_at_center: cent_x = ( From 6f5b4d2ab495536087f1f45703e0d4809d433409 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Apr 2022 16:28:50 -0400 Subject: [PATCH 220/239] add test for finding all surface atom indices --- tests/test_saa.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_saa.py b/tests/test_saa.py index 4b738e53..1642e1c5 100644 --- a/tests/test_saa.py +++ b/tests/test_saa.py @@ -10,6 +10,7 @@ from autocat.saa import generate_saa_structures from autocat.saa import substitute_single_atom_on_surface from autocat.saa import _find_dopant_index +from autocat.saa import _find_all_surface_atom_indices from autocat.saa import AutocatSaaGenerationError from autocat.surface import generate_surface_structures @@ -128,3 +129,31 @@ def test_find_dopant_index(): host[32].symbol = "Au" with raises(NotImplementedError): _find_dopant_index(host, "Au") + + +def test_find_all_surface_atom_indices(): + # Test helper function for finding all surface atoms + # clean elemental surface + ru = generate_surface_structures(["Ru"], supercell_dim=(2, 2, 4))["Ru"]["hcp0001"][ + "structure" + ] + indices = _find_all_surface_atom_indices(ru) + assert indices == [12, 13, 14, 15] + + pt110 = generate_surface_structures(["Pt"], supercell_dim=(1, 1, 4))["Pt"][ + "fcc110" + ]["structure"] + indices = _find_all_surface_atom_indices(pt110) + assert indices == [3] + + # check increasing tolerance + indices = _find_all_surface_atom_indices(pt110, tol=1.4) + assert indices == [2, 3] + + pt100 = generate_surface_structures(["Pt"], supercell_dim=(3, 3, 4))["Pt"][ + "fcc100" + ]["structure"] + pt100[27].z += 0.3 + pt100[30].z -= 0.4 + indices = _find_all_surface_atom_indices(pt100, tol=0.6) + assert indices == [27, 28, 29, 31, 32, 33, 34, 35] From 31d9aa6acbf54d0805d711722455013301020822 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Apr 2022 16:38:35 -0400 Subject: [PATCH 221/239] rm unused AdsorbateSiteFinder import --- src/autocat/saa.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/autocat/saa.py b/src/autocat/saa.py index b405be95..3d97453b 100644 --- a/src/autocat/saa.py +++ b/src/autocat/saa.py @@ -9,7 +9,6 @@ from ase.data import atomic_numbers from ase.data import ground_state_magnetic_moments from pymatgen.io.ase import AseAtomsAdaptor -from pymatgen.analysis.adsorption import AdsorbateSiteFinder from pymatgen.analysis.structure_matcher import StructureMatcher from autocat.surface import generate_surface_structures From e880128f38ab9f34e64f11a07a7e69ef9a0e89d3 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 13 Apr 2022 16:53:03 -0400 Subject: [PATCH 222/239] upgrade deps in req.txt, rm versions from setup.py --- requirements.txt | 6 +++--- setup.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6d717763..41eccb0c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy==1.22.0 -ase==3.21.1 -pymatgen==2022.0.17 +numpy==1.22.3 +ase==3.22.1 +pymatgen==2022.3.29 fire==0.4.0 diff --git a/setup.py b/setup.py index 4ccfeaa0..94e174f0 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,6 @@ ], package_dir={"": "src"}, packages=find_packages(where="src"), - install_requires=["numpy<=1.22.0", "ase", "pymatgen<=2022.0.17", "fire",], + install_requires=["numpy", "ase", "pymatgen", "fire",], include_package_data=True, ) From 3a3fc4f17eb9cb494a90c81402b036c567c01bd2 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 14 Apr 2022 17:23:13 -0400 Subject: [PATCH 223/239] default tol no longer None --- src/autocat/saa.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/autocat/saa.py b/src/autocat/saa.py index 3d97453b..2b539821 100644 --- a/src/autocat/saa.py +++ b/src/autocat/saa.py @@ -34,11 +34,9 @@ def _find_dopant_index(structure, dopant_element): return dopant_index[0][0] -def _find_all_surface_atom_indices(structure, tol=None) -> List[int]: +def _find_all_surface_atom_indices(structure, tol: float = 0.5) -> List[int]: """Helper function to find all surface atom indices within a tolerance distance of the highest atom""" - if tol is None: - tol = 0.5 all_heights = structure.positions[:, 2] highest_atom_idx = np.argmax(all_heights) height_of_highest_atom = structure[highest_atom_idx].z From d038a38a846373f75f117201723001cf0c0553fc Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Thu, 12 May 2022 11:16:29 -0700 Subject: [PATCH 224/239] fix tests acc to changed signature of place_adsorbate fn --- tests/test_perturbations.py | 44 ++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py index 1ea7215d..98e7f437 100644 --- a/tests/test_perturbations.py +++ b/tests/test_perturbations.py @@ -7,6 +7,7 @@ import tempfile from ase import Atoms +from ase.build import molecule from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate @@ -19,7 +20,8 @@ def test_perturb_structure_directions(): sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct = place_adsorbate(sub, "H")["custom"]["structure"] + mol = molecule("H") + base_struct = place_adsorbate(surface=sub, adsorbate=mol) # free in all directions p_struct = perturb_structure(base_struct,) assert (p_struct["structure"].positions[-1] != base_struct.positions[-1]).all() @@ -36,7 +38,8 @@ def test_perturb_structure_sign_constraint(): sub = generate_surface_structures(["Cu"], facets={"Cu": ["111"]})["Cu"]["fcc111"][ "structure" ] - base_struct = place_adsorbate(sub, "O")["custom"]["structure"] + mol = molecule("O") + base_struct = place_adsorbate(surface=sub, adsorbate=mol) # free in +z base_struct[-1].tag = -1 p_struct = perturb_structure(base_struct, direction_sign_constraint=1) @@ -60,7 +63,8 @@ def test_perturb_structure_matrix(): sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + mol = molecule("OH") + base_struct = place_adsorbate(surface=sub, adsorbate=mol) p_struct = perturb_structure(base_struct) o_pert = base_struct.positions[-2] + p_struct["perturbation_matrix"][-2] assert np.allclose(p_struct["structure"].positions[-2], o_pert) @@ -73,7 +77,8 @@ def test_generate_perturbed_dataset_num_of_perturbations(): sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + mol = molecule("OH") + base_struct = place_adsorbate(surface=sub, adsorbate=mol) p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) assert len(p_set["HOPt36_0"].keys()) == 15 @@ -86,8 +91,10 @@ def test_generate_perturbed_dataset_multiple_base_structures(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "NH")["custom"]["structure"] + mol = molecule("OH") + base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) + mol = molecule("NH") + base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) base_struct1[-2].tag = 1 base_struct1[-1].tag = -1 base_struct2[-1].tag = 1 @@ -108,7 +115,8 @@ def test_generate_perturbed_dataset_write_location(): sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + mol = molecule("OH") + base_struct = place_adsorbate(surface=sub, adsorbate=mol) p_set = generate_perturbed_dataset( [base_struct], write_to_disk=True, write_location=_tmp_dir, ) @@ -131,7 +139,8 @@ def test_generate_perturbed_dataset_correction_matrix(): sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + mol = molecule("OH") + base_struct = place_adsorbate(surface=sub, adsorbate=mol) p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=5,) manual_collect = np.zeros((5, 6)) for idx, struct in enumerate(p_set["HOPt36_0"]): @@ -154,8 +163,9 @@ def test_generate_perturbed_dataset_correction_matrix_multiple(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "OH")["custom"]["structure"] + mol = molecule("OH") + base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) + base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) base_struct2[-1].tag = 1 p_set = generate_perturbed_dataset( [base_struct1, base_struct2], num_of_perturbations=5, @@ -177,8 +187,10 @@ def test_generate_perturbed_dataset_correction_matrix_multiple(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "H")["custom"]["structure"] + mol = molecule("OH") + base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) + mol = molecule("H") + base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) base_struct1[-2].tag = 1 p_set = generate_perturbed_dataset( [base_struct1, base_struct2], num_of_perturbations=5, @@ -204,7 +216,8 @@ def test_generate_perturbed_dataset_collected_structure_paths(): sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ "structure" ] - base_struct = place_adsorbate(sub, "OH")["custom"]["structure"] + mol = molecule("OH") + base_struct = place_adsorbate(surface=sub, adsorbate=mol) p_set = generate_perturbed_dataset( [base_struct], write_to_disk=True, write_location=_tmp_dir, ) @@ -226,8 +239,9 @@ def test_generate_perturbed_dataset_collected_structures(): sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ "structure" ] - base_struct1 = place_adsorbate(sub1, "OH")["custom"]["structure"] - base_struct2 = place_adsorbate(sub2, "OH")["custom"]["structure"] + mol = molecule("OH") + base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) + base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) base_struct1[-2].tag = 1 base_struct2[-1].tag = 1 p_set = generate_perturbed_dataset( From 164ccfa4faf53a1c8578a1ea2a2cb92e21afd206 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Thu, 12 May 2022 11:18:37 -0700 Subject: [PATCH 225/239] rm unused imports; fix importing order --- src/autocat/perturbations.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 3828a7ff..2afba30e 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -1,11 +1,11 @@ -from ase.io import read, write -from ase import Atoms +import os +import json from typing import List from typing import Union -from typing import Dict + import numpy as np -import os -import json +from ase.io import read +from ase import Atoms class AutocatPerturbationError(Exception): From bd9b51474932fc4e03f37e53d03799cb83e8cffb Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Thu, 12 May 2022 11:21:45 -0700 Subject: [PATCH 226/239] rm superfluous f-strings; fix line-length for pylance --- src/autocat/perturbations.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py index 2afba30e..1306cc75 100644 --- a/src/autocat/perturbations.py +++ b/src/autocat/perturbations.py @@ -116,7 +116,7 @@ def generate_perturbed_dataset( elif isinstance(structure, str): name = ".".join(structure.split(".")[:-1]) else: - raise TypeError(f"Structure needs to be either a str or ase.Atoms object") + raise TypeError("Structure needs to be either a str or ase.Atoms object") # make sure no base_structures with the same name if name in perturbed_dict: @@ -140,7 +140,7 @@ def generate_perturbed_dataset( if write_to_disk: dir_path = os.path.join(write_location, f"{name}/{str(i)}") os.makedirs(dir_path, exist_ok=dirs_exist_ok) - traj_file_path = os.path.join(dir_path, f"perturbed_structure.traj") + traj_file_path = os.path.join(dir_path, "perturbed_structure.traj") # write perturbed structure to disk perturbed_dict[name][str(i)]["structure"].write(traj_file_path) print( @@ -286,7 +286,10 @@ def perturb_structure( signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=11, size=(1, 3)) if direction_sign_constraint is not None: if direction_sign_constraint not in [-1, 1]: - msg = f"direction_sign_constraint must be either -1 or 1, got {direction_sign_constraint}" + msg = ( + f"direction_sign_constraint must be either -1 or 1," + f" got {direction_sign_constraint} instead" + ) raise AutocatPerturbationError(msg) signs = direction_sign_constraint * abs(signs) # generate perturbation matrix From 73344eeacf97fd2ed338488aa4b29d0e6e7f9a0e Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Thu, 12 May 2022 11:26:07 -0700 Subject: [PATCH 227/239] fix package import order --- src/autocat/learning/featurizers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 6f40778e..12dd8682 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -1,3 +1,9 @@ +from typing import List, Dict +import copy + +import numpy as np +from prettytable import PrettyTable + from dscribe.descriptors import SineMatrix from dscribe.descriptors import CoulombMatrix from dscribe.descriptors import ACSF @@ -8,12 +14,6 @@ from matminer.featurizers.site import OPSiteFingerprint from matminer.featurizers.site import CrystalNNFingerprint -import numpy as np -import copy -from prettytable import PrettyTable - -from typing import List, Dict - from ase import Atoms from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.core.periodic_table import Element From 46643fa296aea6ccf444e81b888282fbcbd547b2 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Wed, 18 May 2022 12:30:34 -0700 Subject: [PATCH 228/239] format with black --- src/autocat/learning/featurizers.py | 10 +++++----- src/autocat/learning/predictors.py | 7 +++++-- src/autocat/learning/sequential.py | 23 +++++++++++++++++------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index 12dd8682..e6a1d995 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -1,23 +1,22 @@ -from typing import List, Dict import copy +from typing import List, Dict import numpy as np from prettytable import PrettyTable +from ase import Atoms from dscribe.descriptors import SineMatrix from dscribe.descriptors import CoulombMatrix from dscribe.descriptors import ACSF from dscribe.descriptors import SOAP - from matminer.featurizers.composition import ElementProperty from matminer.featurizers.site import ChemicalSRO from matminer.featurizers.site import OPSiteFingerprint from matminer.featurizers.site import CrystalNNFingerprint - -from ase import Atoms from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.core.periodic_table import Element + SUPPORTED_MATMINER_CLASSES = [ ElementProperty, ChemicalSRO, @@ -35,7 +34,7 @@ class FeaturizerError(Exception): class Featurizer: def __init__( self, - featurizer_class=None, + featurizer_class=None, # black design_space_structures: List[Atoms] = None, species_list: List[str] = None, max_size: int = None, @@ -204,6 +203,7 @@ def species_list(self, species_list: List[str]): ) self._species_list = sorted_species_list + # TODO: "get_featurization_object" -> "get_featurizer" @property def featurization_object(self): return self._get_featurization_object() diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index d169a9f8..5d28052e 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -27,8 +27,8 @@ class Predictor: def __init__( self, model_class=None, - model_kwargs: Dict = None, - featurizer_class=None, + model_kwargs: Dict = None, # TODO: kwargs -> options? + featurizer_class=None, # black featurization_kwargs: Dict = None, ): """ @@ -255,6 +255,9 @@ def predict( return predicted_labels, unc + # TODO: "score" -> "get_scores"? + # TODO: "testing_structures" -> "structures" + # TODO: "y" -> labels def score( self, testing_structures: List[Atoms], diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index ea81dea3..7b5ddc00 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -1,29 +1,32 @@ import copy -import numpy as np import os import json import importlib -from joblib import Parallel, delayed from typing import List from typing import Dict from typing import Union -from prettytable import PrettyTable +import numpy as np +from joblib import Parallel, delayed +from prettytable import PrettyTable from ase import Atoms from ase.io.jsonio import encode as atoms_encoder from ase.io.jsonio import decode as atoms_decoder from scipy import stats - from sklearn.gaussian_process import GaussianProcessRegressor - from dscribe.descriptors import SineMatrix from autocat.learning.predictors import Predictor + +# TODO: convert HHI_PRODUCTION to HHI["production"]? from autocat.data.hhi import HHI_PRODUCTION from autocat.data.hhi import HHI_RESERVES + +# TODO: convert RABAN1999_SEGREGATION_ENERGIES to SEGREGATION_ENERGIES["raban1999"] from autocat.data.segregation_energies import RABAN1999_SEGREGATION_ENERGIES from autocat.data.segregation_energies import RAO2020_SEGREGATION_ENERGIES + Array = List[float] @@ -75,6 +78,7 @@ def __repr__(self) -> str: def __len__(self): return len(self.design_space_structures) + # TODO: non-dunder method for deleting systems def __delitem__(self, i): """ Deletes systems from the design space. If mask provided, deletes wherever True @@ -173,6 +177,7 @@ def update(self, structures: List[Atoms], labels: Array): self.design_space_labels, labels[i] ) + # TODO: split generating a dictionary representation and writing to desk def write_json( self, json_name: str = None, @@ -216,6 +221,7 @@ class SequentialLearnerError(Exception): pass +# TODO: "kwargs" -> "options"? class SequentialLearner: def __init__( self, @@ -494,6 +500,7 @@ def iterate(self): itc = self.sl_kwargs.get("iteration_count", 0) self.sl_kwargs.update({"iteration_count": itc + 1}) + # TODO: separate dictionary representation and writing to disk def write_json(self, write_location: str = ".", json_name: str = None): """ Writes `AutocatSequentialLearner` to disk as a json @@ -687,6 +694,7 @@ def multiple_simulated_sequential_learning_runs( for i in range(number_of_runs) ] + # TODO: separate dictionary representation and writing to disk if write_to_disk: if not os.path.isdir(write_location): os.makedirs(write_location) @@ -783,7 +791,10 @@ def simulated_sequential_learning( # check that specified number of loops is feasible if number_of_sl_loops > max_num_sl_loops: - msg = f"Number of SL loops ({number_of_sl_loops}) cannot be greater than ({max_num_sl_loops})" + msg = ( + f"Number of SL loops ({number_of_sl_loops}) cannot be greater than" + f" ({max_num_sl_loops})" + ) raise SequentialLearnerError(msg) # generate initial training set From 536717605abcb3075129652a962b6d10e1e3f943 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 18 May 2022 16:08:41 -0400 Subject: [PATCH 229/239] have a single HHI dict of raw values --- src/autocat/data/hhi/__init__.py | 13 ++++++++----- src/autocat/learning/sequential.py | 6 ++---- tests/learning/test_sequential.py | 7 +++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/autocat/data/hhi/__init__.py b/src/autocat/data/hhi/__init__.py index fba5356b..7504ee06 100644 --- a/src/autocat/data/hhi/__init__.py +++ b/src/autocat/data/hhi/__init__.py @@ -1,15 +1,16 @@ import json import pkg_resources -__all__ = ["HHI_PRODUCTION", "HHI_RESERVES"] +__all__ = ["HHI"] """ Values obtained from dx.doi.org/10.1021/cm400893e -HHI_PRODUCTION: - Calculated based on elemental production +Keys: + production: + Calculated based on elemental production -HHI_RESERVES: - Calculated based on known elemental reserves + reserves: + Calculated based on known elemental reserves """ raw_hhi_p = pkg_resources.resource_filename("autocat.data.hhi", "hhi_p.json") @@ -21,3 +22,5 @@ with open(raw_hhi_r) as fr: HHI_RESERVES = json.load(fr) + +HHI = {"production": HHI_PRODUCTION, "reserves": HHI_RESERVES} diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 7b5ddc00..527b45d8 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -18,9 +18,7 @@ from autocat.learning.predictors import Predictor -# TODO: convert HHI_PRODUCTION to HHI["production"]? -from autocat.data.hhi import HHI_PRODUCTION -from autocat.data.hhi import HHI_RESERVES +from autocat.data.hhi import HHI # TODO: convert RABAN1999_SEGREGATION_ENERGIES to SEGREGATION_ENERGIES["raban1999"] from autocat.data.segregation_energies import RABAN1999_SEGREGATION_ENERGIES @@ -1053,7 +1051,7 @@ def calculate_hhi_scores(structures: List[Atoms], hhi_type: str = "production"): msg = "To include HHI, the structures must be provided" raise SequentialLearnerError(msg) - raw_hhi_data = {"production": HHI_PRODUCTION, "reserves": HHI_RESERVES} + raw_hhi_data = HHI max_hhi = np.max([raw_hhi_data[hhi_type][r] for r in raw_hhi_data[hhi_type]]) min_hhi = np.min([raw_hhi_data[hhi_type][r] for r in raw_hhi_data[hhi_type]]) # normalize and invert (so that this score is to be maximized) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index b05d8476..f05d2b07 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -16,8 +16,7 @@ from scipy import stats from ase.io.jsonio import decode as ase_decoder from ase import Atoms -from autocat.data.hhi import HHI_PRODUCTION -from autocat.data.hhi import HHI_RESERVES +from autocat.data.hhi import HHI from autocat.data.segregation_energies import ( RABAN1999_SEGREGATION_ENERGIES, RAO2020_SEGREGATION_ENERGIES, @@ -982,7 +981,7 @@ def test_calculate_hhi_scores(): # test production hhi_prod_scores = calculate_hhi_scores(saa_structs) norm_hhi_prod = { - el: 1.0 - (HHI_PRODUCTION[el] - 500.0) / 9300.0 for el in HHI_PRODUCTION + el: 1.0 - (HHI["production"][el] - 500.0) / 9300.0 for el in HHI["production"] } # check approach properly normalizes and inverts assert np.isclose(norm_hhi_prod["Y"], 0.0) @@ -1003,7 +1002,7 @@ def test_calculate_hhi_scores(): # test reserves hhi_res_scores = calculate_hhi_scores(saa_structs, "reserves") norm_hhi_res = { - el: 1.0 - (HHI_RESERVES[el] - 500.0) / 8600.0 for el in HHI_RESERVES + el: 1.0 - (HHI["reserves"][el] - 500.0) / 8600.0 for el in HHI["reserves"] } # check approach properly normalizes and inverts assert np.isclose(norm_hhi_res["Pt"], 0.0) From d5bc7b945d836809c79830fd841de5af91213357 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 18 May 2022 16:15:53 -0400 Subject: [PATCH 230/239] have a single segregation energy dict --- .../data/segregation_energies/__init__.py | 22 ++++++++++++------- src/autocat/learning/sequential.py | 18 ++++++--------- tests/learning/test_sequential.py | 21 +++++++++--------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/autocat/data/segregation_energies/__init__.py b/src/autocat/data/segregation_energies/__init__.py index 4c3265a3..f059fc0a 100644 --- a/src/autocat/data/segregation_energies/__init__.py +++ b/src/autocat/data/segregation_energies/__init__.py @@ -1,17 +1,18 @@ import json import pkg_resources -__all__ = ["RABAN1999_SEGREGATION_ENERGIES", "RAO2020_SEGREGATION_ENERGIES"] +__all__ = ["SEGREGATION_ENERGIES"] """ -RABAN1999_SEGREGATION_ENERGIES: - Values obtained from https://doi.org/10.1103/PhysRevB.59.15990 - Segregation energies for different host/dopant combinations - For hosts used fcc: 111, bcc:110 (Fe100 also available), hcp:0001 +Keys: + raban1999: + Values obtained from https://doi.org/10.1103/PhysRevB.59.15990 + Segregation energies for different host/dopant combinations + For hosts used fcc: 111, bcc:110 (Fe100 also available), hcp:0001 -RAO2020_SEGREGATION_ENERGIES: - Values obtained from https://doi.org/10.1007/s11244-020-01267-2 - Segregation energies for different host/dopant combinations + rao2020: + Values obtained from https://doi.org/10.1007/s11244-020-01267-2 + Segregation energies for different host/dopant combinations """ raw_raban_seg_ener = pkg_resources.resource_filename( @@ -27,3 +28,8 @@ with open(raw_rao_seg_ener) as fr: RAO2020_SEGREGATION_ENERGIES = json.load(fr) + +SEGREGATION_ENERGIES = { + "raban1999": RABAN1999_SEGREGATION_ENERGIES, + "rao2020": RAO2020_SEGREGATION_ENERGIES, +} diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 527b45d8..21230a2d 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -17,12 +17,8 @@ from dscribe.descriptors import SineMatrix from autocat.learning.predictors import Predictor - from autocat.data.hhi import HHI - -# TODO: convert RABAN1999_SEGREGATION_ENERGIES to SEGREGATION_ENERGIES["raban1999"] -from autocat.data.segregation_energies import RABAN1999_SEGREGATION_ENERGIES -from autocat.data.segregation_energies import RAO2020_SEGREGATION_ENERGIES +from autocat.data.segregation_energies import SEGREGATION_ENERGIES Array = List[float] @@ -1105,18 +1101,18 @@ def calculate_segregation_energy_scores( if data_source == "raban1999": # won't consider surface energies (ie. dop == host) for normalization - max_seg_ener = RABAN1999_SEGREGATION_ENERGIES["Pd"]["W"] - min_seg_ener = RABAN1999_SEGREGATION_ENERGIES["Fe_100"]["Ag"] + max_seg_ener = SEGREGATION_ENERGIES["raban1999"]["Pd"]["W"] + min_seg_ener = SEGREGATION_ENERGIES["raban1999"]["Fe_100"]["Ag"] # normalize and invert (so that this score is to be maximized) norm_seg_ener_data = {} - for hsp in RABAN1999_SEGREGATION_ENERGIES: + for hsp in SEGREGATION_ENERGIES["raban1999"]: norm_seg_ener_data[hsp] = {} - for dsp in RABAN1999_SEGREGATION_ENERGIES[hsp]: + for dsp in SEGREGATION_ENERGIES["raban1999"][hsp]: norm_seg_ener_data[hsp][dsp] = 1.0 - ( - RABAN1999_SEGREGATION_ENERGIES[hsp][dsp] - min_seg_ener + SEGREGATION_ENERGIES["raban1999"][hsp][dsp] - min_seg_ener ) / (max_seg_ener - min_seg_ener) elif data_source == "rao2020": - norm_seg_ener_data = RAO2020_SEGREGATION_ENERGIES + norm_seg_ener_data = SEGREGATION_ENERGIES["rao2020"] else: msg = f"Unknown data source {data_source}" raise SequentialLearnerError(msg) diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index f05d2b07..f1c19217 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -17,10 +17,7 @@ from ase.io.jsonio import decode as ase_decoder from ase import Atoms from autocat.data.hhi import HHI -from autocat.data.segregation_energies import ( - RABAN1999_SEGREGATION_ENERGIES, - RAO2020_SEGREGATION_ENERGIES, -) +from autocat.data.segregation_energies import SEGREGATION_ENERGIES from autocat.learning.predictors import Predictor from autocat.learning.sequential import ( DesignSpace, @@ -1036,24 +1033,26 @@ def test_calculate_segregation_energy_scores(): # test calculating scores from RABAN1999 se_scores = calculate_segregation_energy_scores(saa_structs) assert np.isclose(se_scores[-1], 0.0) - min_seg = RABAN1999_SEGREGATION_ENERGIES["Fe_100"]["Ag"] - max_seg = RABAN1999_SEGREGATION_ENERGIES["Pd"]["W"] + min_seg = SEGREGATION_ENERGIES["raban1999"]["Fe_100"]["Ag"] + max_seg = SEGREGATION_ENERGIES["raban1999"]["Pd"]["W"] assert np.isclose( se_scores[0], 1.0 - - (RABAN1999_SEGREGATION_ENERGIES["Ag"]["Pt"] - min_seg) / (max_seg - min_seg), + - (SEGREGATION_ENERGIES["raban1999"]["Ag"]["Pt"] - min_seg) + / (max_seg - min_seg), ) assert np.isclose( se_scores[1], 1.0 - - (RABAN1999_SEGREGATION_ENERGIES["Ni"]["Pt"] - min_seg) / (max_seg - min_seg), + - (SEGREGATION_ENERGIES["raban1999"]["Ni"]["Pt"] - min_seg) + / (max_seg - min_seg), ) # test getting scores from RAO2020 se_scores = calculate_segregation_energy_scores(saa_structs, data_source="rao2020") - assert np.isclose(se_scores[0], RAO2020_SEGREGATION_ENERGIES["Ag"]["Pt"]) + assert np.isclose(se_scores[0], SEGREGATION_ENERGIES["rao2020"]["Ag"]["Pt"]) assert np.isclose(se_scores[0], 0.8) - assert np.isclose(se_scores[1], RAO2020_SEGREGATION_ENERGIES["Ni"]["Pt"]) + assert np.isclose(se_scores[1], SEGREGATION_ENERGIES["rao2020"]["Ni"]["Pt"]) assert np.isclose(se_scores[1], 1.0) - assert np.isclose(se_scores[-1], RAO2020_SEGREGATION_ENERGIES["Pd"]["W"]) + assert np.isclose(se_scores[-1], SEGREGATION_ENERGIES["rao2020"]["Pd"]["W"]) assert np.isclose(se_scores[-1], 0.0) From dc36ad0e1d176cbc7bb1ee976d035c231ae7cc1d Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 18 May 2022 17:08:49 -0400 Subject: [PATCH 231/239] separate jsonify and write json to disk --- src/autocat/learning/sequential.py | 48 ++++++----- tests/learning/test_sequential.py | 130 +++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 25 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 21230a2d..7745bf74 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -171,20 +171,28 @@ def update(self, structures: List[Atoms], labels: Array): self.design_space_labels, labels[i] ) - # TODO: split generating a dictionary representation and writing to desk - def write_json( - self, - json_name: str = None, - write_location: str = ".", - write_to_disk: bool = True, - return_jsonified_list: bool = False, - ): + def to_jsonified_list(self) -> List: + """ + Returns a jsonified list representation + """ collected_jsons = [] for struct in self.design_space_structures: collected_jsons.append(atoms_encoder(struct)) # append labels to list of collected jsons jsonified_labels = [float(x) for x in self.design_space_labels] collected_jsons.append(jsonified_labels) + return collected_jsons + + def write_json_to_disk( + self, + json_name: str = None, + write_location: str = ".", + write_to_disk: bool = True, + ): + """ + Writes DesignSpace to disk as a json + """ + collected_jsons = self.to_jsonified_list() # set default json name if needed if json_name is None: json_name = "acds.json" @@ -193,9 +201,6 @@ def write_json( json_path = os.path.join(write_location, json_name) with open(json_path, "w") as f: json.dump(collected_jsons, f) - # write jsonified list to memory - if return_jsonified_list: - return collected_jsons @staticmethod def from_json(json_name: str): @@ -494,15 +499,11 @@ def iterate(self): itc = self.sl_kwargs.get("iteration_count", 0) self.sl_kwargs.update({"iteration_count": itc + 1}) - # TODO: separate dictionary representation and writing to disk - def write_json(self, write_location: str = ".", json_name: str = None): + def to_jsonified_list(self) -> List: """ - Writes `AutocatSequentialLearner` to disk as a json + Returns a jsonified list representation """ - jsonified_list = self.design_space.write_json( - write_to_disk=False, return_jsonified_list=True - ) - + jsonified_list = self.design_space.to_jsonified_list() # append kwargs for predictor jsonified_pred_kwargs = {} for k in self.predictor_kwargs: @@ -531,6 +532,13 @@ def write_json(self, write_location: str = ".", json_name: str = None): elif self.sl_kwargs[k] is None: jsonified_sl_kwargs[k] = None jsonified_list.append(jsonified_sl_kwargs) + return jsonified_list + + def write_json_to_disk(self, write_location: str = ".", json_name: str = None): + """ + Writes `SequentialLearner` to disk as a json + """ + jsonified_list = self.to_jsonified_list() if json_name is None: json_name = "acsl.json" @@ -696,7 +704,7 @@ def multiple_simulated_sequential_learning_runs( json_name_prefix = "acsl_run" for i, run in enumerate(runs_history): name = json_name_prefix + "_" + str(i) + ".json" - run.write_json(write_location=write_location, json_name=name) + run.write_json_to_disk(write_location=write_location, json_name=name) print(f"SL histories written to {write_location}") return runs_history @@ -828,7 +836,7 @@ def simulated_sequential_learning( sl.iterate() if write_to_disk: - sl.write_json(write_location=write_location, json_name=json_name) + sl.write_json_to_disk(write_location=write_location, json_name=json_name) print(f"SL dictionary written to {write_location}") return sl diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index f1c19217..655c6fe8 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -69,7 +69,7 @@ def test_sequential_learner_from_json(): ) acsl.iterate() with tempfile.TemporaryDirectory() as _tmp_dir: - acsl.write_json(_tmp_dir, "testing_acsl.json") + acsl.write_json_to_disk(_tmp_dir, "testing_acsl.json") json_path = os.path.join(_tmp_dir, "testing_acsl.json") written_acsl = SequentialLearner.from_json(json_path) assert np.array_equal( @@ -137,7 +137,7 @@ def test_sequential_learner_write_json(): candidate_selection_kwargs=candidate_selection_kwargs, ) with tempfile.TemporaryDirectory() as _tmp_dir: - acsl.write_json(_tmp_dir, "testing_acsl.json") + acsl.write_json_to_disk(_tmp_dir, "testing_acsl.json") with open(os.path.join(_tmp_dir, "testing_acsl.json"), "r") as f: sl = json.load(f) written_structs = [ase_decoder(sl[i]) for i in range(3)] @@ -172,7 +172,7 @@ def test_sequential_learner_write_json(): # test after iteration acsl.iterate() with tempfile.TemporaryDirectory() as _tmp_dir: - acsl.write_json(_tmp_dir, "testing_acsl.json") + acsl.write_json_to_disk(_tmp_dir, "testing_acsl.json") with open(os.path.join(_tmp_dir, "testing_acsl.json"), "r") as f: sl = json.load(f) written_structs = [ase_decoder(sl[i]) for i in range(3)] @@ -211,6 +211,109 @@ def test_sequential_learner_write_json(): assert sl[-1].get("acquisition_scores") == acsl.acquisition_scores.tolist() +def test_sequential_learner_to_jsonified_list(): + # Tests writing a SequentialLearner to disk as a json + sub1 = generate_surface_structures(["Ag"], facets={"Ag": ["110"]})["Ag"]["fcc110"][ + "structure" + ] + sub1 = place_adsorbate(sub1, Atoms("B")) + sub2 = generate_surface_structures(["Li"], facets={"Li": ["100"]})["Li"]["bcc100"][ + "structure" + ] + sub2 = place_adsorbate(sub2, Atoms("Al")) + sub3 = generate_surface_structures(["Ti"], facets={"Ti": ["0001"]})["Ti"][ + "hcp0001" + ]["structure"] + sub3 = place_adsorbate(sub3, Atoms("H")) + structs = [sub1, sub2, sub3] + labels = np.array([0.1, 0.2, np.nan]) + featurization_kwargs = {"preset": "magpie"} + predictor_kwargs = { + "model_class": GaussianProcessRegressor, + "featurizer_class": ElementProperty, + "featurization_kwargs": featurization_kwargs, + } + + candidate_selection_kwargs = {"aq": "MU", "num_candidates_to_pick": 2} + acds = DesignSpace(structs, labels) + acsl = SequentialLearner( + acds, + predictor_kwargs=predictor_kwargs, + candidate_selection_kwargs=candidate_selection_kwargs, + ) + jsonified_list = acsl.to_jsonified_list() + json_structs = [ase_decoder(jsonified_list[i]) for i in range(3)] + assert structs == json_structs + assert np.array_equal(labels, jsonified_list[3], equal_nan=True) + # check predictor kwargs kept + predictor_kwargs["model_class"] = [ + "sklearn.gaussian_process._gpr", + "GaussianProcessRegressor", + ] + predictor_kwargs["featurizer_class"] = [ + "matminer.featurizers.composition.composite", + "ElementProperty", + ] + del predictor_kwargs["featurization_kwargs"]["design_space_structures"] + assert jsonified_list[4] == predictor_kwargs + # check candidate selection kwargs kept + assert jsonified_list[-2] == candidate_selection_kwargs + assert jsonified_list[-1] == { + "iteration_count": 0, + "train_idx": None, + "train_idx_history": None, + "predictions": None, + "predictions_history": None, + "uncertainties": None, + "uncertainties_history": None, + "candidate_indices": None, + "candidate_index_history": None, + "aq_scores": None, + } + + # test after iteration + acsl.iterate() + jsonified_list = acsl.to_jsonified_list() + json_structs = [ase_decoder(jsonified_list[i]) for i in range(3)] + assert structs == json_structs + assert np.array_equal(labels, jsonified_list[3], equal_nan=True) + # check predictor kwargs kept + predictor_kwargs["model_class"] = [ + "sklearn.gaussian_process._gpr", + "GaussianProcessRegressor", + ] + predictor_kwargs["featurizer_class"] = [ + "matminer.featurizers.composition.composite", + "ElementProperty", + ] + assert jsonified_list[4] == predictor_kwargs + # check candidate selection kwargs kept + assert jsonified_list[-2] == candidate_selection_kwargs + assert jsonified_list[-1].get("iteration_count") == 1 + assert jsonified_list[-1].get("train_idx") == acsl.train_idx.tolist() + assert jsonified_list[-1].get("train_idx_history") == [ + ti.tolist() for ti in acsl.train_idx_history + ] + assert isinstance(jsonified_list[-1].get("train_idx_history")[0][0], bool) + assert jsonified_list[-1].get("predictions") == acsl.predictions.tolist() + assert jsonified_list[-1].get("predictions_history") == [ + p.tolist() for p in acsl.predictions_history + ] + assert jsonified_list[-1].get("uncertainties") == acsl.uncertainties.tolist() + assert jsonified_list[-1].get("uncertainties_history") == [ + u.tolist() for u in acsl.uncertainties_history + ] + assert ( + jsonified_list[-1].get("candidate_indices") == acsl.candidate_indices.tolist() + ) + assert jsonified_list[-1].get("candidate_index_history") == [ + c.tolist() for c in acsl.candidate_index_history + ] + assert ( + jsonified_list[-1].get("acquisition_scores") == acsl.acquisition_scores.tolist() + ) + + def test_sequential_learner_iterate(): # Tests iterate method sub1 = generate_surface_structures(["Ca"], facets={"Ca": ["111"]})["Ca"]["fcc111"][ @@ -501,7 +604,7 @@ def test_write_design_space_as_json(): labels = np.array([0.3, 0.8]) with tempfile.TemporaryDirectory() as _tmp_dir: acds = DesignSpace(design_space_structures=structs, design_space_labels=labels,) - acds.write_json(write_location=_tmp_dir) + acds.write_json_to_disk(write_location=_tmp_dir) # loads back written json with open(os.path.join(_tmp_dir, "acds.json"), "r") as f: ds = json.load(f) @@ -510,6 +613,23 @@ def test_write_design_space_as_json(): assert np.array_equal(labels, ds[-1]) +def test_design_space_to_jsonified_list(): + # Tests returning the DesignSpace as a jsonified list + sub1 = generate_surface_structures(["Pd"], facets={"Pd": ["111"]})["Pd"]["fcc111"][ + "structure" + ] + sub2 = generate_surface_structures(["V"], facets={"V": ["110"]})["V"]["bcc110"][ + "structure" + ] + structs = [sub1, sub2] + labels = np.array([0.3, 0.8]) + acds = DesignSpace(design_space_structures=structs, design_space_labels=labels,) + jsonified_list = acds.to_jsonified_list() + json_structs = [ase_decoder(jsonified_list[i]) for i in range(2)] + assert structs == json_structs + assert np.array_equal(labels, jsonified_list[-1]) + + def test_get_design_space_from_json(): # Tests generating DesignSpace from a json sub1 = generate_surface_structures(["Au"], facets={"Au": ["100"]})["Au"]["fcc100"][ @@ -525,7 +645,7 @@ def test_get_design_space_from_json(): labels = np.array([30.0, 900.0, np.nan]) with tempfile.TemporaryDirectory() as _tmp_dir: acds = DesignSpace(design_space_structures=structs, design_space_labels=labels,) - acds.write_json("testing.json", write_location=_tmp_dir) + acds.write_json_to_disk("testing.json", write_location=_tmp_dir) tmp_json_dir = os.path.join(_tmp_dir, "testing.json") acds_from_json = DesignSpace.from_json(tmp_json_dir) From ed2d2979b6ff87fe3805211e6836d7cd7ef373ea Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 18 May 2022 17:17:28 -0400 Subject: [PATCH 232/239] testing_structures, y to structures, labels --- src/autocat/learning/predictors.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/autocat/learning/predictors.py b/src/autocat/learning/predictors.py index 5d28052e..7978a849 100644 --- a/src/autocat/learning/predictors.py +++ b/src/autocat/learning/predictors.py @@ -256,12 +256,10 @@ def predict( return predicted_labels, unc # TODO: "score" -> "get_scores"? - # TODO: "testing_structures" -> "structures" - # TODO: "y" -> labels def score( self, - testing_structures: List[Atoms], - y: np.ndarray, + structures: List[Atoms], + labels: np.ndarray, metric: str = "mae", return_predictions: bool = False, **kwargs, @@ -272,10 +270,10 @@ def score( Parameters ---------- - testing_structures: + structures: List of Atoms objects of structures to be tested on - y: + labels: Labels for the testing structures metric: @@ -296,7 +294,7 @@ def score( """ assert self.is_fit - pred_label, unc = self.predict(testing_structures) + pred_label, unc = self.predict(structures) score_func = {"mae": mean_absolute_error, "mse": mean_squared_error} @@ -304,7 +302,7 @@ def score( msg = f"Metric: {metric} is not supported" raise PredictorError(msg) - score = score_func[metric](y, pred_label, **kwargs) + score = score_func[metric](labels, pred_label, **kwargs) if return_predictions: return score, pred_label, unc From 351c245891dcf0d4d662e6f0c73cc47276bc5dc7 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Wed, 18 May 2022 18:17:57 -0400 Subject: [PATCH 233/239] fix typo aq_scores vs acquisition_scores prop --- src/autocat/learning/sequential.py | 4 ++-- tests/learning/test_sequential.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 7745bf74..09d7fbec 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -283,8 +283,8 @@ def __init__( self.sl_kwargs.update({"candidate_indices": None}) if "candidate_index_history" not in self.sl_kwargs: self.sl_kwargs.update({"candidate_index_history": None}) - if "aq_scores" not in self.sl_kwargs: - self.sl_kwargs.update({"aq_scores": None}) + if "acquisition_scores" not in self.sl_kwargs: + self.sl_kwargs.update({"acquisition_scores": None}) def __repr__(self) -> str: pt = PrettyTable() diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index 655c6fe8..d9099ff5 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -166,7 +166,7 @@ def test_sequential_learner_write_json(): "uncertainties_history": None, "candidate_indices": None, "candidate_index_history": None, - "aq_scores": None, + "acquisition_scores": None, } # test after iteration @@ -209,6 +209,7 @@ def test_sequential_learner_write_json(): c.tolist() for c in acsl.candidate_index_history ] assert sl[-1].get("acquisition_scores") == acsl.acquisition_scores.tolist() + assert sl[-1].get("acquisition_scores") is not None def test_sequential_learner_to_jsonified_list(): @@ -268,7 +269,7 @@ def test_sequential_learner_to_jsonified_list(): "uncertainties_history": None, "candidate_indices": None, "candidate_index_history": None, - "aq_scores": None, + "acquisition_scores": None, } # test after iteration @@ -312,6 +313,7 @@ def test_sequential_learner_to_jsonified_list(): assert ( jsonified_list[-1].get("acquisition_scores") == acsl.acquisition_scores.tolist() ) + assert jsonified_list[-1].get("acquisition_scores") is not None def test_sequential_learner_iterate(): From ea85cabea1fbde0876c494c291c7401ee4d3e069 Mon Sep 17 00:00:00 2001 From: Lance Kavalsky Date: Thu, 19 May 2022 11:32:59 -0400 Subject: [PATCH 234/239] add max width to ds and feat tables --- src/autocat/learning/featurizers.py | 1 + src/autocat/learning/sequential.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/autocat/learning/featurizers.py b/src/autocat/learning/featurizers.py index e6a1d995..72f14df3 100644 --- a/src/autocat/learning/featurizers.py +++ b/src/autocat/learning/featurizers.py @@ -92,6 +92,7 @@ def __repr__(self) -> str: self.design_space_structures is not None, ] ) + pt.max_width = 70 return str(pt) def copy(self): diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 09d7fbec..5997bee2 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -67,6 +67,7 @@ def __repr__(self) -> str: pt.add_row(["maximum label", max_label]) min_label = min(self.design_space_labels) pt.add_row(["minimum label", min_label]) + pt.max_width = 70 return str(pt) def __len__(self): From 205ca3a6466d34fa0b3728991dae5a3673e72a62 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Sun, 22 May 2022 23:04:51 -0700 Subject: [PATCH 235/239] extract_structures -> flatten_structures_dict --- src/autocat/utils.py | 4 ++-- tests/learning/test_featurizers.py | 28 +++++++++++++++------------- tests/learning/test_predictors.py | 14 +++++++------- tests/learning/test_sequential.py | 10 +++++----- tests/test_utils.py | 8 ++++---- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/autocat/utils.py b/src/autocat/utils.py index b3695320..6b53f4a8 100644 --- a/src/autocat/utils.py +++ b/src/autocat/utils.py @@ -13,11 +13,11 @@ def change_working_dir(new_dir: str): os.chdir(current_dir) -def extract_structures(autocat_dict: dict): +def flatten_structures_dict(autocat_dict: dict): structure_list = [] for element in autocat_dict: if isinstance(autocat_dict[element], dict): - structure_list.extend(extract_structures(autocat_dict[element])) + structure_list.extend(flatten_structures_dict(autocat_dict[element])) elif isinstance(autocat_dict[element], Atoms): structure_list.append(autocat_dict[element]) return structure_list diff --git a/tests/learning/test_featurizers.py b/tests/learning/test_featurizers.py index 370bd6da..a23df132 100644 --- a/tests/learning/test_featurizers.py +++ b/tests/learning/test_featurizers.py @@ -16,7 +16,7 @@ from autocat.surface import generate_surface_structures from autocat.saa import generate_saa_structures from autocat.learning.featurizers import Featurizer -from autocat.utils import extract_structures +from autocat.utils import flatten_structures_dict from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.analysis.local_env import VoronoiNN @@ -42,9 +42,9 @@ def test_eq_featurizer(): f1.kwargs.update({"rcut": 13}) assert f != f1 - surfs = extract_structures(generate_surface_structures(["Fe", "V"])) + surfs = flatten_structures_dict(generate_surface_structures(["Fe", "V"])) surfs.extend( - extract_structures( + flatten_structures_dict( generate_surface_structures(["Au", "Ag"], supercell_dim=(1, 1, 5)) ) ) @@ -64,8 +64,8 @@ def test_featurizer_species_list(): assert f.species_list == ["K", "Na", "Li"] # test getting species list from design space structures - surfs = extract_structures(generate_surface_structures(["Fe", "V", "Ti"])) - saas = extract_structures(generate_saa_structures(["Cu", "Au"], ["Fe", "Pt"])) + surfs = flatten_structures_dict(generate_surface_structures(["Fe", "V", "Ti"])) + saas = flatten_structures_dict(generate_saa_structures(["Cu", "Au"], ["Fe", "Pt"])) surfs.extend(saas) f.design_space_structures = surfs assert f.species_list == ["Ti", "V", "Fe", "Pt", "Au", "Cu"] @@ -81,11 +81,13 @@ def test_featurizer_max_size(): assert f.max_size == 50 # test getting max size from design space structures - surfs = extract_structures( + surfs = flatten_structures_dict( generate_surface_structures(["Ru"], supercell_dim=(2, 2, 4)) ) surfs.extend( - extract_structures(generate_surface_structures(["Fe"], supercell_dim=(4, 4, 4))) + flatten_structures_dict( + generate_surface_structures(["Fe"], supercell_dim=(4, 4, 4)) + ) ) f.design_space_structures = surfs assert f.max_size == 64 @@ -93,9 +95,9 @@ def test_featurizer_max_size(): def test_featurizer_design_space_structures(): # tests giving design space structures - surfs = extract_structures(generate_surface_structures(["Li", "Na"])) + surfs = flatten_structures_dict(generate_surface_structures(["Li", "Na"])) surfs.extend( - extract_structures( + flatten_structures_dict( generate_surface_structures(["Cu", "Ni"], supercell_dim=(1, 1, 5)) ) ) @@ -154,7 +156,7 @@ def test_featurizer_featurize_single(): # TEST STRUCTURE FEATURIZERS # test ElementProperty - saa = extract_structures(generate_saa_structures(["Cu"], ["Pt"]))[0] + saa = flatten_structures_dict(generate_saa_structures(["Cu"], ["Pt"]))[0] f = Featurizer(ElementProperty, preset="magpie", max_size=len(saa)) acf = f.featurize_single(saa) ep = ElementProperty.from_preset("magpie") @@ -177,7 +179,7 @@ def test_featurizer_featurize_single(): assert np.array_equal(acf, manual_cm) # TEST SITE FEATURIZERS - ads_struct = extract_structures( + ads_struct = flatten_structures_dict( generate_adsorbed_structures( surface=saa, adsorbates=["OH"], @@ -243,7 +245,7 @@ def test_featurizer_featurize_multiple(): # TEST STRUCTURE FEATURIZER # test ElementProperty - saas = extract_structures( + saas = flatten_structures_dict( generate_saa_structures( ["Au", "Cu"], ["Pd", "Pt"], facets={"Au": ["111"], "Cu": ["111"]} ) @@ -278,7 +280,7 @@ def test_featurizer_featurize_multiple(): ads_structs = [] for struct in saas: ads_structs.append( - extract_structures( + flatten_structures_dict( generate_adsorbed_structures( surface=struct, adsorbates=["NNH"], diff --git a/tests/learning/test_predictors.py b/tests/learning/test_predictors.py index e3619616..471738dd 100644 --- a/tests/learning/test_predictors.py +++ b/tests/learning/test_predictors.py @@ -20,15 +20,15 @@ from autocat.surface import generate_surface_structures from autocat.learning.predictors import Predictor from autocat.learning.predictors import PredictorError -from autocat.utils import extract_structures +from autocat.utils import flatten_structures_dict def test_fit(): # Test returns a fit model - subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) + subs = flatten_structures_dict(generate_surface_structures(["Pt", "Fe", "Ru"])) structs = [] for sub in subs: - ads_struct = extract_structures( + ads_struct = flatten_structures_dict( generate_adsorbed_structures( surface=sub, adsorbates=["OH"], @@ -95,10 +95,10 @@ def test_fit(): def test_predict(): # Test outputs are returned as expected - subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) + subs = flatten_structures_dict(generate_surface_structures(["Pt", "Fe", "Ru"])) structs = [] for sub in subs: - ads_struct = extract_structures( + ads_struct = flatten_structures_dict( generate_adsorbed_structures( surface=sub, adsorbates=["OH"], @@ -141,10 +141,10 @@ def test_predict(): def test_score(): # Tests the score method - subs = extract_structures(generate_surface_structures(["Pt", "Fe", "Ru"])) + subs = flatten_structures_dict(generate_surface_structures(["Pt", "Fe", "Ru"])) structs = [] for sub in subs: - ads_struct = extract_structures( + ads_struct = flatten_structures_dict( generate_adsorbed_structures( surface=sub, adsorbates=["OH"], diff --git a/tests/learning/test_sequential.py b/tests/learning/test_sequential.py index d9099ff5..1f1386c8 100644 --- a/tests/learning/test_sequential.py +++ b/tests/learning/test_sequential.py @@ -34,7 +34,7 @@ from autocat.surface import generate_surface_structures from autocat.adsorption import place_adsorbate from autocat.saa import generate_saa_structures -from autocat.utils import extract_structures +from autocat.utils import flatten_structures_dict def test_sequential_learner_from_json(): @@ -1043,11 +1043,11 @@ def test_choose_next_candidate_segregation_energy_weighting(): # Tests that the segregation energy weighting is properly applied unc = np.array([0.3, 0.3]) pred = np.array([2.0, 2.0]) - structs = extract_structures( + structs = flatten_structures_dict( generate_saa_structures(["Cr"], ["Rh"], facets={"Cr": ["110"]}) ) structs.extend( - extract_structures( + flatten_structures_dict( generate_saa_structures(["Co"], ["Re"], facets={"Co": ["0001"]}) ) ) @@ -1142,13 +1142,13 @@ def test_calculate_hhi_scores(): def test_calculate_segregation_energy_scores(): # Tests calculating segregation energy scores - saa_structs = extract_structures( + saa_structs = flatten_structures_dict( generate_saa_structures( ["Ag", "Ni"], ["Pt"], facets={"Ag": ["111"], "Ni": ["111"]}, ) ) saa_structs.extend( - extract_structures( + flatten_structures_dict( generate_saa_structures(["Pd"], ["W"], facets={"Pd": ["111"]}) ) ) diff --git a/tests/test_utils.py b/tests/test_utils.py index a4ee1ea0..5b3190b6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -8,7 +8,7 @@ from autocat.surface import generate_surface_structures from autocat.saa import generate_saa_structures from autocat.adsorption import generate_adsorbed_structures -from autocat.utils import extract_structures +from autocat.utils import flatten_structures_dict def test_extract_surfaces(): @@ -16,7 +16,7 @@ def test_extract_surfaces(): surfaces = generate_surface_structures( ["Pt", "Cu", "Li"], facets={"Pt": ["100", "111"], "Cu": ["111"], "Li": ["110"]} ) - ex_structures = extract_structures(surfaces) + ex_structures = flatten_structures_dict(surfaces) assert all(isinstance(struct, Atoms) for struct in ex_structures) # checks atoms objects left untouched during extraction pt_struct100 = fcc100("Pt", size=(3, 3, 4), vacuum=10) @@ -37,7 +37,7 @@ def test_extract_saas(): facets={"Cu": ["110"], "Au": ["100"]}, supercell_dim=(2, 2, 5), ) - ex_structures = extract_structures(saas) + ex_structures = flatten_structures_dict(saas) assert all(isinstance(struct, Atoms) for struct in ex_structures) assert saas["Cu"]["Fe"]["fcc110"]["structure"] in ex_structures assert saas["Au"]["Fe"]["fcc100"]["structure"] in ex_structures @@ -54,7 +54,7 @@ def test_extract_adsorption(): adsorption_sites={"custom": [(0.0, 0.0)]}, use_all_sites=False, ) - ex_structures = extract_structures(ads_dict) + ex_structures = flatten_structures_dict(ads_dict) assert all(isinstance(struct, Atoms) for struct in ex_structures) assert ads_dict["NH2"]["custom"]["0.0_0.0"]["structure"] in ex_structures assert ads_dict["Li"]["custom"]["0.0_0.0"]["structure"] in ex_structures From 15f13816cb090ee0e5fd59572a40caef6ce082ae Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Sun, 22 May 2022 23:06:07 -0700 Subject: [PATCH 236/239] break up long line for flake8 --- src/autocat/learning/sequential.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/autocat/learning/sequential.py b/src/autocat/learning/sequential.py index 5997bee2..001bde63 100644 --- a/src/autocat/learning/sequential.py +++ b/src/autocat/learning/sequential.py @@ -777,7 +777,10 @@ def simulated_sequential_learning( # check fully explored if True in np.isnan(full_design_space.design_space_labels): missing_label_idx = np.where(np.isnan(full_design_space.design_space_labels))[0] - msg = f"Design space must be fully explored. Missing labels at indices: {missing_label_idx}" + msg = ( + f"Design space must be fully explored." + f" Missing labels at indices: {missing_label_idx}" + ) raise SequentialLearnerError(msg) # check that specified initial training size makes sense From 71471eaad361e71c92b5262c9561e4b0daf1efeb Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Sun, 22 May 2022 23:06:45 -0700 Subject: [PATCH 237/239] rm perturbations module for now --- src/autocat/perturbations.py | 311 ----------------------------------- tests/test_perturbations.py | 251 ---------------------------- 2 files changed, 562 deletions(-) delete mode 100644 src/autocat/perturbations.py delete mode 100644 tests/test_perturbations.py diff --git a/src/autocat/perturbations.py b/src/autocat/perturbations.py deleted file mode 100644 index 1306cc75..00000000 --- a/src/autocat/perturbations.py +++ /dev/null @@ -1,311 +0,0 @@ -import os -import json -from typing import List -from typing import Union - -import numpy as np -from ase.io import read -from ase import Atoms - - -class AutocatPerturbationError(Exception): - pass - - -def generate_perturbed_dataset( - base_structures: List[Atoms], - minimum_perturbation_distance: float = 0.01, - maximum_perturbation_distance: float = 0.75, - maximum_adsorbate_size: int = None, - num_of_perturbations: int = 10, - direction_sign_constraint: int = None, - write_to_disk: bool = False, - write_location: str = ".", - dirs_exist_ok: bool = False, -): - """ - - Generates a dataset consisting of perturbed structures from - a base list of structures and keeps track of displacement - vectors - - Atoms to be perturbed are specified by their `tag`. - The options for constraints are also set using - `tag`s with the options as follows: - 0: free in all directions - -1: free in z only - -2: free in xy only - -3: free in x only - -4: free in y only - - The direction sign can also be constrained (e.g. - only perturb in the + direction) - - For example, to perturb an atom in only the - +z direction, its tag should be -1 and - direction_sign_constraint = 1 - - Parameters - ---------- - - base_structures: - List of Atoms objects or name of file containing structure - as a strings specifying the base structures to be - perturbed - - minimum_perturbation_distance: - Float of minimum acceptable perturbation distance - Default: 0.01 Angstrom - - maximum_perturbation_distance: - Float of maximum acceptable perturbation distance - Default: 0.75 Angstrom - - maximum_adsorbate_size: - Integer giving the largest number of atoms in an adsorbate - that should be able to be considered. Used to obtain shape - of collected matrices - Default: number of atoms in largest base structure - - num_of_perturbations: - Int specifying number of perturbations to generate. - Default 10 - - direction_sign_constraint: - Int used to restrict the sign of the perturbation. - Options: - 1 = perturb in only the + direction - -1 = perturb in only the - direction - Default: allows both +/- directions. - - write_to_disk: - Boolean specifying whether the perturbed structures generated should be - written to disk. - Defaults to False. - - write_location: - String with the location where the perturbed structure - files written to disk. - - dirs_exist_ok: - Boolean specifying whether existing directories/files should be - overwritten or not. This is passed on to the `os.makedirs` builtin. - Defaults to False (raises an error if directories corresponding the - species and crystal structure already exist). - - Returns - ------- - - perturbed_dict: - Dictionary containing all generated perturbed structures - with their corresponding perturbation matrices - - """ - - perturbed_dict = {} - - corrections_list = [] - collected_structure_paths = [] - collected_structures = [] - - # loop over each base structure - for structure_index, structure in enumerate(base_structures): - # get name of each base structure - if isinstance(structure, Atoms): - name = structure.get_chemical_formula() + "_" + str(structure_index) - elif isinstance(structure, str): - name = ".".join(structure.split(".")[:-1]) - else: - raise TypeError("Structure needs to be either a str or ase.Atoms object") - - # make sure no base_structures with the same name - if name in perturbed_dict: - msg = f"Multiple input base structures named {name}" - raise AutocatPerturbationError(msg) - - # apply perturbations - perturbed_dict[name] = {} - for i in range(num_of_perturbations): - perturbed_dict[name][str(i)] = perturb_structure( - structure, - minimum_perturbation_distance=minimum_perturbation_distance, - maximum_perturbation_distance=maximum_perturbation_distance, - direction_sign_constraint=direction_sign_constraint, - ) - # keeps flattened atomic coordinates difference vector - corrections_list.append(perturbed_dict[name][str(i)]["perturbation_matrix"]) - - traj_file_path = None - pert_mat_file_path = None - if write_to_disk: - dir_path = os.path.join(write_location, f"{name}/{str(i)}") - os.makedirs(dir_path, exist_ok=dirs_exist_ok) - traj_file_path = os.path.join(dir_path, "perturbed_structure.traj") - # write perturbed structure to disk - perturbed_dict[name][str(i)]["structure"].write(traj_file_path) - print( - f"{name} perturbed structure {str(i)} written to {traj_file_path}" - ) - pert_mat_file_path = os.path.join(dir_path, "perturbation_matrix.json") - with open(pert_mat_file_path, "w") as f: - # convert to np.array to list for json - pert_mat_list = perturbed_dict[name][str(i)][ - "perturbation_matrix" - ].tolist() - # write perturbation matrix to json - json.dump(pert_mat_list, f) - print( - f"{name} perturbed matrix {str(i)} written to {pert_mat_file_path}" - ) - # update output dictionary with write paths - perturbed_dict[name][str(i)].update({"traj_file_path": traj_file_path}) - perturbed_dict[name][str(i)].update( - {"pert_mat_file_path": pert_mat_file_path} - ) - # Collects all of the structures into a single list in the same - # order as the collected matrix rows - collected_structures.append(perturbed_dict[name][str(i)]["structure"]) - collected_structure_paths.append(traj_file_path) - - if maximum_adsorbate_size is None: - # find flattened length of largest structure - adsorbate_sizes = [] - for struct in base_structures: - adsorbate_sizes.append(len(np.where(struct.get_tags() <= 0)[0])) - largest_size = 3 * max(adsorbate_sizes) - else: - # factor of 3 from flattening (ie. x,y,z) - largest_size = 3 * maximum_adsorbate_size - # ensures correct sized padding - corrections_matrix = np.zeros((len(corrections_list), largest_size)) - # substitute in collected matrices for each row - for idx, row in enumerate(corrections_list): - corrections_matrix[idx, : 3 * len(row)] = row.flatten() - - # adds collected matrices to dict that will be returned - perturbed_dict["correction_matrix"] = corrections_matrix - correction_matrix_path = None - # write matrix to disk as json if desired - if write_to_disk: - correction_matrix_path = os.path.join(write_location, "correction_matrix.json") - coll = perturbed_dict["correction_matrix"].tolist() - with open(correction_matrix_path, "w") as f: - json.dump(coll, f) - print(f"Correction matrix written to {correction_matrix_path}") - # update output dict with collected structures and paths - perturbed_dict.update( - { - "correction_matrix_path": correction_matrix_path, - "corrections_list": corrections_list, - "collected_structures": collected_structures, - "collected_structure_paths": collected_structure_paths, - } - ) - - return perturbed_dict - - -def perturb_structure( - base_structure: Union[str, Atoms], - minimum_perturbation_distance: float = 0.1, - maximum_perturbation_distance: float = 1.0, - direction_sign_constraint: int = None, -): - """ - - Perturbs specific atoms in a given structure and keeps - track of the displacement vectors of each displaced atom - - Atoms to be perturbed are specified by their `tag`. - The options for constraints are also set using - `tag`s with the options as follows: - 0: free in all directions - -1: free in z only - -2: free in xy only - -3: free in x only - -4: free in y only - - The direction sign can also be constrained (e.g. - only perturb in the + direction) - - For example, to perturb an atom in only the - +z direction, its tag should be -1 and - direction_sign_constraint = 1 - - Parameters - ---------- - - base_structure: - Atoms object or name of file containing structure - as a string specifying the base structure to be - perturbed - - minimum_perturbation_distance: - Float of minimum acceptable perturbation distance - Default: 0.1 Angstrom - - maximum_perturbation_distance: - Float of maximum acceptable perturbation distance - Default: 1.0 Angstrom - - direction_sign_constraint: - Int used to restrict the sign of the perturbation. - Options: - 1 = perturb in only the + direction - -1 = perturb in only the - direction - Default: allows both +/- directions. - - Returns - ------- - - perturb_dictionary: - Dictionary with perturbed structure and displacement vectors - - """ - if isinstance(base_structure, Atoms): - ase_obj = base_structure.copy() - elif isinstance(base_structure, str): - ase_obj = read(base_structure) - else: - raise TypeError("base_structure needs to be either a str or ase.Atoms object") - - pert_matrix = np.zeros(ase_obj.positions.shape) - - atom_indices_to_perturb = np.where(ase_obj.get_tags() <= 0)[0].tolist() - - constr = [ - [True, True, True], # free - [False, True, False], # y only - [True, False, False], # x only - [True, True, False], # xy only - [False, False, True], # z only - ] - - for idx in atom_indices_to_perturb: - # randomize +/- direction of each perturbation - signs = np.array([-1, -1, -1]) ** np.random.randint(low=1, high=11, size=(1, 3)) - if direction_sign_constraint is not None: - if direction_sign_constraint not in [-1, 1]: - msg = ( - f"direction_sign_constraint must be either -1 or 1," - f" got {direction_sign_constraint} instead" - ) - raise AutocatPerturbationError(msg) - signs = direction_sign_constraint * abs(signs) - # generate perturbation matrix - pert_matrix[idx, :] = ( - constr[ase_obj[idx].tag] - * signs - * np.random.uniform( - low=minimum_perturbation_distance, - high=maximum_perturbation_distance, - size=(1, 3), - ) - ) - - ase_obj.positions += pert_matrix - - return { - "structure": ase_obj, - "perturbation_matrix": pert_matrix[atom_indices_to_perturb], - } diff --git a/tests/test_perturbations.py b/tests/test_perturbations.py deleted file mode 100644 index 98e7f437..00000000 --- a/tests/test_perturbations.py +++ /dev/null @@ -1,251 +0,0 @@ -"""Unit tests for the `autocat.perturbations` module""" - -import os -import pytest -import numpy as np - -import tempfile - -from ase import Atoms -from ase.build import molecule - -from autocat.surface import generate_surface_structures -from autocat.adsorption import place_adsorbate -from autocat.perturbations import AutocatPerturbationError, perturb_structure -from autocat.perturbations import generate_perturbed_dataset - - -def test_perturb_structure_directions(): - # Tests fixing directions of perturbations - sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - mol = molecule("H") - base_struct = place_adsorbate(surface=sub, adsorbate=mol) - # free in all directions - p_struct = perturb_structure(base_struct,) - assert (p_struct["structure"].positions[-1] != base_struct.positions[-1]).all() - # free in z - base_struct[-1].tag = -1 - p_struct = perturb_structure(base_struct,) - assert p_struct["structure"].positions[-1][0] == base_struct.positions[-1][0] - assert p_struct["structure"].positions[-1][1] == base_struct.positions[-1][1] - assert p_struct["structure"].positions[-1][-1] != base_struct.positions[-1][-1] - - -def test_perturb_structure_sign_constraint(): - # Tests fixing sign of perturbation - sub = generate_surface_structures(["Cu"], facets={"Cu": ["111"]})["Cu"]["fcc111"][ - "structure" - ] - mol = molecule("O") - base_struct = place_adsorbate(surface=sub, adsorbate=mol) - # free in +z - base_struct[-1].tag = -1 - p_struct = perturb_structure(base_struct, direction_sign_constraint=1) - assert p_struct["structure"].positions[-1][0] == base_struct.positions[-1][0] - assert p_struct["structure"].positions[-1][-1] - base_struct.positions[-1][-1] > 0.0 - # free in -x - base_struct[-1].tag = -3 - p_struct = perturb_structure(base_struct, direction_sign_constraint=-1) - assert p_struct["structure"].positions[-1][0] - base_struct.positions[-1][0] < 0.0 - # free in +xy - base_struct[-1].tag = -2 - p_struct = perturb_structure(base_struct, direction_sign_constraint=1) - assert p_struct["structure"].positions[-1][0] - base_struct.positions[-1][0] > 0.0 - assert p_struct["structure"].positions[-1][1] - base_struct.positions[-1][1] > 0.0 - with pytest.raises(AutocatPerturbationError): - p_struct = perturb_structure(base_struct, direction_sign_constraint=2) - - -def test_perturb_structure_matrix(): - # Tests matrix matches perturbed structure - sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - mol = molecule("OH") - base_struct = place_adsorbate(surface=sub, adsorbate=mol) - p_struct = perturb_structure(base_struct) - o_pert = base_struct.positions[-2] + p_struct["perturbation_matrix"][-2] - assert np.allclose(p_struct["structure"].positions[-2], o_pert) - h_pert = base_struct.positions[-1] + p_struct["perturbation_matrix"][-1] - assert np.allclose(p_struct["structure"].positions[-1], h_pert) - - -def test_generate_perturbed_dataset_num_of_perturbations(): - # Tests number of perturbations generated - sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - mol = molecule("OH") - base_struct = place_adsorbate(surface=sub, adsorbate=mol) - p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=15,) - assert len(p_set["HOPt36_0"].keys()) == 15 - - -def test_generate_perturbed_dataset_multiple_base_structures(): - # Tests giving multiple base_structures - sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ - "structure" - ] - mol = molecule("OH") - base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) - mol = molecule("NH") - base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) - base_struct1[-2].tag = 1 - base_struct1[-1].tag = -1 - base_struct2[-1].tag = 1 - p_set = generate_perturbed_dataset([base_struct1, base_struct2],) - # Check all base structures perturbed - assert "HCu36N_1" in p_set - assert "HOPt36_0" in p_set - # Check correct direction constraints applied to each base_structure - assert np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][0], 0.0) - assert np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][1], 0.0) - assert not np.isclose(p_set["HOPt36_0"]["6"]["perturbation_matrix"][-1][-1], 0.0) - assert not np.isclose(p_set["HCu36N_1"]["1"]["perturbation_matrix"][-1][0], 0.0) - - -def test_generate_perturbed_dataset_write_location(): - # Tests write location - _tmp_dir = tempfile.TemporaryDirectory().name - sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - mol = molecule("OH") - base_struct = place_adsorbate(surface=sub, adsorbate=mol) - p_set = generate_perturbed_dataset( - [base_struct], write_to_disk=True, write_location=_tmp_dir, - ) - assert os.path.samefile( - p_set["HOPt36_0"]["0"]["traj_file_path"], - os.path.join(_tmp_dir, "HOPt36_0/0/perturbed_structure.traj"), - ) - assert os.path.samefile( - p_set["HOPt36_0"]["0"]["pert_mat_file_path"], - os.path.join(_tmp_dir, "HOPt36_0/0/perturbation_matrix.json"), - ) - assert os.path.samefile( - p_set["correction_matrix_path"], - os.path.join(_tmp_dir, "correction_matrix.json"), - ) - - -def test_generate_perturbed_dataset_correction_matrix(): - # Check that matrices properly collected for 1 base_structure - sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - mol = molecule("OH") - base_struct = place_adsorbate(surface=sub, adsorbate=mol) - p_set = generate_perturbed_dataset([base_struct], num_of_perturbations=5,) - manual_collect = np.zeros((5, 6)) - for idx, struct in enumerate(p_set["HOPt36_0"]): - flat = p_set["HOPt36_0"][struct]["perturbation_matrix"].flatten() - manual_collect[idx, : len(flat)] = flat - assert np.allclose(p_set["correction_matrix"], manual_collect) - assert p_set["correction_matrix"].shape == (5, 6) - # Check when given specific maximum_structure_size - p_set = generate_perturbed_dataset( - [base_struct], num_of_perturbations=5, maximum_adsorbate_size=15, - ) - assert p_set["correction_matrix"].shape == (5, 45) - - -def test_generate_perturbed_dataset_correction_matrix_multiple(): - # check that matrices properly collected for 2 base structures of == size - sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ - "structure" - ] - mol = molecule("OH") - base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) - base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) - base_struct2[-1].tag = 1 - p_set = generate_perturbed_dataset( - [base_struct1, base_struct2], num_of_perturbations=5, - ) - manual_collect = np.zeros((10, 6)) - counter = 0 - for base in ["HOPt36_0", "HCu36O_1"]: - for struct in p_set[base]: - flat = p_set[base][struct]["perturbation_matrix"].flatten() - manual_collect[counter, : len(flat)] = flat - counter += 1 - assert np.allclose(p_set["correction_matrix"], manual_collect) - assert p_set["correction_matrix"].shape == (10, 6) - - # check that matrices properly collected for 2 base structures of != size - sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ - "structure" - ] - mol = molecule("OH") - base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) - mol = molecule("H") - base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) - base_struct1[-2].tag = 1 - p_set = generate_perturbed_dataset( - [base_struct1, base_struct2], num_of_perturbations=5, - ) - manual_collect = [] - for base in ["HOPt36_0", "HCu36_1"]: - for struct in p_set[base]: - manual_collect.append(p_set[base][struct]["perturbation_matrix"].flatten()) - manual_collect_array = np.zeros((10, 3)) - for idx, m in enumerate(manual_collect): - manual_collect_array[idx, : len(m)] = m - assert np.allclose(p_set["correction_matrix"], manual_collect_array) - assert p_set["correction_matrix"].shape == (10, 3) - # check corrections list - corr_list = p_set["corrections_list"] - assert len(corr_list) == p_set["correction_matrix"].shape[0] - assert (corr_list[3] == p_set["correction_matrix"][3, : len(corr_list[3][0])]).all() - - -def test_generate_perturbed_dataset_collected_structure_paths(): - # Tests that collected structure paths in correct order - _tmp_dir = tempfile.TemporaryDirectory().name - sub = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - mol = molecule("OH") - base_struct = place_adsorbate(surface=sub, adsorbate=mol) - p_set = generate_perturbed_dataset( - [base_struct], write_to_disk=True, write_location=_tmp_dir, - ) - assert os.path.samefile( - p_set["collected_structure_paths"][0], - os.path.join(_tmp_dir, "HOPt36_0/0/perturbed_structure.traj"), - ) - assert os.path.samefile( - p_set["collected_structure_paths"][7], - os.path.join(_tmp_dir, "HOPt36_0/7/perturbed_structure.traj"), - ) - - -def test_generate_perturbed_dataset_collected_structures(): - # Test that all of the structures are collected - sub1 = generate_surface_structures(["Pt"], facets={"Pt": ["111"]})["Pt"]["fcc111"][ - "structure" - ] - sub2 = generate_surface_structures(["Cu"], facets={"Cu": ["100"]})["Cu"]["fcc100"][ - "structure" - ] - mol = molecule("OH") - base_struct1 = place_adsorbate(surface=sub1, adsorbate=mol) - base_struct2 = place_adsorbate(surface=sub2, adsorbate=mol) - base_struct1[-2].tag = 1 - base_struct2[-1].tag = 1 - p_set = generate_perturbed_dataset( - [base_struct1, base_struct2], num_of_perturbations=5, - ) - assert len(p_set["collected_structures"]) == 10 - assert isinstance(p_set["collected_structures"][3], Atoms) From c0c6846e65450d1f5398c98d07b6203ab9ee049c Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Mon, 23 May 2022 00:23:18 -0700 Subject: [PATCH 238/239] version bump --- src/autocat/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocat/VERSION.txt b/src/autocat/VERSION.txt index b5d4d818..6988b62d 100644 --- a/src/autocat/VERSION.txt +++ b/src/autocat/VERSION.txt @@ -1 +1 @@ -2022.3.31 +2022.5.23 From c977ed6a7a6776f020e55c59daa4231969ed9c14 Mon Sep 17 00:00:00 2001 From: Vinay Hegde Date: Mon, 23 May 2022 00:34:43 -0700 Subject: [PATCH 239/239] add contributing.md to manifest --- MANIFEST.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 9d3f36c3..eda156cb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include src/autocat/data/**/*.json include src/autocat/VERSION.txt -include bin/autocat \ No newline at end of file +include bin/autocat +include CONTRIBUTING.md