diff --git a/.github/workflows/camd-docker.yml b/.github/workflows/camd-docker.yml index 01dc3640..9d1cd6de 100644 --- a/.github/workflows/camd-docker.yml +++ b/.github/workflows/camd-docker.yml @@ -14,7 +14,7 @@ jobs: os: [ ubuntu-latest, ] - python-version: [3.7] + python-version: [3.8] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/camd-test-main.yml b/.github/workflows/camd-test-main.yml index 77714ef1..b56ccd99 100644 --- a/.github/workflows/camd-test-main.yml +++ b/.github/workflows/camd-test-main.yml @@ -13,7 +13,7 @@ jobs: macos-latest, # windows-latest ] - python-version: [3.7] + python-version: [3.9] runs-on: ${{ matrix.os }} @@ -27,7 +27,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt - pip install -e .[tests] + pip install -e .[tests,m3gnet,atomate,proto_dft] - name: pytest env: MPLBACKEND: "Agg" diff --git a/Dockerfile b/Dockerfile index 840b0883..a2c6de09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"] ENV PATH="/opt/conda/bin/:$PATH" RUN mkdir -p /home/camd && \ - conda create -n camd python=3.7 && \ + conda create -n camd python=3.8 && \ apt-get update && \ apt-get -y install gcc g++ @@ -22,4 +22,4 @@ RUN source /opt/conda/bin/activate camd && \ pip install -r requirements.txt COPY camd /home/camd/camd -RUN pip install -e . +RUN pip install -e .[proto_dft,m3gnet,atomate] diff --git a/README.md b/README.md index 1211d201..d6147794 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ![Testing - main](https://github.com/TRI-AMDD/CAMD/workflows/Testing%20-%20main/badge.svg) ![Linting](https://github.com/TRI-AMDD/CAMD/workflows/Linting/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/TRI-AMDD/CAMD/badge.svg)](https://coveralls.io/github/TRI-AMDD/CAMD) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/TRI-AMDD/camd/blob/master/examples/main_tutorial.ipynb) CAMD provides a flexible software framework for sequential / Bayesian optimization type campaigns for materials discovery. Its key features include: * **Agents**: Decision making entities which select experiments to run from pre-determined candidate sets. Agents can combine machine learning with physical or chemical constructs, logic, heuristics, exploration-exploitation strategies and so on. CAMD comes with several generic and structure-discovery focused agents, which can be used by the users as templates to derive new ones. diff --git a/camd/agent/generic.py b/camd/agent/generic.py index 5b0d05d7..d153be48 100644 --- a/camd/agent/generic.py +++ b/camd/agent/generic.py @@ -13,6 +13,8 @@ from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.gaussian_process.kernels import RBF, ConstantKernel from sklearn.linear_model import LinearRegression +from sklearn.ensemble import RandomForestRegressor +from sklearn.neural_network import MLPRegressor from camd.agent.base import HypothesisAgent @@ -61,7 +63,7 @@ def get_hypotheses(self, candidate_data, seed_data=None): self.candidate_data = candidate_data.drop(columns=["target"], axis=1) self.seed_data = seed_data - X_seed = seed_data.drop(columns=["target"], axis=1, errors='ignore') + X_seed = seed_data.drop(columns=["target"], axis=1, errors="ignore") y_seed = seed_data["target"] steps = [ ("scaler", StandardScaler()), @@ -203,8 +205,8 @@ def get_hypotheses(self, candidate_data, seed_data=None): 2 * np.log( len(self.candidate_data) - * _t ** 2 - * np.pi ** 2 + * _t**2 + * np.pi**2 / 6 / self.kwargs.get("delta", 0.1) ) @@ -247,7 +249,7 @@ def get_hypotheses(self, candidate_data, seed_data=None): return self.candidate_data.loc[batch] -class LinearAgent(HypothesisAgent): +class RegressorAgent(HypothesisAgent): """ Linear regression based agent that tries to maximize a target. Best for simple checks and benchmarks. @@ -255,28 +257,29 @@ class LinearAgent(HypothesisAgent): def __init__( self, + model, + features=None, + target="target", candidate_data=None, seed_data=None, n_query: int = None, - fit_intercept: bool = True, - positive: bool = False, ): """ Args: + model (sklearn.RegressorMixin): some regressor with "fit" method candidate_data (pandas.DataFrame): data about the candidates to search over. Must have a "target" column, and at least one additional column that can be used as descriptors. seed_data (pandas.DataFrame): data which to fit the Agent to. n_query (int): number of queries in allowed. Defaults to 1. - fit_intercept (bool): if the intercept is fit for the linear regression - positive (bool): if true, constraint coefficients to be positive for the linear regression """ + self.model = model + self.features = features + self.target = target self.candidate_data = candidate_data self.seed_data = seed_data self.n_query = n_query if n_query else 1 - self.fit_intercept = fit_intercept - self.positive = positive - super(LinearAgent).__init__() + super(RegressorAgent).__init__() def get_hypotheses(self, candidate_data, seed_data=None): """ @@ -291,31 +294,91 @@ def get_hypotheses(self, candidate_data, seed_data=None): """ # Fit on known data - self.candidate_data = candidate_data.drop( - columns=["target"], axis=1, errors="ignore" - ) + self.candidate_data = candidate_data if seed_data is not None: self.seed_data = seed_data else: raise ValueError( - "Linear Agent requires a finite seed as input. " + "RegressorAgent requires a finite seed as input. " "If you are using this as part of a Campaign, consider " "the create_seed option." ) - X_seed = seed_data.drop(columns=["target"], axis=1) - y_seed = seed_data["target"] - steps = [ - ("scaler", StandardScaler()), - ( - "linear", - LinearRegression(), - ), - ] - self.pipeline = Pipeline(steps) - self.pipeline.fit(X_seed, y_seed) - output = self.pipeline.predict(self.candidate_data) + if self.features is not None: + X_seed = seed_data[self.features] + X_cand = candidate_data[self.features] + else: + X_seed = seed_data.drop(columns=[self.target], axis=1) + X_cand = candidate_data.drop( + columns=[self.target], axis=1, errors="ignore" + ) + y_seed = seed_data[self.target] + self.model.fit(X_seed, y_seed) + output = self.model.predict(X_cand) sorted_output = np.argsort(output)[::-1] selected = sorted_output[: self.n_query] return candidate_data.iloc[selected] + + @classmethod + def from_linear( + cls, + features=None, + target="target", + candidate_data=None, + seed_data=None, + n_query: int = None, + **kwargs + ): + """Preset factory method for a Linear Agent""" + linear_reg = LinearRegression(**kwargs) + return cls( + model=linear_reg, + features=features, + target=target, + candidate_data=candidate_data, + seed_data=seed_data, + n_query=n_query, + ) + + @classmethod + def from_random_forest( + cls, + features=None, + target="target", + candidate_data=None, + seed_data=None, + n_query: int = None, + **kwargs + ): + """Preset factory method for a RandomForestRegressor-based Agent""" + rf = RandomForestRegressor(**kwargs) + return cls( + model=rf, + features=features, + target=target, + candidate_data=candidate_data, + seed_data=seed_data, + n_query=n_query, + ) + + @classmethod + def from_mlp( + cls, + features=None, + target="target", + candidate_data=None, + seed_data=None, + n_query: int = None, + **kwargs + ): + """Preset factory method for an MLP-based Agent""" + mlp = MLPRegressor(**kwargs) + return cls( + model=mlp, + features=features, + target=target, + candidate_data=candidate_data, + seed_data=seed_data, + n_query=n_query, + ) diff --git a/camd/agent/m3gnet.py b/camd/agent/m3gnet.py new file mode 100644 index 00000000..c9a1fc80 --- /dev/null +++ b/camd/agent/m3gnet.py @@ -0,0 +1,233 @@ +"""This module defines agents and various functions +that use megnet for candidate selection or candidate filtration +""" +import numpy as np +import tensorflow as tf +from multiprocessing import cpu_count + +from m3gnet.models import M3GNet, Potential, Relaxer +from m3gnet.trainers import PotentialTrainer + +from camd.agent.stability import StabilityAgent +from camd.agent.base import HypothesisAgent + + +def reverse_calcs(retrain_data, data_screening=None): + """ + Prepare the data for training + Args: + retrain_data (pd.DataFrame): + data_screening (dict): retrain data select criteria + example: {"task": "relax1", + "ionic_step": "last_n"} + Returns: lists + X_struct, Xe, Xf, Xs + """ + X_struct = [] + Xe = [] + Xf = [] + Xs = [] + for d in retrain_data: + for task in d: + for step, ionic_steps in enumerate(task["output"]["ionic_steps"]): + # TODO: implement data selection criteria + X_struct.append(ionic_steps["structure"]) + Xe.append( + ionic_steps["e_fr_energy"] / ionic_steps["structure"].num_sites + ) + Xf.append(ionic_steps["forces"]) + Xs.append(ionic_steps["stress"]) + return X_struct, Xe, Xf, Xs + + +class M3GNetStabilityAgent(StabilityAgent): + """Stability agent that uses megnet for estimation of energy""" + + def __init__( + self, + m3gnet=None, + learning_rate=1e-3, + candidate_data=None, + seed_data=None, + n_query=1, + hull_distance=0.0, + parallel=cpu_count(), + ): + """ + + + Args: + m3gnet (M3gnet): m3gnet object, graph CNN from m3gnet package + which determines energy, forces, stresses, and can do relaxation + etc. + learning_rate (float): learning rate for m3gnet fitting + candidate_data (pd.DataFrame): candidate data to predict on + seed_data (pd.DataFrame): seed data to fit on + n_query (int): number of candidates to select + hull_distance (float): hull distance to consider + parallel (int): number of parallel processes to use for + phase determination + """ + super(M3GNetStabilityAgent, self).__init__( + candidate_data=candidate_data, + seed_data=seed_data, + n_query=n_query, + hull_distance=hull_distance, + parallel=parallel, + ) + if not m3gnet: + self.m3gnet = M3GNet(is_intensive=False) + else: + self.m3gnet = m3gnet + self.potential = Potential(model=self.m3gnet) + self.trainer = PotentialTrainer( + potential=self.potential, optimizer=tf.keras.optimizers.Adam(learning_rate) + ) + + def get_hypotheses( + self, candidate_data, seed_data=None, retrain_committee=False, retrain_epochs=1 + ): + """ + Get hypotheses method for downselecting candidates + + Args: + candidate_data (pd.DataFrame): candidate data to predict on and select from + seed_data (pd.DataFrame): seed data to fit on + retrain_committee (bool): whether to retrain m3gnet on each run + retrain_epochs (int): number of epochs for retraining + + Returns: + (pd.DataFrame): selected candidates for experiment + + """ + if "target" in candidate_data.columns: + self.candidate_data = candidate_data.drop(columns=["target"], axis=1) + else: + self.candidate_data = candidate_data + # TODO: handle this here for now, should move later, as in stability agents + self.seed_data = seed_data.dropna(subset=['delta_e']) + X_seed = seed_data.drop(columns=["target"], axis=1, errors="ignore") + # y_seed = seed_data["target"] + if retrain_committee and "calcs_reversed" in X_seed.columns: + self.train(X_seed, retrain_epochs) + relaxer = Relaxer(potential=self.trainer.potential) + t_pred = [] + for s in candidate_data["structure"]: + t = relaxer.relax(s)["trajectory"] + e = t.energies[-1].flatten()[0] + t_pred.append(e) + # TODO: get real formation energy instead of energy per atom + self.update_candidate_stabilities(t_pred, sort=True, floor=-6.0) + # Find the most stable ones up to n_query within hull_distance + stability_filter = self.candidate_data["pred_stability"] <= self.hull_distance + within_hull = self.candidate_data[stability_filter] + return within_hull.head(self.n_query) + + def train(self, train_data, epochs=10, kwargs={}): + """ + Train the potential + Args: + train_data (pd.DataFrame): data for re-train, + same format as seed data + should contain "calcs_reversed" column + epochs (int): number of max epoch to train + kwargs (dict): parameters for trainer.train + + Returns: + None + """ + X_struct, Xe, Xf, Xs = reverse_calcs(train_data["calcs_reversed"]) + self.trainer.train( + graphs_or_structures=X_struct, + energies=Xe, + forces=Xf, + stresses=Xs, + epochs=epochs, + **kwargs + ) + + +class M3GNetHypothesisAgent(HypothesisAgent): + """Generic agent for AL using m3gnet""" + + def __init__(self, m3gnet=None, candidate_data=None, seed_data=None, n_query=None): + + """ + Args: + m3gnet (M3gnet): m3gnet object, graph CNN from m3gnet package + which determines energy, forces, stresses, and can do relaxation + etc. + candidate_data (pd.DataFrame): candidate data to predict on + seed_data (pd.DataFrame): seed data to fit on + n_query (int): number of candidates to select + """ + self.candidate_data = candidate_data + self.seed_data = seed_data + self.n_query = n_query if n_query else 1 + # self.cv_score = np.inf + if not m3gnet: + self.m3gnet = M3GNet(is_intensive=False) + else: + self.m3gnet = m3gnet + self.potential = Potential(model=self.m3gnet) + self.trainer = PotentialTrainer( + potential=self.potential, optimizer=tf.keras.optimizers.Adam(1e-3) + ) + super(M3GNetHypothesisAgent).__init__() + + def get_hypotheses(self, candidate_data, seed_data=None, retrain=False): + + """ + Get hypotheses method for downselecting candidates + + Args: + candidate_data (pd.DataFrame): candidate data to predict on and select from + seed_data (pd.DataFrame): seed data to fit on + retrain (bool): whether to retrain m3gnet on each run + + Returns: + (pd.DataFrame): selected candidates for experiment + + """ + if "target" in candidate_data.columns: + self.candidate_data = candidate_data.drop(columns=["target"], axis=1) + else: + self.candidate_data = candidate_data + self.seed_data = seed_data + X_seed = seed_data.drop(columns=["target"], axis=1, errors="ignore") + # y_seed = seed_data["target"] + if retrain and "calcs_reversed" in X_seed.columns: + self.train(X_seed) + relaxer = Relaxer(potential=self.trainer.potential) + t_pred = [] + for s in self.candidate_data["structure"]: + t = relaxer.relax(s)["trajectory"] + e = t.energies[-1].flatten()[0] + t_pred.append(e) + selected = np.argsort(-1.0 * np.array(t_pred))[: self.n_query] + return candidate_data.iloc[selected] + + def train(self, train_data, epochs=10, kwargs=None): + """ + Train the potential + + Args + train_data (pd.DataFrame): data for re-train, + same format as seed data + should contain "calcs_reversed" column + epochs (int): number of max epoch to train + kwargs (dict): parameters for trainer.train + Returns: + None + """ + if kwargs is None: + kwargs = {} + X_struct, Xe, Xf, Xs = reverse_calcs(train_data["calcs_reversed"]) + self.trainer.train( + graphs_or_structures=X_struct, + energies=Xe, + forces=Xf, + stresses=Xs, + epochs=epochs, + **kwargs + ) diff --git a/camd/agent/meta.py b/camd/agent/meta.py index d97b6fb6..fb5150ec 100644 --- a/camd/agent/meta.py +++ b/camd/agent/meta.py @@ -11,31 +11,31 @@ REGRESSOR_PARAMS = [ - { - "@class": ["sklearn.linear_model.LinearRegression"], - "fit_intercept": [True, False], - "normalize": [True, False] - }, - { - "@class": ["sklearn.ensemble.RandomForestRegressor"], - "n_estimators": [100], - "max_features": list(np.arange(0.05, 1.01, 0.05)), - "min_samples_split": list(range(2, 21)), - "min_samples_leaf": list(range(1, 21)), - "bootstrap": [True, False] - }, - { - "@class": ["sklearn.neural_network.MLPRegressor"], - "hidden_layer_sizes": [ - # I think there's a better way to support this, but need to think - (80, 50), - (84, 55), - (87, 60), - ], - "activation": ["identity", "logistic", "tanh", "relu"], - "learning_rate": ["constant", "invscaling", "adaptive"] - }, - ] + { + "@class": ["sklearn.linear_model.LinearRegression"], + "fit_intercept": [True, False], + "normalize": [True, False], + }, + { + "@class": ["sklearn.ensemble.RandomForestRegressor"], + "n_estimators": [100], + "max_features": list(np.arange(0.05, 1.01, 0.05)), + "min_samples_split": list(range(2, 21)), + "min_samples_leaf": list(range(1, 21)), + "bootstrap": [True, False], + }, + { + "@class": ["sklearn.neural_network.MLPRegressor"], + "hidden_layer_sizes": [ + # I think there's a better way to support this, but need to think + (80, 50), + (84, 55), + (87, 60), + ], + "activation": ["identity", "logistic", "tanh", "relu"], + "learning_rate": ["constant", "invscaling", "adaptive"], + }, +] AGENT_PARAMS = [ @@ -45,14 +45,14 @@ "n_members": list(range(2, 5)), "hull_distance": list(np.arange(0.05, 0.21, 0.05)), "training_fraction": [0.4, 0.5, 0.6], - "model": REGRESSOR_PARAMS + "model": REGRESSOR_PARAMS, }, { "@class": ["camd.agent.agents.AgentStabilityML5"], "n_query": [4, 6, 8], "hull_distance": [0.05, 0.1, 0.15, 0.2], "exploit_fraction": [0.4, 0.5, 0.6], - "model": REGRESSOR_PARAMS + "model": REGRESSOR_PARAMS, }, ] @@ -73,9 +73,11 @@ def convert_parameter_table_to_dataframe(parameter_table, fillna=np.nan): """ df = pd.DataFrame(parameter_table, dtype="int64") - df.index = ['-'.join([str(i) for i in row]) for row in np.array(df)] - df['agent'] = [parameter_table.hydrate_index(i, construct_object=True) - for i in range(len(parameter_table))] + df.index = ["-".join([str(i) for i in row]) for row in np.array(df)] + df["agent"] = [ + parameter_table.hydrate_index(i, construct_object=True) + for i in range(len(parameter_table)) + ] df.fillna(fillna) return df diff --git a/camd/agent/multi_fidelity.py b/camd/agent/multi_fidelity.py index 654db588..8a521132 100644 --- a/camd/agent/multi_fidelity.py +++ b/camd/agent/multi_fidelity.py @@ -34,7 +34,9 @@ def get_features_from_df(df, features): return feature_df -def process_data(candidate_data, seed_data, features, label, y_reshape=False, preprocessor=None): +def process_data( + candidate_data, seed_data, features, label, y_reshape=False, preprocessor=None +): """ Helper function that process data for ML model and returns np.ndarray of training features, training labels, @@ -60,10 +62,23 @@ class EpsilonGreedyMultiAgent(HypothesisAgent): A multi-fidelity agent that allocates the desired budget for high fidelity versus low fidelity candidate data, and acquires candidates in each fidelity via exploitation """ - def __init__(self, candidate_data=None, seed_data=None, features=None, fidelities=('theory_data', 'expt_data'), - target_prop=None, target_prop_val=None, preprocessor=StandardScaler(), model=None, - ranking_method='minimize', total_budget=None, highFi_query_frac=None, similarity_thres=300., - lowFi_per_highFi=1): + + def __init__( + self, + candidate_data=None, + seed_data=None, + features=None, + fidelities=("theory_data", "expt_data"), + target_prop=None, + target_prop_val=None, + preprocessor=StandardScaler(), + model=None, + ranking_method="minimize", + total_budget=None, + highFi_query_frac=None, + similarity_thres=300.0, + lowFi_per_highFi=1, + ): """ Args: candidate_data (df) Candidate search space for the campaign. @@ -116,7 +131,7 @@ def _calculate_similarity(self, comp, seed_comps): comp(pd.core.series): A specific composition represented by Magpie. seed_comps (df): Compostions in seed represented by Magpie. """ - l2_norm = np.linalg.norm(comp.values - seed_comps.values, axis=1) + l2_norm = np.linalg.norm(comp.values - seed_comps.values, axis=1) return l2_norm def _query_hypotheses(self, candidate_data, seed_data): @@ -124,8 +139,12 @@ def _query_hypotheses(self, candidate_data, seed_data): Query hypotheses given candidate and seed data via exploitation. """ # separate the candidate space into high and low fidelity candidates - high_fidelity_candidates = candidate_data.loc[candidate_data[self.fidelities[1]] == 1] - low_fidelity_candidates = candidate_data.loc[candidate_data[self.fidelities[0]] == 1] + high_fidelity_candidates = candidate_data.loc[ + candidate_data[self.fidelities[1]] == 1 + ] + low_fidelity_candidates = candidate_data.loc[ + candidate_data[self.fidelities[0]] == 1 + ] # edge cases: end campaign if there are no high fidelity candidate # use the entire query budget if there are only high fidelity candidate @@ -141,25 +160,40 @@ def _query_hypotheses(self, candidate_data, seed_data): # Query high fidelity candidates first highFi_budget = int(self.total_budget * self.highFi_query_frac) seed_data_fea = get_features_from_df(seed_data, self.features) - highFi_cands_fea = get_features_from_df(high_fidelity_candidates, self.features) + highFi_cands_fea = get_features_from_df( + high_fidelity_candidates, self.features + ) for idx, cand_fea in highFi_cands_fea.iterrows(): if len(selected_hypotheses) < highFi_budget: normdiff = self._calculate_similarity(cand_fea, seed_data_fea) if len(normdiff[(normdiff <= self.similarity_thres)]) >= 1: - selected_hypotheses = selected_hypotheses.append(high_fidelity_candidates.loc[idx]) + selected_hypotheses = selected_hypotheses.append( + high_fidelity_candidates.loc[idx] + ) # query low fidelity candidate for remaining budget remained_highFi_cands_fea = highFi_cands_fea.drop(selected_hypotheses.index) lowFi_candidates_copy = low_fidelity_candidates.copy() for idx, cand_fea in remained_highFi_cands_fea.iterrows(): - if (len(selected_hypotheses) < self.total_budget) & (len(lowFi_candidates_copy) != 0): - lowFi_cands_fea = get_features_from_df(lowFi_candidates_copy, self.features) - lowFi_candidates_copy['normdiff'] = self._calculate_similarity(cand_fea, lowFi_cands_fea) - lowFi_candidates_copy = lowFi_candidates_copy.sort_values('normdiff') - selected_hypotheses = selected_hypotheses.append(lowFi_candidates_copy.head(self.lowFi_per_highFi)) + if (len(selected_hypotheses) < self.total_budget) & ( + len(lowFi_candidates_copy) != 0 + ): + lowFi_cands_fea = get_features_from_df( + lowFi_candidates_copy, self.features + ) + lowFi_candidates_copy["normdiff"] = self._calculate_similarity( + cand_fea, lowFi_cands_fea + ) + lowFi_candidates_copy = lowFi_candidates_copy.sort_values( + "normdiff" + ) + selected_hypotheses = selected_hypotheses.append( + lowFi_candidates_copy.head(self.lowFi_per_highFi) + ) lowFi_candidates_copy = lowFi_candidates_copy.drop( - lowFi_candidates_copy.head(self.lowFi_per_highFi).index) + lowFi_candidates_copy.head(self.lowFi_per_highFi).index + ) return selected_hypotheses def get_hypotheses(self, candidate_data, seed_data): @@ -176,21 +210,32 @@ def get_hypotheses(self, candidate_data, seed_data): """ features_columns = self.features + list(self.fidelities) X_train, y_train, X_test, y_test = process_data( - candidate_data, seed_data, features_columns, self.target_prop, preprocessor=self.preprocessor) + candidate_data, + seed_data, + features_columns, + self.target_prop, + preprocessor=self.preprocessor, + ) self.model.fit(X_train, y_train) y_pred = self.model.predict(X_test) # Make a copy of the candidate data so the original one # does not get modified during hypotheses generation candidate_data_copy = candidate_data.copy() - candidate_data_copy['distance_to_ideal'] = np.abs(self.target_prop_val - y_pred) - - if self.ranking_method == 'minimize': - candidate_data_copy = candidate_data_copy.sort_values(by=['distance_to_ideal']) - elif self.ranking_method == 'ascending': - candidate_data_copy = candidate_data_copy.sort_values(by=y_pred, ascending=True) - elif self.ranking_method == 'descending': - candidate_data_copy = candidate_data_copy.sort_values(by=y_pred, ascending=False) + candidate_data_copy["distance_to_ideal"] = np.abs(self.target_prop_val - y_pred) + + if self.ranking_method == "minimize": + candidate_data_copy = candidate_data_copy.sort_values( + by=["distance_to_ideal"] + ) + elif self.ranking_method == "ascending": + candidate_data_copy = candidate_data_copy.sort_values( + by=y_pred, ascending=True + ) + elif self.ranking_method == "descending": + candidate_data_copy = candidate_data_copy.sort_values( + by=y_pred, ascending=False + ) hypotheses = self._query_hypotheses(candidate_data_copy, seed_data) return hypotheses @@ -206,9 +251,23 @@ class GPMultiAgent(HypothesisAgent): prioritizing exploitation primarily with high-fidelity experimental measurements, offloading exploratory (higher risk) acquisitions first to lower-fidelity computations. """ - def __init__(self, candidate_data=None, seed_data=None, chemsys_col='reduced_formula', features=None, - fidelities=('theory_data', 'expt_data'), target_prop=None, target_prop_val=1.8, total_budget=None, - preprocessor=StandardScaler(), gp_max_iter=200, alpha=1.0, rank_thres=10, unc_percentile=5): + + def __init__( + self, + candidate_data=None, + seed_data=None, + chemsys_col="reduced_formula", + features=None, + fidelities=("theory_data", "expt_data"), + target_prop=None, + target_prop_val=1.8, + total_budget=None, + preprocessor=StandardScaler(), + gp_max_iter=200, + alpha=1.0, + rank_thres=10, + unc_percentile=5, + ): """ Args: candidate_data (df) Candidate search space for the campaign. @@ -251,28 +310,38 @@ def _halluciate_lower_fidelity(self, seed_data, candidate_data, low_fidelity_dat new_candidate_data = candidate_data.copy() low_fidelity = low_fidelity_data.copy() - low_fidelity[self.target_prop] = low_fidelity_data['y_pred'] - low_fidelity = low_fidelity.drop(columns=['y_pred']) + low_fidelity[self.target_prop] = low_fidelity_data["y_pred"] + low_fidelity = low_fidelity.drop(columns=["y_pred"]) new_seed_data = pd.concat([seed_data, low_fidelity]) new_candidate_data = new_candidate_data.drop(low_fidelity.index) pred_candidate_data = self._train_and_predict(new_candidate_data, new_seed_data) return pred_candidate_data - def _get_rank_after_hallucination(self, seed_data, candidate_data, orig_idx, low_fidelity): - halluciated_candidates = self._halluciate_lower_fidelity(seed_data, candidate_data, low_fidelity) - halluciated_candidates = halluciated_candidates.loc[halluciated_candidates[self.fidelities[1]] == 1] - halluciated_candidates = halluciated_candidates.sort_values('pred_lcb') + def _get_rank_after_hallucination( + self, seed_data, candidate_data, orig_idx, low_fidelity + ): + halluciated_candidates = self._halluciate_lower_fidelity( + seed_data, candidate_data, low_fidelity + ) + halluciated_candidates = halluciated_candidates.loc[ + halluciated_candidates[self.fidelities[1]] == 1 + ] + halluciated_candidates = halluciated_candidates.sort_values("pred_lcb") rank_after_hallucination = halluciated_candidates.index.get_loc(orig_idx) return rank_after_hallucination def _train_and_predict(self, candidate_data, seed_data): features_columns = self.features + list(self.fidelities) X_train, y_train, X_test, y_test = process_data( - candidate_data, seed_data, features_columns, - self.target_prop, y_reshape=True, preprocessor=self.preprocessor + candidate_data, + seed_data, + features_columns, + self.target_prop, + y_reshape=True, + preprocessor=self.preprocessor, ) gp = GPy.models.GPRegression(X_train, y_train) - gp.optimize('bfgs', max_iters=self.gp_max_iter) + gp.optimize("bfgs", max_iters=self.gp_max_iter) y_pred, var = gp.predict(X_test) # Make a copy of the candidate data so the original one @@ -280,14 +349,16 @@ def _train_and_predict(self, candidate_data, seed_data): candidate_data_copy = candidate_data.copy() dist_to_ideal = np.abs(self.target_prop_val - y_pred) pred_lcb = dist_to_ideal - self.alpha * var**0.5 - candidate_data_copy['pred_lcb'] = pred_lcb - candidate_data_copy['pred_unc'] = var**0.5 - candidate_data_copy['y_pred'] = y_pred + candidate_data_copy["pred_lcb"] = pred_lcb + candidate_data_copy["pred_unc"] = var**0.5 + candidate_data_copy["y_pred"] = y_pred return candidate_data_copy def _query_hypotheses(self, candidate_data, seed_data): - high_fidelity_candidates = candidate_data.loc[candidate_data[self.fidelities[1]] == 1] - high_fidelity_candidates = high_fidelity_candidates.sort_values('pred_lcb') + high_fidelity_candidates = candidate_data.loc[ + candidate_data[self.fidelities[1]] == 1 + ] + high_fidelity_candidates = high_fidelity_candidates.sort_values("pred_lcb") # edge case: top the campaign if there are no high fidelity candidates if len(high_fidelity_candidates) == 0: @@ -295,14 +366,18 @@ def _query_hypotheses(self, candidate_data, seed_data): else: selected_hypotheses = pd.DataFrame(columns=candidate_data.columns) - unc_thres = np.percentile(np.array(high_fidelity_candidates.pred_unc), self.unc_percentile) + unc_thres = np.percentile( + np.array(high_fidelity_candidates.pred_unc), self.unc_percentile + ) # query hypothesis for idx, candidate in high_fidelity_candidates.iterrows(): if len(selected_hypotheses) < self.total_budget: chemsys = candidate[self.chemsys_col] - low_fidelity = candidate_data.loc[(candidate_data[self.chemsys_col] == chemsys) & - (candidate_data[self.fidelities[0]] == 1)] + low_fidelity = candidate_data.loc[ + (candidate_data[self.chemsys_col] == chemsys) + & (candidate_data[self.fidelities[0]] == 1) + ] # exploit if uncertainty is low or the low fidelity data is not available if (candidate.pred_unc <= unc_thres) or (len(low_fidelity) == 0): @@ -310,13 +385,17 @@ def _query_hypotheses(self, candidate_data, seed_data): # explore else: orig_rank = high_fidelity_candidates.index.get_loc(idx) - new_rank = self._get_rank_after_hallucination(seed_data, candidate_data, idx, low_fidelity) + new_rank = self._get_rank_after_hallucination( + seed_data, candidate_data, idx, low_fidelity + ) delta_rank = new_rank - orig_rank if delta_rank <= self.rank_thres: selected_hypotheses = selected_hypotheses.append(candidate) else: - selected_hypotheses = selected_hypotheses.append(low_fidelity) + selected_hypotheses = selected_hypotheses.append( + low_fidelity + ) return selected_hypotheses def get_hypotheses(self, candidate_data, seed_data): diff --git a/camd/agent/stability.py b/camd/agent/stability.py index 9f03c88c..34da51cd 100644 --- a/camd/agent/stability.py +++ b/camd/agent/stability.py @@ -15,7 +15,7 @@ from qmpy.analysis.thermodynamics.phase import Phase, PhaseData from camd.analysis import PhaseSpaceAL, ELEMENTS from camd.agent.base import HypothesisAgent, QBC -from camd.utils.data import filter_dataframe_by_composition +from camd.utils.data import filter_dataframe_by_composition, get_default_featurizer from pymatgen.core.composition import Composition from sklearn.gaussian_process.kernels import RBF, ConstantKernel @@ -45,6 +45,7 @@ def __init__( seed_data=None, n_query=1, hull_distance=0.0, + feature_labels=None, parallel=cpu_count(), ): """ @@ -54,6 +55,7 @@ def __init__( n_query (int): number of hypotheses to generate hull_distance (float): hull distance as a criteria for which to deem a given material as "stable" + feature_labels ([str]): list of columns to filter for features in ML parallel (bool, int): whether to use multiprocessing for phase stability analysis, if an int, sets the n_jobs parameter as well. If a bool, sets n_jobs to cpu_count() @@ -66,6 +68,10 @@ def __init__( self.hull_distance = hull_distance self.pd = None self.parallel = parallel + if feature_labels is not None: + self.feature_labels = feature_labels + else: + self.feature_labels = get_default_featurizer().feature_labels() # These might be able to go into the base class self.cv_score = np.nan @@ -84,9 +90,8 @@ def get_pd(self, chemsys=None): self.pd = PhaseData() # Filter seed data by relevant chemsys if chemsys: - total_comp = Composition(chemsys.replace('-', '')) - filtered = filter_dataframe_by_composition( - self.seed_data, total_comp) + total_comp = Composition(chemsys.replace("-", "")) + filtered = filter_dataframe_by_composition(self.seed_data, total_comp) else: filtered = self.seed_data @@ -123,24 +128,16 @@ def update_data(self, candidate_data=None, seed_data=None): # Note: In the drop command, we're ignoring errors for # brevity. We should watch this, because we may not # drop everything we intend to. - drop_columns = [ - "Composition", - "N_species", - "delta_e", - "pred_delta_e", - "pred_stability", - "stability", - "is_stable", - "structure", - ] if candidate_data is not None: self.candidate_data = candidate_data - X_cand = candidate_data.drop(drop_columns, axis=1, errors="ignore") + X_cand = self.get_features(self.candidate_data) else: X_cand = None if seed_data is not None: - self.seed_data = seed_data - X_seed = self.seed_data.drop(drop_columns, axis=1, errors="ignore") + # Filter seed data with No delta_e, indicates a failure + # TODO: probably worth considering putting this somewhere else + self.seed_data = seed_data.dropna(subset=["delta_e"]) + X_seed = self.get_features(self.seed_data) y_seed = self.seed_data["delta_e"] else: X_seed, y_seed = None, None @@ -183,7 +180,7 @@ def update_candidate_stabilities(self, formation_energies, sort=True, floor=-6.0 ] # Refresh and copy seed PD filtered by candidates - all_comp = self.candidate_data['Composition'].sum() + all_comp = self.candidate_data["Composition"].sum() pd_ml = deepcopy(self.get_pd(all_comp)) pd_ml.add_phases(candidate_phases) space_ml = PhaseSpaceAL(bounds=ELEMENTS, data=pd_ml) @@ -199,11 +196,21 @@ def update_candidate_stabilities(self, formation_energies, sort=True, floor=-6.0 return self.candidate_data + def get_features(self, dataframe): + """ + Gets features of a dataframe + + Returns: + (pd.DataFrame): dataframe filtered to include only features + """ + return dataframe[self.feature_labels] + class QBCStabilityAgent(StabilityAgent): """ Agent which uses QBC to determine optimal hypotheses """ + def __init__( self, candidate_data=None, @@ -215,6 +222,7 @@ def __init__( training_fraction=0.5, model=None, n_members=10, + feature_labels=None, ): """ Args: @@ -239,13 +247,16 @@ def __init__( n_query=n_query, hull_distance=hull_distance, parallel=parallel, + feature_labels=feature_labels, ) self.alpha = alpha self.model = model self.n_members = n_members self.qbc = QBC( - n_members=n_members, training_fraction=training_fraction, model=model, + n_members=n_members, + training_fraction=training_fraction, + model=model, ) def get_hypotheses(self, candidate_data, seed_data=None, retrain_committee=True): @@ -300,6 +311,7 @@ def __init__( parallel=cpu_count(), model=None, exploit_fraction=0.5, + feature_labels=None, ): """ Args: @@ -320,6 +332,7 @@ def __init__( n_query=n_query, hull_distance=hull_distance, parallel=parallel, + feature_labels=feature_labels, ) self.model = model or LinearRegression() @@ -346,12 +359,12 @@ def get_hypotheses(self, candidate_data, seed_data=None): cv_score = cross_val_score( pipeline, X_seed, - self.seed_data["delta_e"], + y_seed, cv=KFold(5, shuffle=True), scoring="neg_mean_absolute_error", ) self.cv_score = np.mean(cv_score) * -1 - pipeline.fit(X_seed, self.seed_data["delta_e"]) + pipeline.fit(X_seed, y_seed) expected = pipeline.predict(X_cand) @@ -390,6 +403,7 @@ def __init__( hull_distance=0.0, parallel=cpu_count(), alpha=0.5, + feature_labels=None, ): """ Args: @@ -409,6 +423,7 @@ def __init__( n_query=n_query, hull_distance=hull_distance, parallel=parallel, + feature_labels=feature_labels, ) self.multiprocessing = parallel self.alpha = alpha @@ -491,6 +506,7 @@ def __init__( n_estimators=8, max_samples=5000, bootstrap=False, + feature_labels=None, ): """ Args: @@ -510,6 +526,7 @@ def __init__( n_query=n_query, hull_distance=hull_distance, parallel=parallel, + feature_labels=feature_labels, ) self.alpha = alpha @@ -616,6 +633,7 @@ def __init__( exploit_fraction=0.5, diversify=False, dynamic_alpha=False, + feature_labels=None, ): """ Args: @@ -649,6 +667,7 @@ def __init__( n_query=n_query, hull_distance=hull_distance, parallel=parallel, + feature_labels=feature_labels, ) self.model = model self.exploit_fraction = exploit_fraction @@ -673,15 +692,14 @@ def get_hypotheses(self, candidate_data, seed_data=None): """ X_cand, X_seed, y_seed = self.update_data(candidate_data, seed_data) - steps = [("scaler", StandardScaler()), ("ML", self.model)] - pipeline = Pipeline(steps) - adaboost = AdaBoostRegressor( - base_estimator=pipeline, n_estimators=self.n_estimators + base_estimator=self.model, n_estimators=self.n_estimators ) + steps = [("scaler", StandardScaler()), ("ML", adaboost)] + pipeline = Pipeline(steps) cv_score = cross_val_score( - adaboost, + pipeline, X_seed, y_seed, cv=KFold(3, shuffle=True), @@ -697,7 +715,7 @@ def get_hypotheses(self, candidate_data, seed_data=None): overall_adaboost = AdaBoostRegressor( base_estimator=self.model, n_estimators=self.n_estimators ) - overall_adaboost.fit(X_scaled, self.seed_data["delta_e"]) + overall_adaboost.fit(X_scaled, y_seed) X_cand = scaler.transform(X_cand) expected = overall_adaboost.predict(X_cand) @@ -724,7 +742,10 @@ def get_hypotheses(self, candidate_data, seed_data=None): n_exploitation = int(self.n_query * self.exploit_fraction) if self.diversify: to_compute = diverse_quant( - within_hull.index.tolist(), n_exploitation, self.candidate_data + within_hull.index.tolist(), + n_exploitation, + self.candidate_data, + feature_filter=self.feature_labels, ) else: to_compute = within_hull.head(n_exploitation).index.tolist() @@ -757,7 +778,7 @@ def _get_unc_ada(ada, X): return np.array(stds) -def diverse_quant(points, target_length, df, quantiles=None): +def diverse_quant(points, target_length, df, quantiles=None, feature_filter=None): """ Diversify a sublist by eliminating entries based on comparisons with quantiles threshold and Euclidean distance. @@ -807,20 +828,16 @@ def diverse_quant(points, target_length, df, quantiles=None): _df = df.sample(6000) else: _df = df - drop_columns = [ - "Composition", - "N_species", - "delta_e", - "pred_delta_e", - "pred_stability", - "structure" - ] + if feature_filter is not None: + _df = _df[feature_filter] scaler = StandardScaler() - X = scaler.fit_transform(_df.drop(drop_columns, axis=1, errors="ignore")) + X = scaler.fit_transform(_df) flatD = pairwise_distances(X).flatten() _df2 = df.loc[points] - X = scaler.transform(_df2.drop(drop_columns, axis=1, errors="ignore")) + if feature_filter is not None: + _df2 = _df2[feature_filter] + X = scaler.transform(_df2) D = pairwise_distances(X) remove_len = len(points) - target_length diff --git a/camd/agent/tests/test_agents.py b/camd/agent/tests/test_agents.py index cb92afe9..9f46d44c 100644 --- a/camd/agent/tests/test_agents.py +++ b/camd/agent/tests/test_agents.py @@ -2,7 +2,7 @@ import pandas import numpy as np -from camd.agent.generic import LinearAgent +from camd.agent.generic import RegressorAgent from camd.agent.base import SequentialAgent @@ -23,14 +23,17 @@ def test_linear_agent(self): Verify that linear agent can fit a linear function and recover the minimizing argument to new data. """ - agent = LinearAgent(n_query=10) + agent = RegressorAgent.from_linear(n_query=10) predictions = agent.get_hypotheses( seed_data=self.seed_data, candidate_data=self.candidate_data ) self.assertEqual(predictions["domain"][9], 10) def test_sequential_agent(self): - agents = [LinearAgent(n_query=10), LinearAgent(n_query=5)] + agents = [ + RegressorAgent.from_linear(n_query=10), + RegressorAgent.from_linear(n_query=5) + ] agent = SequentialAgent(agents=agents) hypotheses = agent.get_hypotheses(self.candidate_data, self.seed_data) self.assertEqual(len(hypotheses), 5) diff --git a/camd/agent/tests/test_m3gnet.py b/camd/agent/tests/test_m3gnet.py new file mode 100644 index 00000000..57841a13 --- /dev/null +++ b/camd/agent/tests/test_m3gnet.py @@ -0,0 +1,93 @@ +import unittest +import os +import numpy as np +import pandas as pd +from sklearn.model_selection import train_test_split +from m3gnet.models import M3GNet + +from camd import CAMD_TEST_FILES +from camd.agent.m3gnet import M3GNetHypothesisAgent, M3GNetStabilityAgent, reverse_calcs + + +class HypothesisAgentsTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + test_data = pd.read_pickle(os.path.join(CAMD_TEST_FILES, "test_m3gnet.pickle")) + cls.test_data = test_data + cls.seed_data, cls.candidate_data = train_test_split( + test_data, train_size=0.2, random_state=42 + ) + model = M3GNet(is_intensive=False) + cls.agent = M3GNetHypothesisAgent(m3gnet=model) + + def test_reverse_calcs(self): + X_struct, Xe, Xf, Xs = reverse_calcs(self.test_data["calcs_reversed"].iloc[:2]) + self.assertEqual(len(X_struct), 90) + self.assertTrue(Xe[0] - -3.5534271704166667 <= 1e-4) + self.assertTrue( + ( + np.array(Xf[0]) + - np.array( + [ + [-0.03578407, -0.00176274, 0.03967705], + [0.06499199, -0.02277058, 0.03514361], + [-0.06499199, 0.02277058, -0.03514361], + [0.03578407, 0.00176274, -0.03967705], + [-0.03578407, 0.00176274, 0.03967705], + [0.06499199, 0.02277058, 0.03514361], + [-0.06499199, -0.02277058, -0.03514361], + [0.03578407, -0.00176274, -0.03967705], + [0.0, 0.02825717, 0.0], + [0.0, 0.02280701, 0.0], + [-0.02465965, -0.00049394, -0.04887495], + [-0.02165852, 0.02248651, -0.04979937], + [-0.01744417, -0.02081133, 0.03051514], + [0.01744417, 0.02081133, -0.03051514], + [0.02165852, -0.02248651, 0.04979937], + [0.02465965, 0.00049394, 0.04887495], + [0.0, -0.02280701, 0.0], + [0.0, -0.02825717, 0.0], + [-0.02465965, 0.00049394, -0.04887495], + [-0.02165852, -0.02248651, -0.04979937], + [-0.01744417, 0.02081133, 0.03051514], + [0.01744417, -0.02081133, -0.03051514], + [0.02165852, 0.02248651, 0.04979937], + [0.02465965, -0.00049394, 0.04887495], + ] + ) + ).max() + <= 1e-4 + ) + + # def test_train(self): + # X_struct, Xe, Xf, Xs = self.agent.reverse_calcs(self.test_data['calcs_reversed'].iloc[:2]) + # self.agent.train(self.seed_data, epochs=1) + + def test_hypotheses(self): + candidate = self.agent.get_hypotheses( + candidate_data=self.candidate_data, seed_data=self.seed_data, retrain=False + ) + self.assertTrue(list(candidate.columns) == list(self.candidate_data.columns)) + + +class StabilityAgentsTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + test_data = pd.read_pickle(os.path.join(CAMD_TEST_FILES, "test_m3gnet.pickle")) + print(test_data.columns) + cls.test_data = test_data + cls.seed_data, cls.candidate_data = train_test_split( + test_data, train_size=0.2, random_state=42 + ) + model = M3GNet(is_intensive=False) + cls.agent = M3GNetStabilityAgent(m3gnet=model) + + def test_m3gnet_agent(self): + agent = M3GNetStabilityAgent() + hypotheses = agent.get_hypotheses( + candidate_data=self.candidate_data, seed_data=self.seed_data + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/camd/agent/tests/test_stability.py b/camd/agent/tests/test_stability.py index 97a48ec3..ea63acb9 100644 --- a/camd/agent/tests/test_stability.py +++ b/camd/agent/tests/test_stability.py @@ -1,17 +1,19 @@ import unittest -import os -import pandas as pd from sklearn.model_selection import train_test_split -from camd import CAMD_TEST_FILES -from camd.agent.stability import QBCStabilityAgent, AgentStabilityML5, \ - GaussianProcessStabilityAgent, BaggedGaussianProcessStabilityAgent, \ - AgentStabilityAdaBoost +from camd.agent.stability import ( + QBCStabilityAgent, + AgentStabilityML5, + GaussianProcessStabilityAgent, + BaggedGaussianProcessStabilityAgent, + AgentStabilityAdaBoost, +) +from camd.utils.data import load_default_atf_data class StabilityAgentsTest(unittest.TestCase): @classmethod def setUpClass(cls): - test_data = pd.read_csv(os.path.join(CAMD_TEST_FILES, 'test_df.csv')) + test_data = load_default_atf_data().sample(200) cls.seed_data, cls.candidate_data = train_test_split( test_data, train_size=0.2, random_state=42 ) @@ -19,28 +21,33 @@ def setUpClass(cls): def test_qbc_stability_agent(self): agent = QBCStabilityAgent() hypotheses = agent.get_hypotheses( - candidate_data=self.candidate_data, seed_data=self.seed_data) + candidate_data=self.candidate_data, seed_data=self.seed_data + ) def test_ml_agent(self): agent = AgentStabilityML5() hypotheses = agent.get_hypotheses( - candidate_data=self.candidate_data, seed_data=self.seed_data) + candidate_data=self.candidate_data, seed_data=self.seed_data + ) def test_gp_stability_agent(self): agent = GaussianProcessStabilityAgent() hypotheses = agent.get_hypotheses( - candidate_data=self.candidate_data, seed_data=self.seed_data) + candidate_data=self.candidate_data, seed_data=self.seed_data + ) def test_bagged_gp_stability_agent(self): - agent = BaggedGaussianProcessStabilityAgent(max_samples=100) + agent = BaggedGaussianProcessStabilityAgent(max_samples=30) hypotheses = agent.get_hypotheses( - candidate_data=self.candidate_data, seed_data=self.seed_data) + candidate_data=self.candidate_data, seed_data=self.seed_data + ) def test_agent_stability_adaboost(self): agent = AgentStabilityAdaBoost() hypotheses = agent.get_hypotheses( - candidate_data=self.candidate_data, seed_data=self.seed_data) + candidate_data=self.candidate_data, seed_data=self.seed_data + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/camd/analysis.py b/camd/analysis.py index 1f6916d2..56546834 100644 --- a/camd/analysis.py +++ b/camd/analysis.py @@ -8,7 +8,6 @@ import abc import warnings import json -import pickle import os import numpy as np import itertools @@ -27,20 +26,17 @@ ) from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core.structure import Structure -from camd.utils.data import cache_matrio_data, \ - filter_dataframe_by_composition, ELEMENTS +from camd.utils.data import cache_matrio_data, filter_dataframe_by_composition, ELEMENTS from camd import CAMD_CACHE -from monty.os import cd -from monty.serialization import loadfn class AnalyzerBase(abc.ABC): """ The AnalyzerBase class defines the contract - for post-processing experiments and creating - a new seed_data object for the agent. - + for post-processing experiments and reporting + on campaign state """ + def __init__(self): """ Initialize an Analyzer. Should contain all necessary @@ -49,31 +45,18 @@ def __init__(self): self._initial_seed_indices = [] @abc.abstractmethod - def analyze(self, new_experimental_results, seed_data): + def analyze(self, campaign): """ Analyze method, performs some operation on new experimental results in order to place them in the context of the seed data Args: - new_experimental_results (DataFrame): new data - to be added to the seed - seed_data (DataFrame): current seed data from - campaign + campaign (Campaign): Campaign to be analyzed Returns: - (DataFrame): dataframe corresponding to the summary - of the previous set of experiments - (DataFrame): dataframe corresponding to the new - seed data - """ - - @property - def initial_seed_indices(self): + (DataFrame): dataframe corresponding to the analysis summary """ - Returns: The list of indices contained in the initial seed. Intended to be set by the Campaign. - """ - return self._initial_seed_indices class GenericMaxAnalyzer(AnalyzerBase): @@ -81,35 +64,33 @@ class GenericMaxAnalyzer(AnalyzerBase): Generic analyzer that checks new data with a target column against a threshold to be crossed. """ - def __init__(self, threshold=0): + def __init__(self, threshold=0, target="target"): """ Args: threshold (int or float): The target values of new acquisitions are compared to find if they are above this threshold, to keep track of the performance in sequential framework. """ self.threshold = threshold + self.target = target self.score = [] self.best_examples = [] super(GenericMaxAnalyzer, self).__init__() - def analyze(self, new_experimental_results, seed_data): + def analyze(self, campaign, finalize=False): """ Analyzes the results of an experiment by finding the best examples and their scores Args: - new_experimental_results (pandas.DataFrame): new experimental - results to be analyzed - seed_data (pandas.DataFrame): past data to include in analysis Returns: (pandas.DataFrame): one-row dataframe summarizing past results (pandas.DataFrame): new seed data to be passed to agent """ - new_seed = seed_data.append(new_experimental_results) - self.score.append(np.sum(new_seed["target"] > self.threshold)) - self.best_examples.append(new_seed.loc[new_seed.target.idxmax()]) + new_seed = campaign.seed_data.append(campaign.experiment.get_results()) + self.score.append(np.sum(new_seed[self.target] > self.threshold)) + self.best_examples.append(new_seed.loc[new_seed[self.target].idxmax()]) new_discovery = ( [self.score[-1] - self.score[-2]] if len(self.score) > 1 @@ -122,9 +103,11 @@ def analyze(self, new_experimental_results, seed_data): "new_discovery": new_discovery, } ) - return summary, new_seed + return summary +# TODO: this isn't yet a true analyzer - still trying to figure out +# how to implement this consistently class AnalyzeStructures(AnalyzerBase): """ This class tests if a list of structures are unique. Typically @@ -153,7 +136,7 @@ def __init__(self, structures=None, hull_distance=None): self.hull_distance = hull_distance super(AnalyzeStructures, self).__init__() - def analyze( + def _analyze_structures( self, structures=None, structure_ids=None, against_icsd=False, energies=None ): """ @@ -257,6 +240,19 @@ def analyze( # list provided. return self.structure_is_unique + def analyze(self, campaign): + """ + Analysis method for structures, not yet implemented + + Args: + campaign (Campaign): campaign object to be analyzed + + Returns: + (pd.DataFrame): summary of results from structure analysis + + """ + raise NotImplementedError + def analyze_vaspqmpy_jobs(self, jobs, against_icsd=False, use_energies=False): """ Useful for analysis integrated as part of a campaign itself @@ -272,24 +268,42 @@ def analyze_vaspqmpy_jobs(self, jobs, against_icsd=False, use_energies=False): self.energies = [] for j, r in jobs.iterrows(): if r["status"] == "SUCCEEDED": - final_structure = r['result'].final_structure - self.structures.append(final_structure) - self.structure_ids.append(j) - self.energies.append(r['result'].final_energy / len(final_structure)) + # This is a switch for OQMD vs. MP + if "output" in r: + final_structure = r["output"]["structure"] + self.structures.append(final_structure) + self.energies.append(r["output"]["energy_per_atom"]) + self.structure_ids.append(j) + else: + final_structure = r["result"].final_structure + self.structures.append(final_structure) + self.structure_ids.append(j) + self.energies.append( + r["result"].final_energy / len(final_structure) + ) if use_energies: - return self.analyze( + return self._analyze_structures( self.structures, self.structure_ids, against_icsd, self.energies ) else: - return self.analyze(self.structures, self.structure_ids, against_icsd) + return self._analyze_structures( + self.structures, self.structure_ids, against_icsd + ) class StabilityAnalyzer(AnalyzerBase): """ Analyzer object for stability campaigns """ - def __init__(self, hull_distance=0.05, parallel=cpu_count(), entire_space=False, - plot=True): + + def __init__( + self, + hull_distance=0.05, + parallel=cpu_count(), + entire_space=False, + plot=True, + initial_seed_indices=None, + ): """ The Stability Analyzer is intended to analyze DFT-result data in the context of a global compositional seed in @@ -313,6 +327,7 @@ def __init__(self, hull_distance=0.05, parallel=cpu_count(), entire_space=False, self.entire_space = entire_space self.space = None self.plot = plot + self.initial_seed_indices = initial_seed_indices super(StabilityAnalyzer, self).__init__() @staticmethod @@ -343,25 +358,57 @@ def get_phase_space(dataframe): space = PhaseSpaceAL(bounds=ELEMENTS, data=pd) return space - def analyze(self, new_experimental_results, seed_data): + @staticmethod + def add_stability(df, hull_distance=0.2, parallel=cpu_count()): + """ + Adds stability to a dataframe + + Args: + df (pd.DataFrame): dataframe of entries with `delta_e` field + hull_distance (float): distance to the hull to classify as stable + parallel (int): number of parallel processes to use + + Returns: + (pd.DataFrame): dataframe of stabilities and is_stable + + """ + space = StabilityAnalyzer.get_phase_space(df) + new_phases = [p for p in space.phases if p.description in df.index] + + space.compute_stabilities(phases=new_phases, ncpus=parallel) + + # Compute new stabilities and update new seed, note that pandas will complain + # if the index is not explicit due to multiple types (e. g. ints for OQMD + # and strs for prototypes) + new_data = pd.DataFrame( + {"stability": [phase.stability for phase in new_phases]}, + index=[phase.description for phase in new_phases], + ) + new_data["is_stable"] = new_data["stability"] <= hull_distance + return new_data + + def analyze(self, campaign, finalize=False): """ Args: - new_experimental_results (DataFrame): new experimental - results to be added to the seed - seed_data (DataFrame): seed to be augmented via - the new_experimental_results + campaign (Campaign): CAMD campaign + finalize (bool): whether or not analysis is final Returns: (DataFrame): summary of the process, i. e. of the increment or experimental results - (DataFrame): augmented seed data, i. e. "new" - seed data according to the experimental results """ + # On first run get initial seed indices + if self.initial_seed_indices is None: + self.initial_seed_indices = campaign.seed_data.index # Check for new results - new_comp = new_experimental_results['Composition'].sum() - new_experimental_results = new_experimental_results.dropna(subset=['delta_e']) - new_seed = seed_data.append(new_experimental_results) + new_experimental_results = campaign.experiment.get_results() + new_comp = new_experimental_results["Composition"].sum() + new_experimental_results = new_experimental_results.dropna(subset=["delta_e"]) + if not finalize: + new_seed = campaign.seed_data.append(new_experimental_results) + else: + new_seed = campaign.seed_data # Aggregate seed_data and new experimental results include_columns = ["Composition", "delta_e"] @@ -373,21 +420,11 @@ def analyze(self, new_experimental_results, seed_data): # less efficient if larger spaces are without specified chemistry. filtered = filter_dataframe_by_composition(filtered, new_comp) - space = self.get_phase_space(filtered) - new_phases = [p for p in space.phases if p.description in filtered.index] - - space.compute_stabilities(phases=new_phases, ncpus=self.parallel) - - # Compute new stabilities and update new seed, note that pandas will complain - # if the index is not explicit due to multiple types (e. g. ints for OQMD - # and strs for prototypes) - new_data = pd.DataFrame( - {"stability": [phase.stability for phase in new_phases]}, - index=[phase.description for phase in new_phases] + new_data = self.add_stability( + filtered, hull_distance=self.hull_distance, parallel=self.parallel ) - new_data["is_stable"] = new_data["stability"] <= self.hull_distance - # TODO: This is implicitly adding "stability", and "is_stable" columns + # # TODO: This is implicitly adding "stability", and "is_stable" columns # but could be handled more gracefully if "stability" not in new_seed.columns: new_seed = pd.concat([new_seed, new_data], axis=1, sort=False) @@ -396,7 +433,9 @@ def analyze(self, new_experimental_results, seed_data): # Write hull figure to disk if self.plot: - self.plot_hull(filtered, new_experimental_results.index, filename="hull.png") + self.plot_hull( + filtered, new_experimental_results.index, filename="hull.png" + ) # Compute summary metrics summary = self.get_summary( @@ -404,12 +443,7 @@ def analyze(self, new_experimental_results, seed_data): new_experimental_results.index, initial_seed_indices=self.initial_seed_indices, ) - # Drop excess columns from experiment - new_seed = new_seed.drop([ - 'path', 'status', 'start_time', 'jobId', 'jobName', 'jobArn', - 'result', 'error', 'elapsed_time' - ], axis="columns", errors="ignore") - return summary, new_seed + return summary @staticmethod def get_summary(new_seed, new_ids, initial_seed_indices=None): @@ -437,7 +471,9 @@ def get_summary(new_seed, new_ids, initial_seed_indices=None): # of experiments, so can be difficult to determine marginal # value of a given experimental run processed_new = new_seed.loc[new_ids] - initial_seed_indices = initial_seed_indices if initial_seed_indices else [] + initial_seed_indices = ( + initial_seed_indices if initial_seed_indices is not None else [] + ) total_discovery = new_seed.loc[ ~new_seed.index.isin(initial_seed_indices) ].is_stable.sum() @@ -465,12 +501,12 @@ def plot_hull(self, df, new_result_ids, filename=None, finalize=False): (pyplot): plotter instance """ # Generate all entries - total_comp = Composition(df['Composition'].sum()) + total_comp = Composition(df["Composition"].sum()) if len(total_comp) > 4: warnings.warn("Number of elements too high for phase diagram plotting") return None filtered = filter_dataframe_by_composition(df, total_comp) - filtered = filtered[['delta_e', 'Composition']] + filtered = filtered[["delta_e", "Composition"]] filtered = filtered.dropna() # Create computed entry column with un-normalized energies @@ -503,7 +539,7 @@ def plot_hull(self, df, new_result_ids, filename=None, finalize=False): plotkwargs.update({"linestyle": "--"}) else: plotkwargs.update({"linestyle": "-"}) - plotter = PDPlotter(pd, backend='matplotlib', **plotkwargs) + plotter = PDPlotter(pd, backend="matplotlib", **plotkwargs) getplotkwargs = {"label_stable": False} if finalize else {} plot = plotter.get_plot(**getplotkwargs) @@ -520,7 +556,9 @@ def plot_hull(self, df, new_result_ids, filename=None, finalize=False): # measure stabilities wrt. the ultimate hull. pd = PhaseDiagram(filtered["entry"].values, elements=pg_elements) plotter = PDPlotter( - pd, backend="matplotlib", **{"markersize": 0, "linestyle": "-", "linewidth": 2} + pd, + backend="matplotlib", + **{"markersize": 0, "linestyle": "-", "linewidth": 2} ) plot = plotter.get_plot(plt=plot) @@ -558,14 +596,68 @@ def plot_hull(self, df, new_result_ids, filename=None, finalize=False): plot.savefig(filename, dpi=70) plot.close() - def finalize(self, path="."): + def finalize(self, campaign): """ Post-processing a dft campaign """ - update_run_w_structure( - path, hull_distance=self.hull_distance, parallel=self.parallel + # self.analyze(campaign, finalize=True) + all_submitted, all_results = campaign.experiment.agg_history + old_results = campaign.seed_data.drop(all_results.index, errors="ignore") + new_results = campaign.seed_data.drop(old_results.index) + st_a = StabilityAnalyzer( + hull_distance=self.hull_distance, + parallel=self.parallel, + entire_space=False, + plot=False, ) + # Having calculated stabilities again, we plot the overall hull. + # Filter by chemsys + new_comp = new_results["Composition"].sum() + filtered = filter_dataframe_by_composition(campaign.seed_data, new_comp) + filtered = filtered.dropna(subset=["delta_e"]) + + st_a.plot_hull( + filtered, + all_submitted.index, + filename="hull_finalized.png", + finalize=True, + ) + + stabs = self.add_stability( + filtered, hull_distance=self.hull_distance, parallel=self.parallel + ) + stable_discovered = stabs[stabs["is_stable"].fillna(False)] + + # Analyze structures if present in experiment + if "structure" in all_results.columns: + s_a = AnalyzeStructures() + s_a.analyze_vaspqmpy_jobs(all_results, against_icsd=True, use_energies=True) + unique_s_dict = {} + for i in range(len(s_a.structures)): + if s_a.structure_is_unique[i] and ( + s_a.structure_ids[i] in stable_discovered.index + ): + unique_s_dict[s_a.structure_ids[i]] = s_a.structures[i] + + with open("discovered_unique_structures.json", "w") as f: + json.dump(dict([(k, s.as_dict()) for k, s in unique_s_dict.items()]), f) + + with open("structure_report.log", "w") as f: + f.write("consumed discovery unique_discovery duplicate in_icsd \n") + f.write( + str(len(all_submitted)) + + " " + + str(len(stable_discovered)) + + " " + + str(len(unique_s_dict)) + + " " + + str(len(s_a.structures) - sum(s_a._not_duplicate)) + + " " + + str(sum([not i for i in s_a._icsd_filter])) + ) + return True + class PhaseSpaceAL(PhaseSpace): """ @@ -680,272 +772,221 @@ def update_phase_dict(self, ncpus=cpu_count()): ) -def update_run_w_structure(folder, hull_distance=0.2, parallel=True): - """ - Updates a campaign grouped in directories with structure analysis - - """ - with cd(folder): - required_files = ["seed_data.pickle"] - if os.path.isfile("error.json"): - error = loadfn("error.json") - print("{} ERROR: {}".format(folder, error)) - - if not all([os.path.isfile(fn) for fn in required_files]): - print("{} ERROR: no seed data, no analysis to be done") - else: - with open("seed_data.pickle", "rb") as f: - df = pickle.load(f) - - with open("experiment.pickle", "rb") as f: - experiment = pickle.load(f) - # Hack to update agg_history - experiment.update_current_data(None) - - all_submitted, all_results = experiment.agg_history - old_results = df.drop(all_results.index, errors='ignore') - new_results = df.drop(old_results.index) - st_a = StabilityAnalyzer( - hull_distance=hull_distance, parallel=parallel, entire_space=False, plot=False) - summary, new_seed = st_a.analyze(new_results, old_results) - - # Having calculated stabilities again, we plot the overall hull. - # Filter by chemsys - new_comp = new_results['Composition'].sum() - filtered = filter_dataframe_by_composition(new_seed, new_comp) - st_a.plot_hull( - filtered, - all_submitted.index, - filename="hull_finalized.png", - finalize=True, - ) - - stable_discovered = new_seed[new_seed["is_stable"].fillna(False)] - - # Analyze structures if present in experiment - if "structure" in all_results.columns: - s_a = AnalyzeStructures() - s_a.analyze_vaspqmpy_jobs(all_results, against_icsd=True, use_energies=True) - unique_s_dict = {} - for i in range(len(s_a.structures)): - if s_a.structure_is_unique[i] and ( - s_a.structure_ids[i] in stable_discovered.index - ): - unique_s_dict[s_a.structure_ids[i]] = s_a.structures[i] - - with open("discovered_unique_structures.json", "w") as f: - json.dump(dict([(k, s.as_dict()) for k, s in unique_s_dict.items()]), f) - - with open("structure_report.log", "w") as f: - f.write("consumed discovery unique_discovery duplicate in_icsd \n") - f.write( - str(len(all_submitted)) - + " " - + str(len(stable_discovered)) - + " " - + str(len(unique_s_dict)) - + " " - + str(len(s_a.structures) - sum(s_a._not_duplicate)) - + " " - + str(sum([not i for i in s_a._icsd_filter])) - ) - - class GenericATFAnalyzer(AnalyzerBase): """ Generic analyzer provide AL metrics analysis, including: + deALM: decision efficiency metric anyALM: any discover from top n percentile catalyst allALM: fraction of top m percentile simALM: similarity between seed_df and new candidate + This is only used in simulated discovery campaign, aggregated selection of candidates from one specific agent should be passed in """ - def __init__(self, exploration_space_df, percentile=0.01): + def __init__(self, percentile=0.01): """ - Notice the default of SL is to maximize a value, if the target value is + Notice the default of SL is to maximize a value, if the target value is overpotential, please remember to negate the target values Args: - exploration_space_df (pd.DataFrame): - dataframe represent the whole exploration space percentile (float): top percentile of candidates considered as "good" """ - self.exploration_space_df = exploration_space_df self.percentile = percentile super(GenericATFAnalyzer, self).__init__() - def analyze(self, new_experimental_results, seed_data): + def analyze(self, campaign, finalize=False): """ run analysis one by one + Args: - new_experimental_results (pd.DataFrame): - selection of candidiates in the right order - seed_data (pd.DataFrame): - seed data used before first-round CAMD selection starts + campaign (Campaign): campaign object to analyze + finalize (bool): whether this analysis is the final one + Returns: - deALM_val (np.array): - results from GenericATFAnalyzer.gather_deALM() - anyALM_val (np.array): - results from GenericATFAnalyzer.gather_anyALM() - allALM_val (np.array): - results from GenericATFAnalyzer.gather_allALM() - simALM_val (np.array): - results from GenericATFAnalyzer.gather_simALM() + (DataFrame): dataframe with summary stats from campaign + """ + seed_data = campaign.seed_data + new_experimental_results = campaign.experiment.get_results() summary = pd.DataFrame() - deALM_val = self.gather_deALM(new_experimental_results.copy(), - seed_data.copy(), self.exploration_space_df.copy()) - summary['deALM'] = [deALM_val] - anyALM_val = self.gather_anyALM(new_experimental_results.copy(), seed_data.copy(), - self.exploration_space_df.copy(), percentile=self.percentile) - summary['anyALM'] = [anyALM_val] - allALM_val = self.gather_allALM(new_experimental_results.copy(), seed_data.copy(), - self.exploration_space_df.copy(), percentile=self.percentile) - summary['allALM'] = [allALM_val] - simALM_val = self.gather_simALM(new_experimental_results.copy(), seed_data.copy()) - summary['simALM'] = [simALM_val] - new_seed_data = pd.concat([seed_data, new_experimental_results], axis=0) - - return summary, new_seed_data - - def gather_deALM(self, new_experimental_results, seed_data, exploration_space_df): - """ - compute the decision efficiency ALM for one agent + unsampled_candidates = campaign.experiment.dataframe.loc[ + campaign.candidate_data.index + ] + all_results = unsampled_candidates.append( + campaign.experiment.dataframe.loc[campaign.seed_data.index] + ) + unsampled_candidates = unsampled_candidates.drop(new_experimental_results.index) + de_alms = self.get_de_alm(new_experimental_results, unsampled_candidates) + summary["deALM"] = [de_alms] + + any_alm = self.get_any_alm( + new_experimental_results, + all_results, + percentile=self.percentile, + ) + summary["anyALM"] = [any_alm] + + all_sampled = seed_data.append(new_experimental_results).drop_duplicates() + all_alm = self.get_all_alm( + all_sampled, + all_results, + percentile=self.percentile, + ) + summary["allALM"] = [all_alm] + # simALM_val = self.gather_simALM( + # new_experimental_results.copy(), seed_data.copy() + # ) + # summary["simALM"] = [simALM_val] + + return summary + + @staticmethod + def get_de_alm(samples, unsampled_results): + """ + Compute the decision efficiency ALM for one agent. + + de_ALM reflects the percentile of a sample relative + the remaining unsampled results, and is computed as + 2*f_i - 1 in order to adopt a range from [-1, 1] which + can be easily compared to the random case (for which + de_ALM should be 0.) + + If the latest sample was the "best" choice, de_ALM is 1, + if it was the worst, de_ALM is -1. + Args: - new_experimental_results (pd.DataFrame): - selection of candidiates in the right order - seed_data (pd.DataFrame): - seed data used before first-round CAMD selection starts - exploration_space_df (pd.DataFrame): - dataframe represent the whole exploration space + samples (pd.DataFrame): latest samples + unsampled_results (pd.DataFrame): not yet sampled results + from ATF simulation + Returns: - self.deALM (np.array): - decision ALM for one specific agent + (pd.DataFrame): de_ALM """ - deALM = [] - # sort df using target values - exploration_space_df.sort_values(by=['target'], inplace=True) - # take seed data away from exploration_space_df - exploration_space_df.drop(seed_data.index, inplace=True) # go through each selected candidate, find the rank - for i in list(new_experimental_results.index): - index_list = np.array(exploration_space_df.index) - identified_rank_frac = 2 * (np.where(index_list == i)[0][0] / exploration_space_df.shape[0]) - 1 - deALM.append(identified_rank_frac) - # drop the candidate from work df once selected - exploration_space_df.drop(i, inplace=True) - deALM = np.array(deALM) - return deALM - - def gather_anyALM(self, new_experimental_results, seed_data, exploration_space_df, percentile): - """ - compute the any ALM for one agent + de_ALMs = [] + for idx, sample in samples.iterrows(): + temp = unsampled_results.append(sample) + sample_rank_pct = temp["target"].rank(pct=True).loc[idx] + de_ALMs.append(2 * sample_rank_pct - 1) + + return de_ALMs + + @staticmethod + def get_any_alm(samples, all_results, percentile): + """ + Compute the any ALM for one agent, which is a boolean + describing whether or not some sample from the samples thus + far is present in the top n percentile of materials, e.g. + if some sample in samples is in the top 1% of all results + returns True. Note that the any_alm from Rohr et al. is + the first iteration in which this is true for a given + random initialization of an SL campaign. + Args: - new_experimental_results (pd.DataFrame): + samples (pd.DataFrame): selection of candidiates in the right order - seed_data (pd.DataFrame): - seed data used before first-round CAMD selection starts - exploration_space_df (pd.DataFrame): - dataframe represent the whole exploration space + all_results (pd.DataFrame): + all_results to use for percentile determination percentile (float): top percentile of candidates considered as "good" + Returns: - self.anyALM (np.array): - any ALM for one specific agent + (bool): True if any of the samples are in the top + percentile of all_results, False otherwise """ - anyALM = np.array([0] * new_experimental_results.shape[0]) - # sort df using target values - exploration_space_df.sort_values(by=['target'], inplace=True) - target_values_list = exploration_space_df['target'].to_list() - threshold_target_value = target_values_list[round((1-percentile)*exploration_space_df.shape[0])] - # take seed data away from exploration_space_df - exploration_space_df.drop(seed_data.index, inplace=True) - # go through each selected candidate, find the rank - for i in range(new_experimental_results.shape[0]): - if new_experimental_results['target'].to_list()[i] >= threshold_target_value: - anyALM[i:] += 1 - break - return anyALM + sample_ranks = all_results["target"].rank(pct=True).loc[samples.index] + return (sample_ranks > 1 - percentile).any() - def gather_allALM(self, new_experimental_results, seed_data, exploration_space_df, percentile): + @staticmethod + def get_all_alm(all_samples, all_possible_samples, percentile): """ - compute the all ALM for one agent + Compute the all ALM for one agent, defined as the fraction of + samples above the target percentile which are contained in + the samples taken up to this point. For example, if 50% of + the possible catalysts in the top 1% overpotential have + been sampled, allALM is 0.5. + Args: - new_experimental_results (pd.DataFrame): - selection of candidiates in the right order - seed_data (pd.DataFrame): - seed data used before first-round CAMD selection starts - exploration_space_df (pd.DataFrame): - dataframe represent the whole exploration space + all_samples (pd.DataFrame): all taken samples up to this point + all_possible_samples (pd.DataFrame): all possible samples to + compute percentiles from percentile (float): top percentile of candidates considered as "good" Returns: - self.allALM (np.array): - all ALM for one specific agent + (float): percentage of top percentile candidates contained + in aggregated sample set up to this point """ - allALM = [] # sort df using target values - exploration_space_df.sort_values(by=['target'], inplace=True) - target_values_list = exploration_space_df['target'].to_list() - threshold_target_value = target_values_list[round((1-percentile)*exploration_space_df.shape[0])] - # take seed data away from exploration_space_df - exploration_space_df.drop(seed_data.index, inplace=True) - # go through each selected candidate, find the rank - count = 0 - for i in range(new_experimental_results.shape[0]): - if new_experimental_results['target'].to_list()[i] >= threshold_target_value: - count += 1 - allALM.append(count / (exploration_space_df.shape[0]*percentile)) - allALM = np.array(allALM) - return allALM - - def gather_simALM(self, new_experimental_results, seed_data): - """ - compute the sim ALM for one agent - Args: - new_experimental_results (pd.DataFrame): - selection of candidiates in the right order - seed_data (pd.DataFrame): - seed data used before first-round CAMD selection starts - Returns: - simALM (np.array): - sim ALM for one specific agent - """ - simALM = [] - # drop the target values so that only features are left - seed_data.drop('target', inplace=True, axis=1) - new_experimental_results.drop('target', inplace=True, axis=1) - new_experimental_results.reset_index(inplace=True, drop=True) - # go through the new candidate one by one - for i in range(new_experimental_results.shape[0]): - decision_fecture_vector = np.array(new_experimental_results.loc[i].tolist()) - seed_feature_vector = np.array(seed_data.values.tolist()) - similarity = np.linalg.norm(seed_feature_vector-decision_fecture_vector) / seed_data.shape[0] - simALM.append(similarity) - seed_data = pd.concat([seed_data, new_experimental_results[i:(i+1)]], axis=0) - simALM = np.array(simALM) - return simALM + ranked = all_possible_samples["target"].rank(pct=True) + top_percentile = ranked[ranked > 1 - percentile] + all_alm = len(top_percentile.index.intersection(all_samples.index)) / len( + top_percentile + ) + return all_alm + + # joseph.montoya: I don't know what this is, so I'm going to comment it for now + # it looks like it might be a metric related to whether the most recent samples are + # inside or outside the domain? + # def gather_simALM(self, new_experimental_results, seed_data): + # """ + # compute the sim ALM for one agent + # Args: + # new_experimental_results (pd.DataFrame): + # selection of candidiates in the right order + # seed_data (pd.DataFrame): + # seed data used before first-round CAMD selection starts + # Returns: + # simALM (np.array): + # sim ALM for one specific agent + # """ + # simALM = [] + # # drop the target values so that only features are left + # seed_data.drop("target", inplace=True, axis=1) + # new_experimental_results.drop("target", inplace=True, axis=1) + # new_experimental_results.reset_index(inplace=True, drop=True) + # # go through the new candidate one by one + # for i in range(new_experimental_results.shape[0]): + # decision_fecture_vector = np.array(new_experimental_results.loc[i].tolist()) + # seed_feature_vector = np.array(seed_data.values.tolist()) + # similarity = ( + # np.linalg.norm(seed_feature_vector - decision_fecture_vector) + # / seed_data.shape[0] + # ) + # simALM.append(similarity) + # seed_data = pd.concat( + # [seed_data, new_experimental_results[i: (i + 1)]], axis=0 + # ) + # simALM = np.array(simALM) + # return simALM class MultiAnalyzer(AnalyzerBase): """ The multi-fidelity analyzer. """ - def __init__(self, target_prop, prop_range, total_expt_queried=0, - total_expt_discovery=0, analyze_cost=False, total_cost=0.0): + + def __init__( + self, + target_prop, + prop_range, + total_expt_queried=0, + total_expt_discovery=0, + analyze_cost=False, + total_cost=0.0, + ): """ Args: target_prop (str) The name of the target property, e.g. "bandgap". prop_range (list) The range of the target property that is considered ideal. - total_expt_queried (int) The total experimental queries after nth iteration. + total_expt_queried (int) The total experimental queries after nth iteration. tot_expt_discovery (int) The total experimental discovery after nth iteration. analyze_cost (bool) If the input has "cost" column, also analyze that information. - total_cost(float) The total cost of the hypotheses after nth iteration. + total_cost(float) The total cost of the hypotheses after nth iteration. """ self.target_prop = target_prop self.prop_range = prop_range @@ -961,10 +1002,28 @@ def _filter_df_by_prop_range(self, df): Args: df A pd.Dataframe to be filtered. """ - return df.loc[(df[self.target_prop] >= self.prop_range[0]) & - (df[self.target_prop] <= self.prop_range[1])] + return df.loc[ + (df[self.target_prop] >= self.prop_range[0]) + & (df[self.target_prop] <= self.prop_range[1]) + ] - def analyze(self, new_experimental_results, seed_data): + def analyze(self, campaign, finalize=False): + """ + Analysis method for campaign + + Args: + campaign (Campaign): + finalize (bool): + + Returns: + (DataFrame): pandas dataframe with summary of the results + + """ + return self._analyze_results( + campaign.experiment.get_results(), campaign.seed_data + ) + + def _analyze_results(self, new_experimental_results, seed_data): """ Analyze results of multi-fidelity data @@ -977,27 +1036,28 @@ def analyze(self, new_experimental_results, seed_data): (pd.DataFrame): new seed data """ - new_expt_hypotheses = new_experimental_results.loc[new_experimental_results['expt_data'] == 1] + new_expt_hypotheses = new_experimental_results.loc[ + new_experimental_results["expt_data"] == 1 + ] new_discoveries = self._filter_df_by_prop_range(new_expt_hypotheses) # total discovery = up to (& including) the current iteration - new_seed = seed_data.append(new_experimental_results) self.total_expt_queried += len(new_expt_hypotheses) self.total_expt_discovery += len(new_discoveries) if self.total_expt_queried != 0: - success_rate = self.total_expt_discovery/self.total_expt_queried + success_rate = self.total_expt_discovery / self.total_expt_queried else: success_rate = 0 summary = pd.DataFrame( - { - "expt_queried": [len(new_expt_hypotheses)], - "total_expt_queried": [self.total_expt_queried], - "new_discovery": [len(new_discoveries)], - "total_expt_discovery": [self.total_expt_discovery], - "total_regret": [self.total_expt_queried - self.total_expt_discovery], - "success_rate": [success_rate] - } + { + "expt_queried": [len(new_expt_hypotheses)], + "total_expt_queried": [self.total_expt_queried], + "new_discovery": [len(new_discoveries)], + "total_expt_discovery": [self.total_expt_discovery], + "total_regret": [self.total_expt_queried - self.total_expt_discovery], + "success_rate": [success_rate], + } ) if self.analyze_cost: @@ -1006,8 +1066,8 @@ def analyze(self, new_experimental_results, seed_data): summary["iteration_cost"] = [iter_cost] summary["total_cost"] = [self.total_cost] if self.total_expt_discovery != 0: - average_cost_per_discovery = self.total_cost/self.total_expt_discovery + average_cost_per_discovery = self.total_cost / self.total_expt_discovery else: average_cost_per_discovery = np.nan - summary['average_cost_per_discovery'] = [average_cost_per_discovery] - return summary, new_seed + summary["average_cost_per_discovery"] = [average_cost_per_discovery] + return summary diff --git a/camd/campaigns/MProtoDFTCampaign.py b/camd/campaigns/MProtoDFTCampaign.py new file mode 100644 index 00000000..63a27d43 --- /dev/null +++ b/camd/campaigns/MProtoDFTCampaign.py @@ -0,0 +1,109 @@ +""" +Script test of MProtoDFTCampaign +""" +from datetime import datetime +import logging +from fireworks import LaunchPad +from sklearn.neural_network import MLPRegressor +from camd.domain import StructureDomain, heuristic_setup +from camd.agent.m3gnet import M3GNetStabilityAgent +from camd.agent.stability import AgentStabilityAdaBoost +from camd.agent.base import SequentialAgent +from camd.campaigns.structure_discovery import ProtoDFTCampaign +from camd.experiment.dft import AtomateExperiment +from camd.analysis import StabilityAnalyzer +import pandas as pd +from monty.os import cd, makedirs_p + +from m3gnet.models import M3GNet + + +def load_candidate(chemsys, n_max_atoms=20): + """ + Load candidate data + Args: + chemsys (str): chemical system + n_max_atoms (int): number of maximum atoms + """ + element_list = chemsys.split("-") + max_coeff, charge_balanced = heuristic_setup(element_list) + domain = StructureDomain.from_bounds( + element_list, + charge_balanced=charge_balanced, + n_max_atoms=n_max_atoms, + **{"grid": range(1, max_coeff)} + ) + candidate_data = domain.candidates() + return candidate_data + + +def load_seed(): + """ + Helper function + Returns: + list of the MP entries (w/ structure) + in the chemsys + """ + # with MPRester() as mpr: + # entries = mpr.get_entries_in_chemsys(chemsys, inc_structure=True) + # return entries + data = pd.read_pickle("mp_binary.pickle") + data = data.rename( + columns={ + "formation_energy_per_atom": "delta_e", + "pretty_formula": "Composition", + } + ) + data = data.dropna() + return data + + +if __name__ == "__main__": + # Params for experiment + DB_FILE = "/mnt/efs/fw_config/db.json" + LPAD_FILE = "/mnt/efs/fw_config/my_launchpad.yaml" + # Parameter + CHEMSYS = "Si" + NOW = datetime.now().isoformat() + seed_data = load_seed() + exp_data = load_candidate(CHEMSYS) + model = M3GNet(is_intensive=False) + m3gnetagent = M3GNetStabilityAgent(m3gnet=model, hull_distance=1.0, n_query=5) + adagent = AgentStabilityAdaBoost( + model=MLPRegressor(hidden_layer_sizes=(84, 50)), + n_query=5, + hull_distance=0.3, + exploit_fraction=1.0, + uncertainty=True, + alpha=0.5, + diversify=True, + n_estimators=20, + ) + agent = SequentialAgent(agents=[adagent, m3gnetagent]) + analyzer = StabilityAnalyzer(hull_distance=0.2) + lpad = LaunchPad.from_file(LPAD_FILE) + lpad.auto_load() + experiment = AtomateExperiment(lpad, DB_FILE, poll_time=60, launch_from_local=False) + + path = "campaigns/{}".format(NOW) + makedirs_p(path) + logger = logging.Logger("camd") + logger.setLevel("INFO") + log_file = "{}/log.txt".format(path) + file_handler = logging.FileHandler(log_file) + logger.addHandler(file_handler) + logger.addHandler(logging.StreamHandler()) + + with cd(path): + campaign = ProtoDFTCampaign( + candidate_data=exp_data, + agent=agent, + experiment=experiment, + analyzer=analyzer, + seed_data=seed_data, + heuristic_stopper=5, + logger=logger, + ) + campaign.autorun() + with open("done", "w") as f: + f.write(datetime.now().isoformat()) diff --git a/camd/campaigns/base.py b/camd/campaigns/base.py index 3169d429..907d5ce5 100644 --- a/camd/campaigns/base.py +++ b/camd/campaigns/base.py @@ -30,7 +30,7 @@ class Campaign(MSONable): follows closely the "scientific method". Agent is the entity that suggests new Experiments. - Supporting entities are Analyzers and Finalizers. Framework + Supporting entities are Analyzers. Framework is flexible enough to implement many sequential learning or optimization tasks, including active-learning, bayesian optimization or black-box optimization with local or global optima search. @@ -41,7 +41,7 @@ def __init__( candidate_data, agent, experiment, - analyzer, + analyzer=None, seed_data=None, create_seed=False, heuristic_stopper=np.inf, @@ -59,7 +59,7 @@ def __init__( search space for active learning agent (HypothesisAgent): a subclass of HypothesisAgent experiment (Experiment): a subclass of Experiment - analyzer (Analyzer): a subclass of Analyzer + analyzer (Analyzer): a subclass of Analyzer or a list of analyzers seed_data (pandas.DataFrame): Seed Data for active learning, index is to be the assumed uid create_seed (int): an initial seed size to create from the data @@ -87,7 +87,10 @@ def __init__( # Object parameters self.agent = agent self.experiment = experiment - self.analyzer = analyzer + if analyzer is not None: + self.analyzer = analyzer if isinstance(analyzer, list) else [analyzer] + else: + self.analyzer = None # Other parameters # TODO: think about how to abstract this away from the loop @@ -123,7 +126,7 @@ def __init__( self.loop_state = "UNSTARTED" if logger is None: - self.logger = logging.Logger("camd") + self.logger = logging.Logger("camd", level=logging.INFO) self.logger.addHandler(logging.StreamHandler()) else: self.logger = logger @@ -155,14 +158,18 @@ def run(self, finalize=False): # Analyze new results self.logger.info("{} {} state: Analyzing results".format(self.type, self.iteration)) - summary, new_seed_data = self.analyzer.analyze( - new_experimental_results, self.seed_data - ) + summary = pd.DataFrame() + if self.analyzer: + for analyzer in self.analyzer: + analysis = analyzer.analyze(self, finalize=finalize) + summary = pd.concat([summary, analysis], axis=1) - # Augment summary and seed self.history = self.history.append(summary) self.history = self.history.reset_index(drop=True) self.save("history", method="pickle") + + # Augment summary and seed + new_seed_data = self.seed_data.append(new_experimental_results) self.seed_data = new_seed_data self.save("seed_data", method="pickle") @@ -209,9 +216,14 @@ def run(self, finalize=False): self.job_status = self.experiment.submit(suggested_experiments) self.save("job_status") - self.save("experiment", method="pickle") + # if hasattr(self.experiment, "save"): + # experiment.save(self.path) + # else: + # self.save("experiment", method="pickle") self.consumed_candidates += suggested_experiments.index.values.tolist() + self.logger.debug("{} {} state: consumed candidates {}".format( + self.type, self.iteration, self.consumed_candidates)) self.save("consumed_candidates") self.iteration += 1 @@ -251,7 +263,7 @@ def auto_loop(self, n_iterations=10, monitor=False, if save_iterations: self.loop_backup(str(self.iteration - 1)) - self.run(finalize=True) + # self.run(finalize=True) if monitor: self.logger.info("Monitoring experiments") self.experiment.monitor() @@ -288,11 +300,13 @@ def initialize(self, random_state=42): raise ValueError( "No seed data available. Either supply or ask for creation." ) - self.analyzer._initial_seed_indices = self.seed_data.index.tolist() + # self.analyzer._initial_seed_indices = self.seed_data.index.tolist() self.logger.info("{} {} state: Running experiments".format(self.type, self.iteration)) self.job_status = self.experiment.submit(suggested_experiments) self.consumed_candidates = suggested_experiments.index.values.tolist() + self.logger.debug("{} {} state: consumed candidates {}".format( + self.type, self.iteration, self.consumed_candidates)) self.create_seed = False self.initialized = True @@ -327,8 +341,9 @@ def finalize(self): """ self.logger.info("Finalizing campaign.") os.chdir(self.path) - if hasattr(self.analyzer, "finalize"): - self.analyzer.finalize(self.path) + for analyzer in self.analyzer: + if hasattr(analyzer, "finalize"): + analyzer.finalize(self) if self.s3_prefix: self.s3_sync() diff --git a/camd/campaigns/meta_agent.py b/camd/campaigns/meta_agent.py index 5c17c04f..879c1800 100644 --- a/camd/campaigns/meta_agent.py +++ b/camd/campaigns/meta_agent.py @@ -204,37 +204,33 @@ def __init__(self, checkpoint_indices): """ self.checkpoint_indices = checkpoint_indices - def analyze(self, new_experimental_results, seed_data): + def analyze(self, campaign, finalize=False): """ - Beta method for analyzing camapaign results + Beta method for analyzing campaign results Args: - new_experimental_results (pandas.DataFrame): new experimental - results from Experiment class - seed_data (pandas.DataFrame): seed_data from prior to last - experiment + campaign (Campaign): campaign object to + be analyzed at the end of an iteration Returns: summary (DataFrame): dataframe to be appended to "history" that summarizes results - seed_data (DataFrame): new seed data to be carried forward - in campaign """ + new_experimental_results = campaign.experiment.get_results() for key, row in new_experimental_results.iterrows(): history = row.campaign.history for index in self.checkpoint_indices: new_experimental_results.loc[ key, "discovered_{}".format(index) ] = history.loc[index, "total_discovery"] - seed_data = seed_data.append(new_experimental_results) summary = new_experimental_results.loc[ "discovered_{}".format(self.checkpoint_indices[0]): "discovered_{}".format( self.checkpoint_indices[-1] ) ] - return summary, seed_data + return summary def _plot(self, campaign_data): fig = plt.figure(figsize=(10, 5)) diff --git a/camd/campaigns/tests/test_multi_fidelity.py b/camd/campaigns/tests/test_multi_fidelity.py index 9b8e7b68..a298f67c 100644 --- a/camd/campaigns/tests/test_multi_fidelity.py +++ b/camd/campaigns/tests/test_multi_fidelity.py @@ -50,7 +50,8 @@ def test_analyzer(self): 296 # bad theory ]] analyzer = MultiAnalyzer(target_prop='bandgap', prop_range=[1.6, 2.0]) - summary, new_seed = analyzer.analyze(new_experimental_results=sample_candidate_data, seed_data=self.seed_data) + summary = analyzer._analyze_results( + new_experimental_results=sample_candidate_data, seed_data=self.seed_data) self.assertEqual( (summary['expt_queried'].values[0], summary['total_expt_discovery'].values[0], diff --git a/camd/campaigns/tests/test_structure_discovery.py b/camd/campaigns/tests/test_structure_discovery.py index b6696a1d..6cbd1b48 100644 --- a/camd/campaigns/tests/test_structure_discovery.py +++ b/camd/campaigns/tests/test_structure_discovery.py @@ -5,7 +5,7 @@ import os import pandas as pd -os.environ['CAMD_S3_BUCKET'] = 'camd-test' +os.environ["CAMD_S3_BUCKET"] = "camd-test" from camd.campaigns.structure_discovery import ProtoDFTCampaign from camd.agent.stability import AgentStabilityML5, AgentStabilityAdaBoost from camd.analysis import StabilityAnalyzer @@ -14,6 +14,11 @@ from camd.utils.data import filter_dataframe_by_composition, load_dataframe from monty.tempfile import ScratchDir from sklearn.neural_network import MLPRegressor +from m3gnet.models import M3GNet + +from camd import CAMD_TEST_FILES +from camd.agent.m3gnet import M3GNetHypothesisAgent, M3GNetStabilityAgent, reverse_calcs +from camd.agent.base import SequentialAgent CAMD_DFT_TESTS = os.environ.get("CAMD_DFT_TESTS", False) SKIP_MSG = "Long tests disabled, set CAMD_DFT_TESTS to run long tests" @@ -21,7 +26,7 @@ def teardown_s3(): """Tear down test files in s3""" - s3 = boto3.resource('s3') + s3 = boto3.resource("s3") bucket = s3.Bucket(CAMD_S3_BUCKET) bucket.objects.filter(Prefix="proto-dft-2/runs/Si").delete() bucket.objects.filter(Prefix="proto-dft-2/submit/Si").delete() @@ -32,19 +37,21 @@ def test_simulated(self): # Note that there's a small issue with pickled results here that may not have # certain spin and magnetization flags set - pickled Vasprun objects may not # be completely compatible with latest version of pymatgen - exp_dataframe = pd.read_pickle(os.path.join(CAMD_TEST_FILES, "mn-ni-o-sb.pickle")) + exp_dataframe = pd.read_pickle( + os.path.join(CAMD_TEST_FILES, "mn-ni-o-sb.pickle") + ) experiment = ATFSampler(exp_dataframe) candidate_data = exp_dataframe.iloc[:, :-11] # Set up agents and loop parameters agent = AgentStabilityAdaBoost( - model=MLPRegressor(hidden_layer_sizes=(84, 50)), + model=MLPRegressor(hidden_layer_sizes=(40, 20)), n_query=2, hull_distance=0.2, exploit_fraction=1.0, uncertainty=True, alpha=0.5, diversify=True, - n_estimators=20 + n_estimators=5, ) analyzer = StabilityAnalyzer(hull_distance=0.2) # Reduce seed_data @@ -54,25 +61,73 @@ def test_simulated(self): # Add some random other data to test compositional flexibility seed_data = seed_data.append(icsd_data.loc[leftover].sample(30)) del icsd_data - with ScratchDir('.'): + with ScratchDir("."): campaign = ProtoDFTCampaign( - candidate_data=candidate_data, agent=agent, experiment=experiment, - analyzer=analyzer, seed_data=seed_data, - heuristic_stopper=5 + candidate_data=candidate_data, + agent=agent, + experiment=experiment, + analyzer=analyzer, + seed_data=seed_data, + heuristic_stopper=5, ) campaign.autorun() self.assertTrue(os.path.isfile('hull_finalized.png')) + def test_sim_m3gnet(self): + exp_dataframe = pd.read_pickle( + os.path.join(CAMD_TEST_FILES, "test_m3gnet_featurized.pickle") + ) + seed_data = pd.read_pickle( + os.path.join(CAMD_TEST_FILES, "mp_test_data.pickle") + ) + seed_data = seed_data.rename( + columns={ + "formation_energy_per_atom": "delta_e", + "pretty_formula": "Composition", + } + ) + seed_data = seed_data.dropna() + seed_data.index = ["mp-{}".format(n) for n in seed_data.index] + exp_dataframe.index = ["camd-{}".format(n) for n in exp_dataframe.index] + experiment = ATFSampler(exp_dataframe) + exp_dataframe["status"] = "SUCCEEDED" + # candidate_data = exp_dataframe.iloc[:, :-11] + # Set up agents and loop parameters + model = M3GNet(is_intensive=False) + m3gnetagent = M3GNetStabilityAgent(m3gnet=model, hull_distance=2.0) + adagent = AgentStabilityAdaBoost( + model=MLPRegressor(hidden_layer_sizes=(20,)), + n_query=3, + hull_distance=100.0, + exploit_fraction=1.0, + uncertainty=True, + alpha=0.5, + diversify=True, + n_estimators=4, + ) + agent = SequentialAgent(agents=[adagent, m3gnetagent]) + analyzer = StabilityAnalyzer(hull_distance=0.2) + with ScratchDir("."): + campaign = ProtoDFTCampaign( + candidate_data=exp_dataframe.drop("delta_e", axis=1), + agent=agent, + experiment=experiment, + analyzer=analyzer, + seed_data=seed_data, + heuristic_stopper=5, + ) + campaign.autorun() + @unittest.skipUnless(CAMD_DFT_TESTS, SKIP_MSG) def test_cached_campaign(self): - with ScratchDir('.'): + with ScratchDir("."): campaign = ProtoDFTCampaign.from_chemsys_high_quality("Si") # Test seed data has other data self.assertGreater(len(campaign.seed_data), 36581) @unittest.skipUnless(CAMD_DFT_TESTS, SKIP_MSG) def test_simple_dft(self): - with ScratchDir('.'): + with ScratchDir("."): campaign = ProtoDFTCampaign.from_chemsys("Si") # Nerf agent a bit agent = AgentStabilityML5(n_query=2) @@ -81,5 +136,5 @@ def test_simple_dft(self): teardown_s3() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/camd/campaigns/tests/test_worker.py b/camd/campaigns/tests/test_worker.py index 341c7788..7ac3f98f 100644 --- a/camd/campaigns/tests/test_worker.py +++ b/camd/campaigns/tests/test_worker.py @@ -5,7 +5,6 @@ import boto3 import json import time -import shlex from multiprocessing import Pool from camd import CAMD_S3_BUCKET from camd.campaigns.worker import Worker diff --git a/camd/domain.py b/camd/domain.py index 24339a88..b098c619 100644 --- a/camd/domain.py +++ b/camd/domain.py @@ -9,24 +9,9 @@ import numpy as np from protosearch.build_bulk.oqmd_interface import OqmdInterface - - from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.core.composition import Composition, Element -from matminer.featurizers.base import MultipleFeaturizer -from matminer.featurizers.composition import ( - ElementProperty, - Stoichiometry, - ValenceOrbital, - IonProperty, -) -from matminer.featurizers.structure import ( - SiteStatsFingerprint, - StructuralHeterogeneity, - ChemicalOrdering, - StructureComposition, - MaximumPackingEfficiency, -) +from camd.utils.data import get_default_featurizer class DomainBase(abc.ABC): @@ -248,23 +233,7 @@ def featurize_structures(self, featurizer=None, **kwargs): featurizer = ( featurizer if featurizer - else MultipleFeaturizer( - [ - SiteStatsFingerprint.from_preset( - "CoordinationNumber_ward-prb-2017" - ), - StructuralHeterogeneity(), - ChemicalOrdering(), - MaximumPackingEfficiency(), - SiteStatsFingerprint.from_preset( - "LocalPropertyDifference_ward-prb-2017" - ), - StructureComposition(Stoichiometry()), - StructureComposition(ElementProperty.from_preset("magpie")), - StructureComposition(ValenceOrbital(props=["frac"])), - StructureComposition(IonProperty(fast=True)), - ] - ) + else get_default_featurizer() ) features = featurizer.featurize_many( diff --git a/camd/experiment/agent_simulation.py b/camd/experiment/agent_simulation.py index 5ff51b81..1c4e7257 100644 --- a/camd/experiment/agent_simulation.py +++ b/camd/experiment/agent_simulation.py @@ -13,8 +13,16 @@ class LocalAgentSimulation(Experiment): Class that runs Agent simulations synchronously and sequentially for testing in meta-agent campaigns. """ - def __init__(self, atf_candidate_data, seed_data, analyzer, iterations, - current_data=None, job_status=None): + + def __init__( + self, + atf_candidate_data, + seed_data, + analyzer, + iterations, + current_data=None, + job_status=None, + ): """ Args: atf_candidate_data (DataFrame): dataframe corresponding to after @@ -32,7 +40,8 @@ def __init__(self, atf_candidate_data, seed_data, analyzer, iterations, self.analyzer = analyzer self.seed_data = seed_data super(LocalAgentSimulation, self).__init__( - current_data=current_data, job_status=job_status) + current_data=current_data, job_status=job_status + ) def submit(self, data): """ @@ -44,7 +53,7 @@ def submit(self, data): """ self.update_current_data(data) - self.job_status = 'PENDING' + self.job_status = "PENDING" def monitor(self): """ @@ -58,12 +67,12 @@ def monitor(self): if self.job_status == "PENDING": campaigns = [] for index, row in self.current_data.iterrows(): - agent = row.pop('agent') + agent = row.pop("agent") path = str(index) os.mkdir(path) with cd(path): campaigns.append(self.test_agent(agent)) - self.current_data['campaign'] = campaigns + self.current_data["campaign"] = campaigns self.job_status = "COMPLETED" def test_agent(self, agent): @@ -79,14 +88,12 @@ class attributes """ campaign = Campaign( - candidate_data=self.atf_dataframe, - seed_data=self.seed_data, - agent=agent, - analyzer=self.analyzer, - experiment=ATFSampler( - dataframe=self.atf_dataframe - ), - ) + candidate_data=self.atf_dataframe, + seed_data=self.seed_data, + agent=agent, + analyzer=self.analyzer, + experiment=ATFSampler(dataframe=self.atf_dataframe), + ) campaign.auto_loop(n_iterations=self.iterations, initialize=True) return campaign diff --git a/camd/experiment/dft.py b/camd/experiment/dft.py index 7640c679..eb7f2b38 100644 --- a/camd/experiment/dft.py +++ b/camd/experiment/dft.py @@ -4,7 +4,6 @@ to be run asynchronously with a campaign. """ - import os import json import re @@ -20,13 +19,353 @@ from botocore.errorfactory import ClientError from tqdm import tqdm import pandas as pd +import numpy as np from monty.os import cd from monty.tempfile import ScratchDir from pymatgen.io.vasp.outputs import Vasprun -from pymatgen.core.composition import Composition +from pymatgen.core import Composition, Structure +from pymatgen.io.vasp.sets import MPRelaxSet +from pymatgen.entries.computed_entries import ComputedEntry +from pymatgen.entries.compatibility import MaterialsProjectCompatibility from camd.experiment.base import Experiment -from camd.utils.data import QMPY_REFERENCES, QMPY_REFERENCES_HUBBARD, \ - get_chemsys, get_common_prefixes +from camd.utils.data import ( + MP_REFERENCES, + QMPY_REFERENCES, + QMPY_REFERENCES_HUBBARD, + get_chemsys, + get_common_prefixes, +) + +from atomate.vasp.database import VaspCalcDb +from atomate.vasp.config import ( + ADD_WF_METADATA, + DB_FILE, + VASP_CMD, +) +from atomate.vasp.powerups import ( + add_common_powerups, + add_wf_metadata, +) +from atomate.vasp.workflows.base.core import get_wf + +from fireworks.core.rocket_launcher import rapidfire + + +def wf_structure_optimization(structure, wf_name=None, c=None): + """ + Hacking the atomate wf_structure optimization to allow + wf_name added to the metadata + + Args: + structure (Structure) + wf_name (str) + c (dict) + Returns: + + """ + c = c or {} + vasp_cmd = c.get("VASP_CMD", VASP_CMD) + db_file = c.get("DB_FILE", DB_FILE) + user_incar_settings = c.get("USER_INCAR_SETTINGS") + + wf = get_wf( + structure, + "optimize_only.yaml", + vis=MPRelaxSet( + structure, force_gamma=True, user_incar_settings=user_incar_settings + ), + common_params={"vasp_cmd": vasp_cmd, "db_file": db_file}, + wf_metadata={"wf_name": wf_name}, + ) + + wf = add_common_powerups(wf, c) + + if c.get("ADD_WF_METADATA", ADD_WF_METADATA): + wf = add_wf_metadata(wf, structure) + + return wf + + +class AtomateExperiment(Experiment): + """ + A class for brokering atomate experiments + TODO: 1. formation energy solution + """ + + def __init__( + self, + launchpad, + db_file, + fworker=None, + atomate_workflow=wf_structure_optimization, + nlaunches=0, + max_loops=-1, + sleep_time=5, + m_dir=None, + poll_time=60, + current_data=None, + job_status=None, + history=None, + launch_from_local=False, + ): + """ + Initializes an atomate experiment. + + Args: + launchpad (LaunchPad): launchpad + db_file (str): path to atomate db config file + fworker (FWorker object): fworker + atomate_workflow (): atomate workflow, default the optimziation wf + nlaunches (int): 0 means 'until completion', -1 or "infinite" means to loop until max_loops + max_loops (int): maximum number of loops (default -1 is infinite) + sleep_time (int): secs to sleep between rapidfire loop iterations + m_dir (str): the directory in which to loop Rocket running + poll_time (int): time in seconds to wait in between queries of db + current_data (pandas.DataFrame): dataframe corresponding to + currently submitted experiments + job_status (str): status of the experiment, PENDING or COMPLETED + history (pandas.DataFrame): history of past experiments + launch_from_local (bool): whether to launch from local + """ + self.current_data = current_data + self.job_status = job_status + self._history = history or [] + self.launchpad = launchpad + self.fworker = fworker + self.nlaunches = nlaunches + self.max_loops = max_loops + self.sleep_time = sleep_time + self.m_dir = m_dir + self.db_file = db_file + self.wf = atomate_workflow + self.poll_time = poll_time + self.launch_from_local = launch_from_local + + @property + def db(self): + """ + Define the db attribute as a property so that the experiment + object is picklable. Without this, the SSLContext associated + with the DB is not picklable. + """ + return VaspCalcDb.from_db_file(self.db_file).db + + # def save(self, path): + # pass + + def update_current_data(self, data): + """ + Updates current data with dataframe, + stores old data in history + + Args: + data (DataFrame): + + Returns: + None + + """ + if self.current_data is not None: + current_results = self.get_results() + self._history.append((self.current_data, current_results)) + self.current_data = data + + def get_results(self): + """ + Gets the results from the current run. + + Returns: + (DataFrame): dataframe corresponding to the + current set of results + + """ + if self.job_status != "COMPLETED": + self.update_results() + if self.job_status != "COMPLETED": + warnings.warn("Some calculations have not finished") + return self.current_data + + def monitor(self): + """ + Method for continuously polling for completion + Returns: + (str): calculation status string + """ + while self.job_status != "COMPLETED": + time.sleep(self.poll_time) + self.update_results() + self.print_status() + return self.job_status + + def update_results(self): + """ + Update the current data with the latest wf, fw, launch, task information + """ + task_id = None + launch_id = None + task_status = None + output = None + input = None + task_dir = None + calcs_reversed = None + delta_e = None + for structure_id, row in self.current_data.iterrows(): + wf_entry = self.db.workflows.find_one( + { + "metadata.wf_name": row["wf_name"], + "fw_states.{}".format(row["fw_id"]): {"$exists": True}, + } + ) + fw_entry = self.db.fireworks.find_one({"fw_id": row["fw_id"]}) + if len(fw_entry["launches"]) > 0: + launch_id = fw_entry["launches"][-1] + launch_entry = self.db.launches.find_one({"launch_id": launch_id}) + if launch_entry["action"]: + if "task_id" in launch_entry["action"]["stored_data"]: + task_id = launch_entry["action"]["stored_data"]["task_id"] + task_entry = self.db.tasks.find_one({"task_id": task_id}) + task_status = task_entry["state"] + output = task_entry["output"] + input = task_entry["input"] + task_dir = task_entry["dir_name"] + calcs_reversed = task_entry["calcs_reversed"] + if task_status == "successful": + delta_e = self.get_delta_e(task_entry) + update_data = { + "task_id": task_id if task_id else None, + "launch_id": launch_id if launch_id else None, + "wf_status": wf_entry["state"], + "task_status": task_status if task_status else None, + "output": output if output else None, + "input": input if input else None, + "task_dir": task_dir if task_dir else None, + "calcs_reversed": json.dumps(calcs_reversed) + if calcs_reversed + else None, + "final_structure": output["structure"] if output else None, + "final_energy_per_atom": output["energy_per_atom"] if output else None, + "delta_e": delta_e, + } + update_dataframe_row(self.current_data, structure_id, update_data) + self._update_job_status() + + def _update_job_status(self): + """ + Update the job_status flag by checking all wf status + job_status is "COMPLETED" only when all wf_status are COMPLETED/FIZZLED + """ + wf_status = self.current_data["wf_status"] + if np.all([i in ["COMPLETED", "FIZZLED"] for i in wf_status]): + self.job_status = "COMPLETED" + + def print_status(self): + """ + Prints current status of experiment according to + the data in the current_data attribute + """ + status_string = "" + for structure_id, row in self.current_data.iterrows(): + status_string += "{}: {}\n".format(structure_id, row["wf_status"]) + print("Calc status:\n{}".format(status_string)) + # print("Timeout is set as {}.".format(self.timeout)) + + def submit(self, data): + """ + Args: + data (DataFrame): dataframe containing all necessary + data to conduct the experiment(s). May be one + row, may be multiple rows + + Returns: + None + """ + self.update_current_data(data) + new_columns = [ + "wf_spec", + "wf_name", + "fw_id", + "task_id", + "launch_id", + "wf_status", # status of the workflow (ready, waiting, running, fizzeld, completed) + "task_status", # status of the task (successful, failed) + "output", + "input", + "task_dir", + "calcs_reversed", + "final_structure", + "final_energy_per_atom", + "delta_e", # formation energy per atom + ] + for new_column in new_columns: + self.current_data[new_column] = None + self.add_wfs() + if self.launch_from_local: + self.launch() + self.job_status = "PENDING" + return self.job_status + + def add_wfs(self): + """ + Helper function to add workflows to the launchpad + and update the wf_name, fw_ids + """ + for structure_id, row in self.current_data.iterrows(): + wf_name = f"opt_{structure_id}" + wf = wf_structure_optimization(row["structure"], wf_name=wf_name) + fw_ids = self.launchpad.add_wf(wf) + launch_info = { + "wf_spec": wf, + "wf_name": wf_name, + "fw_id": sorted(list(fw_ids.values()))[-1], + } + update_dataframe_row(self.current_data, structure_id, launch_info) + + def launch(self): + """ + Helper method for launching firework + """ + rapidfire( + self.launchpad, + self.fworker, + nlaunches=self.nlaunches, + max_loops=self.max_loops, + sleep_time=self.sleep_time, + m_dir=self.m_dir, + ) + + @property + def agg_history(self): + """ + Aggregated history, i.e. in two single dataframes + corresponding to "current data" attributes and + results + + Returns: + (DataFrame): history of current data + (DataFrame): history of results + + """ + cd_list, cr_list = zip(*self._history) + return pd.concat(cd_list), pd.concat(cr_list) + + def get_delta_e(self, task_entry): + """ + Helper function to get the formation energy from task entry + Args: + task_entry (dict): tasks entry + Returns: + formation energy (float) + """ + total_e = task_entry["output"]["energy"] + composition = Structure.from_dict(task_entry["output"]["structure"]).composition + psp = task_entry["input"]["pseudo_potential"] + functional = psp["functional"].capitalize() + labels = psp["labels"] + potcar_symbols = [" ".join([functional, i]) for i in labels] + hubbards = task_entry["input"]["hubbards"] + return get_mp_formation_energy( + total_e, composition.__str__(), potcar_symbols, hubbards + ) class OqmdDFTonMC1(Experiment): @@ -36,10 +375,18 @@ class OqmdDFTonMC1(Experiment): system. """ - def __init__(self, poll_time=60, timeout=7200, current_data=None, job_status=None, - cleanup_directories=True, container_version="oqmdvasp/3", - batch_queue="oqmd_test_queue", use_cached=False, - prefix_append=None): + def __init__( + self, + poll_time=60, + timeout=7200, + current_data=None, + job_status=None, + cleanup_directories=True, + container_version="oqmdvasp/3", + batch_queue="oqmd_test_queue", + use_cached=False, + prefix_append=None, + ): """ Initializes an OqmdDFTonMC1 instance @@ -72,7 +419,7 @@ def __init__(self, poll_time=60, timeout=7200, current_data=None, job_status=Non "variable to use camd MC1 interface" ) - container, version = container_version.split('/') + container, version = container_version.split("/") parent_dir = os.path.join( tri_path, "model", @@ -82,8 +429,10 @@ def __init__(self, poll_time=60, timeout=7200, current_data=None, job_status=Non "camd", ) if prefix_append is not None: - if '_' in prefix_append: - raise ValueError("Prefix cannot contain underscores for mc1 compatibility") + if "_" in prefix_append: + raise ValueError( + "Prefix cannot contain underscores for mc1 compatibility" + ) parent_dir = os.path.join(parent_dir, prefix_append) self.parent_dir = parent_dir @@ -184,7 +533,7 @@ def clean_current_paths(self): """ for idx, row in self.current_data.iterrows(): # Clean simulation directories - sim_path = row['path'].replace("model", "simulation") + sim_path = row["path"].replace("model", "simulation") if os.path.exists(sim_path): shutil.rmtree(sim_path) @@ -238,25 +587,29 @@ def fetch_cached(self, candidate_data): s3_client = boto3.client("s3") # Scrub tri_path and replace model with simulation # to get s3 key - s3_parent = self.parent_dir.replace('model', 'simulation') - s3_parent = s3_parent.replace(tri_path + '/', "") + s3_parent = self.parent_dir.replace("model", "simulation") + s3_parent = s3_parent.replace(tri_path + "/", "") cached_experiments = pd.DataFrame() # Get all experiment folders - chemsyses = set([get_chemsys(s) for s in candidate_data['structure']]) + chemsyses = set([get_chemsys(s) for s in candidate_data["structure"]]) experiment_dirs = [] for chemsys in chemsyses: chemsys_dirs = get_common_prefixes( - tri_bucket, - os.path.join(s3_parent, chemsys) + tri_bucket, os.path.join(s3_parent, chemsys) ) experiment_dirs.extend(chemsys_dirs) - for structure_id, row in tqdm(candidate_data.iterrows(), total=len(candidate_data)): - if not structure_id.replace('-', '') in experiment_dirs: + for structure_id, row in tqdm( + candidate_data.iterrows(), total=len(candidate_data) + ): + if not structure_id.replace("-", "") in experiment_dirs: continue calc_path = os.path.join( - s3_parent, get_chemsys(row['structure']), - structure_id.replace('-', ''), "_1/") - with ScratchDir('.'): + s3_parent, + get_chemsys(row["structure"]), + structure_id.replace("-", ""), + "_1/", + ) + with ScratchDir("."): # Figure out whether prior submission exists cached_experiments = cached_experiments.append(row) # TODO: figure out whether file exists in s3 @@ -283,13 +636,12 @@ def fetch_cached(self, candidate_data): error_doc = {} try: err_obj = s3_client.get_object( - Bucket=tri_bucket, Key=os.path.join(calc_path, 'err')) - errtxt = err_obj['Body'].read().decode('utf-8') - error_doc.update( - {"mc1_stderr": errtxt} + Bucket=tri_bucket, Key=os.path.join(calc_path, "err") ) + errtxt = err_obj["Body"].read().decode("utf-8") + error_doc.update({"mc1_stderr": errtxt}) except ClientError: - print('No error file for {}'.format(calc_path)) + print("No error file for {}".format(calc_path)) error_doc.update( { "camd_exception": "{}".format(e), @@ -298,7 +650,9 @@ def fetch_cached(self, candidate_data): ) # Dump error docs to avoid Pandas issues with dict values data = {"status": "FAILED", "error": json.dumps(error_doc)} - update_dataframe_row(cached_experiments, structure_id, data, add_columns=True) + update_dataframe_row( + cached_experiments, structure_id, data, add_columns=True + ) return cached_experiments def submit_dft_calcs_to_mc1(self): @@ -317,8 +671,11 @@ def submit_dft_calcs_to_mc1(self): for structure_id, row in self.current_data.iterrows(): # Replace structure id in path to avoid confusing mc1 calc_path = os.path.join( - self.parent_dir, get_chemsys(row['structure']), - structure_id.replace('-', ''), "_1") + self.parent_dir, + get_chemsys(row["structure"]), + structure_id.replace("-", ""), + "_1", + ) os.makedirs(calc_path, exist_ok=True) with cd(calc_path): # Write input cif file and python model file @@ -355,9 +712,11 @@ def submit_dft_calcs_to_mc1(self): ) except subprocess.CalledProcessError as e: print(e.output) - data = {"path": os.getcwd(), - "status": "FAILED", - "error": "failed submission {}".format(e.output)} + data = { + "path": os.getcwd(), + "status": "FAILED", + "error": "failed submission {}".format(e.output), + } update_dataframe_row(self.current_data, structure_id, data) @@ -508,8 +867,51 @@ def get_qmpy_formation_energy(total_e, formula, n_atoms): return energy -def update_dataframe_row(dataframe, index, update_dict, - add_columns=False): +def get_mp_formation_energy( + total_e, formula, potcar_symbols, hubbards={}, explain=False +): + """ + Helper function to computer mp-compatible formation + energy using reference energies extracted from MP + + Args: + total_e (float): total energy (uncorrected) + formula (str): chemical formula + potcar_symbols (list): list of potcar symbols + hubbards (dict): hubbard value, if none, the + run_type = 'GGA' + else run_type = 'GGA+U' + explain (bool): whether to print out the explanation + of the correction + + Returns: + (float): mp-compatible formation energy (eV/atom) + + """ + compatibility = MaterialsProjectCompatibility() + comp = Composition(formula) + run_type = "GGA+U" if hubbards else "GGA" + is_hubbard = True if hubbards else False + entry = ComputedEntry( + composition=comp, + energy=total_e, + parameters={ + "potcar_symbols": potcar_symbols, + "run_type": run_type, + "hubbards": hubbards, + "is_hubbard": is_hubbard, + }, + ) + entry = compatibility.process_entry(entry) + if explain: + print(compatibility.explain(entry)) + energy = entry.energy + for el, occ in comp.items(): + energy -= MP_REFERENCES[el.name] * occ + return energy / comp.num_atoms + + +def update_dataframe_row(dataframe, index, update_dict, add_columns=False): """ Method to update a dataframe row via an update_dictionary and an index, similarly to Dict.update() @@ -529,4 +931,7 @@ def update_dataframe_row(dataframe, index, update_dict, for key, value in update_dict.items(): if add_columns and key not in dataframe.columns: dataframe[key] = None - dataframe.loc[index, key] = value + if isinstance(value, dict): + dataframe.loc[index, key] = json.dumps(value) + else: + dataframe.loc[index, key] = value diff --git a/camd/experiment/tests/Si_test.p b/camd/experiment/tests/Si_test.p new file mode 100644 index 00000000..2a89fd8a Binary files /dev/null and b/camd/experiment/tests/Si_test.p differ diff --git a/camd/experiment/tests/__init__.py b/camd/experiment/tests/__init__.py index df62c768..a3af540a 100644 --- a/camd/experiment/tests/__init__.py +++ b/camd/experiment/tests/__init__.py @@ -1,2 +1 @@ # Copyright (c) 2019 Toyota Research Institute. All rights reserved. - diff --git a/camd/experiment/tests/fe2o3.cif b/camd/experiment/tests/fe2o3.cif new file mode 100644 index 00000000..7248b316 --- /dev/null +++ b/camd/experiment/tests/fe2o3.cif @@ -0,0 +1,36 @@ +# generated using pymatgen +data_Fe2O3 +_symmetry_space_group_name_H-M 'P 1' +_cell_length_a 5.10499814 +_cell_length_b 5.10485749 +_cell_length_c 5.49506295 +_cell_angle_alpha 117.67780043 +_cell_angle_beta 89.99996354 +_cell_angle_gamma 120.00104735 +_symmetry_Int_Tables_number 1 +_chemical_formula_structural Fe2O3 +_chemical_formula_sum 'Fe4 O6' +_cell_volume 104.66803233 +_cell_formula_units_Z 2 +loop_ + _symmetry_equiv_pos_site_id + _symmetry_equiv_pos_as_xyz + 1 'x, y, z' +loop_ + _atom_site_type_symbol + _atom_site_label + _atom_site_symmetry_multiplicity + _atom_site_fract_x + _atom_site_fract_y + _atom_site_fract_z + _atom_site_occupancy + Fe Fe0 1 0.64582500 0.29165200 0.93747400 1 + Fe Fe1 1 0.35417500 0.70834900 0.06252600 1 + Fe Fe2 1 0.85417000 0.70834100 0.56251600 1 + Fe Fe3 1 0.14583000 0.29165900 0.43748400 1 + O O4 1 0.44512100 0.19512000 0.24999300 1 + O O5 1 0.55487900 0.80488000 0.75000600 1 + O O6 1 0.74999900 0.80487800 0.25000500 1 + O O7 1 0.25000100 0.19512100 0.74999500 1 + O O8 1 0.05489000 0.50000000 0.24999900 1 + O O9 1 0.94511000 0.50000000 0.75000100 1 diff --git a/camd/experiment/tests/test_agent_simulation.py b/camd/experiment/tests/test_agent_simulation.py index 5b9c1db5..6c767f6f 100644 --- a/camd/experiment/tests/test_agent_simulation.py +++ b/camd/experiment/tests/test_agent_simulation.py @@ -3,8 +3,11 @@ import unittest import pandas as pd from monty.tempfile import ScratchDir -from camd.utils.data import load_dataframe, partition_intercomp, \ - get_oqmd_data_by_chemsys +from camd.utils.data import ( + load_dataframe, + partition_intercomp, + get_oqmd_data_by_chemsys, +) from camd.agent.base import RandomAgent from camd.experiment.agent_simulation import LocalAgentSimulation from camd.analysis import StabilityAnalyzer @@ -12,18 +15,18 @@ class CampaignSimulationTest(unittest.TestCase): def test_run(self): - with ScratchDir('.'): + with ScratchDir("."): dataframe = get_oqmd_data_by_chemsys("Fe-O") cand, seed = partition_intercomp(dataframe, n_elements=1) agents_df = pd.DataFrame({"agent": [RandomAgent()]}) simulation = LocalAgentSimulation( - cand, iterations=5, seed_data=seed, - analyzer=StabilityAnalyzer()) + cand, iterations=5, seed_data=seed, analyzer=StabilityAnalyzer() + ) simulation.submit(agents_df) simulation.monitor() results = simulation.get_results() self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/camd/experiment/tests/test_atomate.py b/camd/experiment/tests/test_atomate.py new file mode 100644 index 00000000..9c53714d --- /dev/null +++ b/camd/experiment/tests/test_atomate.py @@ -0,0 +1,105 @@ +import unittest +import os +import numpy as np + +from pymatgen.core import Structure +from pymatgen.util.testing import PymatgenTest +from camd.experiment.dft import AtomateExperiment, get_mp_formation_energy +import pandas as pd +from fireworks import LaunchPad + +TEST_DIR = os.path.dirname(__file__) +ATOMATE_DFT_TESTS = np.all( + [ + os.path.isfile(os.path.join(TEST_DIR, i)) + for i in ["db.json", "my_launchpad.yaml"] + ] +) +SKIP_MSG = ( + "Long tests disabled, provide db.json, my_launchpad.yaml " + "in {} to run long tests".format(TEST_DIR) +) + + +class AtomateTest(PymatgenTest): + # @unittest.skipUnless(ATOMATE_DFT_TESTS, SKIP_MSG) + # def test_get(self) -> None: + # good_silicon = PymatgenTest.get_structure("Si") + # bad_silicon = good_silicon.copy() + # + # # Add another site at the same position + # bad_silicon.append("Si", [0.1, 0.1, 0.15]) + # bad_silicon.append("Si", [0.1, 0.333, 0.15]) + # self.assertEqual(len(bad_silicon), 4) + # data = pd.DataFrame({"structure": {"good": good_silicon, "bad": bad_silicon}}) + # db_file = os.path.join(TEST_DIR, "db.json") + # lpad_file = os.path.join(TEST_DIR, "my_launchpad.yaml") + # lpad = LaunchPad.from_file(lpad_file) + # lpad.auto_load() + # experiment = AtomateExperiment( + # lpad, db_file, poll_time=30, launch_from_local=False + # ) + # # experiment.submit(data) + # # status = experiment.monitor() + # results = experiment.get_results() + # + # self.assertAlmostEqual( + # results.loc["good", "final_energy_per_atom"], -5.420645, 5 + # ) + # self.assertIsNone(results.loc["bad", "task_id"]) + # self.assertEqual(experiment.job_status, "COMPLETED") + + @unittest.skipUnless(ATOMATE_DFT_TESTS, SKIP_MSG) + def test_delta_e(self) -> None: + fe2o3 = Structure.from_file(os.path.join(TEST_DIR, "fe2o3.cif")) # mp-19770 + data = pd.DataFrame( + {"structure": {"mp-19770": fe2o3}, "wf_name": "opt_mp-19770", "fw_id": 2749} + ) + db_file = os.path.join(TEST_DIR, "db.json") + lpad_file = os.path.join(TEST_DIR, "my_launchpad.yaml") + lpad = LaunchPad.from_file(lpad_file) + lpad.auto_load() + experiment = AtomateExperiment( + lpad, db_file, poll_time=30, launch_from_local=False + ) + experiment.update_current_data(data) + experiment.update_results() + self.assertAlmostEqual(experiment.current_data["delta_e"].values[0], -1.9072, 5) + + @unittest.skipUnless(ATOMATE_DFT_TESTS, SKIP_MSG) + def testCache(self): + db_file = os.path.join(TEST_DIR, "db.json") + lpad_file = os.path.join(TEST_DIR, "my_launchpad.yaml") + lpad = LaunchPad.from_file(lpad_file) + lpad.auto_load() + current_data = pd.read_pickle(os.path.join(TEST_DIR, "Si_test.p")) + experiment = AtomateExperiment( + lpad, db_file, poll_time=30, launch_from_local=False + ) + experiment.update_current_data(current_data) + experiment.update_results() + results = experiment.get_results() + self.assertAlmostEqual( + results.loc["good", "final_energy_per_atom"], -5.420645, 5 + ) + self.assertIsNone(results.loc["bad", "task_id"]) + self.assertEqual(experiment.job_status, "COMPLETED") + + def test_eform(self): + # SiO2 mp-546794, Fe2O3 mp-19770 + # mp-19770 + # uncorrected e -67.4928 + # corrected e -80.6388 + total_e = -67.491217 + formula = "Fe4O6" + potcar_symbols = ["PBE Fe_pv", "PBE O"] + hubbards = {"Fe": 5.3, "O": 0.0} + self.assertAlmostEqual( + get_mp_formation_energy(total_e, formula, potcar_symbols, hubbards), + -1.9072, + 5, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/camd/experiment/tests/test_base.py b/camd/experiment/tests/test_base.py index 30284dc0..34d156b9 100644 --- a/camd/experiment/tests/test_base.py +++ b/camd/experiment/tests/test_base.py @@ -7,15 +7,15 @@ class ATFSamplerTest(unittest.TestCase): def test_submit_get_results(self): - test_dataframe = pd.DataFrame({"index": np.arange(5), - "squared": np.arange(5) ** 2}) + test_dataframe = pd.DataFrame( + {"index": np.arange(5), "squared": np.arange(5) ** 2} + ) simple_exp = ATFSampler(test_dataframe) simple_exp.submit(test_dataframe.loc[[0, 2, 3]]) self.assertEqual(simple_exp.job_status, "COMPLETED") simple_exp.monitor() - self.assertTrue( - (simple_exp.get_results()['squared'] == [0, 4, 9]).all()) + self.assertTrue((simple_exp.get_results()["squared"] == [0, 4, 9]).all()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/camd/experiment/tests/test_dft.py b/camd/experiment/tests/test_dft.py index f60c8ee8..eb0a0bd7 100644 --- a/camd/experiment/tests/test_dft.py +++ b/camd/experiment/tests/test_dft.py @@ -27,32 +27,24 @@ def test_get(self): bad_silicon.append("Si", [0.1, 0.1, 0.15]) bad_silicon.append("Si", [0.1, 0.333, 0.15]) self.assertEqual(len(bad_silicon), 4) - data = pd.DataFrame( - {"structure": { - "good": good_silicon, - "bad": bad_silicon - } - } - ) + data = pd.DataFrame({"structure": {"good": good_silicon, "bad": bad_silicon}}) - experiment = OqmdDFTonMC1(poll_time=30, timeout=150, prefix_append=str(test_uuid)) + experiment = OqmdDFTonMC1( + poll_time=30, timeout=150, prefix_append=str(test_uuid) + ) experiment.submit(data) status = experiment.monitor() results = experiment.get_results() - self.assertAlmostEqual(results.loc['good', 'delta_e'], 0, 5) - self.assertIsNone(results.loc['bad', 'result']) + self.assertAlmostEqual(results.loc["good", "delta_e"], 0, 5) + self.assertIsNone(results.loc["bad", "result"]) # Test cleanup functionality experiment.poll_time = 1 experiment.timeout = 1 - old_paths = experiment.current_data['path'] + old_paths = experiment.current_data["path"] data = pd.DataFrame( - {"structure": { - "newgood": good_silicon, - "newbad": bad_silicon - } - } + {"structure": {"newgood": good_silicon, "newbad": bad_silicon}} ) experiment.submit(data) experiment.monitor() @@ -69,13 +61,16 @@ def test_caching(self): bad_silicon.append("Si", [0.1, 0.333, 0.15]) self.assertEqual(len(bad_silicon), 4) data = pd.DataFrame( - {"structure": { - "good": good_silicon, - "bad": bad_silicon, - } + { + "structure": { + "good": good_silicon, + "bad": bad_silicon, + } } ) - experiment = OqmdDFTonMC1(poll_time=30, timeout=150, prefix_append="camd-cache-test") + experiment = OqmdDFTonMC1( + poll_time=30, timeout=150, prefix_append="camd-cache-test" + ) # This generates the data if needed in the future # experiment.submit(data) # status = experiment.monitor() @@ -84,28 +79,25 @@ def test_caching(self): experiment.update_current_data(data) results = experiment.fetch_cached(data) - self.assertAlmostEqual(results.loc['good', 'delta_e'], 0, 5) - self.assertIsNone(results.loc['bad', 'result']) - self.assertEqual(results.loc['bad', 'status'], "FAILED") + self.assertAlmostEqual(results.loc["good", "delta_e"], 0, 5) + self.assertIsNone(results.loc["bad", "result"]) + self.assertEqual(results.loc["bad", "status"], "FAILED") @unittest.skipUnless(CAMD_DFT_TESTS, SKIP_MSG) def test_structure_suite(self): - mp_ids = ["mp-702", - "mp-1953", - "mp-1132", - "mp-8409", - "mp-872"] + mp_ids = ["mp-702", "mp-1953", "mp-1132", "mp-8409", "mp-872"] with MPRester() as mpr: - structure_dict = {mp_id: mpr.get_structure_by_material_id(mp_id) - for mp_id in mp_ids} + structure_dict = { + mp_id: mpr.get_structure_by_material_id(mp_id) for mp_id in mp_ids + } data = pd.DataFrame({"structure": structure_dict}) experiment = OqmdDFTonMC1(poll_time=25) experiment.submit(data) status = experiment.monitor() results = experiment.get_results() - self.assertTrue(all(results['status'] == "SUCCEEDED")) + self.assertTrue(all(results["status"] == "SUCCEEDED")) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/camd/tests/test_analysis.py b/camd/tests/test_analysis.py index 62c73909..9b05d610 100644 --- a/camd/tests/test_analysis.py +++ b/camd/tests/test_analysis.py @@ -4,10 +4,12 @@ import os import pandas as pd import pickle -from monty.serialization import loadfn from monty.tempfile import ScratchDir from camd import CAMD_TEST_FILES from camd.analysis import StabilityAnalyzer, AnalyzeStructures, GenericATFAnalyzer +from camd.experiment.base import ATFSampler +from camd.agent.base import RandomAgent +from camd.campaigns.base import Campaign from camd.utils.data import filter_dataframe_by_composition @@ -44,17 +46,18 @@ def test_analyze(self): new_exp_indices = ["mp-30998", "mp-572822"] new_experimental_results = seed_data.loc[new_exp_indices] seed_data = seed_data.drop(index=new_exp_indices) - summary, seed_data = analyzer.analyze( - new_experimental_results=seed_data, seed_data=pd.DataFrame(), + agent = RandomAgent() + exp = ATFSampler(df) + campaign = Campaign( + candidate_data=new_experimental_results, + agent=agent, experiment=exp, ) - summary, new_seed = analyzer.analyze( - new_experimental_results=new_experimental_results, - seed_data=seed_data - ) - self.assertAlmostEqual(new_seed.loc['mp-30998', 'stability'], 0) - self.assertAlmostEqual(new_seed.loc["mp-572822", 'stability'], 0.52784795) - self.assertTrue(new_seed.loc['mp-30998', 'is_stable']) - self.assertFalse(new_seed.loc["mp-572822", 'is_stable']) + exp.submit(new_experimental_results) + summary1 = analyzer.analyze(campaign) + campaign.seed_data = seed_data + summary2 = analyzer.analyze(campaign) + self.assertTrue(summary1.loc[0, 'new_discovery'], 2) + self.assertTrue(summary2.loc[0, 'new_discovery'], 1) class StructureAnalyzerTest(unittest.TestCase): @@ -73,15 +76,18 @@ def test_analyze(self): seed_size = 10 exploration_df = pd.read_csv(os.path.join(CAMD_TEST_FILES, "test_df_ATF.csv")) record = pickle.load(open(os.path.join(CAMD_TEST_FILES, "seed_data_ATF.pickle"), "rb")) - analyzer = GenericATFAnalyzer(exploration_df, percentile=0.01) - seed_data = pd.DataFrame(record[:seed_size]) - new_experimental_results = pd.DataFrame(record[seed_size:]) - summary, _ = analyzer.analyze(new_experimental_results, seed_data) + analyzer = GenericATFAnalyzer(percentile=0.01) + agent = RandomAgent() + exp = ATFSampler(exploration_df) + campaign = Campaign(candidate_data=exploration_df, agent=agent, experiment=exp) + # Submit best ones, ALM should be 1, True, 1 respectively + exp.submit(exploration_df.loc[[1679, 1737]]) + summary = analyzer.analyze(campaign) - self.assertEqual(summary['deALM'].to_list()[0].shape[0], record.shape[0]-seed_size) - self.assertEqual(summary['anyALM'].to_list()[0].shape[0], record.shape[0]-seed_size) - self.assertEqual(summary['allALM'].to_list()[0].shape[0], record.shape[0]-seed_size) - self.assertEqual(summary['simALM'].to_list()[0].shape[0], record.shape[0]-seed_size) + self.assertEqual(tuple(summary['deALM'].loc[0]), (1, 1)) + self.assertEqual(summary['anyALM'].loc[0], True) + self.assertAlmostEqual(summary['allALM'].loc[0], 2 / 19) + # self.assertEqual(summary['simALM'].to_list()[0].shape[0], record.shape[0]-seed_size) if __name__ == '__main__': diff --git a/camd/tests/test_atf_examples.py b/camd/tests/test_atf_examples.py index ad25c7fb..249cf41f 100644 --- a/camd/tests/test_atf_examples.py +++ b/camd/tests/test_atf_examples.py @@ -7,6 +7,7 @@ from sklearn.neural_network import MLPRegressor from sklearn.gaussian_process.kernels import RBF, ConstantKernel +from sklearn.model_selection import train_test_split from camd.agent.stability import QBCStabilityAgent, GaussianProcessStabilityAgent, \ BaggedGaussianProcessStabilityAgent, AgentStabilityAdaBoost @@ -58,6 +59,13 @@ def test_qbc_agent_loop(self): class AtfLoopTest(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.test_data = load_default_atf_data().sample(400) + cls.seed_data, cls.candidate_data = train_test_split( + cls.test_data, train_size=0.2, random_state=42 + ) + def setUp(self): self.pwd = os.getcwd() self.tempdir = tempfile.mkdtemp() @@ -68,13 +76,11 @@ def tearDown(self): shutil.rmtree(self.tempdir) def test_random_agent_loop(self): - df = load_default_atf_data() n_seed = 200 # Starting sample size agent = RandomAgent(n_query=10) analyzer = StabilityAnalyzer(hull_distance=0.05, parallel=False) - experiment = ATFSampler(dataframe=df) - candidate_data = df - new_loop = Campaign(candidate_data, agent, experiment, analyzer, + experiment = ATFSampler(dataframe=self.test_data) + new_loop = Campaign(self.test_data, agent, experiment, analyzer, create_seed=n_seed) new_loop.initialize() @@ -85,7 +91,7 @@ def test_random_agent_loop(self): self.assertTrue(True) # Testing the continuation - new_loop = Campaign(candidate_data, agent, experiment, analyzer, + new_loop = Campaign(self.candidate_data, agent, experiment, analyzer, create_seed=n_seed) self.assertTrue(new_loop.initialized) self.assertEqual(new_loop.iteration, 6) @@ -99,8 +105,9 @@ def test_qbc_agent_loop(self): df = pd.read_csv(os.path.join(CAMD_TEST_FILES, 'test_df.csv')) df_sub = df[df['N_species'] <= 3] n_seed = 200 # Starting sample size - agent = QBCStabilityAgent(model=MLPRegressor(hidden_layer_sizes=(84, 50)), - n_query=10, hull_distance=0.05, alpha=0.5) + agent = QBCStabilityAgent( + model=MLPRegressor(hidden_layer_sizes=(40, )), + n_query=10, hull_distance=0.05, alpha=0.5, feature_labels=df.columns[3:274]) analyzer = StabilityAnalyzer(hull_distance=0.05, parallel=False) experiment = ATFSampler(dataframe=df_sub) candidate_data = df_sub @@ -118,7 +125,10 @@ def test_simple_gp_loop(self): df_sub = df[df['N_species'] <= 3] n_seed = 200 # Starting sample size n_query = 10 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) - agent = GaussianProcessStabilityAgent(n_query=n_query, hull_distance=0.05, alpha=0.5, parallel=False) + agent = GaussianProcessStabilityAgent( + n_query=n_query, hull_distance=0.05, alpha=0.5, parallel=False, + feature_labels=df.columns[3:274] + ) analyzer = StabilityAnalyzer(hull_distance=0.05, parallel=False) experiment = ATFSampler(dataframe=df_sub) candidate_data = df_sub @@ -141,7 +151,8 @@ def test_gp_bagging(self): alpha=0.5, # Fraction of std to include in expected improvement n_estimators=2, max_samples=195, - parallel=False + parallel=False, + feature_labels=df.columns[3:274] ) analyzer = StabilityAnalyzer(hull_distance=0.05, parallel=False) experiment = ATFSampler(df_sub) @@ -161,7 +172,7 @@ def test_adaboost_loop(self): n_seed = 200 # Starting sample size agent = AgentStabilityAdaBoost(model=MLPRegressor(hidden_layer_sizes=(84, 50)), n_query=10, exploit_fraction=1.0, alpha=0.5, - n_estimators=10) + n_estimators=10, feature_labels=df.columns[3:274]) analyzer = StabilityAnalyzer(hull_distance=0.05, parallel=False) experiment = ATFSampler(df_sub) candidate_data = df_sub @@ -211,7 +222,7 @@ def test_mp_loop(self): self.assertEqual(new_loop.iteration, 7) -class GPBatchUCBAgent(unittest.TestCase): +class GPBatchUCBAgentTest(unittest.TestCase): def setUp(self): self.pwd = os.getcwd() self.tempdir = tempfile.mkdtemp() diff --git a/camd/tests/test_files/mn-ni-o-sb.pickle b/camd/tests/test_files/mn-ni-o-sb.pickle index 3ca2607a..3ac2220f 100644 Binary files a/camd/tests/test_files/mn-ni-o-sb.pickle and b/camd/tests/test_files/mn-ni-o-sb.pickle differ diff --git a/camd/tests/test_files/mp_test_data.pickle b/camd/tests/test_files/mp_test_data.pickle new file mode 100644 index 00000000..70810a6b Binary files /dev/null and b/camd/tests/test_files/mp_test_data.pickle differ diff --git a/camd/tests/test_files/test_m3gnet.pickle b/camd/tests/test_files/test_m3gnet.pickle new file mode 100644 index 00000000..410da7a1 Binary files /dev/null and b/camd/tests/test_files/test_m3gnet.pickle differ diff --git a/camd/tests/test_files/test_m3gnet_featurized.pickle b/camd/tests/test_files/test_m3gnet_featurized.pickle new file mode 100644 index 00000000..8db86662 Binary files /dev/null and b/camd/tests/test_files/test_m3gnet_featurized.pickle differ diff --git a/camd/utils/data.py b/camd/utils/data.py index 498ad3db..56f5ed5e 100644 --- a/camd/utils/data.py +++ b/camd/utils/data.py @@ -13,7 +13,22 @@ import numpy as np from pymatgen.core.composition import Composition from pymatgen.core.structure import Structure +from pymatgen.ext.matproj import MPRester from monty.os import makedirs_p +from matminer.featurizers.base import MultipleFeaturizer +from matminer.featurizers.composition import ( + ElementProperty, + Stoichiometry, + ValenceOrbital, + IonProperty, +) +from matminer.featurizers.structure import ( + SiteStatsFingerprint, + StructuralHeterogeneity, + ChemicalOrdering, + StructureComposition, + MaximumPackingEfficiency, +) from camd import CAMD_CACHE, tqdm @@ -32,7 +47,7 @@ def load_dataframe(dataset_name): (DataFrame): dataframe corresponding to specified dataset """ - filename = '{}.pickle'.format(dataset_name) + filename = "{}.pickle".format(dataset_name) cache_matrio_data(filename) return pd.read_pickle(os.path.join(CAMD_CACHE, filename)) @@ -49,7 +64,7 @@ def load_default_atf_data(): """ df = load_dataframe("oqmd_1.2_voronoi_magpie_fingerprints") - return df[df['N_species'] == 2].sample(frac=0.2) + return df[df["N_species"] == 2].sample(frac=0.2) def filter_dataframe_by_composition(df, composition, formula_column="Composition"): @@ -73,8 +88,9 @@ def filter_dataframe_by_composition(df, composition, formula_column="Composition # Get elements in formula, composition, then filter chemsys = set(Composition(composition).keys()) all_comps = df[formula_column].apply(Composition) - indices_to_include = [ind for ind, comp in all_comps.items() - if comp.keys() <= chemsys] + indices_to_include = [ + ind for ind, comp in all_comps.items() if comp.keys() <= chemsys + ] return df.loc[indices_to_include] @@ -95,117 +111,222 @@ def get_oqmd_data_by_chemsys(chemsys, drop_duplicates=True): """ all_data = load_dataframe("oqmd_1.2_voronoi_magpie_fingerprints") - dataset = filter_dataframe_by_composition( - all_data, chemsys.replace('-', '') - ) + dataset = filter_dataframe_by_composition(all_data, chemsys.replace("-", "")) if drop_duplicates: - dataset = dataset.drop_duplicates(keep='first') + dataset = dataset.drop_duplicates(keep="first") return dataset +def get_mp_ele_ref(element): + """ + Helper function to get the MP compatible elemental references + to calculate the formation energy + Args: + element (str): the element + Returns: + the minimum energy of the unary phase of the element from MP + """ + with MPRester() as mpr: + energies = [e.energy_per_atom for e in mpr.get_entries_in_chemsys([element])] + return min(energies) + + +MP_REFERENCES = { + "H": -3.39271585, + "He": -0.00902216, + "Li": -1.9089228533333333, + "Be": -3.739412865, + "B": -6.679390641666667, + "C": -9.22676982, + "N": -8.336493965, + "O": -4.94795546875, + "F": -1.9114557725, + "Ne": -0.02582742, + "Na": -1.312223005, + "Mg": -1.5968959166666667, + "Al": -3.74557583, + "Si": -5.423390655, + "P": -5.413285861428571, + "S": -4.136449866875, + "Cl": -1.8485262325, + "Ar": -0.06880822, + "K": -1.110398947, + "Ca": -1.999462735, + "Sc": -6.332469105, + "Ti": -7.895052840000001, + "V": -9.08235617, + "Cr": -9.65304747, + "Mn": -9.161706470344827, + "Fe": -8.46929704, + "Co": -7.108317795, + "Ni": -5.7798218, + "Cu": -4.09920667, + "Zn": -1.259460605, + "Ga": -3.02808992, + "Ge": -4.61751163, + "As": -4.65850806, + "Se": -3.49591147765625, + "Br": -1.63692654, + "Kr": -0.05671467, + "Rb": -0.9805340725, + "Sr": -1.6894812533333334, + "Y": -6.466074656666667, + "Zr": -8.54770063, + "Nb": -10.10130504, + "Mo": -10.84563514, + "Tc": -10.36061991, + "Ru": -9.27438911, + "Rh": -7.33850956, + "Pd": -5.17648694, + "Ag": -2.8325290566666665, + "Cd": -0.90620278, + "In": -2.75168373, + "Sn": -3.99229498, + "Sb": -4.128999585, + "Te": -3.14330093, + "I": -1.524009065, + "Xe": -0.03617417, + "Cs": -0.8954023720689656, + "Ba": -1.91897083, + "La": -4.936007105, + "Ce": -5.933089155, + "Pr": -4.780899145, + "Nd": -4.76814321, + "Pm": -4.7505423225, + "Sm": -4.717682476666667, + "Eu": -10.292043475, + "Gd": -14.07612224, + "Tb": -4.6343661, + "Dy": -4.60678684, + "Ho": -4.58240887, + "Er": -4.5674248, + "Tm": -4.475058396666666, + "Yb": -1.5395952733333333, + "Lu": -4.52095052, + "Hf": -9.95718477, + "Ta": -11.85777728, + "W": -12.95812647, + "Re": -12.444527185, + "Os": -11.22733445, + "Ir": -8.83843042, + "Pt": -6.07090771, + "Au": -3.27388154, + "Hg": -0.30362902, + "Tl": -2.36165292, + "Pb": -3.71258955, + "Bi": -3.88641638, + "Ac": -4.1211750075, + "Th": -7.41385825, + "Pa": -9.51466466, + "U": -11.29141001, + "Np": -12.94777968125, + "Pu": -14.26782579, +} + QMPY_REFERENCES = { - u'Ac': -4.1060035325, - u'Ag': -2.8217729525, - u'Al': -3.74573946, - u'Ar': -0.00636995, - u'As': -4.651918435, - u'Au': -3.26680174, - u'B': -6.67796758, - u'Ba': -1.92352708, - u'Be': -3.75520865, - u'Bi': -4.038931855, - u'Br': -1.31759562258416, - u'C': -9.2170759925, - u'Ca': -1.977817, - u'Cd': -0.90043514, - u'Ce': -4.7771708225, - u'Cl': -1.47561368438088, - u'Co': -7.089565, - u'Cr': -9.50844998, - u'Cs': -0.85462775, - u'Cu': -3.7159594, - u'Dy': -4.60150328333333, - u'Er': -4.56334055, - u'Eu': -1.8875732, - u'F': -1.45692429086889, - u'Fe': -8.3078978, - u'Ga': -3.031846515, - u'Gd': -4.6550712925, - u'Ge': -4.623692585, - u'H': -3.38063384781582, - u'He': -0.004303435, - u'Hf': -9.955368785, - u'Hg': -0.358963825033731, - u'Ho': -4.57679364666667, - u'I': -1.35196205757168, - u'In': -2.71993876, - u'Ir': -8.8549203, - u'K': -1.096699335, - u'Kr': -0.004058825, - u'La': -4.93543556, - u'Li': -1.89660627, - u'Lu': -4.524181525, - u'Mg': -1.54251595083333, - u'Mn': -9.0269032462069, - u'Mo': -10.8480839, - u'N': -8.11974103465649, - u'Na': -1.19920373914835, - u'Nb': -10.09391206, - u'Nd': -4.762916335, - u'Ne': -0.02931791, - u'Ni': -5.56661952, - u'Np': -12.94027372125, - u'O': -4.52329546412125, - u'Os': -11.22597601, - u'P': -5.15856496104006, - u'Pa': -9.49577589, - u'Pb': -3.70396484, - u'Pd': -5.17671826, - u'Pm': -4.7452352875, - u'Pr': -4.7748066125, - u'Pt': -6.05575959, - u'Pu': -14.29838348, - u'Rb': -0.9630733, - u'Re': -12.422818875, - u'Rh': -7.26940476, - u'Ru': -9.2019888, - u'S': -3.83888286598664, - u'Sb': -4.117563025, - u'Sc': -6.328367185, - u'Se': -3.48117276, - u'Si': -5.424892535, - u'Sm': -4.7147675825, - u'Sn': -3.9140929231488, - u'Sr': -1.6829138, - u'Ta': -11.85252937, - u'Tb': -5.28775675533333, - u'Tc': -10.360747885, - u'Te': -3.14184237666667, - u'Th': -7.41301719, - u'Ti': -7.69805778621374, - u'Tl': -2.359420025, - u'Tm': -4.47502416, - u'U': -11.292348705, - u'V': -8.94097896, - u'W': -12.96020695, - u'Xe': 0.00306349, - u'Y': -6.464420635, - u'Yb': -1.51277545, - u'Zn': -1.2660268, - u'Zr': -8.54717235} + "Ac": -4.1060035325, + "Ag": -2.8217729525, + "Al": -3.74573946, + "Ar": -0.00636995, + "As": -4.651918435, + "Au": -3.26680174, + "B": -6.67796758, + "Ba": -1.92352708, + "Be": -3.75520865, + "Bi": -4.038931855, + "Br": -1.31759562258416, + "C": -9.2170759925, + "Ca": -1.977817, + "Cd": -0.90043514, + "Ce": -4.7771708225, + "Cl": -1.47561368438088, + "Co": -7.089565, + "Cr": -9.50844998, + "Cs": -0.85462775, + "Cu": -3.7159594, + "Dy": -4.60150328333333, + "Er": -4.56334055, + "Eu": -1.8875732, + "F": -1.45692429086889, + "Fe": -8.3078978, + "Ga": -3.031846515, + "Gd": -4.6550712925, + "Ge": -4.623692585, + "H": -3.38063384781582, + "He": -0.004303435, + "Hf": -9.955368785, + "Hg": -0.358963825033731, + "Ho": -4.57679364666667, + "I": -1.35196205757168, + "In": -2.71993876, + "Ir": -8.8549203, + "K": -1.096699335, + "Kr": -0.004058825, + "La": -4.93543556, + "Li": -1.89660627, + "Lu": -4.524181525, + "Mg": -1.54251595083333, + "Mn": -9.0269032462069, + "Mo": -10.8480839, + "N": -8.11974103465649, + "Na": -1.19920373914835, + "Nb": -10.09391206, + "Nd": -4.762916335, + "Ne": -0.02931791, + "Ni": -5.56661952, + "Np": -12.94027372125, + "O": -4.52329546412125, + "Os": -11.22597601, + "P": -5.15856496104006, + "Pa": -9.49577589, + "Pb": -3.70396484, + "Pd": -5.17671826, + "Pm": -4.7452352875, + "Pr": -4.7748066125, + "Pt": -6.05575959, + "Pu": -14.29838348, + "Rb": -0.9630733, + "Re": -12.422818875, + "Rh": -7.26940476, + "Ru": -9.2019888, + "S": -3.83888286598664, + "Sb": -4.117563025, + "Sc": -6.328367185, + "Se": -3.48117276, + "Si": -5.424892535, + "Sm": -4.7147675825, + "Sn": -3.9140929231488, + "Sr": -1.6829138, + "Ta": -11.85252937, + "Tb": -5.28775675533333, + "Tc": -10.360747885, + "Te": -3.14184237666667, + "Th": -7.41301719, + "Ti": -7.69805778621374, + "Tl": -2.359420025, + "Tm": -4.47502416, + "U": -11.292348705, + "V": -8.94097896, + "W": -12.96020695, + "Xe": 0.00306349, + "Y": -6.464420635, + "Yb": -1.51277545, + "Zn": -1.2660268, + "Zr": -8.54717235, +} QMPY_REFERENCES_HUBBARD = { - u'Co': 2.0736240219357, - u'Cr': 2.79591214925926, - u'Cu': 1.457571831687, - u'Fe': 2.24490453841424, - u'Mn': 2.08652912841877, - u'Ni': 2.56766185643768, - u'Np': 2.77764768949249, - u'Pu': 2.2108747749433, - u'Th': 1.06653674624248, - u'U': 2.57513786752409, - u'V': 2.67812162528461 + "Co": 2.0736240219357, + "Cr": 2.79591214925926, + "Cu": 1.457571831687, + "Fe": 2.24490453841424, + "Mn": 2.08652912841877, + "Ni": 2.56766185643768, + "Np": 2.77764768949249, + "Pu": 2.2108747749433, + "Th": 1.06653674624248, + "U": 2.57513786752409, + "V": 2.67812162528461, } ELEMENTS = [ @@ -321,10 +442,10 @@ def cache_download(url, path): if not os.path.isfile(cache_path): makedirs_p(os.path.split(cache_path)[0]) r = requests.get(url, stream=True) - total_size = int(r.headers.get('content-length', 0)) + total_size = int(r.headers.get("content-length", 0)) block_size = 1024 # 1 Kibibyte - t = tqdm(total=total_size, unit='iB', unit_scale=True) - with open(cache_path, 'wb') as f: + t = tqdm(total=total_size, unit="iB", unit_scale=True) + with open(cache_path, "wb") as f: for data in r.iter_content(block_size): t.update(len(data)) f.write(data) @@ -335,11 +456,11 @@ def cache_download(url, path): "oqmd1.2_exp_based_entries_featurized_v2.pickle": "5e3b0e9bc91e209071f33ce8", "oqmd_1.2_voronoi_magpie_fingerprints.pickle": "5e39ce2cd9f13e075b7dfaaf", "oqmd_ver3.db": "5e39ce96d9f13e075b7dfab3", - "oqmd1.2_exp_based_entries_structures.json": "5e45befef23b399192b35242" + "oqmd1.2_exp_based_entries_structures.json": "5e45befef23b399192b35242", } -def get_chemsys(formula_or_structure, seperator='-'): +def get_chemsys(formula_or_structure, seperator="-"): """ Gets a sorted, character-delimited set of elements, e.g. Fe-Ni-O or O-Ti @@ -396,8 +517,7 @@ def partition_intercomp(dataframe, n_elements=None): (DataFrame): data which are below the n_element threshold """ - all_n_elts = [len(Composition(formula)) - for formula in dataframe['Composition']] + all_n_elts = [len(Composition(formula)) for formula in dataframe["Composition"]] if n_elements is None: n_elements = max(all_n_elts) - 1 @@ -426,7 +546,7 @@ def s3_sync(s3_bucket, s3_prefix, sync_path="."): for path, subdirs, files in os.walk(sync_path): # Get relative path prefix relpath = os.path.relpath(path, sync_path) - if not relpath.startswith('.'): + if not relpath.startswith("."): prefix = os.path.join(s3_prefix, relpath) else: prefix = s3_prefix @@ -448,11 +568,11 @@ def s3_key_exists(key, bucket): (bool): whether the key was found in the bucket """ - s3 = boto3.resource('s3') + s3 = boto3.resource("s3") try: s3.Object(bucket, key).load() except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == "404": + if e.response["Error"]["Code"] == "404": # The object does not exist. return False else: @@ -476,7 +596,7 @@ def download_s3_file(key, bucket, output_filename): (bool): whether the key was found in the bucket """ - s3_client = boto3.client('s3') + s3_client = boto3.client("s3") s3_client.download_file(bucket, key, output_filename) return True @@ -494,7 +614,7 @@ def upload_s3_file(key, bucket, filename): (bool): whether the key was found in the bucket """ - s3_client = boto3.client('s3') + s3_client = boto3.client("s3") s3_client.upload_file(filename, bucket, key) return True @@ -511,11 +631,36 @@ def get_common_prefixes(bucket, prefix): Returns: """ - if not prefix.endswith('/'): + if not prefix.endswith("/"): prefix += "/" - client = boto3.client('s3') - paginator = client.get_paginator('list_objects') - result = paginator.paginate(Bucket=bucket, Delimiter='/', Prefix=prefix) - return [common_prefix['Prefix'].split('/')[-2] - for common_prefix in result.search("CommonPrefixes") - if common_prefix] + client = boto3.client("s3") + paginator = client.get_paginator("list_objects") + result = paginator.paginate(Bucket=bucket, Delimiter="/", Prefix=prefix) + return [ + common_prefix["Prefix"].split("/")[-2] + for common_prefix in result.search("CommonPrefixes") + if common_prefix + ] + + +def get_default_featurizer(): + """ + Utility function to get CAMD's default featurizer from Ward et al. (2017) + + Returns: + (Featurizer): default CAMD featurizer + + """ + return MultipleFeaturizer( + [ + SiteStatsFingerprint.from_preset("CoordinationNumber_ward-prb-2017"), + StructuralHeterogeneity(), + ChemicalOrdering(), + MaximumPackingEfficiency(), + SiteStatsFingerprint.from_preset("LocalPropertyDifference_ward-prb-2017"), + StructureComposition(Stoichiometry()), + StructureComposition(ElementProperty.from_preset("magpie")), + StructureComposition(ValenceOrbital(props=["frac"])), + StructureComposition(IonProperty(fast=True)), + ] + ) diff --git a/examples/adaboost_atf_structure_discovery.ipynb b/examples/adaboost_atf_structure_discovery.ipynb new file mode 100644 index 00000000..61f9d252 --- /dev/null +++ b/examples/adaboost_atf_structure_discovery.ipynb @@ -0,0 +1,361 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "787affea", + "metadata": {}, + "source": [ + "# Using the Adaboost agent for structure discovery" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "274a4b05", + "metadata": {}, + "outputs": [], + "source": [ + "# Copyright Toyota Research Institute 2019\n", + "from camd.campaigns.base import Campaign\n", + "from sklearn.neural_network import MLPRegressor\n", + "from camd.agent.stability import AgentStabilityAdaBoost\n", + "from camd.analysis import StabilityAnalyzer\n", + "from camd.experiment.base import ATFSampler\n", + "from camd.utils.data import load_default_atf_data\n", + "import os\n", + "import shutil" + ] + }, + { + "cell_type": "markdown", + "id": "4c15b8d0", + "metadata": {}, + "source": [ + "Load dataset - this default dataset contains all of the OQMD binary compounds, including Composition (or reduced_formula), delta_e (formation_energy_per_atom), and features computed according to Ward et al. (2017)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "fc965547", + "metadata": {}, + "outputs": [], + "source": [ + "df = load_default_atf_data()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6699af90", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Compositiondelta_e
15232Tb2Se3-1.618293
310747Pr3Os0.322557
17129Tl2Pt-0.062342
310689Gd3F-0.196685
1103653RbGe0.868843
\n", + "
" + ], + "text/plain": [ + " Composition delta_e\n", + "15232 Tb2Se3 -1.618293\n", + "310747 Pr3Os 0.322557\n", + "17129 Tl2Pt -0.062342\n", + "310689 Gd3F -0.196685\n", + "1103653 RbGe 0.868843" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df[['Composition', 'delta_e']].head()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "323811ca", + "metadata": {}, + "outputs": [], + "source": [ + "##########################################################\n", + "# Binary stable material discovery 50:50 explore/exploit agent\n", + "##########################################################\n", + "n_seed = 200 # Starting sample size - a seed of this size will be randomly chosen.\n", + "n_query = 20 # This many new candidates are \"calculated with DFT\" (i.e. requested from Oracle -- DFT)\n", + "agent = AgentStabilityAdaBoost(\n", + " model=MLPRegressor(hidden_layer_sizes=(40, 20)),\n", + " n_query=n_query,\n", + " hull_distance=0.05,\n", + " uncertainty=True,\n", + " exploit_fraction=0.5,\n", + " n_estimators=10\n", + ")\n", + "analyzer = StabilityAnalyzer(hull_distance=0.05)\n", + "experiment = ATFSampler(dataframe=df)\n", + "candidate_data = df" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "59ace130", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "/Users/josephmontoya/PycharmProjects/camd/camd_public/camd/analysis.py:505: UserWarning: Number of elements too high for phase diagram plotting\n", + " warnings.warn(\"Number of elements too high for phase diagram plotting\")\n", + "Campaign 0 state: Agent AgentStabilityAdaBoost hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "/Users/josephmontoya/PycharmProjects/camd/camd_public/camd/analysis.py:505: UserWarning: Number of elements too high for phase diagram plotting\n", + " warnings.warn(\"Number of elements too high for phase diagram plotting\")\n", + "Campaign 1 state: Agent AgentStabilityAdaBoost hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "/Users/josephmontoya/PycharmProjects/camd/camd_public/camd/analysis.py:505: UserWarning: Number of elements too high for phase diagram plotting\n", + " warnings.warn(\"Number of elements too high for phase diagram plotting\")\n", + "Campaign 2 state: Agent AgentStabilityAdaBoost hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "/Users/josephmontoya/PycharmProjects/camd/camd_public/camd/analysis.py:505: UserWarning: Number of elements too high for phase diagram plotting\n", + " warnings.warn(\"Number of elements too high for phase diagram plotting\")\n", + "Campaign 3 state: Agent AgentStabilityAdaBoost hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "/Users/josephmontoya/PycharmProjects/camd/camd_public/camd/analysis.py:505: UserWarning: Number of elements too high for phase diagram plotting\n", + " warnings.warn(\"Number of elements too high for phase diagram plotting\")\n", + "Campaign 4 state: Agent AgentStabilityAdaBoost hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "/Users/josephmontoya/PycharmProjects/camd/camd_public/camd/analysis.py:505: UserWarning: Number of elements too high for phase diagram plotting\n", + " warnings.warn(\"Number of elements too high for phase diagram plotting\")\n" + ] + } + ], + "source": [ + "# Usually takes ~10 minutes\n", + "path = os.path.join(os.getcwd(), \"adaboost_structure_discovery\")\n", + "shutil.rmtree(path, ignore_errors=True)\n", + "os.makedirs(path)\n", + "new_loop = Campaign(\n", + " candidate_data, agent, experiment, analyzer,\n", + " create_seed=n_seed, path=path\n", + ")\n", + "new_loop.auto_loop(n_iterations=4, initialize=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "448c40e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
new_candidatesnew_discoverytotal_discovery
0200108108
1201529
220927
3201032
4201032
\n", + "
" + ], + "text/plain": [ + " new_candidates new_discovery total_discovery\n", + "0 200 108 108\n", + "1 20 15 29\n", + "2 20 9 27\n", + "3 20 10 32\n", + "4 20 10 32" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_loop.history" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "13a90c71", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "new_loop.history.plot.bar(y='total_discovery')\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/agent_adaboost/agent_adaboost.py b/examples/agent_adaboost/agent_adaboost.py deleted file mode 100644 index f083e5ab..00000000 --- a/examples/agent_adaboost/agent_adaboost.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright Toyota Research Institute 2019 -from camd.campaigns.base import Campaign - -from sklearn.neural_network import MLPRegressor -from camd.agent.stability import AgentStabilityAdaBoost -from camd.analysis import StabilityAnalyzer -from camd.experiment.base import ATFSampler -from camd.utils.data import load_default_atf_data - -########################################################## -# Load dataset and filter by N_species of 2 or less -########################################################## -df = load_default_atf_data() - -########################################################## -# Binary stable material discovery 50:50 explore/exploit agent -########################################################## -n_seed = 5000 # Starting sample size - a seed of this size will be randomly chosen. -n_query = 200 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) -agent = AgentStabilityAdaBoost( - model=MLPRegressor(hidden_layer_sizes=(84, 50)), - n_query=n_query, - hull_distance=0.05, - uncertainty=True, - exploit_fraction=0.75, - n_estimators=20 -) -analyzer = StabilityAnalyzer(hull_distance=0.05) -experiment = ATFSampler(dataframe=df) -candidate_data = df - -new_loop = Campaign( - candidate_data, agent, experiment, analyzer, - create_seed=n_seed -) - -new_loop.auto_loop(n_iterations=4, initialize=True) diff --git a/examples/agent_adaboost/consumed_candidates.json b/examples/agent_adaboost/consumed_candidates.json deleted file mode 100644 index d0df54d9..00000000 --- a/examples/agent_adaboost/consumed_candidates.json +++ /dev/null @@ -1 +0,0 @@ -[759310, 308874, 35062, 758431, 323672, 307394, 321672, 305831, 309823, 1105371, 35638, 308630, 344302, 322148, 309075, 1218224, 754660, 322397, 1007068, 311942, 757654, 754185, 346952, 754748, 325248, 313989, 349309, 304664, 752088, 1223877, 1228698, 30365, 299540, 301352, 302271, 1218897, 327516, 323581, 298349, 302429, 1105362, 300170, 337972, 758671, 1230360, 1225711, 347445, 21303, 308561, 1105994, 307939, 322790, 2752, 1106291, 343502, 327678, 426474, 308467, 759341, 755799, 1224072, 298307, 314049, 324473, 1235992, 345511, 758834, 308570, 314937, 337991, 289632, 325418, 324751, 303182, 1105465, 1104498, 320608, 643983, 31118, 327131, 325481, 755360, 35409, 757527, 289466, 320785, 73215, 311608, 321494, 1105157, 324091, 1228450, 335994, 19330, 343543, 19774, 1224170, 67069, 1228452, 753986, 1228422, 1105231, 425838, 289824, 1232968, 35464, 7477, 1229173, 319820, 759497, 1228496, 307046, 310287, 25113, 29912, 1229582, 753961, 304969, 300952, 45036, 1228940, 336939, 298959, 662645, 301671, 290369, 3388, 322059, 326912, 1218381, 343345, 313577, 313774, 305289, 348324, 323083, 757486, 308776, 302698, 337465, 344381, 8187, 297460, 1223761, 4801, 299095, 349397, 6056, 301943, 338855, 757570, 290475, 290290, 1230185, 1222892, 1234856, 310889, 306281, 337915, 302507, 14945, 25658, 1234870, 327506, 318842, 4391, 29538, 1105177, 309959, 309473, 325903, 1105448, 1235008, 28699, 1221104, 1235006, 302518, 759116, 1104066, 17558, 336085, 304869, 1223499, 309611, 301910, 1228049, 1223805, 751974, 90794, 1224222, 752796, 994657, 319367, 290742, 325339, 592447, 321692, 348811, 321593, 759245, 328559, 1104148, 328347, 328791, 326124, 760359, 319630, 300340, 344952, 1105821, 647142, 307142, 300342, 325699, 301251, 34828, 1218118, 344345, 325958, 307498, 304206, 348459, 17932, 323802, 304355, 1104563, 338991, 18584, 308948, 302145, 30958, 1106516, 14767, 306013, 1224039, 1235939, 303059, 343663, 672048, 345699, 325252, 757614, 10445, 425507, 327916, 348699, 324296, 322618, 757240, 311163, 321492, 290896, 754330, 17764, 753506, 717240, 4505, 290183, 348879, 314889, 323187, 13826, 1232359, 336933, 309174, 327764, 94922, 758629, 303019, 1231156, 1236262, 300545, 758807, 336401, 1230528, 299731, 755351, 7958, 314407, 28999, 754929, 327609, 337373, 302885, 1232219, 327419, 307313, 320757, 1222396, 754322, 759694, 344969, 312036, 1230376, 343098, 753828, 749384, 308909, 326730, 425731, 19912, 306399, 7729, 29081, 349265, 754187, 306668, 344073, 345985, 754732, 66908, 305517, 323447, 318769, 1104792, 298574, 1105914, 347775, 1218387, 313614, 426495, 311184, 1036082, 17940, 302272, 18900, 1230337, 311870, 3731, 753277, 305711, 326125, 302378, 30160, 323871, 297612, 303648, 290257, 336316, 644822, 1227078, 1227815, 1230355, 1227964, 304521, 311696, 314299, 328966, 343636, 320347, 306253, 309620, 304856, 22994, 301796, 30376, 1104115, 1106081, 290126, 302546, 321375, 323053, 8194, 309018, 1220309, 311886, 1229957, 339281, 328790, 1223617, 311397, 345286, 1230580, 311446, 343252, 2560, 1103858, 1104226, 307006, 310040, 319010, 328401, 1106588, 302663, 343732, 349405, 321199, 22613, 345884, 67339, 751748, 348624, 338971, 313377, 758304, 322911, 348791, 4549, 35081, 8104, 757097, 753656, 314893, 320490, 298401, 337618, 1222176, 759608, 752616, 323713, 35025, 91367, 755295, 323208, 302477, 1105234, 28043, 1105150, 323605, 35003, 1229385, 1224061, 345114, 320713, 337359, 691766, 325374, 17583, 298599, 325704, 1218535, 1225893, 1228420, 320683, 312196, 1236190, 715990, 319842, 1230736, 1106138, 1105531, 343522, 322416, 759775, 324837, 346933, 327155, 18879, 752313, 345019, 291004, 339034, 300176, 346767, 313556, 336543, 318583, 1105549, 753817, 1227865, 1218813, 22483, 8239, 327285, 323021, 309172, 10969, 308210, 347947, 325239, 305692, 320319, 1105778, 308597, 1220900, 310995, 425652, 321355, 1228624, 327776, 321637, 305862, 320469, 301937, 338749, 344223, 1225184, 322577, 307622, 1227823, 337371, 1105367, 303421, 1104255, 345592, 1039041, 343710, 1222590, 1222776, 1222370, 312462, 310978, 318763, 338669, 300253, 327953, 1229532, 1230861, 301325, 322496, 344158, 7940, 337682, 31085, 1222394, 14927, 298076, 17954, 290406, 754617, 345134, 2425, 1225105, 1103990, 321242, 758924, 1104486, 1218962, 752681, 1218245, 298355, 7984, 309032, 1231171, 307018, 308386, 300274, 760303, 303173, 318792, 289572, 290486, 1224132, 337254, 303490, 1104126, 760193, 754837, 1223258, 327151, 337911, 18561, 324177, 757623, 1230204, 1218302, 1230846, 326547, 303699, 289563, 755764, 347652, 343313, 320868, 346386, 12866, 297519, 426479, 754420, 290441, 349308, 344096, 321049, 1227917, 319896, 1234069, 753096, 426304, 328378, 1222718, 302771, 6973, 343256, 310199, 310801, 30143, 348557, 349858, 348255, 1229984, 302529, 313702, 300696, 299393, 337071, 301465, 290299, 426006, 306287, 1222887, 1229233, 301797, 1218747, 757595, 343982, 309964, 304386, 322004, 306793, 298936, 328481, 757060, 290381, 303773, 34927, 4958, 305069, 344792, 297964, 320780, 305303, 321909, 323006, 752447, 1040072, 338013, 306697, 321611, 306370, 757739, 35503, 313131, 345296, 1040124, 322451, 305489, 328686, 322598, 312026, 1234858, 1218873, 314544, 304871, 1104723, 343923, 752648, 344218, 301242, 754474, 1219023, 1228305, 324760, 1222351, 44873, 753575, 1224253, 1218323, 19484, 1228231, 337495, 318955, 337730, 1229574, 8278, 313133, 302716, 19132, 1232446, 307804, 35580, 1219256, 1218785, 5187, 328622, 301418, 4426, 299413, 303011, 346437, 1810, 1104533, 1217577, 339155, 1039321, 1228030, 290859, 309550, 349291, 5897, 313575, 300843, 67305, 308436, 759424, 1225669, 28228, 345346, 347584, 47717, 4934, 347776, 310929, 1218343, 1224163, 1218479, 67340, 1227961, 1107051, 325148, 1228933, 307667, 337176, 337548, 346102, 751932, 349080, 298923, 337397, 336188, 326262, 313097, 320798, 297404, 1104159, 311631, 324854, 7889, 339377, 756770, 758856, 302929, 759921, 752481, 1105376, 327137, 1104040, 752461, 299408, 428191, 349274, 305688, 5079, 348630, 343267, 711515, 1230339, 325681, 10967, 306862, 756329, 304104, 1219376, 319907, 755336, 346950, 347747, 346200, 339196, 752024, 754052, 347494, 2965, 299956, 301057, 67180, 1104774, 426526, 310476, 348062, 347222, 304055, 323723, 345443, 107703, 1229767, 1202082, 1217850, 313188, 311587, 2086, 1223471, 6700, 339384, 1223144, 711585, 336509, 752019, 759263, 308610, 337662, 321638, 751670, 1232040, 1224063, 1228932, 1105121, 1230663, 347671, 1222464, 336452, 314498, 337477, 1039288, 299976, 301268, 1105554, 336377, 344852, 7335, 323807, 289902, 303777, 1222723, 351811, 348723, 302839, 31229, 1221941, 323425, 427954, 320022, 1226079, 35492, 67174, 645788, 643385, 751580, 304507, 322753, 1233775, 345440, 1106910, 756283, 306549, 1235966, 336765, 755469, 311015, 337431, 312551, 755797, 336383, 10603, 320759, 320273, 35049, 4609, 344564, 78494, 1105535, 1230997, 310291, 754421, 1104672, 1230783, 319027, 754313, 20277, 303450, 1106927, 343158, 322712, 1236029, 327359, 19440, 1795, 5151, 1228201, 300137, 756142, 346303, 1104264, 347644, 339066, 347862, 325444, 336024, 304688, 10352, 8293, 337716, 32532, 321575, 290753, 345860, 305383, 753161, 7806, 34803, 1223213, 326924, 310480, 751863, 308575, 29987, 337511, 298414, 1229752, 304353, 1232485, 297606, 753086, 325902, 17663, 759414, 756161, 301035, 1223589, 327505, 427522, 344387, 303183, 1223434, 323102, 1228981, 1105972, 343651, 307929, 323852, 30418, 1103755, 326725, 1105666, 754034, 309350, 1039358, 759856, 1229538, 347621, 349723, 1104944, 1228150, 47340, 336117, 755755, 338394, 1105161, 753934, 1232321, 322647, 312701, 67038, 319327, 753441, 759327, 757853, 1224192, 322518, 754840, 348327, 759730, 31086, 347608, 298830, 325807, 1039231, 298611, 321504, 338988, 327249, 1106655, 1223429, 66890, 1232443, 1226023, 1105912, 318605, 1661, 338778, 310697, 3602, 313571, 52555, 289556, 427834, 305262, 298357, 1218396, 314206, 336362, 752663, 323561, 323557, 760462, 309330, 755980, 336227, 6693, 323844, 325491, 328560, 289629, 300519, 346306, 752179, 1106190, 337000, 646921, 304339, 298301, 308705, 324504, 751797, 318943, 327533, 306521, 29824, 67409, 760203, 33655, 321512, 307983, 297869, 349860, 755948, 427032, 753072, 1103965, 305324, 1226159, 9969, 751549, 303324, 1104352, 318856, 320328, 327255, 326213, 309632, 1230550, 336686, 300782, 11137, 758938, 651469, 298445, 322573, 343398, 310056, 17754, 426343, 7828, 1235760, 312693, 327489, 346213, 298806, 308809, 313631, 18119, 35234, 24207, 303475, 311220, 752581, 309828, 299765, 346942, 596685, 311426, 312647, 327650, 1230600, 347136, 326337, 90153, 1230591, 30332, 297655, 326016, 426709, 759143, 1224734, 291001, 17734, 338198, 678198, 321442, 299585, 305770, 752417, 759494, 344084, 302679, 309659, 1225493, 303966, 311683, 345351, 1209454, 20278, 348308, 339071, 308101, 34973, 312312, 755921, 307024, 73435, 326521, 10777, 312199, 428477, 324623, 18279, 338473, 1106850, 31175, 311719, 1234129, 1235842, 310113, 1106720, 346824, 759661, 310083, 757072, 66830, 347516, 1105841, 756950, 302793, 348445, 305455, 4918, 298595, 301830, 339318, 327195, 88014, 755130, 1103738, 1236140, 1227758, 336230, 1106564, 30870, 21792, 319483, 307097, 1229838, 752152, 318679, 1232993, 305250, 307158, 752654, 344815, 324610, 304878, 325560, 1230967, 1105383, 309489, 1106876, 336593, 345737, 754433, 299616, 1233898, 347251, 347096, 348304, 67525, 298897, 310575, 345937, 29555, 324305, 18364, 320944, 1104931, 1104765, 759321, 299182, 1235955, 751585, 7474, 4662, 754934, 1223229, 755511, 302974, 756027, 322632, 314774, 754401, 1218841, 754522, 19338, 314201, 325060, 321091, 305609, 35038, 10693, 94925, 312115, 327163, 337297, 326228, 302852, 347604, 309740, 1229985, 312053, 328442, 754463, 9974, 307678, 343348, 70508, 98140, 646812, 290556, 347021, 1107064, 312009, 313441, 60131, 308726, 322541, 17482, 304337, 299294, 344025, 755680, 302887, 298211, 313682, 1105131, 312824, 1104727, 320125, 758949, 754778, 1229041, 314530, 67271, 313876, 339029, 314832, 312962, 336142, 760093, 1105668, 307849, 309479, 28618, 758518, 1234864, 328406, 34955, 301948, 345437, 307258, 302137, 322797, 1106077, 346097, 305172, 348171, 1228718, 307717, 307357, 303227, 339138, 427731, 29361, 664016, 91377, 17533, 314008, 320529, 345763, 307167, 31160, 319754, 319581, 324884, 345843, 4707, 314486, 1232509, 759645, 306968, 325169, 349648, 1104179, 323938, 327386, 314743, 1232235, 324702, 17163, 11132, 305658, 338226, 1230594, 310554, 314436, 314270, 1225452, 122638, 313640, 694442, 17786, 301523, 755066, 297494, 304420, 12170, 35390, 760278, 1039225, 301100, 338997, 304164, 308693, 1234045, 752124, 337248, 345152, 322057, 1231237, 30851, 28621, 328771, 691762, 327193, 1104936, 291066, 748475, 2162, 327059, 300508, 325467, 343497, 298105, 1230588, 1228480, 760730, 670524, 305043, 35092, 308823, 1222913, 320094, 299738, 427632, 337418, 426972, 323304, 753278, 344529, 1230548, 319352, 308758, 291079, 1221106, 1103890, 1232925, 324679, 298837, 1231095, 324574, 319598, 325957, 90576, 16288, 326919, 290101, 300229, 17088, 1220296, 754022, 297401, 345600, 35244, 759557, 304625, 1222511, 321239, 345058, 299876, 1221127, 1106552, 3539, 1231413, 757309, 309966, 348874, 1228533, 302228, 312334, 311979, 349028, 318850, 753829, 322168, 755173, 35441, 1228481, 1105112, 1231514, 326957, 308157, 342999, 1039057, 337998, 319239, 1222906, 309897, 338420, 307497, 757474, 289546, 754502, 47309, 8205, 346461, 318666, 1218527, 348078, 323485, 318786, 30240, 344520, 1039256, 5411, 343856, 320461, 301117, 17134, 326619, 336524, 302959, 755476, 349946, 348627, 753108, 312392, 344099, 1106899, 347212, 311169, 1228224, 1229392, 1223090, 1222862, 1235989, 5809, 1106074, 345735, 679393, 676239, 321519, 338541, 305435, 1106563, 343703, 324954, 309602, 289701, 1106871, 757434, 348651, 309879, 320665, 318720, 24978, 1231167, 343382, 308620, 336325, 302729, 1104712, 1229036, 321855, 1229539, 311678, 324467, 328035, 1106902, 91371, 1228919, 21054, 339183, 312707, 1105104, 34932, 343114, 303871, 1233245, 661139, 1228024, 343161, 753633, 347634, 301582, 754873, 319306, 289484, 346448, 299813, 324410, 20308, 1230575, 306064, 426570, 314164, 321948, 1225515, 753171, 427850, 339372, 310462, 759751, 1228134, 309102, 306559, 302411, 301995, 302917, 324315, 299130, 757574, 17420, 337255, 324245, 10893, 349844, 44729, 1039177, 301382, 1232696, 336484, 319081, 48974, 309384, 67574, 2058, 311980, 337999, 305170, 825014, 1104468, 312314, 312061, 309246, 299202, 1230019, 758194, 1104284, 1223833, 322424, 323132, 321320, 344677, 313728, 306708, 1223616, 328046, 1105786, 343726, 308127, 752236, 349127, 348785, 18937, 1231138, 1106220, 305026, 28226, 1106316, 337616, 759810, 337048, 760279, 307560, 758830, 1220319, 646226, 4885, 659809, 303306, 1232453, 1218663, 1227094, 758412, 1230819, 1105876, 753850, 302653, 347904, 760424, 1229892, 1229647, 343854, 290090, 306131, 335991, 327333, 314394, 346509, 313580, 1222660, 324919, 3465, 290576, 308014, 758480, 427374, 343876, 337617, 1105059, 1228670, 1217408, 309306, 756721, 344159, 310326, 306419, 314276, 321842, 345353, 754475, 346903, 313744, 338979, 324068, 314233, 1223890, 311255, 311364, 694999, 1233893, 752603, 1223339, 1039012, 348756, 756230, 324142, 298253, 47427, 755245, 314864, 15760, 325751, 20296, 1103957, 343013, 1235719, 89616, 1233881, 324583, 29477, 1228025, 298484, 314417, 345969, 337704, 308016, 312594, 337742, 659358, 1105499, 1222268, 302204, 323587, 1228006, 17219, 755662, 328095, 337981, 756206, 22304, 324297, 318821, 307912, 752176, 646174, 1232884, 321308, 336709, 337111, 757210, 27963, 311610, 1222467, 309821, 326615, 753377, 311604, 18135, 336037, 325570, 312684, 18719, 345624, 303579, 319967, 300022, 323016, 757586, 301693, 337837, 752109, 711625, 313436, 1106656, 759671, 344028, 310190, 301202, 324709, 347139, 306462, 1228486, 120782, 326859, 753337, 312407, 757672, 1230715, 1230126, 347757, 325031, 328502, 326787, 311482, 305849, 303569, 297373, 756393, 759632, 6404, 309347, 17288, 757112, 323926, 344709, 756898, 349880, 311863, 313516, 300248, 300618, 760238, 1105980, 43946, 327496, 327684, 1230410, 328204, 31755, 308191, 22537, 313438, 2193, 327636, 325662, 300582, 35124, 314091, 1231567, 349486, 308151, 35431, 20281, 338553, 753801, 345976, 321379, 322020, 322952, 337428, 339082, 1103601, 11722, 298637, 338592, 328612, 323972, 304002, 324967, 308152, 105561, 314157, 1105472, 759147, 305240, 323069, 1103774, 1225494, 1218597, 322321, 755244, 323261, 327111, 759629, 320099, 336160, 347565, 1222528, 305320, 327773, 1104602, 749377, 310096, 64047, 647197, 290355, 322042, 290067, 1228865, 1235080, 343137, 308637, 756491, 7961, 323379, 1104896, 1223269, 308691, 319227, 302075, 1222940, 1229885, 31057, 318896, 426601, 753026, 349530, 311265, 1106179, 323087, 1221101, 338206, 348681, 326488, 17155, 305503, 321255, 1223604, 1038997, 309951, 756799, 1229131, 116551, 1223263, 324265, 347369, 35303, 312819, 327307, 321950, 326731, 318647, 318875, 336349, 343003, 1107069, 7499, 1104133, 298299, 310857, 753594, 307191, 312508, 17315, 314433, 309898, 1105551, 318557, 338758, 348922, 758786, 1231526, 11142, 308652, 30541, 348221, 312756, 306929, 303082, 309949, 24052, 347057, 343220, 347110, 425599, 305822, 1229140, 346881, 321269, 348434, 324094, 20901, 16940, 1229632, 297642, 338048, 299787, 1105863, 322601, 1103705, 748457, 66831, 325220, 760004, 760380, 313785, 755211, 30560, 1232047, 344252, 1106236, 1103669, 14943, 29514, 337033, 290412, 327487, 309007, 300752, 319621, 6905, 1234976, 312581, 313207, 327840, 319918, 426982, 1229664, 325051, 300301, 1232001, 324398, 1218942, 756376, 300709, 326592, 336223, 325971, 322536, 1228652, 344836, 310388, 306958, 349141, 303664, 289460, 290330, 754223, 756256, 88375, 1230729, 347204, 4151, 2444, 683109, 312084, 1233956, 1229008, 313910, 303179, 325352, 336888, 327755, 759279, 320472, 757899, 290247, 324171, 755732, 304606, 1230856, 18894, 759813, 300811, 1039219, 309824, 1232967, 304239, 312178, 301814, 327190, 13568, 1221393, 328574, 678196, 299113, 342956, 647294, 343363, 307475, 343907, 297870, 345155, 326715, 1227011, 1228152, 336876, 297599, 17395, 324807, 346235, 320455, 321766, 1230598, 13370, 35542, 1229263, 323759, 347933, 337296, 2157, 319653, 756649, 344690, 1228644, 1104541, 1104065, 1222525, 1230025, 328811, 12625, 17904, 314602, 324065, 310337, 1103690, 301073, 347206, 425569, 307539, 310975, 298665, 303770, 290677, 18146, 308511, 1229766, 349711, 309604, 1106463, 759156, 313394, 18873, 339292, 308089, 314861, 427629, 427636, 291113, 336944, 754744, 695010, 320948, 339061, 310067, 11326, 326938, 308030, 309042, 304020, 757505, 311923, 20232, 336093, 754886, 305501, 289956, 1224338, 91357, 1230283, 347493, 336772, 31251, 1105608, 751914, 306228, 1223972, 309553, 312121, 7991, 310797, 297643, 305138, 1226770, 299960, 320815, 326560, 345375, 323320, 1236273, 343289, 324733, 344438, 1221077, 427491, 1218416, 327212, 289749, 2435, 1039006, 18459, 308588, 321952, 314560, 320810, 336729, 48118, 759425, 312770, 17360, 311360, 1105968, 304437, 1228447, 300446, 1235047, 1230215, 759885, 11011, 760602, 305437, 299957, 1103826, 323968, 306884, 7868, 302303, 1218794, 321108, 309401, 1230722, 1219931, 298147, 760400, 344482, 1234521, 299512, 305393, 302622, 312422, 1222764, 310964, 758852, 1224732, 3328, 346439, 1222225, 1106025, 4504, 752700, 338796, 426965, 327261, 756489, 757538, 1105271, 347827, 752229, 758813, 307043, 289580, 346414, 344613, 299518, 752009, 1229836, 303723, 757211, 319310, 4824, 306578, 17626, 303764, 345776, 1228356, 308531, 1230646, 752877, 1039274, 346758, 313283, 1224153, 17424, 321414, 336540, 327510, 298246, 322705, 28390, 310163, 312896, 55248, 1103775, 1231058, 752892, 1105140, 754921, 338570, 346638, 307729, 1230021, 303757, 304855, 314506, 344149, 324886, 323369, 1106629, 290112, 337469, 323642, 324151, 1039159, 345060, 3605, 1232392, 300454, 18283, 17501, 307726, 19320, 311857, 4949, 301403, 757610, 1222935, 13990, 1104551, 1106578, 326444, 34891, 343152, 321048, 1106443, 302265, 305206, 307760, 1222595, 300432, 89408, 327105, 323064, 2201, 755763, 302781, 309403, 323890, 349003, 298333, 290811, 346149, 755067, 8176, 326797, 303124, 29957, 338524, 299892, 299984, 752114, 324731, 336871, 344063, 10073, 323002, 349570, 289434, 347457, 324898, 756008, 89960, 1039263, 30415, 757728, 13786, 1105337, 1222794, 646139, 321852, 306525, 1106173, 348743, 349128, 324088, 326070, 323412, 289815, 661198, 290147, 290496, 754210, 336327, 349630, 313568, 319643, 328126, 303191, 647162, 1228596, 318469, 336145, 324676, 337738, 312598, 300491, 302050, 1103807, 324271, 301678, 1218796, 757493, 759776, 339121, 5632, 1106274, 336417, 337808, 305897, 753338, 338699, 301099, 1221396, 314072, 309084, 1232384, 306126, 56862, 1106816, 31712, 344260, 299613, 1219780, 759924, 1236197, 325612, 319559, 755433, 345113, 755766, 307979, 299593, 298782, 336169, 346977, 309023, 1104402, 304200, 346628, 753233, 11478, 311074, 1105223, 312421, 753152, 348191, 1104424, 1222819, 759791, 326209, 1235925, 314686, 311110, 1223171, 347238, 318866, 711550, 1232972, 309758, 319323, 301644, 1105352, 311760, 349796, 758437, 305670, 336930, 309491, 1040086, 30345, 305162, 309860, 1229669, 1105798, 1225061, 320065, 1223981, 711614, 1223747, 310942, 307756, 298437, 752235, 1104089, 46252, 20575, 322279, 324932, 308505, 321333, 694988, 1226809, 1218926, 306688, 320394, 323248, 753997, 346115, 1105546, 344958, 1107017, 302534, 349814, 306455, 300636, 757216, 428141, 1222333, 1106303, 319025, 314069, 758208, 307895, 17842, 756984, 757323, 326348, 343081, 312463, 1230995, 318519, 300872, 5166, 324592, 1217592, 756633, 313263, 40103, 302455, 319231, 426533, 303084, 427925, 760656, 1218540, 4881, 327150, 307924, 18110, 15436, 748471, 647034, 753631, 1218704, 757954, 6503, 647177, 321105, 314292, 1222643, 1233446, 21325, 314614, 312760, 1105822, 327811, 757828, 319207, 309819, 337722, 321988, 1231085, 1106592, 298964, 1228843, 29715, 35339, 345311, 322113, 1222744, 324231, 1103866, 304079, 321259, 31096, 300584, 1103822, 756448, 324095, 18721, 325020, 1235753, 1218593, 671355, 307493, 29911, 760216, 19086, 322031, 307170, 760699, 325000, 748467, 337677, 322622, 754235, 347505, 326282, 1105153, 755296, 17838, 1217535, 299959, 348672, 311300, 320475, 321549, 1106058, 297511, 304964, 325973, 338374, 339413, 339119, 759140, 310539, 1103785, 1225502, 1104183, 303000, 326457, 307977, 298163, 1104535, 314420, 1228046, 312068, 759158, 297790, 752490, 343416, 306322, 290847, 308047, 31780, 323928, 647201, 313613, 11318, 304914, 754411, 1106449, 327743, 35593, 348212, 298691, 314766, 289911, 1231255, 34937, 35678, 753602, 1857, 1040146, 1230639, 752857, 300471, 335964, 669630, 302601, 1105715, 303520, 67624, 313736, 337308, 29113, 312746, 34826, 344800, 348335, 325048, 3648, 344261, 754144, 1104728, 303412, 758953, 343012, 754396, 758110, 427981, 1229608, 8417, 754745, 324763, 347396, 303384, 1233871, 314217, 645619, 336908, 1039103, 1229094, 345631, 678201, 19776, 760386, 1223855, 347261, 309312, 299639, 345380, 35254, 695455, 305328, 1106096, 337692, 322540, 312374, 11010, 753099, 313916, 754756, 30906, 343958, 326819, 28720, 307530, 309141, 29039, 1235009, 1218534, 320902, 1232906, 289683, 307431, 1103926, 298807, 1218435, 752474, 290051, 755087, 308947, 299786, 343099, 752480, 312405, 300748, 338808, 1217518, 115590, 426515, 323760, 344987, 300885, 318900, 326417, 755179, 319809, 338153, 304512, 322664, 1218941, 1225499, 1227089, 335969, 338125, 10876, 328630, 753644, 752344, 17230, 1235793, 306606, 116424, 757076, 1106124, 324505, 349675, 1232955, 1106118, 325555, 755657, 290864, 328595, 426530, 1223588, 323259, 318921, 1229628, 347906, 345275, 314610, 1230467, 299256, 345835, 1228618, 695465, 754614, 305136, 299619, 67462, 314633, 309895, 1228063, 299809, 1226078, 1921, 323508, 758263, 338449, 643922, 327358, 17502, 758274, 1235732, 303364, 299905, 311489, 34806, 319102, 321031, 1217773, 312650, 310487, 309950, 344505, 753803, 326127, 322704, 1229480, 338528, 312391, 1233995, 752000, 1222719, 298596, 6338, 1218162, 312777, 29920, 319175, 345593, 11005, 323692, 349446, 752146, 349800, 1228218, 299433, 312172, 1236146, 34930, 13840, 1104258, 318462, 308687, 320380, 663598, 1039015, 308722, 298391, 40065, 289627, 427848, 301823, 322735, 310375, 7668, 324254, 757110, 314357, 345670, 299386, 754634, 752394, 758553, 754495, 347840, 321506, 1222372, 31784, 318688, 348234, 318800, 426138, 343519, 311651, 1232420, 306203, 753136, 346234, 299091, 345968, 1105823, 318577, 1228071, 1039298, 304916, 4592, 1219020, 757803, 304739, 324201, 300855, 1105088, 19608, 1230711, 303286, 757348, 751517, 17732, 758824, 302150, 20803, 311153, 308966, 345696, 1226942, 337946, 7279, 323948, 757437, 312764, 759372, 1223924, 1230114, 1228669, 427505, 110918, 306456, 663312, 322519, 347909, 325859, 757719, 1229066, 301091, 1228448, 338177, 311794, 9972, 304467, 297887, 13022, 758868, 322313, 34867, 347034, 1039084, 312395, 661106, 3179, 647083, 327410, 1228811, 347258, 300998, 756427, 30965, 301668, 1106997, 289660, 3493, 56986, 299533, 1231041, 34804, 29915, 349942, 10856, 343151, 1233140, 313392, 314913, 301833, 309656, 319123, 309432, 290932, 3318, 425709, 344109, 1106082, 322806, 297687, 758972, 753996, 338081, 300633, 290040, 10060, 313473, 759825, 290098, 325809, 326062, 751949, 760170, 309110, 17347, 300805, 1230620, 318903, 308430, 321033, 18444, 30238, 751889, 759953, 1104279, 309225, 690176, 325274, 336228, 303625, 7721, 313024, 347535, 1222950, 346654, 4506, 1229473, 116699, 304566, 322912, 298283, 343524, 298557, 309298, 1105671, 298868, 328597, 22205, 322524, 35330, 759950, 307282, 300277, 1218604, 1228879, 300080, 1218600, 1218328, 310345, 428087, 1236229, 303129, 326660, 303537, 309859, 337199, 305839, 303115, 115218, 290784, 1039220, 320457, 3914, 298325, 290798, 89198, 35648, 13792, 299875, 1219819, 322292, 35632, 325981, 318457, 320052, 752715, 758554, 298281, 319033, 290144, 304034, 5116, 346493, 290824, 310042, 755417, 337680, 305821, 28707, 304867, 338039, 758146, 1236222, 19405, 323679, 1228996, 319341, 1218267, 305453, 1224036, 337522, 758328, 758264, 319441, 8007, 339322, 1222627, 1105884, 298810, 347560, 338552, 347784, 323135, 345788, 426112, 753933, 1229304, 1222836, 1218383, 290671, 321558, 324885, 349177, 66843, 1104717, 2653, 311091, 337236, 326237, 1223939, 303463, 308993, 336571, 18768, 1229463, 318543, 35660, 346786, 1221119, 338584, 1106834, 669573, 306943, 31066, 302447, 298814, 428612, 347049, 336177, 302056, 760251, 301299, 303572, 309529, 35565, 301502, 1231142, 28723, 348652, 755553, 346542, 752709, 301420, 19106, 310346, 321394, 751933, 34782, 321921, 339423, 343009, 18120, 322816, 337166, 308817, 17397, 290243, 22011, 345273, 1236034, 1106646, 299078, 304000, 302842, 66867, 337462, 348523, 303381, 4131, 323862, 1231786, 343315, 302766, 1228810, 304970, 759351, 343014, 1235876, 304530, 1103880, 343336, 35575, 347274, 759488, 428603, 1223942, 323251, 307682, 314085, 305841, 1235041, 11015, 1232084, 1230230, 322550, 326980, 1222287, 336495, 1103876, 7742, 299460, 349471, 299801, 298889, 337633, 10497, 1235912, 7471, 760174, 299737, 1218729, 35539, 290515, 309684, 10816, 1228142, 426971, 348061, 1227906, 325822, 426923, 348684, 319996, 47314, 1235020, 21129, 18093, 325147, 320271, 339110, 328309, 326620, 760243, 324901, 686852, 312574, 309389, 1223529, 757742, 319054, 1220317, 322585, 10919, 319638, 1228444, 300182, 754197, 1231649, 1224127, 345326, 754708, 310543, 756747, 337463, 347758, 304449, 308961, 312117, 3206, 10683, 313342, 1224027, 756974, 345170, 318610, 13766, 324076, 35269, 1106735, 314404, 758816, 757065, 6033, 343078, 326140, 313789, 305739, 24089, 289811, 324550, 759089, 304704, 426502, 343804, 1224514, 30412, 299252, 349704, 759056, 27123, 754579, 339277, 325582, 1106965, 757819, 344734, 1228946, 320947, 18901, 752872, 659645, 328848, 1228228, 711666, 1218150, 324471, 1236193, 758711, 752710, 1103671, 326206, 336113, 758097, 89554, 67189, 757877, 347712, 757487, 313604, 323705, 1230629, 348954, 751740, 310437, 1218291, 1229961, 298233, 428203, 1232934, 300911, 759553, 324498, 1231383, 1222143, 324947, 1222610, 1233409, 327301, 754639, 349070, 752832, 349773, 345496, 1103786, 2478, 306823, 337341, 711629, 299014, 11170, 319561, 313061, 30773, 305349, 301761, 336711, 301546, 339058, 752305, 302613, 325098, 1230267, 1224416, 1106944, 311021, 318801, 323525, 308123, 311647, 346492, 309365, 349803, 1105017, 1230703, 1104656, 1223694, 319959, 1105642, 759692, 1224092, 10808, 347270, 1236194, 758739, 1232155, 324730, 290743, 8484, 60318, 298992, 67231, 324809, 1229192, 328370, 311296, 338327, 324938, 307848, 324329, 323143, 310693, 305208, 303663, 34996, 1234801, 1230699, 298384, 319484, 309863, 297907, 302735, 1224419, 347439, 755082, 297967, 338220, 754990, 345133, 20756, 301097, 322335, 309505, 302657, 345514, 20873, 298420, 22326, 760628, 759121, 321456, 328650, 307400, 338245, 1230476, 302453, 1222985, 298749, 299776, 1223396, 1223345, 305619, 344693, 656191, 324331, 760169, 1222374, 309159, 309500, 751539, 1106388, 326511, 425579, 326752, 1217780, 1233901, 1226859, 324546, 753224, 31076, 760554, 1235074, 291055, 320609, 324035, 21509, 66976, 67548, 1228866, 311993, 322979, 346020, 301712, 30497, 1105891, 325939, 756683, 328930, 68864, 323956, 1223019, 1231695, 300453, 338627, 302041, 313555, 428209, 15246, 1105481, 306125, 1104615, 4349, 1039125, 1222148, 347868, 30715, 1235075, 301896, 14013, 325255, 310921, 1230688, 756139, 753614, 753974, 297475, 754746, 347465, 1105533, 311323, 312587, 320418, 299895, 325413, 756044, 753473, 322914, 22666, 19529, 1219093, 338840, 314848, 326868, 307426, 320486, 325167, 311122, 344911, 1231013, 755270, 318574, 309834, 346228, 4579, 344603, 688039, 759164, 337509, 344293, 337531, 305228, 304537, 1039137, 1222305, 297722, 13405, 348181, 9639, 313241, 311970, 307333, 322691, 308348, 297777, 68761, 711513, 312343, 300174, 338487, 27947, 675178, 304909, 347902, 301652, 34942, 312834, 337057, 323739, 22299, 298714, 338954, 321688, 348781, 664090, 1224612, 309424, 358206, 313573, 24734, 751945, 339131, 1222808, 319869, 31221, 1221812, 11266, 319646, 1224542, 319043, 1234109, 307668, 302664, 309654, 344507, 326895, 318650, 343561, 753330, 347740, 325663, 754125, 682762, 1222874, 754644, 310472, 319854, 325110, 336952, 302937, 305531, 760485, 1229563, 323228, 751883, 307496, 348857, 66905, 290702, 290780, 308255, 306960, 309283, 298471, 324249, 1222149, 754609, 309640, 297975, 35045, 324761, 51790, 299145, 319427, 1224044, 302914, 17485, 13851, 1229270, 320341, 756646, 348044, 757791, 308281, 18096, 311349, 1217454, 323860, 343680, 344078, 15977, 299268, 1105707, 1224337, 757330, 758252, 321254, 323134, 1236046, 346781, 338188, 327818, 1218866, 349579, 760038, 309777, 336978, 289935, 1104256, 322927, 308661, 298457, 309039, 307870, 319070, 308209, 314929, 302307, 1223671, 291027, 22880, 324691, 306748, 309921, 67590, 753198, 35540, 1230596, 1232888, 304998, 318835, 320188, 14164, 290500, 1222181, 1218488, 323511, 298344, 310538, 5072, 345484, 309220, 758226, 1229437, 349330, 756220, 298199, 338583, 1219804, 327492, 313958, 309205, 344453, 337655, 307257, 325045, 8156, 759578, 5176, 301323, 326583, 306858, 1217978, 1231133, 1106488, 309419, 338108, 312003, 757124, 324873, 1231747, 299262, 343986, 1232388, 752543, 305496, 328851, 312665, 307805, 311395, 298161, 302910, 349683, 336181, 6432, 1222301, 760013, 326940, 759860, 751591, 759456, 752321, 343378, 428147, 758901, 40978, 326723, 759583, 302144, 18905, 759558, 711562, 307692, 759821, 299084, 323515, 302250, 1105101, 35668, 17210, 1219234, 336782, 311135, 1105762, 310594, 323993, 1104052, 758248, 343347, 755977, 327914, 18698, 299278, 755872, 752325, 1236345, 306536, 349524, 308973, 308502, 759696, 759738, 675180, 646933, 320437, 1105123, 3139, 1218179, 26379, 306689, 345769, 1219838, 336715, 66787, 1039297, 649747, 338256, 321437, 305175, 324338, 1228419, 338409, 755739, 337925, 345818, 324722, 67834, 311103, 752538, 346639, 89398, 321349, 7649, 327965, 31040, 344579, 1236083, 1218760, 328547, 755696, 323530, 1218158, 670406, 302317, 298747, 1233983, 322013, 325818, 1223665, 1220536, 299347, 1228199, 321472, 10793, 303811, 349398, 306695, 312787, 116024, 301955, 312337, 338218, 18818, 825025, 30876, 19456, 1106421, 1229278, 299365, 13113, 312358, 2474, 310626, 19257, 312360, 25007, 47772, 1226000, 322603, 300002, 30531, 320580, 321278, 754927, 1234113, 31769, 1218008, 349491, 27623, 319857, 307943, 99306, 752934, 1222870, 1232734, 1229400, 1228797, 301004, 338096, 647622, 320032, 756260, 345697, 322672, 343023, 320796, 1232297, 678214, 17405, 290791, 319114, 328137, 1106107, 345141, 302826, 309194, 303759, 35203, 647332, 426323, 759128, 337671, 755410, 348896, 345172, 64050, 314927, 310130, 322033, 346572, 300721, 89489, 302799, 308221, 753957, 346810, 760021, 290686, 298362, 759826, 324650, 752605, 753576, 22813, 68794, 758839, 1228082, 337444, 314892, 290675, 752151, 29025, 1224002, 322625, 752954, 338643, 1232525, 348345, 337189, 323024, 312533, 313733, 1226335, 6320, 325320, 345568, 346185, 35415, 1104807, 310394, 337202, 116120, 754588, 1228858, 1104373, 751645, 339368, 345655, 19495, 345486, 327330, 308427, 298634, 35350, 753453, 760145, 337356, 302692, 322544, 324281, 1236039, 298201, 1229872, 1106211, 301909, 1103829, 8124, 302426, 23682, 755969, 298354, 322311, 336849, 1222893, 297892, 311660, 338493, 29544, 339041, 670677, 748472, 299088, 328170, 306493, 1104265, 299298, 323895, 27874, 298099, 758521, 321155, 321306, 320138, 1233653, 345190, 300852, 759278, 326321, 289936, 751946, 1218735, 760398, 47380, 327010, 324683, 349561, 1228735, 13743, 321736, 1223368, 347636, 15569, 344532, 345465, 343170, 753643, 1106722, 303513, 757269, 309315, 1106685, 1223216, 338806, 323976, 67277, 289431, 289598, 346870, 346353, 757812, 300069, 1228815, 753195, 348403, 318518, 347378, 7989, 302090, 759044, 313303, 1224516, 676885, 1232010, 300516, 758017, 337681, 338193, 1218052, 18689, 757552, 29406, 308914, 1229851, 66866, 325767, 345290, 1104843, 311063, 30667, 345609, 347346, 759195, 1218805, 1231081, 353416, 312151, 1232005, 60902, 311786, 1106312, 1106215, 327264, 299682, 29360, 301314, 311339, 301702, 35326, 343026, 1218165, 758388, 1107032, 339388, 1222693, 67044, 1218978, 327283, 669601, 290826, 646950, 307148, 753357, 328344, 1228342, 320973, 305955, 324847, 1219354, 757199, 1228610, 302247, 1219127, 756729, 311626, 326084, 1233764, 1106863, 754145, 305803, 30550, 1105970, 302516, 310936, 308387, 67158, 313101, 349187, 757159, 301953, 326172, 313783, 312761, 305802, 759400, 752180, 327722, 1230613, 306773, 303279, 325895, 337037, 1235780, 320559, 1038999, 307114, 337796, 11600, 755218, 301066, 752873, 24115, 346803, 347967, 18136, 1104756, 1219025, 34880, 1230611, 320385, 304404, 47422, 311946, 1223051, 325835, 29536, 297979, 318706, 1234936, 354234, 345602, 760196, 307494, 6828, 1104587, 301897, 311328, 1231033, 23318, 289695, 17873, 310842, 338872, 338211, 308870, 338923, 348981, 338874, 18546, 1104687, 347112, 298679, 759992, 3034, 31763, 301254, 348560, 758985, 1221836, 303281, 3590, 328770, 312446, 7880, 1223790, 336779, 1106310, 319839, 31723, 359417, 1221411, 1219101, 314920, 324598, 1104029, 304109, 17866, 343129, 3946, 319040, 344255, 297733, 343810, 348313, 302328, 1236254, 291088, 321067, 1229301, 307840, 324907, 323418, 323372, 338619, 17867, 1222984, 322461, 326375, 349934, 346606, 290174, 1229376, 306787, 336128, 646904, 344601, 1106832, 326848, 345713, 310334, 428148, 345506, 1223920, 1229179, 1225463, 759113, 327741, 345913, 336867, 305843, 313011, 30303, 349759, 34848, 755899, 1232966, 752936, 297564, 337665, 303442, 4136, 1105833, 760348, 752804, 1226104, 760562, 5184, 759658, 1229208, 760507, 754618, 320903, 1228193, 349336, 337291, 349262, 297474, 1222397, 756543, 314461, 319028, 754888, 325673, 428128, 347653, 326047, 299531, 322733, 326425, 336736, 1227853, 35325, 29878, 1106255, 319636, 1230849, 306279, 348877, 303655, 1231155, 759566, 311922, 303790, 323167, 327176, 308916, 290050, 10575, 313561, 755123, 308537, 346226, 302336, 290361, 753649, 34950, 348020, 343145, 1104873, 17399, 1219357, 1105243, 301755, 7695, 318639, 758677, 753235, 1230320, 338460, 760344, 312607, 300793, 303367, 314042, 759499, 759627, 755187, 1231521, 36861, 1040110, 302107, 318436, 325651, 319119, 344182, 65113, 349815, 322327, 66911, 304163, 290804, 1218155, 1230573, 343422, 1104620, 1219831, 1107016, 348567, 326889, 1106792, 1236049, 301852, 1224142, 755765, 760568, 299544, 309521, 300202, 755152, 303151, 1219803, 290630, 757558, 309041, 1106341, 322869, 300989, 303544, 342979, 321745, 752033, 306094, 757081, 303439, 752790, 306457, 347026, 298359, 299553, 2053, 1230991, 756009, 19827, 343179, 1224888, 1231452, 345659, 323532, 759942, 309509, 290862, 1222205, 757617, 427077, 1219792, 754064, 338369, 306791, 322849, 345990, 337620, 757562, 754657, 1223656, 324367, 338527, 322823, 305979, 314884, 318624, 306330, 1230268, 757863, 7664, 327198, 1234412, 343046, 114175, 646875, 17221, 313178, 344909, 755112, 310301, 17645, 298643, 345666, 1106191, 753062, 325913, 298880, 320492, 302629, 320658, 1224038, 307949, 17456, 311532, 1104297, 35285, 4285, 1106612, 336575, 28958, 323173, 1219274, 1224458, 304082, 305794, 320990, 298450, 94273, 320893, 322559, 311655, 324718, 289851, 667797, 318935, 1104678, 343410, 323770, 300265, 346600, 325086, 348347, 756457, 19075, 336574, 306105, 325470, 646695, 1229287, 1218455, 322387, 349060, 1217500, 19088, 759832, 337603, 758041, 23147, 1219395, 6982, 311754, 18052, 290795, 338203, 346395, 309876, 311058, 756941, 16445, 322931, 1106475, 297630, 1222489, 1228310, 22245, 1218816, 344208, 339353, 344396, 345828, 749741, 320381, 290242, 314038, 67282, 339037, 328694, 327947, 1218651, 348210, 326381, 1232962, 35596, 1223593, 301975, 323806, 755944, 326304, 4566, 314909, 349428, 345903, 348427, 304502, 7733, 302273, 327081, 753820, 298369, 1223054, 35432, 297457, 301263, 328775, 312248, 1235038, 1104664, 6510, 7937, 348705, 343420, 31220, 344016, 11001, 314065, 344705, 66858, 307952, 311819, 18694, 759112, 347552, 1223982, 1234873, 1219004, 338333, 1223323, 320363, 17641, 10970, 1106035, 1222751, 1221137, 1234911, 754763, 760390, 318931, 326629, 297539, 302413, 755896, 299144, 1230728, 427473, 1870, 300513, 348395, 322139, 314163, 289469, 348513, 751828, 323181, 308636, 314594, 303139, 1105373, 758854, 297489, 31250, 297462, 1219408, 1223609, 1106019, 347074, 313253, 31115, 290740, 338275, 313973, 344580, 328759, 305543, 320302, 1230517, 326517, 39682, 755423, 43170, 8243, 312177, 28988, 757334, 1223367, 312947, 349755, 325960, 19150, 8165, 1007092, 17409, 338309, 343051, 307079, 321785, 303521, 1105482, 759872, 30644, 34810, 309213, 325365, 349732, 313296, 290618, 88966, 323246, 1223388, 290389, 290966, 1106300, 306018, 24995, 759476, 1235096, 313978, 311686, 312385, 1104359, 344026, 1222772, 310534, 756024, 756564, 305057, 348205, 301735, 1222536, 343861, 318810, 306155, 1219122, 312211, 756526, 301381, 647443, 328018, 1234884, 306641, 336433, 426399, 1106248, 326877, 326539, 34965, 1217519, 319619, 426438, 324150, 304617, 1223806, 711582, 290350, 35328, 308859, 35609, 304723, 18586, 757705, 47712, 338236, 303622, 1231039, 313373, 306177, 346738, 14925, 756076, 1222083, 35000, 345940, 756209, 311011, 313130, 311790, 754059, 1229043, 312311, 1229492, 755199, 349538, 754863, 344739, 321710, 311480, 1219295, 304288, 1104786, 1039106, 348995, 347774, 324236, 1229404, 314043, 343385, 47303, 30784, 760311, 336465, 1104054, 427652, 755411, 314793, 1103886, 753384, 313591, 307180, 91353, 305729, 757856, 756059, 1229660, 2817, 326761, 759666, 338650, 326566, 298840, 1224457, 314700, 349190, 751679, 31724, 10115, 328769, 320526, 19858, 825008, 1233303, 322241, 754327, 758254, 318473, 426078, 752331, 346356, 7897, 755568, 18299, 298537, 1230815, 752254, 326978, 322946, 1230907, 760294, 299100, 8476, 313340, 426529, 343776, 758997, 748940, 290484, 1223105, 344180, 326354, 64633, 427015, 298838, 1229898, 35255, 307385, 336498, 298428, 1230961, 1218452, 345970, 30556, 302414, 344884, 2887, 347756, 34967, 323090, 17255, 290951, 1220304, 307216, 23282, 324719, 297466, 345140, 351220, 298998, 307927, 338272, 346189, 757976, 322419, 304300, 756883, 1106504, 28129, 1228549, 1106758, 1105202, 336084, 20240, 1224316, 1106209, 323267, 325289, 1104354, 323144, 52894, 755507, 1236213, 22687, 1233776, 755995, 337560, 346232, 755012, 289890, 35630, 31069, 322866, 290335, 349514, 756223, 3892, 343105, 1103697, 313658, 756673, 338505, 1105874, 310371, 3237, 300666, 1106844, 17320, 67155, 87101, 324564, 314315, 11997, 754693, 325864, 1007093, 337028, 17435, 349204, 300799, 115589, 326709, 290934, 326759, 35300, 3839, 349588, 297761, 308592, 1230113, 323067, 67367, 301120, 306510, 349729, 1219796, 314232, 644803, 300040, 1223488, 1106186, 1229433, 300783, 753550, 327438, 7406, 1236063, 1224173, 25853, 67175, 344303, 3649, 1223541, 759471, 428200, 1219237, 1228308, 825080, 313552, 753070, 7002, 327412, 320967, 1218582, 318485, 28232, 647176, 325626, 1228265, 311007, 1222515, 314094, 346584, 426960, 12695, 751952, 343750, 313959, 349225, 337838, 303528, 1222780, 302235, 345461, 758796, 758504, 290015, 19970, 343028, 7120, 89232, 324090, 339380, 1229189, 303101, 318456, 1219081, 1232021, 304262, 1228002, 323142, 348999, 326665, 321813, 425805, 320654, 290106, 758148, 344217, 1235002, 339026, 323874, 319249, 337175, 297946, 428162, 346694, 1228423, 29885, 346146, 318985, 760044, 320795, 344763, 325102, 336906, 320744, 323314, 327491, 308926, 758510, 1105594, 757460, 1223052, 1228613, 328799, 327432, 1105772, 320753, 299717, 311515, 1232048, 301692, 1229686, 1223603, 314350, 323583, 328674, 1103686, 323863, 349592, 344619, 311541, 306311, 322036, 306660, 301613, 328565, 1228165, 427680, 1227913, 17547, 756917, 301003, 313890, 17479, 322087, 345272, 1228724, 321616, 711566, 758664, 307422, 10590, 34947, 311060, 1104178, 346940, 1236209, 427856, 348039, 328835, 757368, 1223692, 312606, 290176, 751703, 757545, 1235801, 300555, 314783, 1222311, 117131, 343227, 17127, 348239, 760333, 348254, 29204, 324103, 12316, 29207, 1219103, 301608, 17112, 29186, 760377, 29154, 760388, 1219150, 6737, 319356, 678211, 6661, 301565, 348214, 17148, 324217, 344822, 301524, 324187, 324153, 301533, 1040230, 17136, 17133, 1105761, 1219053, 324129, 324127, 301553, 760307, 344812, 324149, 348275, 1229523, 20600, 20635, 645434, 17068, 28967, 307122, 28965, 17064, 307112, 301775, 348317, 1219307, 28951, 1233052, 760572, 28937, 760571, 17053, 760584, 1105809, 29043, 29044, 694985, 17099, 310474, 323974, 1219197, 1219198, 323970, 643205, 20626, 760476, 17086, 323967, 20633, 344762, 1219260, 343246, 301759, 307149, 760514, 307147, 324252, 348322, 301475, 307247, 324629, 29714, 348128, 324625, 324611, 17206, 20467, 1229616, 62490, 324595, 324578, 301257, 324554, 1232883, 694482, 1227955, 760022, 307344, 344877, 17192, 89924, 301220, 17234, 1229649, 344904, 324746, 29829, 301181, 348092, 17217, 324648, 759949, 71826, 29800, 324707, 357468, 17212, 29756, 29754, 1104267, 1209456, 29602, 324496, 344869, 1218903, 6489, 307283, 29468, 6509, 6512, 17165, 301433, 348186, 760185, 324377, 357140, 324370, 6538, 324365, 1232933, 321232, 313834, 337641, 5135, 1233740, 290697, 314829, 1039228, 15356, 756226, 669653, 756075, 299665, 297786, 297666, 1232714, 55542, 354237, 1105415, 5066, 647845, 326882, 5806, 319830, 313540, 319603, 308813, 338392, 299205, 1103870, 67291, 322301, 344664, 30366, 1104022, 319669, 1231429, 342957, 327448, 344350, 17434, 299033, 327887, 337392, 756855, 54336, 312369, 753577, 425566, 756985, 343762, 17539, 336723, 336730, 322860, 17531, 312443, 336756, 336768, 336780, 9107, 336798, 304031, 322837, 304045, 336816, 17522, 322821, 304070, 322818, 41805, 336717, 1229958, 27953, 28058, 336558, 28044, 336588, 322930, 336614, 17546, 303870, 336646, 107477, 336650, 322895, 336663, 17544, 27979, 27975, 290117, 303911, 1221418, 322882, 1221387, 304108, 27895, 17521, 337107, 304341, 337118, 1229914, 337131, 345177, 337150, 322698, 337163, 337104, 304397, 27782, 107803, 1221922, 27767, 17491, 304434, 304445, 52523, 17480, 304398, 336555, 322715, 304307, 27878, 336921, 1232147, 322785, 336925, 19737, 336943, 304183, 336956, 337077, 304189, 355551, 322781, 17517, 322757, 336997, 1221734, 17507, 715942, 337070, 1229933, 322628, 107169, 17561, 8354, 8377, 8379, 336060, 8381, 336065, 323176, 1232064, 336097, 323153, 8424, 8430, 8433, 290384, 336127, 1230036, 314566, 1230016, 28229, 336017, 314555, 303248, 1232058, 303114, 303126, 17651, 303157, 323273, 303167, 347319, 8269, 345317, 323137, 8282, 8295, 8297, 290490, 323242, 335990, 303229, 323229, 303233, 303235, 323264, 8473, 8474, 8475, 336370, 336372, 1221129, 323040, 28124, 323033, 290260, 26825, 346631, 21650, 754129, 756471, 325101, 323537, 305137, 1220295, 1234818, 322237, 311149, 2782, 11024, 758177, 19303, 4540, 1222438, 690202, 756712, 20404, 1745, 89650, 338093, 35248, 344254, 124346, 756474, 337949, 328393, 23747, 3353, 754966, 18079, 756425, 754560, 17743, 31728, 1104415, 323001, 35644, 300562, 348983, 345386, 756927, 1228129, 1718, 751805, 755618, 309831, 314794, 758066, 4402, 302178, 299311, 11025, 646648, 343169, 1007091, 302197, 343178, 4363, 302209, 310416, 4351, 299259, 310421, 29965, 305928, 299250, 24044, 318947, 1007069, 4326, 343225, 1105369, 27335, 752412, 89597, 11013, 318982, 299336, 326365, 289448, 4502, 302092, 662556, 322231, 758161, 343063, 1227802, 29989, 758139, 752360, 299385, 310300, 324892, 4459, 1223452, 1105552, 4446, 15413, 646731, 299357, 4442, 646709, 1227867, 89642, 310367, 326387, 337285, 1227986, 27339, 1219793, 757855, 1219798, 1228103, 302295, 659538, 310600, 29922, 1219813, 302309, 1219823, 7407, 4149, 343391, 10981, 318791, 299050, 343409, 4135, 289509, 299036, 318760, 343427, 1105191, 1236261, 299023, 4110, 10983, 302221, 4194, 1219790, 318911, 1105334, 1105322, 324870, 302225, 757948, 29951, 343293, 318887, 4276, 322264, 299186, 299181, 322267, 43767, 310559, 318876, 299158, 343331, 89497, 343335, 7360, 1228079, 289499, 289501, 757881, 299120, 757862, 15373, 324949, 646800, 306079, 47673, 299703, 4789, 92677, 11163, 299693, 7046, 299690, 4778, 758438, 301971, 4771, 758429, 11160, 11158, 319297, 1105717, 338833, 322130, 4747, 299658, 338807, 15018, 4739, 306059, 328706, 758885, 26253, 1233097, 754816, 754312, 18313, 14811, 302981, 1234298, 18094, 306256, 344246, 17811, 5236, 348904, 297515, 343951, 13610, 310844, 29518, 754600, 30878, 346470, 343018, 758141, 646270, 1038166, 1230987, 10894, 337644, 298775, 17684, 301078, 323707, 757200, 312931, 58420, 338746, 3604, 304330, 1220327, 752774, 339339, 306727, 305460, 1039022, 343992, 339285, 343897, 338137, 338140, 354460, 321681, 10469, 10468, 321679, 338149, 321744, 10484, 10493, 321630, 10507, 10510, 338207, 305445, 10547, 305461, 10489, 321756, 26847, 665785, 125299, 337980, 10307, 1222727, 305226, 321881, 1222755, 338026, 321870, 305261, 305266, 1222770, 10365, 92492, 92491, 338057, 321831, 321821, 305300, 338234, 337973, 321571, 321561, 321326, 321318, 305637, 10726, 10729, 321305, 321301, 321293, 10700, 321248, 305674, 353976, 10770, 10775, 321163, 1223200, 321160, 10789, 321241, 10694, 321339, 10685, 321536, 321475, 338281, 321468, 338286, 321448, 10617, 321438, 305568, 321415, 338342, 10663, 10664, 305576, 10668, 305577, 305584, 10679, 338363, 10570, 1222703, 321930, 305196, 304725, 322488, 337518, 289709, 304767, 27547, 337554, 337559, 304719, 304817, 304831, 1222340, 322380, 337626, 337635, 289593, 304882, 322359, 337596, 322534, 337472, 337470, 304348, 322792, 304448, 337227, 304478, 337265, 337319, 289900, 27756, 337351, 337354, 304619, 337403, 304648, 322621, 322612, 322595, 27652, 1222199, 304900, 322351, 322349, 682795, 10122, 10128, 337821, 289417, 337827, 27253, 27238, 665546, 337887, 10228, 305160, 27087, 1222668, 321991, 344087, 756488, 66982, 324273, 346458, 642531, 759328, 306731, 298965, 3287, 109150, 671227, 321938, 1104650, 344640, 301074, 302066, 756104, 20699, 349277, 313953, 298410, 10226, 301305, 325581, 28157, 12141, 306405, 758709, 427009, 645135, 669609, 303775, 345739, 759757, 17474, 338732, 309751, 1222951, 27812, 298519, 298203, 6036, 1106370, 748634, 116604, 343166, 336644, 1228939, 1219214, 1228678, 321603, 10103, 305018, 321587, 305025, 10120, 321384, 321310, 321158, 648832, 337910, 305156, 321121, 321109, 10263, 305181, 321089, 321684, 10323, 321705, 321734, 289445, 322034, 321939, 304896, 1222429, 321872, 337702, 75563, 1222448, 1222459, 1222471, 10057, 1222475, 1222478, 10061, 304980, 10070, 10071, 322218, 91608, 320943, 1106979, 320525, 10661, 320505, 58323, 25548, 1106885, 320448, 91067, 25520, 320349, 320312, 320290, 338441, 25366, 320277, 25364, 320548, 338012, 320552, 320611, 320942, 10384, 320930, 305312, 320896, 124272, 91492, 320832, 25899, 320808, 305386, 25810, 320719, 10517, 1222948, 25758, 320656, 320578, 1106695, 289463, 60134, 336347, 303598, 336373, 303666, 650799, 336451, 28203, 323114, 290337, 28184, 28181, 336518, 323079, 303760, 28164, 290298, 28151, 303570, 323034, 1221048, 323218, 303069, 303078, 8167, 8175, 8181, 1220598, 28503, 8283, 94008, 28452, 323338, 683765, 303347, 323305, 650976, 336249, 336252, 336294, 289505, 290258, 336633, 27616, 748432, 289680, 322431, 337416, 289586, 337461, 322341, 289571, 289570, 289552, 289549, 337480, 289542, 289527, 289519, 322282, 289762, 290256, 304484, 322623, 322987, 28066, 1231617, 52046, 302805, 349167, 7789, 297524, 326075, 345382, 312439, 88112, 324858, 752249, 23698, 318580, 755914, 20294, 313513, 759099, 300595, 756406, 314410, 5234, 4100, 755782, 18562, 752278, 31760, 348296, 347624, 35318, 1230718, 349011, 758162, 756998, 1234541, 298143, 30752, 304576, 311516, 754358, 22935, 48063, 325207, 323763, 1230602, 346801, 1104331, 309899, 30398, 67010] \ No newline at end of file diff --git a/examples/agent_adaboost/discovered_0.json b/examples/agent_adaboost/discovered_0.json deleted file mode 100644 index 006d4a96..00000000 --- a/examples/agent_adaboost/discovered_0.json +++ /dev/null @@ -1 +0,0 @@ -[35062, 758431, 321672, 1105371, 344302, 322148, 309075, 1218224, 754660, 313989, 304664, 30365, 299540, 1218897, 302429, 758671, 308561, 2752, 327678, 426474, 308467, 1235992, 758834, 337991, 1104498, 643983, 31118, 325481, 757527, 320785, 321494, 324091, 1228450, 19330, 19774, 7477, 759497, 25113, 29912, 753961, 45036, 1228940, 662645, 301671, 3388, 322059, 313774, 305289, 337465, 344381, 8187, 297460, 4801, 299095, 349397, 338855, 290290, 14945, 25658, 1234870, 4391, 29538, 1105177, 325903, 28699, 1235006, 17558, 304869, 1223499, 1228049, 751974, 90794, 994657, 290742, 348811, 321593, 1104148, 328347, 326124, 319630, 647142, 34828, 1218118, 325958, 307498, 348459, 323802, 304355, 18584, 302145, 30958, 1106516, 14767, 306013, 1224039, 343663, 345699, 10445, 327916, 17764, 717240, 4505, 1232359, 94922, 758629, 1231156, 1236262, 300545, 336401, 1230528, 7958, 28999, 302885, 307313, 320757, 1222396, 759694, 312036, 749384, 19912, 7729, 29081, 344073, 345985, 66908, 305517, 318769, 347775, 1218387, 313614, 311184, 1036082, 17940, 18900, 753277, 302378, 30160, 644822, 1227078, 1230355, 1227964, 304856, 30376, 290126, 323053, 8194, 311886, 1229957, 328790, 311397, 1230580, 2560, 319010, 1106588, 22613, 751748, 322911, 4549, 8104, 337618, 91367, 755295, 28043, 691766, 17583, 298599, 1230736, 1105531, 327155, 752313, 345019, 346767, 336543, 1105549, 1218813, 22483, 8239, 327285, 323021, 10969, 1105778, 320469, 337371, 1222370, 310978, 1229532, 1230861, 322496, 344158, 7940, 31085, 17954, 2425, 1104486, 298355, 7984, 289572, 290486, 337254, 1104126, 1223258, 18561, 324177, 757623, 1230204, 290441, 344096, 1227917, 753096, 426304, 6973, 310801, 30143, 348255, 290299, 426006, 301797, 306793, 298936, 4958, 344792, 305303, 323006, 752447, 1040072, 338013, 306370, 757739, 322451, 305489, 312026, 1234858, 314544, 344218, 44873, 753575, 19484, 318955, 19132, 1219256, 5187, 301418, 4426, 299413, 1810, 1217577, 1039321, 290859, 349291, 5897, 28228, 47717, 4934, 1218479, 67340, 325148, 1228933, 307667, 337176, 337548, 298923, 336188, 326262, 1104159, 7889, 756770, 759921, 327137, 1104040, 299408, 428191, 305688, 5079, 325681, 10967, 306862, 756329, 319907, 755336, 346950, 2965, 301057, 67180, 426526, 345443, 107703, 1229767, 1202082, 1217850, 2086, 6700, 339384, 711585, 752019, 1232040, 336452, 1039288, 1105554, 336377, 7335, 303777, 351811, 302839, 31229, 1221941, 35492, 67174, 645788, 643385, 756283, 1235966, 336765, 755469, 10603, 320273, 4609, 78494, 1105535, 1230997, 1230783, 319027, 754313, 20277, 303450, 322712, 1236029, 19440, 1795, 347862, 10352, 8293, 337716, 32532, 321575, 290753, 305383, 7806, 34803, 29987, 298414, 753086, 325902, 17663, 427522, 323852, 30418, 309350, 759856, 1229538, 1104944, 1228150, 47340, 755755, 1105161, 322647, 67038, 319327, 753441, 757853, 322518, 31086, 298830, 338988, 318605, 1661, 3602, 1218396, 314206, 755980, 6693, 289629, 346306, 752179, 646921, 751797, 327533, 29824, 67409, 760203, 33655, 297869, 755948, 753072, 1103965, 9969, 751549, 327255, 300782, 11137, 651469, 298445, 17754, 7828, 308809, 18119, 35234, 24207, 299765, 596685, 312647, 327650, 326337, 90153, 30332, 426709, 291001, 17734, 338198, 678198, 321442, 299585, 305770, 345351, 1209454, 339071, 34973, 755921, 73435, 10777, 428477, 18279, 31175, 1234129, 1235842, 66830, 347516, 756950, 302793, 348445, 4918, 339318, 327195, 88014, 755130, 1106564, 30870, 21792, 319483, 1229838, 344815, 325560, 1105383, 309489, 336593, 345737, 347096, 348304, 67525, 29555, 324305, 18364, 320944, 7474, 4662, 755511, 1218841, 754522, 19338, 325060, 321091, 305609, 10693, 94925, 302852, 309740, 312053, 754463, 70508, 98140, 646812, 347021, 60131, 17482, 304337, 755680, 298211, 320125, 1229041, 67271, 336142, 760093, 28618, 758518, 301948, 322797, 305172, 307717, 307357, 303227, 29361, 91377, 17533, 345763, 307167, 31160, 4707, 314486, 759645, 323938, 327386, 324702, 11132, 314436, 314270, 1225452, 122638, 17786, 297494, 12170, 752124, 337248, 30851, 28621, 691762, 291066, 2162, 325467, 343497, 298105, 670524, 305043, 299738, 323304, 753278, 1103890, 324679, 298837, 324574, 325957, 90576, 16288, 17088, 1220296, 297401, 3539, 312334, 35441, 1039057, 309897, 338420, 289546, 47309, 8205, 5411, 17134, 326619, 336524, 348627, 344099, 1106899, 311169, 1222862, 5809, 1106074, 345735, 679393, 676239, 338541, 324954, 348651, 320665, 318720, 24978, 343382, 302729, 324467, 1106902, 91371, 1228919, 21054, 339183, 1105104, 34932, 303871, 1233245, 1228024, 289484, 20308, 314164, 753171, 339372, 759751, 301995, 324315, 17420, 10893, 44729, 301382, 336484, 48974, 67574, 2058, 337999, 305170, 1104468, 312314, 1104284, 321320, 18937, 28226, 759810, 337048, 307560, 758830, 4885, 659809, 758412, 1230819, 1105876, 753850, 302653, 1229892, 1229647, 306131, 335991, 327333, 313580, 3465, 758480, 337617, 306419, 754475, 346903, 313744, 756230, 298253, 47427, 15760, 20296, 1235719, 89616, 29477, 314417, 337704, 659358, 1222268, 1228006, 17219, 755662, 307912, 752176, 646174, 1232884, 337111, 27963, 1222467, 309821, 311604, 18135, 325570, 18719, 319967, 759671, 324709, 326859, 1230715, 1230126, 328502, 759632, 6404, 17288, 756898, 300248, 43946, 1230410, 328204, 31755, 2193, 300582, 308151, 35431, 753801, 322952, 11722, 298637, 304002, 105561, 1105472, 322321, 327111, 320099, 347565, 1104602, 64047, 647197, 290355, 1235080, 756491, 7961, 323379, 1223269, 319227, 1222940, 1229885, 31057, 318896, 349530, 1106179, 1221101, 348681, 17155, 321255, 756799, 116551, 336349, 7499, 17315, 318557, 758786, 11142, 30541, 24052, 425599, 20901, 16940, 338048, 322601, 66831, 325220, 760004, 760380, 755211, 30560, 1232047, 1103669, 14943, 29514, 300752, 6905, 426982, 300301, 1232001, 324398, 325971, 310388, 303664, 290330, 754223, 756256, 88375, 4151, 2444, 683109, 1233956, 325352, 757899, 290247, 755732, 304606, 1230856, 18894, 304239, 301814, 327190, 13568, 1221393, 678196, 342956, 647294, 297870, 326715, 1228152, 17395, 13370, 1229263, 337296, 2157, 319653, 344690, 1228644, 12625, 17904, 301073, 307539, 298665, 290677, 18146, 1229766, 1106463, 313394, 18873, 314861, 339061, 11326, 304020, 311923, 20232, 289956, 91357, 347493, 31251, 306228, 312121, 7991, 310797, 326560, 1236273, 324733, 344438, 1218416, 2435, 18459, 314560, 48118, 17360, 1105968, 304437, 1228447, 300446, 759885, 11011, 760602, 7868, 1230722, 1234521, 3328, 4504, 426965, 756489, 1105271, 347827, 289580, 346414, 306578, 17626, 1039274, 17424, 28390, 312896, 55248, 752892, 754921, 307729, 344149, 323369, 290112, 323642, 345060, 1232392, 300454, 18283, 17501, 19320, 311857, 4949, 13990, 343152, 321048, 307760, 89408, 2201, 302781, 309403, 346149, 8176, 29957, 338524, 299984, 752114, 324731, 336871, 10073, 89960, 30415, 757728, 13786, 1222794, 646139, 306525, 1106173, 324088, 289815, 661198, 290147, 754210, 319643, 303191, 647162, 300491, 757493, 1106274, 338699, 1221396, 56862, 31712, 299613, 1219780, 759924, 325612, 345113, 755766, 304200, 11478, 311074, 1105223, 753152, 348191, 318866, 711550, 309758, 319323, 301644, 349796, 336930, 30345, 1225061, 711614, 310942, 46252, 20575, 321333, 346115, 302534, 300636, 1106303, 319025, 17842, 756984, 312463, 318519, 5166, 324592, 756633, 313263, 426533, 303084, 427925, 4881, 327150, 18110, 647034, 757954, 6503, 647177, 321105, 327811, 757828, 319207, 337722, 1231085, 1228843, 29715, 322113, 1222744, 304079, 31096, 324095, 18721, 325020, 29911, 19086, 322031, 1105153, 755296, 17838, 1217535, 320475, 321549, 325973, 338374, 339413, 339119, 1103785, 1104183, 303000, 298163, 314420, 312068, 759158, 297790, 306322, 31780, 323928, 647201, 11318, 289911, 1231255, 34937, 1857, 752857, 669630, 1105715, 67624, 313736, 337308, 29113, 34826, 3648, 754144, 303412, 758110, 324763, 314217, 645619, 1039103, 760386, 347261, 309312, 35254, 11010, 753099, 313916, 30906, 326819, 28720, 29039, 320902, 307431, 298807, 752474, 343099, 752480, 1217518, 115590, 426515, 323760, 344987, 318900, 755179, 319809, 338153, 322664, 338125, 10876, 752344, 306606, 116424, 1106124, 349675, 323259, 318921, 347906, 314610, 1228618, 695465, 67462, 309895, 1228063, 1226078, 1921, 643922, 17502, 303364, 321031, 312650, 1229480, 1233995, 6338, 29920, 11005, 349446, 752146, 299433, 34930, 13840, 318462, 308687, 663598, 1039015, 298391, 40065, 289627, 310375, 7668, 345670, 1222372, 31784, 343519, 311651, 753136, 318577, 1228071, 4592, 300855, 19608, 1230711, 303286, 751517, 17732, 302150, 20803, 311153, 337946, 7279, 757437, 1228669, 110918, 306456, 322519, 347909, 325859, 301091, 1228448, 9972, 297887, 13022, 758868, 34867, 3179, 647083, 327410, 756427, 30965, 301668, 3493, 1231041, 29915, 10856, 290932, 3318, 297687, 758972, 290040, 10060, 325809, 751949, 17347, 18444, 30238, 690176, 7721, 347535, 4506, 116699, 304566, 298557, 22205, 759950, 307282, 428087, 326660, 303537, 115218, 3914, 89198, 35648, 13792, 1219819, 322292, 35632, 325981, 318457, 320052, 758554, 319033, 5116, 755417, 305821, 28707, 338039, 19405, 323679, 319341, 8007, 1105884, 298810, 338552, 426112, 753933, 1229304, 66843, 2653, 326237, 336571, 18768, 318543, 35660, 31066, 298814, 428612, 760251, 309529, 28723, 19106, 34782, 339423, 343009, 18120, 17397, 290243, 22011, 345273, 299078, 66867, 4131, 1228810, 304970, 759351, 347274, 323251, 11015, 1230230, 7742, 10497, 7471, 299737, 290515, 10816, 1228142, 426971, 348061, 1227906, 325822, 348684, 47314, 1235020, 21129, 18093, 320271, 339110, 328309, 686852, 1220317, 10919, 1224127, 345326, 310543, 304449, 3206, 10683, 1224027, 756974, 318610, 13766, 324076, 35269, 6033, 313789, 24089, 289811, 426502, 343804, 30412, 759056, 27123, 339277, 325582, 757819, 344734, 18901, 752872, 659645, 324471, 326206, 336113, 89554, 67189, 757877, 347712, 323705, 1230629, 751740, 1232934, 300911, 324947, 1222610, 1233409, 327301, 754639, 752832, 349773, 2478, 306823, 337341, 711629, 11170, 319561, 30773, 336711, 301546, 752305, 349803, 1104656, 759692, 10808, 1232155, 290743, 67231, 328370, 34996, 347439, 297967, 338220, 20756, 20873, 298420, 22326, 760628, 328650, 302453, 298749, 656191, 309500, 751539, 425579, 1233901, 31076, 1235074, 324035, 66976, 322979, 30497, 325939, 756683, 68864, 1223019, 338627, 428209, 15246, 1105481, 4349, 1222148, 30715, 1235075, 14013, 325255, 756139, 347465, 311323, 320418, 322914, 22666, 19529, 1231013, 4579, 344603, 688039, 759164, 304537, 297722, 13405, 348181, 9639, 322691, 68761, 675178, 312834, 323739, 22299, 664090, 1224612, 313573, 24734, 31221, 1221812, 11266, 1234109, 307668, 302664, 343561, 347740, 325663, 754125, 682762, 1222874, 754644, 302937, 760485, 1229563, 323228, 348857, 66905, 290702, 298471, 35045, 302914, 17485, 308281, 18096, 1217454, 323860, 15977, 321254, 338188, 327818, 336978, 322927, 302307, 1223671, 291027, 22880, 309921, 67590, 1230596, 1232888, 318835, 14164, 323511, 5072, 345484, 758226, 1229437, 349330, 338583, 344453, 8156, 5176, 301323, 306858, 1232388, 305496, 328851, 336181, 6432, 326940, 759860, 759456, 752321, 428147, 40978, 302144, 18905, 299084, 17210, 1105762, 323993, 758248, 755977, 18698, 752325, 1236345, 306536, 759696, 675180, 646933, 320437, 1105123, 3139, 26379, 345769, 1219838, 336715, 66787, 649747, 305175, 338409, 67834, 346639, 89398, 7649, 327965, 31040, 670406, 325818, 1228199, 10793, 312787, 301955, 338218, 18818, 30876, 19456, 1106421, 13113, 19257, 25007, 47772, 300002, 30531, 1234113, 31769, 349491, 27623, 99306, 1232734, 1229400, 301004, 647622, 345697, 17405, 290791, 1106107, 309194, 303759, 755410, 64050, 300721, 89489, 308221, 753957, 324650, 752605, 753576, 22813, 68794, 1228082, 290675, 29025, 322625, 338643, 312533, 6320, 325320, 116120, 754588, 339368, 345655, 19495, 327330, 337356, 298201, 1229872, 301909, 8124, 23682, 338493, 29544, 670677, 328170, 306493, 27874, 758521, 321306, 320138, 326321, 289936, 1218735, 47380, 327010, 349561, 13743, 321736, 347636, 15569, 344532, 303513, 289598, 300069, 753195, 7989, 759044, 313303, 676885, 1232010, 18689, 29406, 30667, 347346, 312151, 1232005, 60902, 327264, 299682, 29360, 301314, 35326, 67044, 669601, 646950, 1219354, 302247, 326084, 1233764, 754145, 30550, 67158, 349187, 757159, 326172, 752180, 306773, 325895, 337037, 1235780, 307114, 11600, 24115, 346803, 18136, 1104756, 47422, 311946, 29536, 318706, 307494, 6828, 301897, 23318, 17873, 310842, 338211, 338923, 18546, 1104687, 347112, 3034, 31763, 758985, 1221836, 303281, 312446, 7880, 336779, 319839, 31723, 1221411, 1104029, 17866, 3946, 319040, 297733, 321067, 1229301, 324907, 338619, 17867, 290174, 1229376, 336128, 646904, 344601, 428148, 1223920, 327741, 313011, 30303, 349759, 34848, 297564, 4136, 760562, 5184, 759658, 760507, 319028, 754888, 428128, 35325, 29878, 306279, 327176, 10575, 346226, 302336, 343145, 17399, 7695, 758677, 759499, 755187, 36861, 302107, 325651, 319119, 65113, 66911, 1230573, 1104620, 1219831, 1107016, 1224142, 299544, 300202, 321745, 306094, 306457, 2053, 1230991, 756009, 19827, 759942, 754064, 338369, 306791, 314884, 757863, 7664, 114175, 646875, 17221, 755112, 310301, 17645, 298643, 298880, 307949, 17456, 4285, 1106612, 336575, 28958, 323173, 320990, 94273, 320893, 322559, 311655, 289851, 667797, 343410, 323770, 300265, 346600, 325086, 19075, 646695, 1229287, 19088, 337603, 23147, 6982, 311754, 18052, 290795, 309876, 311058, 756941, 16445, 22245, 344208, 344396, 67282, 348210, 35596, 1223593, 301975, 326304, 4566, 348427, 7733, 302273, 327081, 35432, 297457, 1104664, 6510, 7937, 31220, 11001, 344705, 66858, 307952, 18694, 347552, 338333, 320363, 17641, 1106035, 1221137, 1234911, 297539, 427473, 1870, 323181, 314594, 1105373, 758854, 297489, 31250, 1106019, 313253, 31115, 290740, 338275, 320302, 326517, 39682, 43170, 8243, 28988, 349755, 19150, 8165, 17409, 343051, 759872, 30644, 309213, 88966, 24995, 1235096, 1222772, 348205, 306155, 301381, 647443, 328018, 426399, 1217519, 426438, 324150, 711582, 290350, 35328, 35609, 18586, 47712, 303622, 306177, 756076, 345940, 311011, 754059, 1229492, 755199, 344739, 321710, 1219295, 1039106, 1229404, 314043, 47303, 30784, 760311, 336465, 427652, 307180, 91353, 757856, 1229660, 2817, 759666, 298840, 1224457, 314700, 349190, 751679, 31724, 10115, 19858, 825008, 318473, 426078, 752331, 7897, 18299, 1230815, 752254, 1230907, 8476, 758997, 748940, 290484, 64633, 427015, 298428, 345970, 30556, 2887, 347756, 34967, 323090, 17255, 307216, 23282, 324719, 322419, 756883, 1106504, 28129, 1106758, 20240, 325289, 52894, 755507, 22687, 35630, 31069, 290335, 756223, 3892, 310371, 3237, 1106844, 17320, 67155, 87101, 11997, 754693, 17435, 115589, 290934, 326759, 3839, 644803, 1229433, 300783, 753550, 327438, 7406, 1236063, 25853, 67175, 3649, 428200, 1228308, 825080, 7002, 28232, 426960, 751952, 313959, 337838, 345461, 758504, 19970, 343028, 7120, 89232, 303101, 318456, 1232021, 321813, 290106, 1235002, 339026, 428162, 29885, 318985, 344763, 336906, 320744, 323314, 327491, 758510, 327432, 320753, 311515, 1232048, 301692, 314350, 328674, 1103686, 323863, 344619, 301613, 1228165, 17547, 301003, 17479, 322087, 321616, 307422, 10590, 311060, 348039] \ No newline at end of file diff --git a/examples/agent_adaboost/discovered_1.json b/examples/agent_adaboost/discovered_1.json deleted file mode 100644 index 1084719a..00000000 --- a/examples/agent_adaboost/discovered_1.json +++ /dev/null @@ -1 +0,0 @@ -[117131, 343227, 17127, 348239, 348254, 29204, 324103, 12316, 29207, 1219103, 301608, 17112, 29186, 29154, 1219150, 6737, 319356, 678211, 6661, 348214, 17148, 324187, 324153, 1040230, 17136, 17133, 1105761, 1219053, 324129, 324127, 301553, 344812, 324149, 348275, 1229523, 20600, 20635, 645434, 17068, 28967, 28965, 17064, 307112, 1219307, 28951, 760572, 28937, 760571, 17053, 760584, 29043, 29044, 17099, 323974, 643205, 20626, 760476, 323967, 1219260, 343246, 301759, 307149, 760514, 307147, 348322, 307247, 29714, 348128, 324611, 17206, 20467, 1229616, 62490, 324578, 301257, 324554, 1232883, 17192, 89924, 301220, 17234, 344904, 29829, 301181, 348092, 17217, 324648, 759949, 71826, 29800, 324707, 17212, 29756, 29754, 1104267, 1209456, 29602, 324496, 344869, 1218903, 6489, 307283, 29468, 6509, 6512, 17165, 348186, 324377, 357140, 324370, 6538, 324365, 1232933, 321232, 313834, 5135, 1233740, 290697, 314829, 1039228, 15356, 756226, 669653, 299665, 297666, 1232714, 55542, 5066, 647845, 5806, 313540, 319603, 308813, 299205, 1103870, 67291, 322301, 344664, 30366, 319669, 1231429, 342957, 327448, 344350, 17434, 337392, 756855, 54336, 312369, 756985] \ No newline at end of file diff --git a/examples/agent_adaboost/discovered_2.json b/examples/agent_adaboost/discovered_2.json deleted file mode 100644 index 00582ca4..00000000 --- a/examples/agent_adaboost/discovered_2.json +++ /dev/null @@ -1 +0,0 @@ -[343762, 17539, 336723, 322860, 17531, 312443, 336756, 336780, 9107, 322837, 304045, 336816, 17522, 304070, 41805, 336717, 1229958, 27953, 28058, 336558, 28044, 336588, 322930, 17546, 303870, 107477, 17544, 27979, 27975, 290117, 303911, 1221418, 322882, 1221387, 304108, 27895, 17521, 304341, 337118, 1229914, 345177, 337150, 337163, 337104, 304397, 27782, 107803, 27767, 17491, 304445, 52523, 17480, 304398, 336555, 322715, 304307, 27878, 336921, 1232147, 322785, 336925, 19737, 336943, 304183, 336956, 337077, 304189, 355551, 322781, 17517, 322757, 336997, 1221734, 17507, 715942, 337070, 1229933, 17561, 8354, 8377, 336060, 8381, 336065, 1232064, 323153, 8430, 8433, 290384, 336127, 1230036, 314566, 1230016, 28229, 336017, 314555, 303248, 1232058, 303114, 303126, 17651, 303157, 323273, 303167, 347319, 8269, 345317, 323137, 8282, 8295, 8297, 290490, 335990, 303229, 323229, 303233, 303235, 323264, 8473, 8474, 8475, 336370, 336372, 1221129, 323040, 28124, 323033, 290260, 26825, 346631, 754129, 756471, 325101, 323537, 1234818, 322237, 311149, 2782, 11024, 758177, 19303, 4540, 1222438, 690202, 756712, 20404, 1745, 89650, 344254, 124346, 756474, 337949, 23747, 3353, 754966, 18079, 756425, 754560, 17743, 31728, 323001, 35644, 756927, 1228129, 1718, 751805, 755618] \ No newline at end of file diff --git a/examples/agent_adaboost/discovered_3.json b/examples/agent_adaboost/discovered_3.json deleted file mode 100644 index 734b2277..00000000 --- a/examples/agent_adaboost/discovered_3.json +++ /dev/null @@ -1 +0,0 @@ -[314794, 758066, 4402, 302178, 299311, 11025, 646648, 343169, 302197, 343178, 4363, 310416, 4351, 299259, 310421, 29965, 305928, 24044, 4326, 343225, 1105369, 27335, 752412, 89597, 11013, 318982, 326365, 289448, 4502, 662556, 322231, 758161, 343063, 29989, 752360, 299385, 324892, 4459, 1105552, 4446, 15413, 646731, 299357, 4442, 646709, 1227867, 89642, 310367, 326387, 337285, 1227986, 27339, 1219793, 757855, 1228103, 302295, 659538, 29922, 1219813, 302309, 1219823, 7407, 4149, 10981, 318791, 299050, 4135, 289509, 318760, 1105191, 1236261, 4110, 10983, 1219790, 318911, 1105334, 324870, 302225, 757948, 29951, 343293, 4276, 322264, 299186, 299181, 43767, 318876, 89497, 343335, 7360, 289501, 757881, 299120, 757862, 15373, 324949, 646800, 47673, 299703, 4789, 11163, 299693, 7046, 4778, 758438, 301971, 4771, 758429, 11160, 11158, 319297, 1105717, 322130, 4747, 338807, 15018, 4739, 306059, 328706, 758885, 26253, 1233097, 754816, 754312, 18313, 14811, 18094, 306256, 344246, 17811, 5236, 348904, 343951, 29518, 754600, 30878, 346470, 343018, 646270, 1230987, 10894, 298775, 301078, 323707, 58420, 338746, 3604, 1220327, 339339, 306727, 343992, 339285] \ No newline at end of file diff --git a/examples/agent_adaboost/discovered_4.json b/examples/agent_adaboost/discovered_4.json deleted file mode 100644 index 1fcecb43..00000000 --- a/examples/agent_adaboost/discovered_4.json +++ /dev/null @@ -1 +0,0 @@ -[343897, 338140, 321681, 10469, 10468, 321679, 321744, 10484, 10493, 10507, 10510, 338207, 10547, 305461, 10489, 321756, 26847, 665785, 125299, 337980, 10307, 1222727, 305226, 321881, 1222755, 338026, 305266, 10365, 92492, 92491, 338057, 321831, 321821, 305300, 337973, 321571, 321561, 321326, 321318, 10726, 10729, 321293, 10700, 321248, 305674, 10770, 10775, 321163, 10789, 10694, 321339, 10685, 321536, 321475, 321448, 10617, 321415, 338342, 10663, 10664, 305576, 10668, 305577, 305584, 10679, 10570, 1222703, 305196, 304725, 322488, 337518, 289709, 304767, 27547, 337554, 337559, 304817, 304831, 1222340, 322380, 337626, 337635, 304882, 322359, 322534, 304448, 337227, 304478, 337319, 289900, 27756, 337351, 337354, 304619, 337403, 304648, 322621, 322612, 322595, 27652, 1222199, 304900, 322351, 322349, 682795, 10122, 10128, 289417, 337827, 27253, 27238, 665546, 337887, 10228, 305160, 27087, 756488, 66982, 346458, 642531, 306731, 298965, 3287, 109150, 671227, 1104650, 344640, 302066, 756104, 349277, 313953, 298410, 10226, 325581, 28157, 12141, 306405, 427009, 645135, 669609, 303775, 17474, 309751, 1222951, 27812, 298203, 6036, 116604, 343166, 1228939, 1219214] \ No newline at end of file diff --git a/examples/agent_adaboost/discovered_5.json b/examples/agent_adaboost/discovered_5.json deleted file mode 100644 index 09a47441..00000000 --- a/examples/agent_adaboost/discovered_5.json +++ /dev/null @@ -1 +0,0 @@ -[1228678, 321603, 10103, 305018, 321587, 305025, 10120, 321384, 321310, 321158, 648832, 305156, 321121, 321109, 10263, 305181, 10323, 321734, 289445, 322034, 321939, 304896, 1222429, 321872, 75563, 1222448, 1222459, 10057, 1222475, 1222478, 10071, 322218, 91608, 320943, 1106979, 320525, 10661, 320505, 58323, 25548, 1106885, 320448, 25520, 320349, 320312, 320290, 25366, 320277, 25364, 320552, 320942, 10384, 320930, 320896, 124272, 91492, 320832, 25899, 25810, 320719, 10517, 1222948, 25758, 320656, 1106695, 289463, 60134, 336347, 336373, 303666, 650799, 28203, 290337, 28184, 28181, 336518, 323079, 303760, 28164, 290298, 28151, 303570, 323034, 1221048, 323218, 303078, 8167, 8175, 8181, 1220598, 28503, 8283, 94008, 28452, 323338, 683765, 323305, 336249, 336252, 336294, 289505, 290258, 336633, 27616, 337416, 289586, 337461, 322341, 289571, 289552, 289549, 289527, 322282, 290256, 322623, 322987, 28066, 52046, 302805, 7789, 88112, 324858, 752249, 23698, 20294, 759099, 300595, 756406, 5234, 4100, 755782, 18562, 752278, 31760, 35318, 1230718, 349011, 758162, 1234541, 30752, 22935, 48063, 325207, 1230602, 346801, 1104331, 30398, 67010] \ No newline at end of file diff --git a/examples/agent_adaboost/iteration.json b/examples/agent_adaboost/iteration.json deleted file mode 100644 index 7813681f..00000000 --- a/examples/agent_adaboost/iteration.json +++ /dev/null @@ -1 +0,0 @@ -5 \ No newline at end of file diff --git a/examples/agent_adaboost/job_status.json b/examples/agent_adaboost/job_status.json deleted file mode 100644 index 7ca8a4d1..00000000 --- a/examples/agent_adaboost/job_status.json +++ /dev/null @@ -1 +0,0 @@ -{"1228678": "completed", "321603": "completed", "10103": "completed", "305018": "completed", "321587": "completed", "305025": "completed", "10120": "completed", "321384": "completed", "321310": "completed", "321158": "completed", "648832": "completed", "337910": "completed", "305156": "completed", "321121": "completed", "321109": "completed", "10263": "completed", "305181": "completed", "321089": "completed", "321684": "completed", "10323": "completed", "321705": "completed", "321734": "completed", "289445": "completed", "322034": "completed", "321939": "completed", "304896": "completed", "1222429": "completed", "321872": "completed", "337702": "completed", "75563": "completed", "1222448": "completed", "1222459": "completed", "1222471": "completed", "10057": "completed", "1222475": "completed", "1222478": "completed", "10061": "completed", "304980": "completed", "10070": "completed", "10071": "completed", "322218": "completed", "91608": "completed", "320943": "completed", "1106979": "completed", "320525": "completed", "10661": "completed", "320505": "completed", "58323": "completed", "25548": "completed", "1106885": "completed", "320448": "completed", "91067": "completed", "25520": "completed", "320349": "completed", "320312": "completed", "320290": "completed", "338441": "completed", "25366": "completed", "320277": "completed", "25364": "completed", "320548": "completed", "338012": "completed", "320552": "completed", "320611": "completed", "320942": "completed", "10384": "completed", "320930": "completed", "305312": "completed", "320896": "completed", "124272": "completed", "91492": "completed", "320832": "completed", "25899": "completed", "320808": "completed", "305386": "completed", "25810": "completed", "320719": "completed", "10517": "completed", "1222948": "completed", "25758": "completed", "320656": "completed", "320578": "completed", "1106695": "completed", "289463": "completed", "60134": "completed", "336347": "completed", "303598": "completed", "336373": "completed", "303666": "completed", "650799": "completed", "336451": "completed", "28203": "completed", "323114": "completed", "290337": "completed", "28184": "completed", "28181": "completed", "336518": "completed", "323079": "completed", "303760": "completed", "28164": "completed", "290298": "completed", "28151": "completed", "303570": "completed", "323034": "completed", "1221048": "completed", "323218": "completed", "303069": "completed", "303078": "completed", "8167": "completed", "8175": "completed", "8181": "completed", "1220598": "completed", "28503": "completed", "8283": "completed", "94008": "completed", "28452": "completed", "323338": "completed", "683765": "completed", "303347": "completed", "323305": "completed", "650976": "completed", "336249": "completed", "336252": "completed", "336294": "completed", "289505": "completed", "290258": "completed", "336633": "completed", "27616": "completed", "748432": "completed", "289680": "completed", "322431": "completed", "337416": "completed", "289586": "completed", "337461": "completed", "322341": "completed", "289571": "completed", "289570": "completed", "289552": "completed", "289549": "completed", "337480": "completed", "289542": "completed", "289527": "completed", "289519": "completed", "322282": "completed", "289762": "completed", "290256": "completed", "304484": "completed", "322623": "completed", "322987": "completed", "28066": "completed", "1231617": "completed", "52046": "completed", "302805": "completed", "349167": "completed", "7789": "completed", "297524": "completed", "326075": "completed", "345382": "completed", "312439": "completed", "88112": "completed", "324858": "completed", "752249": "completed", "23698": "completed", "318580": "completed", "755914": "completed", "20294": "completed", "313513": "completed", "759099": "completed", "300595": "completed", "756406": "completed", "314410": "completed", "5234": "completed", "4100": "completed", "755782": "completed", "18562": "completed", "752278": "completed", "31760": "completed", "348296": "completed", "347624": "completed", "35318": "completed", "1230718": "completed", "349011": "completed", "758162": "completed", "756998": "completed", "1234541": "completed", "298143": "completed", "30752": "completed", "304576": "completed", "311516": "completed", "754358": "completed", "22935": "completed", "48063": "completed", "325207": "completed", "323763": "completed", "1230602": "completed", "346801": "completed", "1104331": "completed", "309899": "completed", "30398": "completed", "67010": "completed"} \ No newline at end of file diff --git a/examples/agent_adaboost/report.log b/examples/agent_adaboost/report.log deleted file mode 100644 index 3b838cb4..00000000 --- a/examples/agent_adaboost/report.log +++ /dev/null @@ -1,7 +0,0 @@ -Iteration N_Discovery Total_Discovery N_candidates model-CV - 0 1936 1936 9412 nan - 1 150 2057 9212 0.160817 - 2 166 2173 9012 0.163230 - 3 152 2281 8812 0.159764 - 4 151 2409 8612 0.158827 - 5 148 2527 8412 0.158125 diff --git a/examples/agent_adaboost/report.png b/examples/agent_adaboost/report.png deleted file mode 100644 index 78431532..00000000 Binary files a/examples/agent_adaboost/report.png and /dev/null differ diff --git a/examples/agent_adaboost/submitted_experiment_requests.json b/examples/agent_adaboost/submitted_experiment_requests.json deleted file mode 100644 index 01bd6144..00000000 --- a/examples/agent_adaboost/submitted_experiment_requests.json +++ /dev/null @@ -1 +0,0 @@ -[1228678, 321603, 10103, 305018, 321587, 305025, 10120, 321384, 321310, 321158, 648832, 337910, 305156, 321121, 321109, 10263, 305181, 321089, 321684, 10323, 321705, 321734, 289445, 322034, 321939, 304896, 1222429, 321872, 337702, 75563, 1222448, 1222459, 1222471, 10057, 1222475, 1222478, 10061, 304980, 10070, 10071, 322218, 91608, 320943, 1106979, 320525, 10661, 320505, 58323, 25548, 1106885, 320448, 91067, 25520, 320349, 320312, 320290, 338441, 25366, 320277, 25364, 320548, 338012, 320552, 320611, 320942, 10384, 320930, 305312, 320896, 124272, 91492, 320832, 25899, 320808, 305386, 25810, 320719, 10517, 1222948, 25758, 320656, 320578, 1106695, 289463, 60134, 336347, 303598, 336373, 303666, 650799, 336451, 28203, 323114, 290337, 28184, 28181, 336518, 323079, 303760, 28164, 290298, 28151, 303570, 323034, 1221048, 323218, 303069, 303078, 8167, 8175, 8181, 1220598, 28503, 8283, 94008, 28452, 323338, 683765, 303347, 323305, 650976, 336249, 336252, 336294, 289505, 290258, 336633, 27616, 748432, 289680, 322431, 337416, 289586, 337461, 322341, 289571, 289570, 289552, 289549, 337480, 289542, 289527, 289519, 322282, 289762, 290256, 304484, 322623, 322987, 28066, 1231617, 52046, 302805, 349167, 7789, 297524, 326075, 345382, 312439, 88112, 324858, 752249, 23698, 318580, 755914, 20294, 313513, 759099, 300595, 756406, 314410, 5234, 4100, 755782, 18562, 752278, 31760, 348296, 347624, 35318, 1230718, 349011, 758162, 756998, 1234541, 298143, 30752, 304576, 311516, 754358, 22935, 48063, 325207, 323763, 1230602, 346801, 1104331, 309899, 30398, 67010] \ No newline at end of file diff --git a/examples/agent_gp_bagging/agent_gp_bagging.py b/examples/agent_gp_bagging/agent_gp_bagging.py deleted file mode 100644 index 6d494307..00000000 --- a/examples/agent_gp_bagging/agent_gp_bagging.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright Toyota Research Institute 2019 -from camd.campaigns.base import Campaign - -from camd.agent.stability import BaggedGaussianProcessStabilityAgent -from camd.analysis import StabilityAnalyzer -from camd.experiment.base import ATFSampler -from camd.utils.data import load_default_atf_data - -########################################################## -# Load dataset and filter by N_species of 2 or less -########################################################## -df = load_default_atf_data() - -########################################################## -# Binary stable material discovery GP bagging -########################################################## -n_seed = 5000 # Starting sample size -n_query = 200 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) -agent = BaggedGaussianProcessStabilityAgent( - n_query=n_query, - hull_distance=0.05, # Distance to hull to consider a finding as discovery (eV/atom) - alpha=0.5, - n_estimators=5, - max_samples=1000 -) - -analyzer = StabilityAnalyzer(hull_distance=0.05) -experiment = ATFSampler(dataframe=df) -candidate_data = df - -new_loop = Campaign( - candidate_data, agent, experiment, analyzer, - create_seed=n_seed -) - -new_loop.auto_loop(n_iterations=4, initialize=True) diff --git a/examples/agent_gp_simple/agent_gp_simple.py b/examples/agent_gp_simple/agent_gp_simple.py deleted file mode 100644 index 54aa7c56..00000000 --- a/examples/agent_gp_simple/agent_gp_simple.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright Toyota Research Institute 2019 -from camd.campaigns.base import Campaign - -from camd.agent.stability import GaussianProcessStabilityAgent -from camd.analysis import StabilityAnalyzer -from camd.experiment.base import ATFSampler -from camd.utils.data import load_default_atf_data - -########################################################## -# Load dataset and filter by N_species of 2 or less -########################################################## -df = load_default_atf_data() - -########################################################## -# Binary stable material discovery GP based agent recipe -########################################################## -n_seed = 5000 # Starting sample size -n_query = 200 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) -agent = GaussianProcessStabilityAgent(n_query=n_query, - hull_distance= 0.05, - alpha=0.5 - ) -analyzer = StabilityAnalyzer(hull_distance=0.05) -experiment = ATFSampler(dataframe=df) -candidate_data = df -########################################################## -new_loop = Campaign(candidate_data, agent, experiment, analyzer, create_seed=n_seed) - -new_loop.auto_loop(n_iterations=4, initialize=True) diff --git a/examples/agent_ml5_v2/agent_ml5_v2.py b/examples/agent_ml5_v2/agent_ml5_v2.py deleted file mode 100644 index 3c643a20..00000000 --- a/examples/agent_ml5_v2/agent_ml5_v2.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright Toyota Research Institute 2019 -from camd.campaigns.base import Campaign - -from sklearn.neural_network import MLPRegressor -from camd.agent.stability import AgentStabilityML5 -from camd.analysis import StabilityAnalyzer -from camd.experiment.base import ATFSampler -from camd.utils.data import load_default_atf_data - -########################################################## -# Load dataset and filter by N_species of 2 or less -########################################################## -df = load_default_atf_data() - -## Epsilon-Greedy -n_seed = 5000 # Starting sample size - a seed of this size will be randomly chosen. -n_query = 200 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) -agent = AgentStabilityML5(model=MLPRegressor(hidden_layer_sizes=(84, 50)), - n_query=n_query, - hull_distance=0.05, - exploit_fraction=0.5 - ) -analyzer = StabilityAnalyzer(hull_distance=0.05) -experiment = ATFSampler(dataframe=df) -candidate_data = df -########################################################## -new_loop = Campaign(candidate_data, agent, experiment, analyzer, create_seed=n_seed) - -new_loop.auto_loop(n_iterations=4, initialize=True) diff --git a/examples/agent_qbc_v2/agent_qbc_v2.py b/examples/agent_qbc_v2/agent_qbc_v2.py deleted file mode 100644 index b8629b29..00000000 --- a/examples/agent_qbc_v2/agent_qbc_v2.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright Toyota Research Institute 2019 -from camd.campaigns.base import Campaign - -from sklearn.neural_network import MLPRegressor -from camd.agent.stability import QBCStabilityAgent -from camd.analysis import StabilityAnalyzer -from camd.experiment.base import ATFSampler -from camd.utils.data import load_default_atf_data - -########################################################## -# Load dataset and filter by n_species of 2 or less -########################################################## -df = load_default_atf_data() - -########################################################## -# Binary stable material discovery QBC based agent recipe -########################################################## -n_seed = 5000 # Starting sample size - a seed of this size will be randomly chosen. -n_query = 200 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) -agent = QBCStabilityAgent(MLPRegressor(hidden_layer_sizes=(84, 50)), - n_query=n_query, - hull_distance=0.05, - training_fraction=0.5, - n_members=5 - ) -analyzer = StabilityAnalyzer(hull_distance=0.05) -experiment = ATFSampler(dataframe=df) -candidate_data = df - -new_loop = Campaign(candidate_data, agent, experiment, analyzer, create_seed=n_seed) -new_loop.auto_loop(n_iterations=4, initialize=True) diff --git a/examples/agent_random_v2/agent_random_v2.py b/examples/agent_random_v2/agent_random_v2.py deleted file mode 100644 index df4e2eb6..00000000 --- a/examples/agent_random_v2/agent_random_v2.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright Toyota Research Institute 2019 -from camd.campaigns.base import Campaign - -from camd.agent.base import RandomAgent -from camd.analysis import StabilityAnalyzer -from camd.experiment.base import ATFSampler -from camd.utils.data import load_default_atf_data - -########################################################## -# Load dataset and filter by n_species of 2 or less -########################################################## -df = load_default_atf_data() - -########################################################## -# Binary stable material discovery Random Agent example -########################################################## -n_seed = 5000 # Starting sample size -n_query = 200 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) -agent = RandomAgent(n_query=n_query) -analyzer = StabilityAnalyzer(hull_distance=0.05) -experiment = ATFSampler(dataframe=df) -candidate_data = df - -new_loop = Campaign(candidate_data, agent, experiment, analyzer, create_seed=n_seed) -new_loop.auto_loop(n_iterations=4, initialize=True) diff --git a/examples/agent_svgp/agent_svgp.py b/examples/agent_svgp/agent_svgp.py deleted file mode 100644 index c1a737a6..00000000 --- a/examples/agent_svgp/agent_svgp.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright Toyota Research Institute 2019 -from camd.campaigns.base import Campaign - -from camd.agent.stability import SVGProcessStabilityAgent -from camd.analysis import StabilityAnalyzer -from camd.experiment.base import ATFSampler -from camd.utils.data import load_default_atf_data - -########################################################## -# Load dataset and filter by n_species of 2 or less -########################################################## -df = load_default_atf_data() - - -########################################################## -# Binary stable material discovery SVGP based agent -########################################################## -n_seed = 5000 # Starting sample size -n_query = 200 # This many new candidates are "calculated with DFT" (i.e. requested from Oracle -- DFT) -agent = SVGProcessStabilityAgent(n_query=n_query, hull_distance=0.05, alpha=0.5) - -analyzer = StabilityAnalyzer(hull_distance=0.05) -experiment = ATFSampler(dataframe=df) -candidate_data = df - -new_loop = Campaign(candidate_data, agent, experiment, analyzer, create_seed=n_seed) -new_loop.auto_loop(n_iterations=4, initialize=True) diff --git a/examples/generic_gp_ucb.ipynb b/examples/generic_gp_ucb.ipynb new file mode 100644 index 00000000..575115d1 --- /dev/null +++ b/examples/generic_gp_ucb.ipynb @@ -0,0 +1,374 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Black-box optimization with a generic GP agent" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example, we will show how to setup a \"textbook\" black-box optimization problem of finding extrema of an unknown function, as a campaign within the CAMD framework, and solve the problem with an off-the-shelf Gaussian Process based agent with an upper-confidence-bound approach for exploration/exploitation tradeoff.\n", + "\n", + "We will follow this outline:\n", + "\n", + "1. Define a sample function to generate synthetic data\n", + "2. Define data, agent, experiment and analyzer of our campaign\n", + "3. Intiailize the campaign with a random seed\n", + "4. Run several iterations and keep track of the progress of the agent. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "pycharm": { + "is_executing": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Failed to import pyspglib.Download at: http://sourceforge.net/projects/spglib/ andfollow instructions for installing python API\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os, shutil\n", + "\n", + "from camd.agent.generic import GenericGPUCB\n", + "from camd.analysis import GenericMaxAnalyzer\n", + "from camd.experiment.base import ATFSampler\n", + "from camd.campaigns.base import Campaign\n", + "\n", + "from sklearn.preprocessing import OrdinalEncoder\n", + "from sklearn.gaussian_process.kernels import RBF, ConstantKernel, Matern\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "def plot_state(x, y, campaign):\n", + " \"\"\"\n", + " this is a helper function to make plots\n", + " \"\"\"\n", + " mu, std = campaign.agent.pipeline.predict(\n", + " x.reshape(-1,1), return_std=True\n", + " )\n", + " plt.plot(x, mu, label='agent')\n", + " plt.fill_between(x, mu+std, mu-std, alpha=0.2)\n", + " plt.plot(campaign.seed_data['x'], campaign.seed_data['target'], 'ko',alpha=0.7, label='acquired')\n", + " requests = campaign.experiment.dataframe.loc[campaign.consumed_candidates[-campaign.agent.n_query:]]\n", + " plt.plot(requests['x'],requests['target'],'rx',alpha=0.9, label='requested')\n", + " plt.plot(x, y, label='true f(x)')\n", + " plt.legend()\n", + " return plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's generate some synthetic data using an arbitrary function, with one column for X and one target column." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "def f(x):\n", + " return np.sin(x)*np.sin(1*x)*(x**2)\n", + "x = np.linspace(0,10,500)\n", + "y = f(x)\n", + "df = pd.DataFrame({'x': x, 'target': y})\n", + "plt.plot(x,y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, say we want to maximize this \"unknown\" function. We will use a generic GP-UCB agent and a generic analyzer of campaigns for acquiring 'maxima' from a candidate set. Say we also want to know number of points with target value above 58." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "N_query = 2 # This many experiments are requested in each iteration\n", + "N_seed = 5 # This many samples are randomly acquired in the beginning to form a seed.\n", + "\n", + "agent = GenericGPUCB(\n", + " n_query=N_query,\n", + " kernel=ConstantKernel(100.)*RBF(10.)*ConstantKernel(1.0)\n", + ")\n", + "analyzer = GenericMaxAnalyzer(threshold=58)\n", + "experiment = ATFSampler(dataframe=df)\n", + "candidate_data = df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we setup a campaign and initialize it. Here we instructed the Loop to generate a seed with 5 randomly selected points." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n" + ] + } + ], + "source": [ + "path = os.path.join(os.getcwd(), \"generic_gp_ucb\")\n", + "shutil.rmtree(path, ignore_errors=True)\n", + "os.makedirs(path)\n", + "new_campaign = Campaign(\n", + " candidate_data, agent, experiment, analyzer, \n", + " create_seed=N_seed, path=path)\n", + "new_campaign.initialize(random_state=6)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's have our agent acquire two new points and plot our results." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent GenericGPUCB hypothesizing\n", + "Campaign 0 state: Running experiments\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "new_campaign.run()\n", + "plt = plot_state(x, y, new_campaign)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that the GP-UCB agent requested two new points where it thought there would be a maximum, but its prediction was pretty far off there. Let's run another iteration." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent GenericGPUCB hypothesizing\n", + "Campaign 1 state: Running experiments\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "new_campaign.run()\n", + "plt = plot_state(x, y, new_campaign)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now it knows it's previous acquisition was low, so it's able to make a more intelligent choice, but still hasn't yet explored the region where the max is." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent GenericGPUCB hypothesizing\n", + "Campaign 2 state: Running experiments\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "new_campaign.run()\n", + "plt = plot_state(x,y,new_campaign)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now it's more or less converged on the right-most region, and correctly identifies the potential of the intermediate region." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent GenericGPUCB hypothesizing\n", + "Campaign 3 state: Running experiments\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXkAAAD4CAYAAAAJmJb0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAABK70lEQVR4nO3dd3zV1f348de5M7k3exISIGHvIARkgyxFLI6Wqq1WW2tbWzosWlcdbfX79fur2mqprbSu2qqts+6BShCUETYkYSZAICF73CR3fs7vjxsiIzt35eY8H488yF2fcy5J3vd83p9z3kdIKVEURVHCky7YHVAURVH8RwV5RVGUMKaCvKIoShhTQV5RFCWMqSCvKIoSxgzB7sCZkpKSZGZmZrC7oSiK0qds27atUkqZ3NZjIRXkMzMzycvLC3Y3FEVR+hQhxNH2HlPpGkVRlDCmgryiKEoYU0FeURQljIVUTr4tLpeLkpIS7HZ7sLsSViIiIsjIyMBoNAa7K4qi+FHIB/mSkhKio6PJzMxECBHs7oQFKSVVVVWUlJSQlZUV7O4oiuJHIZ+usdvtJCYmqgDvQ0IIEhMT1dmRovQDIR/kARXg/UD9nyoh5cknYdUq2Ljxq/s2bvTe9+STwetXGAj5dI2iKP1AdjY88gi88Qa88IL3vuuv9/57+rbSI31iJB/uamtreVKNVpT+bOZMHH+8E/fERjyPfA3t7uVoJs0b4GfNCnbv+jQV5EOACvJKfyYbyjj25+WYt9+OLtuBbowb3UVuPCuaqBKHgt29Pk8F+S664oormDJlCuPGjWPNmjUAPP3004wcOZJp06Zx8803s3LlSgAqKir4+te/ztSpU5k6dSobW/KMDzzwAN/73veYP38+Q4cO5YknngDgzjvv5PDhw0yaNInbb789OG9QUYLAXllM5ePzSa7YxBu2S6h71krjX4zU/0uHp8xF4kc/o/z9/w12N/s0n+TkhRBxwN+B8YAEvgfsB/4NZALFwDellDW9aec3b+8j/2R9bw5xnrEDY7j/a+M6fd4zzzxDQkICzc3NTJ06lWXLlvG73/2O7du3Ex0dzYIFC8jOzgbg5z//ObfeeiuzZ8/m2LFjXHzxxRQUFABQWFjIZ599RkNDA6NGjeKWW27h4YcfZu/evezcudOn701RQpmnqYa6v15KhKuOTy2/4PJ/PI3OYEa+/W8aHR6c3/k2nrlNpPAwtbFpxM28Mdhd7pN8deH1ceADKeU3hBAmwALcDXwipXxYCHEncCdwh4/aC7gnnniCN954A4Djx4/zwgsvMG/ePBISEgBYsWIFBw4cAGDt2rXk5+e3vra+vh6bzQbAsmXLMJvNmM1mUlJSOHXqVIDfiaKEACk59LcbGOoq460L1vD1k+Vw1ZVw1VWIWbOIAjwvv8zBvzyLue5zBn78K1yZUzAOnBDsnvc5vQ7yQohYYC5wI4CU0gk4hRCXA/NbnvY8sI5eBvmujLj9Yd26daxdu5Yvv/wSi8XC/PnzGT16dOvo/FyaprFp0yYiIiLOe8xsNrd+r9frcbvdfuu3ooSqQ2ufZlRNLm+m/pivX/GNNp+jnzObkbNmsfqtjXx7xzW4X/4xqb/IBZ3KMneHL/63soAK4FkhxA4hxN+FEFYgVUpZ2vKcMiC1rRcLIX4ghMgTQuRVVFT4oDu+V1dXR3x8PBaLhcLCQjZt2kRjYyO5ubnU1NTgdrt57bXXWp+/ZMkS/vSnP7Xe7iwNEx0dTUNDg7+6ryghxVFfQdLG37BXjGTx937T4XN1OsEPl83guajvk1q/m9ovnglQL8OHL4K8AZgM/EVKeQHQiDc100pKKfHm6s8jpVwjpcyRUuYkJ7dZ8z7oLrnkEtxuN2PGjOHOO+9k+vTppKenc/fddzNt2jRmzZpFZmYmsbGxgDe1k5eXx8SJExk7dix//etfOzx+YmIis2bNYvz48erCqxL2Cv59H9GyAcclj2GNMHX6fLNRz4IVP2OrNgqx7n/A1RyAXoYRKWWvvoABQPEZt+cA7+K98JrWcl8asL+zY02ZMkWeKz8//7z7QkVDQ4OUUkqXyyUvu+wy+frrrwe5R90Tyv+3SniqPXFIOu5LkOt//81uv/bxvz8r5f0xsuyD3/u+Y30ckCfbiau9HslLKcuA40KIUS13LQTygbeAG1ruuwH4b2/bCjUPPPAAkyZNYvz48WRlZXHFFVcEu0uKEtKK/vs7JDDwit92+7VXXvlNNmgTiNyyGlyq7lJX+Wp2zU+Bf7XMrDkCfBdvKug/QoibgKPAN33UVsh45JFHgt0FRekz6itPMrrsHbbEXMyc4aM6f8E5BiVYeDvre8w+eitVm/5J4pzv+6GX4ccnl6mllDulN68+UUp5hZSyRkpZJaVcKKUcIaVcJKWs9kVbiqL0TQVvPUaEcDFg6aoeH2Ppsm+Srw3BvXE1yDYv8ynnUHORFEXxO2ezjVHHXmZ75AxGjJ3S4+NkpUSxMWkFqfYinEUbO3+BooK8oij+V/DR08TRgJi5stfHGnbRdTTISE58usYHPQt/KsgriuJ3kXtf5IgYRPbMS3t9rPnjMvlEP5u0kg/A7tsyJ+FIBfkQcN9997F27doev37dunVcdtllPuyRovhOcf4WRroKKRv2TXT63occnU7QPP5aInBQ9uVLPuhheAu7IJ+bm8uKFSvIyclhxYoV5ObmBrtLnfrtb3/LokWLzrvf4/EEoTeK4ltln/0NpzQw5uKbfXbMhQsv5aCWjivvHz47ZrgKqyCfm5vLqlWrKC8vJzU1lfLyclatWuWTQN9WqeEPPviAyZMnk52dzcKFCwGoqqpiyZIljBs3ju9///sMGTKEyspKiouLGT9+fOvxHnnkER544AEAbrzxRl599VUAMjMzueOOO5g8eTKvvPIKH330ETNmzGDy5MmsWLGitdDZBx98wOjRo5k8eTKvv/56r9+foviDy9HE6Ir32BM9m/jkNJ8dNyU2ki0xSxjUuBdZU+yz44ajsAryq1evxmq1EhMTg06nIyYmBqvVyurVq3t97GeeeYZt27aRl5fHE088walTp7j55pt57bXX2LVrF6+88goAv/nNb5g9ezb79u3jyiuv5NixY91uKzExke3bt7No0SIefPBB1q5dy/bt28nJyeGxxx7Dbrdz88038/bbb7Nt2zbKysp6/f4UxR8KPn+NOGyIC67z+bGjpqwA4MTGf/v82OEkrIJ8UVERUVFRZ90XFRVFUVFRr4/9xBNPkJ2dzfTp0zl+/Dhr1qxh7ty5ZGVlAbSWHF6/fj3XXef9hV62bBnx8fHdbuvqq68GYNOmTeTn5zNr1iwmTZrE888/z9GjRyksLCQrK4sRI0YghGhtT1FCjWvXa9QQzfg5l/v82POnT2OvzELmv+HzY4eTsAryWVlZremM02w2W2sg7qkzSw3v2rWLCy64gEmTJnXrGAaDAU3TWm/b7e0vy7ZarYC3rtDixYvZuXMnO3fuJD8/n6effrpH70FRAq2hvpYx9V9wMGkhJlPnhci6KzbSSEHcAgY1FeCpPurz44eLsAryK1eupLGxkfr6ejRNo76+nsbGxtZt+XqqrVLDdrud9evXt54lVFd7F/TOnTuXF198EYD333+fmhrvZlinrxFUVVXhcDh45513OmxTSknO1Gls2LiRvfn7aXK6qaypY8++AoYOH0lxcTGHDx8G4KWX1AwDJfTsW/cKFuEgburVfmsjdqo3ZVOy8WW/tdHXhVWQnzdvHo8++mjrjkspKSk8+uijzJs3r1fHbavUcHJyMmvWrOGqq64iOzu7NcVy//33s379esaNG8frr7/O4MGDATAajdx3331MmzaNxYsXM3r06Hbbc3s07C6N2IREnvrb09zwnW8zbcoFzJ87m8LCQgwmE0/8+S9ceukyJk+eTEpKSq/en6L40ukZbpVv/JqyZgMnm8ydv6iHZuTksE/LRBS+5bc2+johQ6j+Q05OjszLyzvrvoKCAsaMGROkHvVeZmYmeXl5JCUldfpcKSVOt4anGz8TARj0Oow9mH/c1/9vldBzeoZbQpSZt+bs46Ujsfx5f7JPBlvtefWRn3CV7V+I2w8hrJ3/nYUjIcQ2KWVOW4+F1Ui+L9M0iaObAR68O7G4PBpOt4dQ+sBW+qfVq1fjcrkY6tpPhF7ynz1NuFwun8xwa4957FJ0SE5te9tvbfRlKsj7WXFxcaej+NMBXutFkHZrEqdHU4FeCapdu3ZRXFzMsiwXx+ph6wkPxcXF7Nq1y29tTp25gHIZR/3ujq9z9VcqyAeZJr0BXra9O2K3eDSJy6N1/kRF8ZOmpiYi9LBwCLx7RKA3GFvv95cBcRa2m6eSXvUFeFx+a6evUkE+iE7n4H0R4E9zq0CvBJHVamVOhobFCB8V63G73a33+5MjazFW2UT9gc/92k5fpIJ8EDl7maJpj8uj4dFUoFcCb+LEiVw+3kq9Az4/6sZsNpOZmcnEiRP92m7mtGU4pIHyvDf92k5fpIJ8kLg83b/I2h3++gBRlI78+JZbWJLhYN2pKC6cOZsRI0ZgNBp7vValM+Oz0tkpRhN5XI3kz6WCfBBomsTdg5TK7//v4S4/VwIut8Zzzz3n9z8wRTltQIwk1aKxvTndp2tVOqPXCU4kTCfdeQTZoGo5nSm8gvyTT8LGc7YE27jRe7+PSCnPKk/Qk9c7PVqPsvDdCfIAHilV2kYJqLodb+GWOm7987vk5eXxyiuv+D3An2Yd7S3XfXLHhwFpr68IryCfnQ0//OFXgX7jRu/t7OxeHba4uJhRo0bxne98h/Hjx/O73/2OqVOnMnHiRO6///7W5z300EOMHDmS2bNnc+211/LII48AMH/+fE4v8iorr2DUiGGAt1783XfewZyZ05k25QKe/pu3hHFpaSlLFl7E9KlTyLlgEhs3bODee+6mubmZ6VOn8N0brgfgpRf/xdxZM5g+dQo//fEtrfXn//H8c2SPG8vcWTPYsHGjmlapBMyA0k8pNE8gNiE54G1PnDqHGhlFff7HAW87lBmC3QGfmjULnnrKG9hvuAGef957e9asXh/64MGDPP/889TX1/Pqq6+yZcsWpJQsX76c9evXY7Vaefnll9m5cydut5vJkyczZcrZGxafm6Z5/tlniI2N5fMvNuFwOFg4fy4LFy3mv2++yaLFS/jVnXfh8Xhoampi1uzZPPWXJ9m0dRsAhQUFvPbKK3yybj1Go5Ff/HQlL7/0IgsWLuKh3/2WDV9uJjY2lqVLFjFp0gW9fv+K0pny4wfJ1I6xcdA3gtJ+WnwU64zZTCj/EqQEIYLSj1ATXkEevAH9hhvgD3+AW2/1SYAHGDJkCNOnT+e2227jo48+4oILvIHTZrNx8OBBGhoauPLKK7FYLAAsX778vGO4zknTfLJ2LXv37OGN118DoL6unkOHDjElJ4dbfnAzLpeLy5YvJzt70nnHWvfZp+zYsZ05M6cDYG+2k5ySQt6WLcyZO5fkZO9I6uvfWMGhgwdxaxoGXXiduCmhpXjLO6QAA6cEbyvKhoGzSDy2kebSfCIHjgtaP0JJ+P3Vb9zoHcHfeqv333Nz9D10Zvnfu+66q7X876FDh7jppps6fK3BYMDpduOREscZJYallDzyhz+yaes2Nm3dRv6BgyxavJjZc+bw0SefMnDgQH74/Zv41z9fOO+YUkq+fd31ra/duXcf99x7X7t9cHukStsofiWOrKOCBDJHB+/MMXXSUgCO570ftD6EmvAK8qdz8E89Bbff/lXqxkeBHuDiiy/mmWeeaa1bf+LECcrLy5k7dy5vvvkmzc3NNDQ08PbbX9XRGDJkCHl53jTL6VE7wKLFi/n7mqdwubyr9A4eOEBjYyPHjh4lJTWV7970fW787vfYuWMH4K1kefq58xcs4M3XX6e8vBzwljo+dvQoOdOmseHzz6mqqsLlcrW2p0mJW1NBXvEPh9PJCFsex+KnI4J4xjhxQjZHZSry8GdB60OoCa90za5dZ+fgT+fod+3yWdpmyZIlFBQUMGPGDMC789Q///lPJk+ezNVXX012djYpKSlMnTq19TW/+OUqrr3map7++9+4ZOmlrfff+L2bOHr0KDMvnIqUkJycxMuvvMb69bn88bHHMBoNREVF8bennwXguzd9nwunTCb7gkk8+/wL3Peb37B82VI0TcNoNPKHx59g2oXTufvX97Jg7hxi42KZOPGri85uj4ZBJxAqV6n4WOGODWQLG6aRC4Pajwijnv2WKcyu+ww8btCHV4jrCVVq2E8eeOABoqKiWLVqFXaXb0sX9IZBp8Nk8I60+ur/rRJ6cp++k3nH/0LjzwqxJvhuw+6eeOfF1Vx24B5s139E1LALg9qXQFGlhoPIrcmQCfAAHk2thFV8L750A0WGoUEP8ABp2d758qW71ga5J6FBncv4yQMPPICUErvLE+yunEXivQhrMqiUjeIbtbXVjHblszvjW/RuN2XfGDdyBEdkGrJ4Q7C7EhLUSN6PvKP40KNG84ovHdryESbhIWb8kmB3BfDm5Y9Yskmv3wlaaA2ygsFnQV4IoRdC7BBCvNNyO0sIsVkIcUgI8W8hhO+3aw9hUvasPk0gnB7NK4ovOA58gl0ayZq8KNhdaeXKmImVJhqKdwS7K0Hny5H8z4GCM27/H/AHKeVwoAboeDJ5mAnVUfxpHk3tIqX4xsCqLzlkmYjRbAl2V1oNmOid5XNC5eV9E+SFEBnAMuDvLbcFsAB4teUpzwNX+KKtvsA7ig/tACoBNW1e6a2Txw6TJY/TlDE32F05y9gxYzgmU5DFvlsj01f5aiT/R+BXwOn8RCJQK6V0t9wuAdLbeqEQ4gdCiDwhRF5FRYWPuhNcnhCbUdMejybxqEiv9MLxvHcBSGlZaRoqzAY9hy2TSK/fAf28Emuvg7wQ4jKgXEq5rSevl1KukVLmSClzTtdbCSW1tbU82c1Sxd1ZWepwOFh2ycVMnzqFV1/5DwDfvuZqio4cafc1TqeTJQsvat1arTeqGh29PobSf+mK1lFFHEPGtDlFO6hcGTOIkQ3UH98d7K4ElS9G8rOA5UKIYuBlvGmax4E4IcTpKZoZwAkftBVwHQX5toJsd2eu7NrpvTC0aes2vrHim+Tn78Pj8ZA1dGi7rzGZTMy/aEHrh0JvVNmcaGo0r/SA5vEwrGErRTFTETp9sLtznpSJ3gvBJTv6d16+1/PkpZR3AXcBCCHmA7dJKb8thHgF+AbewH8D8N/etsX7d0LZnl4f5iwDJsDS9jfjuPPOOzl8+DCTJk1i8eLFLFu2jHvvvZf4+HgKCwv56KOPuOyyy9i7dy8Av//9IzTYbNxz730cOXyYW3/+MyorK7BYLKx+8q+MGj269djl5eXcdOONVFZWMH3qFF58+T/8+6WXWPa1rwFw7OhRLlt6CZ+u/5yEhAQuXrSAO+66h0WLF3PZ8uXc/+tfc8213+rV23d7JLXNLhKs/Wryk+IDRfu2MIx6Dg29KNhdadOYMeM4IZOgeANwW7C7EzT+XAx1B/CyEOJBYAfwtB/b8puHH36YvXv3snPnTgDWrVvH9u3b2bt3L1lZWRQXF7c+V9PkWRczV/74Fp5Y/WeGjxjB1i2b+cXPf8r7H361oUFKSgp//utTPP6Hx3jtTe9n4KYvv2DF1VcDMHjIEH5522384qcrmZIzldGjx7Bo8WIAxo0bz/ZtZ5eA6Kkqm0MFeaXbKna9zzBgyNRLO31uMJgNeg5FZpNdl9ev68v7NMhLKdcB61q+PwJM8+XxOxpxB9K0adPIyjp/bd+ZuXibzcbmTV9y3beuab3P6XB2euyy0jKSkr66NnHj927i9dde4+m/reHLrV8Fdb1ej9FkoqGhgejo6J6+FQDsLo0Gu4voCGOvjqP0L9aS9RzRDWFoeiisc21b08DpxB35hKbSAiwDxwa7O0Ghyhr0wOna8uCtFa+1zDn3aBoOh7devKZpxMbFte7k1FURkRFn1Zxvamri5Anv5QybzXZWQHc6HERERPTmrbSqsjlVkFe6zNFsY6R9L9tSrqL9q0fBlzBmLhx5iJO7PmV4Pw3yqqxBJ6Kjo2loaGj38dTUVMrLyzlVUYnd4eD997xTymJiYsjMzOT117xLBaSU7N69q9P2Ro8ew+HDh1tv33v3XVx97bX8+v77WXnLj1rvr6qqIjExCaPRN4G5we7G4VZLwJWuObJtLWbhImJUcEsLd2bM+MlUyhicR/rvfHkV5DuRmJjIrFmzGD9+PLfffvt5jxuNRu677z5mzpjO1y69hJGjRrU+9sxz/+D5Z5/lwpzJ5EzK5t0zNhJpz8VLl/L5+lwAPl+/nm3b8vjlbbdzzbXfwmQy8Y/nnwNgfe46Ll7q27nJVbbO00mKAmDb9xEOaWD4tIuD3ZUOxUSaKDSOI6m6RzO8w4KqJ+8DHk36bBTc3NzM0iWL+GTdevT69qelXfvNFfz2wYcYMXJkj9s6uH8/uviM1ts6HYweEINe1z8vUCldV/S7STToopl4z+fB7kqn3l9zL0tPPoHr53sxxg8Kdnf8QtWT9zOPD1fURUZGcs+997fm4dvidDq5bPnyXgX4tmga1Dap0bzSsYaqE2R5iqhNmxPsrnSJdaS35ELp7k+D3JPg6BNBPpTONs7lveDq2/4tXrKEQYMHt/u4yWTi29dd36s22vs/rWpUQV7pWPGW9wCInxAapYU7MzJ7BjYZge1A6J91+EPIB/mIiAiqqqpCNtCHerXJtkgpqa2pBv35F20dLg2bo/flEpTw5T6wlhoZzchJvtk32d8GxEexTz+amArfrCvpa0J+CmVGRgYlJSWEavEyl0cjRD9/OqY3IqIS23yo2uYkyhzyvxpKMEjJoNrNFFomM8NHM7sCoTJhMlMrn0U21SAs8cHuTkCF/F+y0Whsc+FRKGh2ejhUbiPcLlPW2124PBpGfcif6CkBVnlkJ0myhn1D5gW7K91iHjobXeUzlO1bx4CpVwa7OwGl/op7oTpML1JKCTUqN6+0oWSbNx+fNjk0Sxm0JzN7Dk6pp7ZgfbC7EnAqyPeQpsmwnolS3eQM2esgSvAYi9dRxECGDx/d+ZNDyLCByeSL4USUbg52VwJOBfkearC7w3ovApdb0qAuwCpnkC47w5p2Uhx7Ibo+tpZCCMHJmEmkN+8HV3Owu3OW3NxcVqxYQU5ODitWrCA3N9enx1dBvodqwngUf1q1WgGrnOHEnnVE4EQ3fEGwu9IjYshMjLipOfhFsLvSKjc3l1WrVlFeXt5aImXVqlU+DfQqyPeAy9M/phk22N043WF8uqJ0S8WuD3BJPcOnXRLsrvTIwInz0aSgYu+6YHel1erVq7FarUxLaiLB0ERMTAxWq5XVq1f7rA0V5HugtsnVN6dN9kB/OGNRuib25Ab26UeRnpoS7K70yJiswRxkEPrjXwa7K62KioqIjorkwbEHmRfrXeUeFRVFUVGRz9pQQb4H6pr7T+CrblQXYBVwNVSQ6TzEqaQZwe5Kj5kMOoqsE0lr2Aue0DgTz8rKwuKoxKyXHLd7S5jbbDafThtXQb6b7C4Pzc7+k8JweyT19tD4g1CCp3jLe+iExDpmcbC70ivujOlYaKbp+I5gdwWAlStXkqqrAeCE3UJ9fT2NjY2sXLnSZ22oIN9NtU2uYHch4KrVnPl+r7HwY+qkhfFT5we7K72SNHY+EDrFyubNm8cl86ehScgvayIlJYVHH32UefN8t9gs5Fe8hprafpSqOc3WsqGI2dB+6WMljElJetUm9piymR0VGeze9Mr4MWM49kYyWnHozLBJM9k46Urjiy3+ObtQI/luaHS4cbn7Z366prH/ncEoXg3H95KsVfSZ0sIdiTIbOGCeQErtDkJh9kRubi5RpZvZfbTGL3PkQQX5bqlt7r+BrkatgO23ird4dzRLvmBZkHviG81p04jV6nCeKgxqP3Jzc/nVbbcy1OrgmDPWL3PkQQX5LpNSUtcP8/GnuT2S+mZ1AbY/MhZ/xhE5kEkTJgS7Kz4RN9q7icjJPZ8FtR+rV69maKzAoIPjjii/zJEHFeS7rMHh9vnmIH1NuBZkU9onnU1k2XZSaJ0aNtdkxoyfQoWMwXEouJt779q1C2tTMQCf7y2hsrLS53PkQQX5LuvPo/jTTl+AVfqP0j3rMOPEmXlRsLviM0nRERQYxhFfFbxNRHJzc6moqGBEtAu3Bgcr3RQUFFBSUuLz0uoqyHeBpknq7SrIg5pO2d9U7noPhzQwLOfiYHfFp+qSc0hxl6HVtr+Xsj+tXr2awYMHMyYJDlRLNJ0RKSXHjh3z6Rx5UEG+Sxoc4V1xsjtqGl1oYZy20jRJg91Flc1BTaMTu8t75uLvSoGhKqH0c3aKMYzPSgt2V3zKMsI7U6h0T3DmyxcVFZGRkcH4VAOFNTocDgcWi4WUlBSfzpEHNU++S1Sq5iseTVLX7CLeagp2V3zK5dEorW2icNs6OPAh8fUFxLiqQAgqHCa25J/AaEsmNTWtdRaErxethJqmimNkuIr5Mv5mLhR9q7RwZ0Zmz8C2PoKGA5/DnOsD3n5WVhbV5aVkRnt4rzyNOXNGUF9fT0qK7+sCqZF8J1Sq5nxVYZayqaxrZO2Lj6H9aRpLvvg2CyteIMFdQaMpkSZjPEnNRayaWM+Lsw7zu2HbyE50+GUWRKgpyXsXAMPIvl3KoC0ZidHs040i+tTWoLS/cuVK4mUVOgElzZF+KWdwmhrJd6Le3n8qTnZVs9NDs9NDpKnvz7bYvOETEj5ZxVJZxGFdFmtHPED6tCtJSkkjzawnwqBn2rSpZA6IY0rUKa4bVMoTEwt4/2QMv9tRQ02jk+gIA4Yw3A/XdXAtp2Qck3JmBrsrPieEoCJhMlOrngvK5t7z5s2j5qqlUPs0+aecpKSlsHLlSr+cGaog34m6frwAqiNVjQ4yTJZgd6PHpKbx2bO/Zt6xJ6kWcXw68feMX3g986PM5wXsrKwsysvL+UJksrlgEHOji1g5/CQTL2pm7+a1xI+aRWykkaQoc1h88AE0250MrtnMBkMOlyRFBbs7fmEaOgtd1bOU568nJefygLc/wNyIXRp5/bMdGIz+S3/2evghhBgkhPhMCJEvhNgnhPh5y/0JQoiPhRAHW/4N7EelD3g0SYOqwNim2iZXn103YG+ysfWRy1lw/M9ss87B8cMvuejKm0mJjWxzRL5y5UoaGxupr6/HJQX/PZnC1Z+m4TFGkZN7A9U73qa2ycWhchvFlY2tF2v7surDm4mWDVQOmI0Is3z8aUOy5+KUemoK1gWl/eiafI4Zs/wa4ME3OXk3sEpKORaYDvxECDEWuBP4REo5Avik5Xaf0qBSNe2Ssm9Op7TVVXP4D5eQ0/g564f8lCm/fJOMtLQOA9m8efN49NFHSUlJ4dSpU6SkpPCL/3mK6us/4qhuEDO3/pTavFcA725ah8ptnKq39+kyEBXb30WTguSJ4TV18kwjBqaQL4YRcXJLwNuWmsYg52FqYsf4va1ep2uklKVAacv3DUKIAiAduByY3/K054F1wB29bS+QVKqmY1WNDpKiTCE/0svNzWX16tWUFR/gTzPLGRdnZ8vk/2Pu5T/s8jHmzZt3Xr5U0yTbrnmDgn9/kwt33EGeJZbosUuQEsrrHTTYXWTEW4gw9q0Ujs3hJq7kM3YxnKnjRga7O36j03k39x5X/7p3c29j4Cpslh47yEAaIdX/pSJ8erVICJEJXABsBlJbPgAAyoDUdl7zAyFEnhAir6Kiwpfd6RWVqumcyx36G4qc3ii5uryU/zetnHGxTazcmIgjbnSvj63TCcZkplNz+T85KgYy6Yuf0Hzsq3KxzU6NQ+W2PjdYqCs/RqajkMKYWcRbwmuq7HlaNveuPRjYLQHL9nvPHuKGTvF7Wz4L8kKIKOA14BdSyvozH5Pe89Y2z12llGuklDlSypzk5GRfdafXVKqma6psjmB3oUOrV68m2hrBPWOKuTCpiQcKhlHoSvfZ9Mcos4GsQekUL32Bemll8Mc3Ixu/GqxICceqmiivt/ukPX/TNEnNzncAcA1bgk4X2mdpvTVwwkVoUlC+N7DFyhwlO/FIweAxU/3elk+CvBDCiDfA/0tK+XrL3aeEEGktj6cB5b5oK1D62ugrWBod3umUoerIkcP8cOgJFgxo4MGCTHbb031eBGpAbAQDB2XxRc7jxGs1mN64CbSzz3BO1TsoqWkK+Tx9vd2F8fDHnJCJjJ/Ud/dz7aqxQ4OzuXdk1T6O6zOItEb7vS1fzK4RwNNAgZTysTMeegu4oeX7G4D/9ratQFGpmu6pDOHR/LWjPVwzpJqnDg9ga/NgwPcbJRv1OpKjzYy4YB5vpK9itH0nzZ/+/rzn1TS6OF7dHNKBvqauniG1m9lsnMbwAf4PQMFmMug4GpUd8M2905oOUBk1KiBt+WIkPwu4HlgghNjZ8nUp8DCwWAhxEFjUcrtPUKma7qlrduF0h15xn52fvcqtw4/yztEIXjqWiqZpfltZmGQ1Y9ALRl38Iz4xzGNy8VO4j24673l1zS6OVYfmiN7h9uA8tJ4IHFSkzcdq6h/LaOTgGVhopqZoe0Daq6koJZUq3CnjA9Jer4O8lHKDlFJIKSdKKSe1fL0npaySUi6UUo6QUi6SUlb7osOBoDbH6B4pvTNtQsnR/TsZuu6nFOkzMV/zDMkpqa3TH/1Rc0anE6TGRGDQ6/Bc+gilMpGBn/wU4ag777n1ze6QDPQ1jS48he/RJM0kj1+EPszz8aelT1wAwMndnwSkvZJ8b2ooasjkgLTXPz6qu0HVqumZKpuTlOiIkAgMDQ31uP59A26hJ/L6f7M4axSLL/b/1nXxFiPlDXbSUlL5bOz/8K38H3Lqw3sQy8+/yFvf7KakppmM+MiQmIKqaZJqm4MBp3L5gglMGR5eVSc7MnrkKI7LFOTRwOTlbUc2o0nB4AmzA9Je+BXc6KUGu1ulanpAytCYaSOlZPvffsxwrZhjcx8jPSsweU/w1kNJiY4AIHvmEt6MvJKJ5W8hj7Rdlri2ycXJutCYdVPX7MJ4agcJ7nIOxs8nLtynTp7BoNdxPGoi6XU7A7K5d2T5To7rM4iJS/R7W6CC/HnUrJqeq7Q5g15r/tPXnmJe/dt8MeA6Ji34ZsDbj7cYMRoEOiGIuuRejspUBqz/FcLd3Obzq21OToXA9MqqRgdy35u4pB5GLcUaJjV4ukobPIN46jhVtNev7UhNY3BzPuUxgcnHgwryZ1Gpmt7xaJLKIObmd+/ewYV7HqDQMJpp33us8xf4gRCCRKsZgPSkBD4cejcp7lLEZ//T7mvK6x1BPQuyOdw0OzwkHvuQL+Q4po0dGpZVNTuSNsGbly/Zudav7ZQePUAC9WgD/b8I6rT+9ZPsRINDpWp6q7LBGZTCZRU1dZjfvAmP0GH91j8wmMwB78NpiVYTupa/rCnzLuct3SJGH30BfdmOdl9zstYetM1pKhocRFTtI9F1kj0x80mLDdzy/lCRNWoSp0hAX+TfRVEn8z8HIHFU4Mo3qyB/hnqVquk1jyYDPip1ujX2PP8LRmmHKZj2MIOGBi4P3xad7qvRvMmgo/miB6iSMUR/cgdo7S8cO17ThM0R2JldzU4PNrsbUfAWHinwjFiK1dz/5mPo9DoOx1zI0IY8pMd/ccB9dCt2aWTImBy/tXEuFeRbSKlSNb5SYXPg9gRm3ryUkvVvPcuC2tf5PPEbXLg08Fu5tSUxysTpSTNjswbzeuIPGNRciGHXC+2+Rko4WhXYUsWn6u0gJXHF77JJG0v2qOH9Lh9/mjZsATE0UrJ3o9/aiKveTZFpBMYAnmmqIN9CbdbtO5oG5Q2BGc3vzd/D1F33ckA/nEnffTwkpiOCdxVsbKSx9faIRTexVY4mY8fv0dtr2n2dpkFRZWNAFpc1Otw02N2Yaw6Q5DjOdutc0uPbrqnfHwyffhkeKSjf8Y5fju902MlyHaIuIdsvx29P2Pw0Xb0cOapUjW9VNzpxuP07Iq2pb0T/2vfRoyG//gzRUaG1g1Fi1FfTEFNiIvlixB1Eehoxrn+ow9e5PZLiqka/nw2VtkzfNBa8hkcK7MMv7ZepmtMGpA7kgGEkMSc+98vxj+zegFm4MGVN98vx2xM2Qb43tWaklGqVq49JCaW1/psaqGmSHc/9krHafrZP+g2jxgZ2dNQVFpPhrO0AZ86az390Sxl27BVM5bs7fK3DpVFc1ei3i9g1jU5vYTmpEX/4TTZoE5gwamS/DvIAVQPmMNS5n/oa39dTrMn/FIDMyYHdGD1sgnyzy9PjkWOj09Nnt7ILZQ12t9/WHXz+/r9YUP0yG2KXM2P5zX5pwxcSrV+N5iOMemwzbvdehP30TpAdj9Sbnd5A7+u1B26P1jqKt5RtJc5ZxhfWhaTHRxLVz4N8wsSl6IXk8Cbfp2yspZso0g0hISXd58fuSNgEeeh5zRm1AMp/TtY2+/wDtOjwfrK33MEhXRajb/wTxhDOIcdGGs8q9TB9TBbPW7/HQNteLPkvd/r6JofH54H+ZK299Wdiyn+FRmmG0ZcRadKHRFmKYBo5eS71WHHt/9inx3U5HQxv3kt5QuDmx58Wun8dPdCT2THeVI0K8v7i9khK69pe7dkTTrsd+0vfwYibpuV/Jyk+zmfH9gedTpBwxmheCMHQBTexVRtJ6pb/Reeo7fQYjQ4PRT5K3VQ3OlsHNcJtJ+noe3yoTWX66MH9fhQPYDCaOBSVQ1btl8gOprt21+HdG7AIB8Zhc312zK4KqyDf5PB0+wJsk9OD26NSNf5U0+jy2dnS1mduZYy7kE0THmBCduBHRT0RbzWedXvEgBjezVhFpLue6I3/26VjNDk8HKmw9WrWTaPDzcnarz5wY4rfJ1KzsSPuEpKizERFqCAP4BpxKcnUcGTnep8ds2afNx8/JMD5eAizIA/dvwCrUjWBUVLT1OvZNjvWvsis8hfJjVnO/Kt+FDLTJTtjNujPC6AL5i3kX/ISMo68TGTFri4dx+7SOFxho7EHC6aand60z5kruiN3v8BRLYWYsYsQAix9bMNxfxk28ypcUk/Nttc7f3IXWUs3UawbRGJqhs+O2VVhF+S7G7TVAqjA0DTvXqc9TTmcLN7P0A23cUA3lEnff7LP5Y7PTNkAxFtNnLjgl1TIWGI/63gl7JncHklRZSOn6u1drkffYHdxpNJ21joQc81Bkqvz+I9cyJyRKVjNhrDfz7WrkpJT2GfOZkDpWp9UpXTYmxjevIdTCf7fz7UtYRfkGx3uLgeSJqcbl1ulagLF7tI42o2LiLm5uaxYsYKZUy+g4o/zEJob0zX/IDam721LFxNhwKA/O4hePHkEfzZ+j+T6fGIL/tnlY0npLWp24JSNuiZXu8He7dE4UdtMcWXTeQv9YvL/hVPqKcm8CqvZoPLx52gaupQM7STH9vd+t6gDWz7EIhxEjL3YBz3rvrAL8lJ6Ry5doVI1gdfo8HC0uvMRfW5uLqtWraK8/BR3ZlcxIbaZ2zbGcrS0z2wwdhYhxHmjeaNeR+a869noGUfKlv/D0FTRrWM63RrHqpsoLGugpKaJKpuD2iYnFQ0OjlV576+2Oc/vi6uJuIOv8qE2lRkTRgMQrfLxZxk+52o0KTjx5X96fazGve9jl0ZGXuj/jWvaEnZBHro+lTKUF0DZXR6O1zRRWFbP9qM15BVXs+NYDftO1nGyttm7kKWPstndHKmwdVijZfXq1VitVq5Mr2R5Rh2PH0xnv3sgq1efv8tSXxHfxkYcU7MS+XfKz9G7m0n48sEeHdftkdQ0ujhZa+d4dTNldXbqmtvfpzjhwH+IcNfzXuRyxqbFYNALIlQ+/iwp6UPYbxpD2vH3ep2ySa/4nP2Rk4i0BucMNCw/vuvtLjRNdphjbHZ6Qmbz6Sqbg8KyBgrL6jlYbqO0zk514/kjsHPFW4wMSbSSlWRl/MBYxqfHYOkjmy/bXRqHym0kR5tJtJrOq5dSVFTE7EGSn404wX9L4vikfihRUZKioqIg9bj3TAYdUREGbOdMDlg6fy5/+8/X+HHRGzSWXktjmh+XvWtuYnetIU8bycAJFyGEUKmadtQMu5IxhQ9xLH8zg8f17Gdy/OAuBsmTnBjyHR/3ruvC8qcrpbfg2JkFos4VzFSNJiWFZQ1sLapmS3E1x6qbADDqBcOSo7hgUBxpcZGkRpuJjjB6F6kIgVvTcLg1apucVDU6OVHTzNHqJt7ZfZI3dpxAJ2BkajQzhyUya3hS61Z0oep0brmiwYHVbCDCqEMnBE63xqQMCw+N3c2e2giePzUWIQQNDQ1kZWUFu9u9kmA1nRfkM+ItfDrmFo4XbiR53a9oWvER0uCfn11s0XtYm0p4Tt7GdWNTAZWqac/wi67HUfB/nPr82R4H+ZKNLzEIyJoT+F3KTgvbn259s6vDIB+MWTVHqxr5bH8FuQfKqbQ50esE49JiWDgzk/HpsWQlWXu0etPp1igsq2f3iTryiqt5ZmMxz2wsZvSAaC4eO4DZI5JC+nRcSm8Kx9ZS6qbmWD7/b8IBTjXquGdPFjJSh81WT2NjIytXrgxuZ3vp9AXYc9dmfP3CETx44Ec81fg7UvJ+z6np9/q+cakRv+PPHJFp6Md8VYxMjeTblpKaRp51BkPL3sfldPSoPHDK8Q8oNI5ldMYwP/Swa8L2p+vdkFu2OZfa7vLgcAUmVeN0a6w/WMG7u0s5VGFDJ2Dy4HhumJFJTmaCT/7ATAYdEzPimJgRx3UXDqG0rpkNhyr5rLCcxz89yN83HmHh6FSWTUhjYFxo7/pTW3qEYR99B/RGduY8ROTxDygqKiIrK4uVK1cyb968YHexV4QQxFtMVJxTitlqNjBh7hX889Mv+Pbev9OQtZSmVN9uLBF75G2iawu4330Ll03yzteONOn7bWnhrjBM/jaJGz5ne+7rTF58bbdee/zgLoZ5itg07HY/9a5rRFfn2gZCTk6OzMvL69FrT9Q2nzeTIDPJQnTE+aP5U/V2yuv9W++8yubg/X1lfLC3jLpmF4MTLFw8LpU5I5LbvADnD1JK9p6s54O9pXxxuAqPJpk5LJFvTBnE8JTQKssLUF96mMx3ryZa2ihY/E8unL0w2F3yC4fbw4Ey23n3Syn5f2/l8XD5j0iItlL09ffRjFaftCk8Toa/chHFDToeTP8rd1w6DoDUGDMpMaGd1gsmt9NB/f+M4FjkGCbd8WG3Xrvpbz9nasnzVP1gBynp/k0zCiG2SSnbHBWE7UgeoN7ubjPI+zMff+BUA2/tOsmGQ5VommRqZgLLswcyMSM24Cs0hRBMSI9lQnosNY1O3t59knf3lLLxcBWTBsWxYkoGE9ID36+2VJYcYNQH3yJK2tg291lmXDg/2F3ym9MrYM/NzQsh+O6CCdz14i08Z3uQgRvuomT+4+CDn09CwT+JsB3nf113cM2Fma33x3SQ0lTAYDJTmLGC6cefpvTIPtKGjuvS69wuJ8NO/Je9lmlk+znAdya8g3yzi/Rz0hP+SNW4PRpfHK7i7d0nKSxrINKoZ9mENN+nR6RE52pE52pA53EgPE6E5kLqTGhGC5rBgsccC+L80+94q4nvzMjk65MzeH9vGf/ddYJ73tzLqNRoVuRkMC0zIWjBvqxgIxds+BEm4WbbvOcYPmlOSF9D8IUEy/kXYAFSoiMYPf1SHvuikNsP/4emAVOpHtO7LQ0NjWWkbHuEjdoEdMMXMiTRe3ZgNKipk10x7NKf4X7qWY6+/xhpP3m6S6/Z89l/uIAaSibf4OfedS6sg7zbI2l0uM/aCMGXFSfrml18uK+M9/aUUtXoJC02gpvnDGXRmJQeT2UUHiemuiLMdYcx1x3BXHsIU8MxDE3lGJsr0Lk7ruio6Yy4Lam4LKk44obhiB+FPX4U9oQxuC3JWM0GvjElg+XZA/mk8BSvbivhwXcLyEy0sGLKIGYNTwpoyYDiDf9mUcE91Ig4Dlz8EsmDx5MSE7j9L4MlJrLtC7AAl01M4zdF3yG3/ACzv3yA5qSJNCf3cFMUKRn45f1It4P73N/lzjNG8W2d5SrnSx04hK3xFzOp/L9Uld5HYtqQTl9jzvsrp0hkwvwVAehhx8I6Jw/eLdjOHE0fPNWAvZcj+UPlNt7efZLPD1bg8kgmDYrjaxMHkpMZj647o2HNRUTNASIrdhNZuZvIyj1EVBei0756H05rGs6YTG/gjkzGbUnBY4pG6k1InQmpNyE8TnTuJnSuRgzNFRgbyzA2lmKuPYSx+atVlI7owTQNmEbjgGnY0mbgihmCR5PkHqjg1W3HOV7TTFpsBN+YksFFo1L8WqfdYW+i+r93s6ThdfYbRlK7/AUsCWnEW41kxFv81m4oKauzn3cB9rSaRif3vrSel8UdJJk1jix/A2dMZrfbiC98iYwNd/Cw6xpOjv8RN88Z2vpYe9eslPMdPbSPgS/MYUfKFUz7yTMdPjf/i/cY+9G1bBr1K6Zfe09A+tdRTj7sg7zRIBg9IAbwpmoOnjr/gldX2F0eNh2p4r09pRSUNRBh1LFgdCqXTUhjUEIXgpLmwVx3mMiKXVgqdhFZuZuI6gJ0Hu8fuccUQ3PSBO9XwlgcccNwxg7t9YU3fXMVETWFRFbuxXIqD+uprRjs3tIAjpgsGgZd5P1KncaXxxp5Ja+EQxU2kqJMXDEpnYVjUn0+xa5o32ZGb/oVI2URXyRehXXZ/6IzRSJa5vmbDP1jtofTrbG/rKHdx7cdreGFdz7iv5G/xWhN4MjXXsVtSe3y8SMrdjH0nRVsZxQ/lPfw5HU5rWeYOh2MTYsJiesxfcWWP32HCyrf4eTVHzBk7LQ2nyM1jYKH55DqPI71V/lEWAIzwaFfB3mAYSlWLCYD5fV2TnVjVo0mJQWl9XxSWM6Gg5U0uzykxUZw2cQ0Fo5ObX8/TCkx2kq+CugVu4is2oPe1QiAx2ilOXECzckTvafiSRNxxgxuM5fuc1Jirj1E1InPiS5Zh7X0S3QeB5o+AtvAmTRkzGOzbjJPFwj2nazHZNAxb0QyF48bwMjUqF4FhZKSY8j1v2dJ49s0iCh2T/4dCZOvbH08Icp03jWUcFdc2dhheex/bT5KYd5n/DvyfxGWJIou+QfOuM7nXJtrDzH0nRXUeUwsabiPW5ZN58KsxNbH4yzGrg1OlFbV5SfhyWlUGAYy9I6NGI3nnwVteeMJpu26l83j7uXCFbcFrG9BDfJCiEuAxwE98Hcp5cPtPbenQd7u8nCsuqnNC6p5mzbw+j+fprTkGMlpg/jmDd8nZ/rsDo+172Q9m4uq2HykmuomJxFGHbOHJ7FwdCpjB8Z8lZKREkNzOebaw0TU7Mdcs5+I6v1E1BxA7/KO0DSdCXviWJqTs2lqya06YoeBLjQueAl3M9bSTUQf/4zoks8w1x8FwBGTyYnEmbzbPI6nS9KpdZtIiTYzc1gS04cmMDI1ukvpnEaHm0MFO4je+wKLm9/HjJO8pCswL7kfvTXhq34IGDWga8cMJ/V2F0crm9p9XErJox8foObgJl62PoZJLymZ+ygNQ9rffMJStpkhH/8ApyZYZruH0eMu4Mfzh5/1nMGJlg4XCypt2/7u35i89Ta2JH+DprHX8cBvfsOuXd79AJZMGcaaGcc5ETGc0XeuR6cP3N940IK8EEIPHAAWAyXAVuBaKWV+W8/vaZD/OP8UP3phG1lJVsYOjGFMWgyDEyycLNjGHx/6NRZrFElxMZTX1GFvauS2+x4iZ/psHG4PJ2qaKalp5kiljX0n6zlcXo9RcxBvdHJhmpEZAySTEtxYXNXo7dUYG8sw2Y5jbCjBZCtpTbcAuM1x2ONH40gYhT1+JM1JE7EnjEHqAzMv3hdMdcVEl6wjqmQdUaVfonM3o+lMlFlHsd0zjI/rB7Hfk85JXSoZKckMTrCQEmMmwWLCqNfh1jSctir0FQUkVW1lYtNmJukO40bP3vhFyDm3Y0gZeV6751476S+klOw/1dBhyWuXR+PBd/OpPL6fV+L+TGrzIeoyl1Ix8RbvBdmWQYexoYSkPWtIzH+eBssgrqz7JaaU4Tx4+fizUmBCeFM1qn58z3zx5I+YWf4Sf99n5o6PmpB6E2MTNV7+moZer2P7hX9i2Td6NyOqu4IZ5GcAD0gpL265fReAlLLNPc96GuRP7PgQ9ycP0Wh30exyI6SGDomn5iRCetDrdegFCKT3Mb0eY0wiaBoCiUm4seDw1nym48JgbnMczuhBuKIH4YwahDN6EM6YTOwJo3FHJvtkTnOoEG47llNbiS5Zj6V8O5GVu8/6UKsWcVTLKGyaCYmOaJqIEzaSRD0AGoKSyFFUD16KcfK30Kxt55P7Wy7+XBUNDsrq7B0+x+XR+P2H+9l2pIzfp63jMtsr6F02XBFJuKIGonfWYa4/ihR69qRewfXHlhEbl8D/XDnhvBG7StX0jubx8I/vj+PGIaVUNElKGgTZKXCqUfLNt4wMyF7AK6+8EtA+BXMxVDpw/IzbJcCFZz5BCPED4AcAgwcP7lkjcRYccVG4NImGjmaXxO7WOFRaAnozHhdo8owvt4v0gcMx6g1EmgzIiAjclmgaTFbqDJGtc841YxTuyCTcEQm4I5PwRMQj9eE/ve80aYigMX0OjelzvHdoLiKq92OuO4Kp4Rim+mMkO+tIcjbh9rhxGaJoNsVSHD8ULXkM9qSJeCIT0QMdzWeKt5r6bYAHbzVR705P7T/HqNdxxyWjeWFTJD/fbuQxy3x+lXWAKWI/VmcFjqgMCgZ+nTVV2bxXbGRsWgy/XjamzdkzsRaVpukNnV7P6j1R/HuTxvUT9SRGSB7erOO5vQZONThpDrFKqUGfJy+lXAOsAe9IvkcHyZpD5Tdyzrvw+n8f30BVZQWxMTFEGvXYnG5sDQ0kJiXz6DXPA2Bv+VK6QGfEnjQee9J4nx1SCEiO6j8fnG0x6HXEWYzUNHa8hkOvE9w4M5NpWQk8s6GInxSMA8ZhMelxujXcmsRq1nPDjEFceUF6m+sddDqIVgXJei0rK4t1646yZa2GweD9/3S73ZhMppCrlOrvn/YJYNAZtzNa7guIa2+8mUd+ew8mg47I2BiabTbsTY1ce+PdgeqC0ok4i7Ffj+JPS4oydxrkTxubFsMjK7I5UmFjd0kdpxrsmA06hiVHMWVIfIcL8eIsJjVt0gdWrlzJ7t27OXnyZGshRKfTSVpaWshVSvV3Tt6A98LrQrzBfSvwLSnlvrae748plHmbNvD2S89y7GgxAzMGc+V1N3U4u0YJrJEDojAbQmOmUbB1Np3SF05PJ1Z6Lzc3l/vvv791ds3EiRP57W9/G5RKqcGeQnkp8Ee8UyifkVI+1N5z/RHkI4w6RqR6t93qzWIoxffUBcCz2Rxuiioa/XZ8s1HHyNS+twm60rmgVqGUUr4HvOfvdtpz5kWmCKOeCKOu12UNFN9Iju7fufhzRZkNRJr0ftu/N1AlrpXQEvbJ0LjIs3+x1QKQ0BATaVAVENvgrw8+IbyzeJT+J6yDfKRJf95FPTV9LDSoUXzbYiONRBh9/2cZG2lUO0D1U2H9U49rI6CbDXoiTWH9tkOe1axXF/864I+dmtSHav8VttFOiPZTM7GRKjcZTCrgdCw20ujTgUhUhEqN9WdhG+StZkO7xa5UXj54Ik06VcO8CwbE+q6Oj/pQ7d/CNsjHdRDITQYdFrMa2QRDcpTaNLoroswGYiJ7n9KymPU+3w9A6VvCMsgL0fkGxWo0H3gmg84ngau/GBAb0et6d2mx6kO1vwvLIB8baex0n9LYSGM4FYzsE5KjzWpJfTeYDfpe7XcbZzGqC9xKmAb5LkyTNOp17e/spPicQS86TKEpbUuOMhNp6n5qUafzngkoStgFeb1OdLnKngo6gZMYZVKbVPSAEILBCZZOz0zPlR4X2e922VLaFna/BXEWY5dTAjEqZRMQOh0kWtUMj54yGXQMTrR0+Xc13mokTpUwUFqEXZDvTn0OvU6oC7ABkGg1d3skqpwtymxgUELngT4m0tDvNkNXOhZWQT7CqOt2/lKVOfAvIbypGqX3YiONDE22tlt/PzHKxOAEi7q4rZwlrK489uQUNdpsQK8TeDT/llzur+IsRpUb9iGLycCIlChqm1002F14NEmEUU+8xdSjC7RK+AubIC/oWZU9IQTxViOVDR1v4K30TFI/39rPH3Q6QYLVRIJVnSEpnQubIVZ0hKHHVfbOLUes+Ia3oqIaXSpKMIVRkO95bj3SpPdLedf+TtVMUZTgU5GthZpy5ltWs17liBUlBKgg3yLeoubM+5IaxStKaFBBvoVBryM6ImyuQweVKiesKKFDBfkzxKvZCj6hygkrSuhQQf4M0WYDBr3K2fSG2ahTC8wUJYSoIH8GIUS3yiIo50tW8+IVJaSoIH+OeKsahfaU0SDa3DxdUZTgUUH+HGaDnih1AbZHkqPUpiCKEmpUkG+DWi7efQa9SnUpSihSQb4NMRHqAmx3JUWZ1aYgihKCVJBvgxBCjea7Qa8TJKr/L0UJSSrItyPBalIrYLsoKVpt7acooUoF+XYY9Tpi1KrNTnlH8WrapKKEql4FeSHE74UQhUKI3UKIN4QQcWc8dpcQ4pAQYr8Q4uJe9zQI1I5GnUuKNqmt/RQlhPV2JP8xMF5KORE4ANwFIIQYC1wDjAMuAZ4UQvS5koRWs4FIkzrZaY8axStK6OtVBJNSfiSldLfc3ARktHx/OfCylNIhpSwCDgHTetNWsKidjdqnRvGKEvp8OUz9HvB+y/fpwPEzHitpue88QogfCCHyhBB5FRUVPuyOb8RGGtV0yjbodYIkNYpXlJDXaZAXQqwVQuxt4+vyM55zD+AG/tXdDkgp10gpc6SUOcnJyd19ud8JIVRuvg0pMWpevKL0BZ2u35dSLurocSHEjcBlwEIppWy5+wQw6IynZbTc1yclWs1UNDjQtGD3JDQYDYIEtbpVUfqE3s6uuQT4FbBcStl0xkNvAdcIIcxCiCxgBLClN20Fk7rAeLaU6Ag1ileUPqK3lbhWA2bg45bCVJuklD+SUu4TQvwHyMebxvmJlNLTy7aCKjHKRKXNQeu5Sj9lNuqIV5UmFaXP6FWQl1IO7+Cxh4CHenP8UGLU64i3mqi2OYPdlaBKjYlQlSYVpQ9Rk8C7wVtKN9i9CB6LWU9spBrFK0pfooJ8N5gMun69D2xarNq7VVH6GhXku6m/jubjLEYsJrWZiqL0NSrId5PJoOt3ZYiF8ObiFUXpe1SQ74Hk6P41mk+JNmMyqF8VRemL1F9uDxj1OpKj+8e8eZNBp+r3KEofpoJ8DyVHmftFTZu0OLXwSVH6MhXke0inE2Gfp46NNKqNUxSlj1NBvhfiLcawrTev03lH8Yqi9G3hGaECRAjBwLjIYHfDL9JiIzHq1a+HovR16q+4lywmA/HW8EppWM36fjdNVFHClQryPpAWGxk2F2F1OsiItwS7G4qi+IgK8j6g1wkGxoZH2mZgbKSaE68oYUT9NftIrMXY54t3xUYa+3VtHkUJRyrI+9DAuIg+m7YxGXSkx4fH2YiiKF9RQd6HDHodgxL6Xj5bCBicYEGvFj0pSthRQd7HoswGUmL6VhmA9LhIIk36YHdDURQ/UEHeD1KizURF9I2yvEnRJpWHV5QwpoK8HwghGJxgCflZKtERBgaEeWkGRenvQjsK9WF6nSAzKXTz3BaznsEJFrVfq6KEORXk/chs0JOZZAm52vORJh2ZiVZVXVJR+gEV5P3MYjKQmWQNmUB/OsCH6hmGoii+pYJ8AESZDWQlWdEF+X/bYtaTlRSFQRUeU5R+Q/21B4jVbGBoUlTQFkvFWYxkqRG8ovQ7KsgHUKRJz/CUqIDOSRcCUmPNDEqwqBy8ovRDKsgHmFGvY1iylaRo/89NNxl0ZCVZSYlW0yQVpb/qGyt2wowQgrTYSGIijJysbcbu0nx8fEiKMpMSbVajd0Xp51SQDyKr2cDwlChqmlyUN9hxuWWvjieEt5JkSowZs0GVKVAURQX5oBNCkGA1EW8xUt/sprrJSaPDjexGvDcaBPEWE/EWU8ivslUUJbBUkA8RQghvTXqLEbdHo9Hhocnlxu7ScHk0PJpESu/OTQadDrNBR6RJT5TZQIRRjdoVRWmbT4K8EGIV8AiQLKWsFN618o8DlwJNwI1Syu2+aKs/MOh1xFp0xNK3NyFRFCX4en1uL4QYBCwBjp1x91JgRMvXD4C/9LYdRVEUpft8kcD9A/Ar4Mws8uXAP6TXJiBOCJHmg7YURVGUbuhVkBdCXA6ckFLuOuehdOD4GbdLWu5r6xg/EELkCSHyKioqetMdRVEU5Ryd5uSFEGuBAW08dA9wN95UTY9JKdcAawBycnJ6N4dQURRFOUunQV5Kuait+4UQE4AsYFdLTfIMYLsQYhpwAhh0xtMzWu5TFEVRAqjH6Rop5R4pZYqUMlNKmYk3JTNZSlkGvAV8R3hNB+qklKW+6bKiKIrSVf6aJ/8e3umTh/BOofyun9pRFEVROuCzIN8ymj/9vQR+4qtjK4qiKD2j1sAriqKEMSG7UyTFz4QQFcDRHr48Caj0YXf6AvWe+wf1nvuH3rznIVLK5LYeCKkg3xtCiDwpZU6w+xFI6j33D+o99w/+es8qXaMoihLGVJBXFEUJY+EU5NcEuwNBoN5z/6Dec//gl/ccNjl5RVEU5XzhNJJXFEVRzqGCvKIoShgLiyAvhLhECLFfCHFICHFnsPvjb0KIQUKIz4QQ+UKIfUKInwe7T4EghNALIXYIId4Jdl8CRQgRJ4R4VQhRKIQoEELMCHaf/EkIcWvL7/ReIcRLQoiIYPfJH4QQzwghyoUQe8+4L0EI8bEQ4mDLv/G+aKvPB3khhB74M97dqMYC1wohxga3V37nBlZJKccC04Gf9IP3DPBzoCDYnQiwx4EPpJSjgWzC+P0LIdKBnwE5UsrxgB64Jri98pvngEvOue9O4BMp5Qjgk5bbvdbngzwwDTgkpTwipXQCL+PdmSpsSSlLT++ZK6VswPuH3+amLOFCCJEBLAP+Huy+BIoQIhaYCzwNIKV0Silrg9op/zMAkUIIA2ABTga5P34hpVwPVJ9z9+XA8y3fPw9c4Yu2wiHId3kXqnAkhMgELgA2B7kr/vZHvNtMakHuRyBlARXAsy1pqr8LIazB7pS/SClPAI/g3S+6FG+J8o+C26uASj2jJHsZkOqLg4ZDkO+3hBBRwGvAL6SU9cHuj78IIS4DyqWU24LdlwAzAJOBv0gpLwAa8dEpfChqyUFfjvfDbSBgFUJcF9xeBUdLJV+fzG8PhyDfL3ehEkIY8Qb4f0kpXw92f/xsFrBcCFGMNx23QAjxz+B2KSBKgBIp5emztFfxBv1wtQgoklJWSCldwOvAzCD3KZBOCSHSAFr+LffFQcMhyG8FRgghsoQQJrwXat4Kcp/8Snj3W3waKJBSPhbs/viblPIuKWVGy54F1wCfSinDfoTXssvacSHEqJa7FgL5QeySvx0DpgshLC2/4wsJ4wvNbXgLuKHl+xuA//rioP7aGSpgpJRuIcRK4EO8V+OfkVLuC3K3/G0WcD2wRwixs+W+u6WU7wWvS4qf/BT4V8sA5ghhvMualHKzEOJVYDveGWQ7CNPyBkKIl4D5QJIQogS4H3gY+I8Q4ia8Jde/6ZO2VFkDRVGU8BUO6RpFURSlHSrIK4qihDEV5BVFUcKYCvKKoihhTAV5RVGUMKaCvKIoShhTQV5RFCWM/X+DXJek6eXOHwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "new_campaign.run()\n", + "plt = plot_state(x,y,new_campaign)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At this point, the agent's able to make a very optimal choice." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Conclusion**: In the context of materials science experiments, for many \"low-throughput\" campaigns (e.g. up to a few thousand examples) and/or low-dimensional search spaces (e.g. up to ten or twenty parameters to control), this approach is often worth trying." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/generic_gp_ucb/black-box_optimization_example.ipynb b/examples/generic_gp_ucb/black-box_optimization_example.ipynb deleted file mode 100644 index adf14346..00000000 --- a/examples/generic_gp_ucb/black-box_optimization_example.ipynb +++ /dev/null @@ -1,408 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Black-box optimization with a generic GP agent" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example, we will show how to setup a \"textbook\" black-box optimization problem of finding extrema of an unknown function, as a campaign within the CAMD framework, and solve the problem with an off-the-shelf Gaussian Process based agent with an upper-confidence-bound approach for exploration/exploitation tradeoff.\n", - "\n", - "We will follow this outline:\n", - "\n", - "1. Define a sample function to generate synthetic data\n", - "2. Define data, agent, experiment and analyzer of our campaign\n", - "3. Intiailize the campaign with a random seed\n", - "4. Run several iterations and keep track of the progress of the agent. " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "pycharm": { - "is_executing": false - } - }, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "\n", - "from camd.agent.generic import GenericGPUCB\n", - "from camd.analysis import GenericMaxAnalyzer\n", - "from camd.experiment.base import ATFSampler\n", - "from camd.campaigns.base import Campaign\n", - "\n", - "from sklearn.preprocessing import OrdinalEncoder\n", - "from sklearn.gaussian_process.kernels import RBF, ConstantKernel\n", - "import warnings\n", - "warnings.filterwarnings(\"ignore\")\n", - "\n", - "def plot_state(x, y, campaign):\n", - " \"\"\"\n", - " this is a helper function to make plots\n", - " \"\"\"\n", - " mu,std = campaign.agent.pipeline.predict(x.reshape(-1,1), return_std=True)\n", - " plt.plot(x,mu, label='agent')\n", - " plt.fill_between(x, mu+2*std, mu-2*std, alpha=0.2)\n", - " plt.plot(campaign.seed_data['x'], campaign.seed_data['target'], 'ko',alpha=0.7, label='acquired')\n", - " requests = campaign.experiment.dataframe.loc[campaign.consumed_candidates[-campaign.agent.n_query:]]\n", - " plt.plot(requests['x'],requests['target'],'rx',alpha=0.9, label='requested')\n", - " plt.plot(x, y, label='true f(x)')\n", - " plt.legend()\n", - " return plt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's generate some synthetic data using an arbitrary function, with one column for X and one target column." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def f(x):\n", - " return np.sin(x)*np.sin(x)*(x**2)\n", - "x = np.linspace(0,10,500)\n", - "y = f(x)\n", - "df = pd.DataFrame({'x': x, 'target': y})\n", - "plt.plot(x,y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, say we want to maximize this \"unknown\" function. We will use a generic GP-UCB agent and a generic analyzer of campaigns for axquiring 'maxima' from a candidate set. Say we also want to know number of points with target value above 58." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "N_query = 2 # This many experiments are requested in each iteration\n", - "N_seed = 5 # This many samples are randomly acquired in the beginning to form a seed.\n", - "agent = GenericGPUCB(n_query=N_query, \n", - " kernel=ConstantKernel(100.0)+RBF(10.0)*ConstantKernel(1.0))\n", - "analyzer = GenericMaxAnalyzer(threshold=58)\n", - "experiment = ATFSampler(dataframe=df)\n", - "candidate_data = df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we setup a campaign and initialize it. Here we instructed the Loop to generate a seed with 5 randomply selected points." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign initialization state: Agent RandomAgent hypothesizing\n", - "Campaign 0 state: Running experiments\n" - ] - } - ], - "source": [ - "new_campaign = Campaign(candidate_data, agent, experiment, analyzer, create_seed=N_seed)\n", - "new_campaign.initialize(random_state=20)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's have our agent acquire two new points and plot our results." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign 0 state: Getting new results\n", - "Campaign 0 state: Analyzing results\n", - "Campaign 0 state: Agent GenericGPUCB hypothesizing\n", - "Campaign 0 state: Running experiments\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "new_campaign.run()\n", - "plt = plot_state(x,y,new_campaign)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that the GP-UCB agent requested two new points close to maximum. Let's run the agent one more time." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign 1 state: Getting new results\n", - "Campaign 1 state: Analyzing results\n", - "Campaign 1 state: Agent GenericGPUCB hypothesizing\n", - "Campaign 1 state: Running experiments\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXkAAAD4CAYAAAAJmJb0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nOydd2CV1fnHP+9dyb3ZO0CAhD1CCBtkiDK0xYFatNYOax212p9t0aq1Ko4qVm3rqquttVZbi9rWSRWUMAQEwk5CgISE7ORm3j3e8/vjkkjIzbw3yb037+cfyDvOOXc973Oe85zvIwkhUFBQUFAITVSDPQAFBQUFhf5DMfIKCgoKIYxi5BUUFBRCGMXIKygoKIQwipFXUFBQCGE0gz2As0lMTBTp6emDPQwFBQWFoGLfvn11Qogkb+cCysinp6ezd+/ewR6GgoKCQlAhSVJJZ+eUcI2CgoJCCKMYeQUFBYUQRjHyCgoKCiFMQMXkveF0OikrK8Nmsw32UEKK8PBw0tLS0Gq1gz0UBQWFfiTgjXxZWRlRUVGkp6cjSdJgDyckEEJgNBopKysjIyNjsIejoKDQjwR8uMZms5GQkKAYeD8iSRIJCQnK7EhBYQgQ8EYeUAx8P6C8pwoKQ4OgMPIKCgoKCn1DMfIBQGNjI3/84x8HexgKCgGPxeGiqsnGqTozRbUmSo0Walps2F3uwR5awBIyRj6Yi58oRl5BoWvMdhcnakycrDFT22KnxebCbHfTZHVS3WSnsMrE6XoLTrc82EMNOELGyDvdAqvDjc3pxuGScbllZFn4zfivXr2aWbNmMXXqVF555RUA/vznPzNhwgTmzp3LTTfdxO233w5AbW0tV111FXPmzGHOnDns2LEDgHXr1nHDDTewdOlSxowZw7PPPgvAPffcw8mTJ8nOzuauu+7yy3gVFEIBIQSVTVaKas1YHV17640WJ4XVLTTbnAM0uuAg4FMoz+ahD46SV9Hs9ZwQIPBu0CUAScLbUuOU4dE8eOnUbvv+y1/+Qnx8PFarlTlz5rBq1SoeeeQRcnNziYqK4sILL2T69OkA3HHHHfz85z9n0aJFlJaWctFFF5Gfnw9AQUEBX3zxBS0tLUycOJFbb72V9evXc+TIEQ4cONCTt0FBYUjglgUlRjNme89DMbIMJXUWRsTpiY/Q9ePogoegMvJ9RQAIzyOgK4PfFc8++yz//ve/ATh9+jRvvPEG559/PvHx8QCsWbOGwsJCADZt2kReXl7bvc3NzZhMJgBWrVpFWFgYYWFhJCcnU11d7dNrU1AIRdyyoLiue++9M8obrKgkiDUohj6ojHxXHrfDJeOSex6Pk5DQqCU0qu7N/ZYtW9i0aRM7d+7EYDCwdOlSJk2a1Oadn4ssy+zatYvw8PAO58LCwtr+r1arcblcPR6zgsJQQAhBab2lzwa+lbIGK1q1ioiwoDJzfidkYvK9RSBwumVsTk/8vqvYfVNTE3FxcRgMBgoKCti1axdms5mcnBwaGhpwuVy8++67bdevXLmS5557ru3v7sIwUVFRtLS0+P6iFBRCgMomGyab786PEFCqLMYOXSPfikDgcMvYXZ6FWm9cfPHFuFwuJk+ezD333MP8+fMZMWIEv/rVr5g7dy4LFy4kPT2dmJgYwBPa2bt3L1lZWUyZMoWXXnqpyzEkJCSwcOFCMjMzlYVXhSFNk8WJ0eTwW3sut6C8weq39oIRKZBSD2fPni3OLRqSn5/P5MmTu723t+Eab0iAVq1Co+7Zs89kMhEZGYnL5eKKK67ghhtu4IorrvBpDANJT99bBYWBwOGSOV7Tgo8/Y6+kxemJC+GFWEmS9gkhZns7N+Q9+bMRgMMt43B1Hb5pZd26dWRnZ5OZmUlGRgarV6/u/0EqKIQif/wjlv/7GeG7d7UdMuzZRerD9xH/+p98br6yyYZriIZthvaKRCe4ZBmBhE6t6lLj5amnnhrAUSkohC4tE6egX/8EyZ+8x/9uXotTm8zlf7gXjUrF6Wde9rl9tyyobrEzIlbvh9EGF4qR7wS3LHAIGZ2ma0OvoKDgG25ZsM1lZfR3EpgafpJv2dfhroeWqRqen/M4i7NmE+mHfupNDhIidIRr1X5oLXhQwjVd4BaeRdlAWrdQUAg1cjY8x7Ivv0tKeD1lplm4N8pIlYLYBS4uVL3Kund2YTTZ/dJXdfPQk9dWjHw3uGWB060YeQWF/uCrjX/n/LwHKNRNpWHSEyT9qwi5yID7f3pcWyKYqTrJQ5bf8OC/92O2+55W2Wx1+Zx/H2z4xchLkhQrSdI7kiQVSJKUL0nSAkmS4iVJ+kySpONn/o3zR1+DgUuWh+yijYKCv8nJyWHNmjVcvCCTydt+SoFrOAmZd5J+991IQOnLf6P0lb/hLtIh54QxXzrKNZY3eerTY8h+mFXXtAwtb95fnvwzwEYhxCRgOpAP3ANsFkKMBzaf+Ttocbo7z6P3lQceeIBNmzb1+f4tW7ZwySWX+HFECgr9Q05ODmvXrqW2ppoHZxqRheAXn6sxb/kC7beuwvX669jmzccyZz5lz7xMw/irMEsz+LH6A1yle/jkSJXPY2i2urA5h4437/PCqyRJMcAS4HoAIYQDcEiSdDmw9MxlrwNbgLt97a87tm3dyssvvkBx8SkyMtK55dbbWLxkic/ttqZXhkn+X4h9+OGHvR53u92o1UNrkUghtHn++eeJiIhgcXIzC5IsPJyXjk0Xy6/KytiwYQMGIN3uorjOjGWOx9irHC1MeOdCntC8xeVfjmfO6DiSoztKhvSG2hY7I+MN/nlRAY4/PPkMoBZ4TZKk/ZIk/UmSpAggRQhReeaaKiDF282SJN0sSdJeSZL21tbW+jSQbVu3cu8v76KmppaUlBRqamq595d3sW3rVp/aBbjmW1exYN4cpmZmtkkNb9y4kZkzZzJ9+nSWLVsGgNFoZOXKlUydOpUbb7yR0aNHU1dXx6lTp8jMzGxr76mnnmLdunUAXH/99bzzzjsApKenc/fddzNz5kw2bNjAp59+yoIFC5g5cyZr1qxpEzrbuHEjkyZNYubMmbz33ns+vz4FhYGguLiYuMhwfpxRyoF6PXssI4mMjKS4uLjtmogwDcnRX2s8ybooqmetZaKrgBXs5q87T/k8jiarc8jIHfjDyGuAmcCLQogZgJlzQjPCk57iNdYhhHhFCDFbCDE7KSnJp4G8/OILGCIiiI6ORqVSER0djSEigpdffMGndgFefOVVduz6im1f7uLZZ5+lurqam266iXfffZeDBw+yYcMGAB566CEWLVrE0aNHueKKKygtLe11XwkJCeTm5rJ8+XIeffRRNm3aRG5uLrNnz+Z3v/sdNpuNm266iQ8++IB9+/ZRVeX7FFZBYSDIyMhgdlgRIwwuXj2dAZKEyWQiIyOj3XVJkWGEab82Tw3j12CLHc99hvfZdryWvErvkuM9RQioN/tPPiGQ8YeRLwPKhBC7z/z9Dh6jXy1J0jCAM//W+KGvLikuPkVkZPuM2sjISE6dOuVz2y++8DzzZs9k6eKFnD59mpdffpklS5a0fTlbJYe3bt3Kd7/7XcAjKxwX1/v15muuuQaAXbt2kZeXx8KFC8nOzub111+npKSEgoICMjIyGD9+PJIktfWn8DVuWVBvdnC63kJxnZnT9RbqTPYh470FKj++5Wa+n1HHzppwTjniaW5uxmw2txXcaUWSJFKizgrJqNTUTv8JqfYiLtUf4c3dJT6Ppd7sGBLp0T4beSFEFXBakqSJZw4tA/KA94EfnDn2A+C/vvbVHRkZ6W3hjFZMJhPp6ek+tbs1J4cvPt/MF1u3s3tvLtOzs5mWNb1XbWg0GuSzRDlsts5X+CMiIgCP5OqKFSs4cOAABw4cIC8vjz//+c99exFDBCEENS02CqqaKW+w0mhxYrK5aLQ4qWy0cayqhaomW78toit0TZTpOKOiZN5vGEd1dTXJyck8/fTTnH/++R2ujTFo23nzjWMvwxExnLURGzlU1kS+j968yy1otoa+1Le/smt+CrwpSdIhIBt4DFgPrJAk6Tiw/Mzf/cott96GxWymubkZWZZpbm7GYjZzy623+dRuc1MTsbEeqeFjBQV8tXs3FquVrVu3tsUS6+vrAViyZAlvvfUWAJ988gkNDQ0AZ9YIajAajdjtdj788MNu+50/fz47duzgxIkTAJjNZgoLC5k0aRKnTp3i5MmTAPzjH//w6fWFCi63TFGdmeome6ciV0J4Ft1O1ppwuBSvfiARskzSoZcoVo3isTe3snfvXjZs2ODVwLeScLaomEqLccoPSDftZ3p4Ff/cc9rnMdVbQj9k4xcjL4Q4cCauniWEWC2EaBBCGIUQy4QQ44UQy4UQ9f7oqysWL1nC4799kuTkJGpqqklOTuLx3z7pc3bNiosuwuVyMTNrGg/8+j7mzptHQmISL7z4EldeeSXTp09vC7E8+OCDbN26lalTp/Lee+8xatQoALRaLQ888ABz585lxYoVTJo0qdt+k5KS+Otf/8q1115LVlYWCxYsoKCggPDwcF555RVWrVrFzJkzSU5O9un1hQIOl8zJWjOWHpaKszllTtaasLuGTirdYFOweyMZcglVU29CUvUsayzOoEN1lpVqmLAGWaXl7qTd5JY2cKLG1PnNPcBkc4X8d0CRGvYBCQjTqlF1kVKZnp7O3r17SUxMHLiB9ZBQkRpu9eDtzt5//lqNxNikSLQ9lJdW6Du7nv4WU1u2o7nrOPqIqB7fV95opf4sjfmRm28lonwHs6zPM3tMKj9fMcGncaVEh/mckjnYKFLD/YQAZSfsICOE4HSDtU8GHsDp8pSaCyRnJxRpajAyvTmHgoSLemXgAeIM2nZ/N0y4Gq2jkdvSitl6vJYmq9OnsYV6yEYx8j7ikkWXi3inTp0KSC8+VKhpsftcKs5id1Pd7B8BLAXvFGx6Db3kIH7xj3p9r0GnQaf52lSZRizCFRbHpeqduGTB/476lkLsdAm/6OIEKoqR9wMDHSZS8GBxuKjxk3GubbFjcYTuD32wiTv2NsWq0YzJWtSn+2P0Z3nzKi1NGd8kteoL5qWF88mRStw+Zks1hLA3rxh5P+CWhV+EkxR6jhCCMj/X7ixvsCphm36g7MQRJrgKqcq4AknVN5PTzsgDTWMuQ+WyclNKIXUmB7mlDT6NsdnqCtnPXjHyfsATmw/NL0igUmuy9zkO3xk2p0ydH4tIK3g4te1NAMYu7fumPb1OjVbzdYKDOXUuTkMys1u+IEavZXN+tU9jdMuClhAN2ShG3k+4ZaW4yEDhdMt+C9OcS03L0K0F2l+knN5IoXYSySPH+9ROdPjZIRs1TenfJLp8CyvGRbK7uJ5mHxdgmyy+3R+oKEbeTwg8i7D9yWOPPdbre/7617922DIe7FQ32+iv56kse2YJCv6h9PghxstFNGas8rmtqPD2ornNo1egcttZE1+ESxZsO+6bwGGzzRmSjlpIGXnNSy+i+nJHu2OqL3egeelFv/UhhGgnT3A2vi7+dEdfjHyoYXO6aTD3r8dlNDkUjRs/UbHjnwCMXnytz21Fhmk4e0uKJXUebm0k45p2MCYxgk0FvsljyTIhGbIJKSMvZ2Whu+22NkOv+nIHuttuQ87K8qndklOnyM6cyo03XM+cGdmsf+w3LD5vPnNnzeDRhx9qu279448xYcIEFi1axLXXXstTTz0FwNKlS2nd5FVXV9empeN2u7nrrruYM2cOWVlZvPyypyp9ZWUlS5YsITs7m8zMTLZt28Y999yD1WolOzub6667DoC///3vzJ07l+zsbG655Rbcbs/Ovddee40JEyYwd+5cduxo/9ALdmpb+t/LFgLqFG/eLySe3kiBZhIpPoZqwCNaFhn2tTcv1DpMaUuILt3MsklJnKgxUWI0+9RHKIZsQsvIn7cQxwsvoLvtNjRPP43utttwvPAC8nkLfW77xInj3HzLj1n/5FNUlJezdcdOdu3Zx/7cXLZv28b+3H1s+Nfb7N67j48//pg9e/Z02+af//xnYmJi2LNnD3v27OHVV1+luLiYt956i4suuogDBw5w8OBBsrOzWb9+PXq9ngMHDvDmm2+Sn5/P22+/zY4dOzhw4ABqtZo333yTyspKHnzwQXbs2MH27dvJy8vz+bUHCnaXm8YB+hEaTQ4lNu8jFadPMs59ksZRK/zWZoeQzchlaC3VfDOpDrVKYrOP3nyLLfSybHyuDBVoyOctxPXd76F57hlcP73DLwYeYNSo0cydN5977/4lmzdvYsFczw5is8nMyRPHaWlp4bLLVxMeridcq+ayyy7rts1PP/2UQ4cOtRUMaWpq4vjx48yZM4cbbrgBp9PJ6tWryc7O7nDv5s2b2bdvH3PmzAHAarWSnJzM7t27Wbp0Ka3a/Ndccw2FhYV+eQ8Gm4Hw4ltp1RsP9u3ug0nxl/9mODBy3hV+azMirL3Jahl5AQKJ4TU5zBi5nG3H67j+vPQupUa6wi0LzA53uxlDsBNSnjycicH//Q1cP70Dzd/f6BCj7yuGs+R/77zrl+zas49de/ZxOL+AH/zwhrbrvC3Ani0zfLbEsBCC5557rk1KuLi4mJUrV7JkyRK2bt3KiBEjuP766/nb3/7WYTxCCH7wgx+03Xvs2LG2SlOhiNMtD5gX34pxiOiN9xf64k1UScmMmDDTb22Ga9unUrr1iViTsokq/ZzF45OoM9k5VtXiUx++ZukEGiFl5Ftj8I4XXsC1dm1b6MZfhh5g+YqV/O31v7bp1leUl1NTU8PCxYv54P3/YrVaaWxq5oMPPmi7Jz09nX379gG0ee0AF110ES+++CJOp+dLVVhYiNlspqSkhJSUFG666SZuvPFGcnNzAY+SZeu1y5Yt45133qGmxjM9ra+vp6SkhHnz5pGTk4PRaMTpdLZVrAp2GsyOfsuo6QyXW/isizJUsVrMTLLmUpq4GPxcEzlCd443n7YEfd1BFqZp0Koln7NsWnyUyQg0QsvIHzrULgbfGqNXHTrktz6Wr1jB1dd8mwuWLGLOzGyuu/YaTC0tzJgxk2+tuZr5s2dx+aWrmD17Tts9d955Jy+++CIzZsygrq6u7fiNN97IlClTmDlzJpmZmdxyyy24XC62bNnC9OnTmTFjBm+//TZ33HEHADfffDNZWVlcd911TJkyhUcffZSVK1eSlZXFihUrqKysZNiwYaxbt44FCxawcOHCkFCZFEJgHKRSbYPVb7BzbPfHGCQ7hqnf9Hvb54ZSTMMXIQmZRONeZo2OY8cJo0+Zbg6XjM0ZOvLDitRwP/H4o48QEx3FnXfeOdhD6ZRgkRpusjgprbcMWv/jUyIJ1/ZM/1zBw67nf0hW7Ueo7y0mLDzCr207XHK7kIzkdjDljWnUT7iGtxN/ypOfHuOxK6YxbURMn/tIiQkjOSp41mMUqeFBQBZCief6CaN5cNMZh0rBZ38hZJnRxm0cM8z0u4EH0GlU7VQphVqHOXU+keXbmJMej06jUkI2Z6EY+X7ivvsf4Bdr1w72MIIem9ONuYfVnvqLRkto7oTsL04X7meYqMU+xn+pk+di0LWfWZlGLCK86STRjmrmpsfz5UnfQjYWuztkUmgVI9+P9PcO2KHAQGfUeMMtD42Cz/6ifN/HAIyee2m/9XFuKqVpuEfCOKJiBwvHJdJkdZLnY6FvU4jsflWMfD/ilpWQjS8IIQJG5ztQxhEMhJ/eSplqOMNG+1aWryvO9eRt8RNxhScQWbGDWaPi0KoldhUZfeojVEI2ipHvRwQoOvM+YLK7AkbC2TOW0Ji+9yd2u40J1oNUJszr137CtWrUqrNSMyUV5tS5RFTtQa9TM2NkHDuLjD45WYqRV+gRSsim7wRCqKYVIVBy5ntA0YEcIiQ7mnEX9ntfEWHtvXlz6lx0ptNoTRUsGJNAbYudk7V917JxywKrI/hTKRUj38+4ZZSQTR+Q5cDbiNQYYOMJRJrzNiELiYzZF/d7X4ZzNkWZUz2zB0PVV8zJiEclwU5fQzb24P/MFSPfDY2Njbzig1SxQNCVM2+321m+fDnZ2dm8/fbbAHzrW9+iqKio03scDgdLlizB5QqN6aQ3PNregz2K9ljsbkWCuBtiK7/kuGYcsQnJ/d5Xx7j8ZNzaKCKqdhOj15I5PMZnI+9rkfhAQDHy3dDU2MgrZySAz6WnRrarkM3+/fsBOHDgANdccw1Hjx7F7XYzZsyYTu/R6XQsW7as7aEQigSaF99KoI4rEHBamhhjz6cmccGA9KfXqtsrJqjUWFJmE1H1FQDzxyRwut5CuQ+1gC0ON3KQh1yDS2rtk3ug6rDXUxoh6MueRDklE+eKzotxPPDr+yguOsn8ObO4cNlyLv7GN3nkoQeJjY2j8Ngx3v/oY666YjV79x8A4A+/+x1ms4n77n+AopMn+fkd/0ddXR2REQZeffVVJk2a1NZ2TU0N3/3ud6mtrSU7O5t3332XN998k8svvxyAkpISli9fzs6dO4mPj+f888/n/vvvZ+XKlaxevZp77723TVs+lHDLImAXvZqsThIjwwZ7GAHJqdxNjJfc6Cb0fzweQKWSCNeq28XNzalzSN37BWpbPfPHJPDKtiJ2Fhn51qy0PvUhBJgcrvalB4MMxZPvhocf/Q0ZY8aya88+Hlv/BAAH9u/nyad/x8GjXWu13/6TW3n6939gx67d/Pa3T/KTn/yk3fnk5GT+9Kc/sXjxYg4cOMDYsWPZsWMHs2bNAmD06NHcfffd3HrrrTz99NNMmTKFlStXApCZmdkjzfpgpNkaeKGaVpSQTedYCjZhE1rGzRoYIw8dQzbm1LkARFTtISkqjHHJkewsqvN2a48J9pBNcHny31jf6SnXAGrXzJ4zh/SMjC6vMZlM7N61k+9+59uAp6qNw9799vzKyso2LXjwiJht2LCBl156iQMHDrQdV6vV6HQ6WlpaiIqK6uMrCUyabYEdEmmxuYiP0A32MAKO+OovydNOYWZs3zVjeotBp+bsqLs1aTqyOoyIqt00p1/EgjEJvLGrBKPJTkIfZ2DmIN8UpXjyfcBg+FqPQ6PRIM56uNjtHr14WZaJiY1t053fvWcf+fn53bat1+vbac5bLBbKysoA2uSNv+7LTnh48Igo9QQ5gEM1rYSa3rg/cDVVMtJ5irqk8wa0X/05nrxQh2FJysZwJi6/YGwCgE8bo2xOOahnb4qR74bIqChMps6LECSnpFBbW4PRaMRut/PJxx8BEB0dTXp6Ou+969GPd8sy+8/yxDtj8uTJnDhxou3vu+++m+uuu46HH36Ym266qe240WgkMTERrTZ4Y4Xe8JRfG+xRdI3J7lL2P5xFTk4OT9x+JQBvb9xJTk7OgPUdpjlnUxRgSZ2L3ngEldPMyDgDaXF6n7NsgtmbV4x8NyQkJDB/wXnMnpHNr+65u8N5rVbLPb+6j/MXnsel37yYCRMntp37y1//xuuvvca82TOZnT2d//7nv932t2rVKrZs2QJ4fjx79uxpM/Q6nY7XXnsNgC+++IJVq1b550UGEIEeqoEzi3FB/KP3Jzk5Oaxdu5YMqZxmh4pjdW7Wrl07oIb+3Li8JWUWkpDR13rqSMzPSOBIRbNPsfVg/rwVPfkBRK2SCNN0nQNktVq54IIL2LFjB2p159deeeWVrF+/ngkT+q4PEmh68kII8iqbCfCPEYBYg5aR8YbBHsagc/7555OXl0fONXZKW1Q8VDgZnU5HcnLygFUlq262UdP89XqX2tbAlL9Pp2rO3dROv41jVS3c+c5B1q6YwNKJfcvf12okJqVG+2vIfkfRkw8Q5B4Ilun1eh566CHKy8s7vcbhcLB69WqfDHwgYna4g8LAQ+jomvhCTk4Oe/fuJVbnZkqixI4yQX5+Pna7neLi4gEbx7lxeXd4HPaYMRiqPWUzx6dEEm/Q+RSXd7oEdldwShwEhZEPpNmGL3gEy7q/7qKLLmLUqFGdntfpdHz/+9/3bSwB+J4G04KmWxZYHEPb0D///PMYDAbmDfN8l3ZXaVCr1Zw8eZKMbrLP/InBS9UuS/IM9DX7QQhUksS8MfHsK23A4eq7FzHYdQ36SsAb+fDwcIxG39TkAolA2D0nhMBoNAZcZk6wecfBNl5/U1xczLhx41gwzI3FKThSp0IIgcVi4fbbbx+wcWjUKrSacxZfk2aitdWhNZ0GPHF5m1PmYFljn/sJ1sVXv+XJS5KkBvYC5UKISyRJygD+CSQA+4DvCSF6LcqdlpZGWVkZtbVdl/NyySIgDGh3SBJo1YP/bA0PDyctrW+7APsDm9Ptk5c1GLTYXKQEbpi238nIyKCmpoYlGTp2Vbqw2J1otVrmzJnD+eefP6BjMWg1NLm+nglaUmZ6jlfn0hQ1imlpMRh0anYVGZmTHt+nPoJ18dWfm6HuAPKB1q/9E8DvhRD/lCTpJeBHQK+VvrRabY+mfuWNVupNwVHYYdywqIAw9IFEMP6ArA5PiTjNEP0sb7/9dh745R1MjXPyTE0K06cPw2w289BDDw34WMJ1KprOkqixxU3ErTFgqMmladxqtGoVs0fH8VVxPW5ZdEi77Akutycu313yRKDhl2+nJElpwCrgT2f+loALgXfOXPI6sNoffYUCwTrt60+CNfQRjA8nf3H++efz61uuQiXB3kqPTMfTTz894F48dJQdRqXBmjQdQ+3+tkPzxyTQaHVyrLrzfS/dEYxxeX958n8Afgm07q9PABqFEK2/gDJghLcbJUm6GbgZ6HKxMZRosbmINSjb4luRZRG0D76h/lmmSLXYhYan3tvFsIS+hUH8gb6TxdekQ68guWwITTizRsehUUnsPGlkyrC+xdnM9uCTtPDZk5ck6RKgRgixry/3CyFeEULMFkLMPluzJZQZyt6fN0yOwN/l2hlD/bOMqtpNgXr8oBp48OxB0WnamzNL8kwk4UJf51GuNeg0ZKXFsru474kc5iDMqPJHuGYhcJkkSafwLLReCDwDxEqS1DpTSAM6T/weYrjcApsz+KZ9/UUwq/wN5c9StrUw0l5IXYLXPTgDzrnevDVpBgCGmrNDNvFUNtkorbf0qY9gzJf32cgLIe4VQqQJIdKBbwOfCyGuA74Avs+eUtEAACAASURBVHXmsh8A3e/pH0IMdQ/wbIL9vQjW9QRfKTu0BQ0yujGLB3soQMdNUS5DEo6okRhqc9uOzcvwXbDMEmRx+f5MC7gb+IUkSSfwxOj/3I99BR3BGoP2Nw6XjN0ZXKmT5zJUP8uG/C24hIoxMy8Y7KEAHY08eEI2rTtfAeIjdExMiWJXUX2f+wm2kI1fjbwQYosQ4pIz/y8SQswVQowTQqwRQnQvpj6EMNldIbPByxeC3YuHoftZGip3U6gaw4iU/q/n2hO8Lr4mzUBrqUJjrmw7Nn9MAidqTdS29M0kBVuGzdBM8A0AZBmsQzSWezbBHI9vRQhPLdChhHBYGG3Lpzo+MOLx0Nnia2tc/muZ7wVjPCGb3cV9C9k4XMGlL68Y+UEkFLxYXwmV92CohWzKjm5HhwvNmEWDPZR2nOvN2xKmIKu07fLlR8TpGRmnHzJxecXIDyLBNu3zN1aHO2SKb7QMMSNvPPI5spBIz1422ENpR7iuvUkT6jBsCVPQ1x5sd3z+mAQOlzf1eSYZTHF5xcgPIuYhGsttpcUePKqT3WF1uINCO8lfhFV8xUnVKNKGDxvsobSjs7i8vu4QyF87VfPHJCAL2FPStwXYYJq5KUZ+EBmKsdyzCaWZjBDB5d35gnA5SLceoSp2Jh4Fk8DBm5G3Jk1H7TQT1vh1Wc1xyZHER/RdY97mlINmFqoY+UFmqBiGcxEieKUMOiOUHlpdUZa/Gz12VOkDW7S7J3iVHW5dfD0rLq+SJOZlxJNb2tDnzU3B8ttVjPwgM1QMw7lYHO6glTLojGD50ftKzdEvABiVvXyQR+Kdc715R3QGbl00+toD7Y7PH3NGY/50U5/6CZbFV8XIDzJDNS4fal48DJ24vOb0Lk6TStqogav+1Bs6hGwkCUtSdrs0SoBpI85ozPcxlTJYHuqKkR9khBia+fKhkjp5NkMhLi9kN6PNhyiPzg64eHwr4V53vs4gvKEAyfm1Zo1HYz6+TWO+t1gd7qBw0BQjHwAMtZCNLIuQXXAO1dfVSvnxg8TSAqMCLx7fSmeLr5KQ0RsPtzs+f0w8TVYnBVXNve4nWBInFCMfAIRi6KIrLM7Qi8e3EoozlLOpOLQZgGHTLxzkkXSOVq3qUPnJkpQN0CFk06ox31ctm2CYuSlGPgAwO4ZWXD6UH2qhHpeXSndSRyyjxk4d7KF0ybliZW59Io7IkR0WXw06DdNH9l1j3qp48go9QZbBHmRFrH0hlI28EJ6ZSigihGBky0FKIqcjqQLbdHivFJWN4RwjDzA/I4HKJhslxt5rzAdDqDWwP6khRKhP81sJ5Xh8K5YQ/SzLTxWSSh3ukQsGeyjd0tnOV52pHI2lpt3xeWPiUUmw/URdr/txy4FfNEYx8gFCsOTc+oo1hOPxrZhD9CFWdmATACmZgRuPb+VcDRvwLL4CHUI2cQYd00bEsPV4bZ9CNoHutChGPkAIhgUcfxDKoZpWQnXvg/vUDlowMGrSrMEeSreEadScG1GyJk5DSOoOi68Ai8cnUdlk42Studd9Wfzx2z38DlTn+d6OFxQjHyC43MFXO7IvhKqXezZCeLRNQo0RTQco1k9DUmu6vzgAODdkIzTh2OInd/DkwaMxr1ZJbD9R2+t+fPbk3U749y1w+F++tdMJipEPIEI9ZBOKejWdEWprLOVlpaRTjmPEvMEeSo/xWg4wKRtD7UEQ7R/C0Xot09Ni2Xa8rtezMLtTxuVLEZGGEpBdkDC+7210gWLkA4hQD9kMhXh8K36ZwgcQJ/Z54vFJQRCPb8XrpqjkbNTOFsKaijqcWzw+kZoWO4XVpl735VNGlfG4599ExciHPIG+gOMrwZBu5i9C7bW6irdjQ8uozMDd6Xou4V4zbDybovQ1+zucmz8mAY1KYtvxPoRsfPm8684Y+YRxfW+jCxQjH0D4PO0LcIZKqAaCI7WupwghGNZ0gJLwyUiasMEeTo8J06g4V17HHjMWtzbSE7I5h8gwDbNGx7H9RB1yL6ecPs3CjcfBkACG+L630QWKkQ8wQnlhMtTDUecSDLshe8LJsiomykVYhs0d7KH0CkmSOsblVWqsSVnoazt68gCLxiViNDvIq+idlo1PYmV1J/otHg+KkQ84Qi2W24rN6UYO3UmKV0Jl8fXY3s2oJUHy1OCJx7fSaTlAYz6Sy9bh3LyMBMK1Kr44VtPhXFf4pCZrPA6J/ROqAcXIBxyhFsttZSiFaloJFQlp+dSXuFAxfNqSwR5Kr/GuSJmNJFzojUc7Xq9Tc97YRLafqOt1SnOffrvWRjDXKp78UMLj8YZeCkqoLyp7IxTWWGxOF8Ob9nNaNw4pLGqwh9NrvKZRJp9ZfO0kZHPhpGQsDje7e6lM2afwnPFM3dl+yqwBxcgHHKFaRGSoxeNbCfY1liOltUwVxzGlzBnsofQJb4uvLkMKjohhXhdfwVMxKjEyjM97GbLp03e8LbNGMfJDilAziA6XjNMVerOTnhDsaywn928hXHISOyX44vHgWXz1lkppTcr2mkYJniLfF0xMYn9pAw1mR4/7crkFjt6qyRpPgKSGuPTe3dcLFCMfgITaztdgN3S+EMxhKqvDjbZ0BzISadOXDfZw+kxnO1/DWkpR27yHZC6clIwsYEth77z5Xn/Xjcc9Bl6j6919vSAkjHyT1UlRrSlk8pKD2TB4I9hDFr4QzEVE6s12RjXv88TjDXGDPZw+433n6wwAr/ryAGlxBiamRPFZfk2vUiN7/dutO9Gv8XiA4FAa6obtx+u47a1cAOIMWlKjw0mJCWdUvIGMxAgyEiKIj9AFbOHhc2ndSONtmhmMBJu+utpqxFB3EI25GlRqnIZULElZyGGxvW6rdY0lIiy4fmqyLDhSWsNScZxjKd8e7OH4hMGLJ+9RpFShr9lPy0jvoaiLpqbw7OcnyKtsZurwmB711StPXpah/iSMvaDn9/SB4PrmdcKs0XGsu2wKJ2rMVDfZqG62caS8mS3Hvt6eHKvXMnlYNJkjYsgcHs3ohIgOdSADCYsjNIy854EVBBkmspuY4o+IP/YWkRVfdjgtJDWmEYupnXYz5uEL6bCa1wUWR/AZ+Wabk9q87YRJTmKm9K8R6m9aF1/PdshlbQS22AmdLr6CR374T9uL2XikqsdG3uaUkWWBqie2pek0uGyKJ98TUmPCWTEllVmj2i+SmOwuTtWZKa4zc7ymhaMVzewsMgIQo9cyJz2OeRkJZI+MDTiDanG4iI/ovzjdQBEMi8j6mlyGf3k/hrrDOKJGUj3z55iGnYczKg2EjK65hMiK7cQdf5cxn3yHlrQLKF/0GM7IET1q3+PdBY8cAECdyUFY2XbcqBieFZyLrq20Lr6em+JoTc4m+tRGj/X38tAO16q5YGIyn+ZVcZN1DNF6bbd9tZZ/jOzJQ93Y/5k1ECJGvjMiwzQez33E10/hmhYbRyua2XuqgZ0njWzKr0GvVbNofCLLJiUzZVh0QIR1QiUuH9CLyEIm6cALpOQ+jUufROnSZ2kaexlI7ZeqnFEjMY9YRM3MnxOf9wYp+55i3L+/QemylzAP716wK9g2uFkcLpqtTjJMuZTpJzA6Injj8a3odR2NvCUpm/hj/0TXXIIjJt3rfRdPTeWjw5V8XlDD6hk9fKjbXT0z8nVfq0+63DIadf8skYa0kfdGclQ4yRPDuWBiMk63zJHyJnIKa9l2vJbP8qpJi9Nz+fQRXDApiTDN4Hn3rRtp+uuDHygC1ZOXXDZGfvFTYkr+R+PYyylf+DiyLrLLe4Q6DOO0G2kZtZzRn/2IjE+uo3zxEzRMuLrL+9yypyDMYH6fekNdi4PjZdVcwQlODPv+YA/HLxi0as7No7EmtS6+7u/UyKcnRjA5NYqNR6u4PHt4jxzAHjtoNfmgj4eIJMxWFzGG/vmt+9yqJEkjJUn6QpKkPEmSjkqSdMeZ4/GSJH0mSdLxM/8GnDugVauYMSqOny2fwN9+OI87lo0nTKPihS0n+NHre/nnntJBTf/zSaM6ABBCBKRIl8rRQvrG7xNd8ikV89dxeumz3Rr4s3HEpHPysv9gGr6QEVvvIrZwQ7f3BPSM5ixsTjdNVidNhTvQSW7igkg/viu8pVHa4sYja/ReK0WdzcWZwyhvtHLgdGOP+uqxY1OTD8lTQJJosTt7dk8f8MejwwWsFUJMAeYDt0mSNAW4B9gshBgPbD7zd8Ci16lZPjmF31+dzWOrMxmfHMmbu0u56W97ef9gOc5B2J4eLIahMwKxSIjkspK+8ftEVO/l9AXPYsy8oVeLqK3IuihKVvwJ04hFpG29k+jiT7q8PlBnNOdS22IHIKJyJ25URI1fPMgj8g/edr6i0mBJzPJa8/VsFo9PJM6g5T8HynvUlyzTfTq3EFBbAMmTcbrlfk1O8NnICyEqhRC5Z/7fAuQDI4DLgdfPXPY6sNrXvgYCSZKYlhbLg5dO5ek100lPiODVbcX8+O/72HOqd1oWvhIshqEzAi4WLbsYtfknGGpyKb3wOZrGXu5Tc0ITTsmKP2FJnsnInJ8RXnek02sDcUZzLlaHm0aLkyark0m2A1QYJqGP7H3aaCDiVXYYz87XcONRJHfnO1u1ahWXZA0nt7SREmPPCn13G7JpLgd7MyRP6nfxPr8GgSRJSgdmALuBFCFE5ZlTVUBKJ/fcLEnSXkmS9tbW9r4iS38yISWKR1dn8tBlUwnTqHj4wzwe+zifOpN9QPr3SaM6AAiona5CMGL7PUSf3kzFwkdpzljln2Y1ekqXv4wrLJbRn/0ItdXo9TqbU8Yd4JuiKpqsAOSdqiBLKsKWdl5Apxn3Fm/58pbkbFSyg/D6/C7v/UZmKmEaVY+9+W4Nd82Z/pKn0GILEiMvSVIk8C7wMyFEO8V94bFUXr/hQohXhBCzhRCzk5KS/DUcvyFJEjNHxfHMt2fw/fmj2VfSwE/ezGVzfnW/G+BgFysLJE8+8fCrxBf+i+Kpt7M99nK+Kjayq8jIV8VGjlW1UNti77MRdhmSKVnxJzS2etK2rqWzGFUgz8yMJntbeNB6PAet5CZq8vJBHpV/MWg75pm0Lb52omPTSlS4luWTU9hyrLZHejbd/m5bjXzSpH6vO+CX7BpJkrR4DPybQoj3zhyuliRpmBCiUpKkYUDvRCACDK1axZrZI1k8Pok/bC7kD5uPs6ekgduWjiUqvPv82b5icbgx6IIvCcrmdA+65yqE4FhVC8Yjn3FTyWNslOfy430LYN8hr9eHa1WMS4pkYmoUs0fHM3lYdI89WVviNKrm/orhOx8k4ehrnlj/OVgdbqL78bvSV+wuN5VNngIashAk1ezAIenQZgRPPdeeEK7r6NM6I4bh1Cd1u/gKcNn04Xx8uJL3D1bwg/PSu7y22+y4mnyITMWmjcHlNtGf23R8th6SJ6foz0C+EOJ3Z516H/gBsP7Mv//1ta9AIDUmnN+snsZ7+8t4c3cpBZXN3PONSUxKje6X/ix2N/Q88SNgGMw8/waLg48OV/JFQQ3qlgo+DHuAcvVwPh+/jtsSEkmOCiMqXIMkSchC0GhxYjTbKa23cLzaxH8PVPBubjnR4RoWj0/iG5mpjE6I6LZf45TriSzbSuqexzGlLcEe277aTyAWTnHLglKjpW3yUVjdwlz5IJUJs0g2BOEXrwvCNGrUKqm98yFJWJJndKphczbDY/UsGp/Ih4crWD1jBDHdbI4yO9zE6Dsx8rX5kDx5QKqH+cNFXAh8DzgsSVLrO/UrPMb9X5Ik/QgoAbpOJg4i1CqJNbNGMmNkHOs35nPve4e5eckYLp6a6veNVIE8xe+KwYjHG012/rWvjE151TjdMjPSonlW/xqxVoHx8r/xvdielVizOFzkljby5ck6/ne0io8OVzJtRAzXzBnJ9LQuFiIlifLFTzD+3WWM2HY3RZdsaLexynJmjSUQNtuBR5+mxGhul9lxrDCfNaoKTo25nnBtcO/R8IZBp+4QA7cmZRNT8ikqe2O3+kTXzhnFjhN1vJdbxg8XZnR5rdXh9v4gkGWoKYDZP8TUz/F48IORF0JsBzr71gavPmkPGJccye+vzuapTwv545aTHK8xcev5Y9H6cQNTq0a1ThNcP7iB9OSdbpn/7C/nX/tO43ILLpyUzJUz0sgq/wfDd+VStvjJDl51Vxh0GhaNS2TRuESarE4251fz/sEKfv2fI2QOj+b68zKYmOq9SpLLkEzVvPtJ23on8QVvUj/5e23nhPAswHrL8hhoHC6Z0nozVkf71D3tqRwAVOOWBczDyJ94M/KWJE+lKEPtIUxpXZc4HBlvYMmEJD48XMnq7BHEdSE90qmD1ngKXFbEAMTjIUSkhgeTqHAtD1wyhatnj+SzvGrWvX/U7x9cQGWp9ACXW8Y+QKJkhdUt/PQf+/nbrhJmjIzjxe/O4qcXjidDVUnqnvU0j7yw2x2pXRGj13LlzDRe+d5sbl48hvJGK3e+c5BnNx+nyep9A0vD+DWYhi8k9avH0Zgr253zx8zM4ZJpMDuobLJS1mChvNFKdbONBrMDi8PVpbSxyy1T02KjsLqlg4GvarYxxbqXFm0imtQpPo8zEPGeRpmFQOpRXB483rzLLfNOblmX13WaHVftqS1rjZs4IPtIgm9FLwBRqyS+N380aXF6nt18nLvfPcSDl04hOSrcL+2bHW5iDX5pakAYCP14tyz4197T/HNPKfERYTx06VRmjj6zqVp2k5azFqHWUb5ofZ82O52LTqPi0unDWTY5mbf3nOa/ByvYVWTktgvGsXBcYvuLJYmyReuZ8O5yhu1+lNMXvtB2ypd8eYvDRXWzvUdT/DCtCp1ahUYtoTqz9mB3yWcMj/d79hTVcqvqCM3DVwSdamZP8ZbEIOuisceO7TbDppXhsXqWTUrhkyOVXDp9OKnR3n/nrdlxHfqsPASSiuboCWDr9UvoNYon70cumJjMQ5dNxWiyc9eGQz3eONEdwabH3t8zjxabk3UfHOWtr0pZMj6J566d8bWBBxKP/ImImn1ULHgIV0SqX/s26DT8cGEGz357Bqkx4azfWMDvPjvWYVHVGT2a2qxbiS36AEPV7rbjffHkhRBUNFo5WWPucQzX7pRpsbloMDsxmhw0mJ1Y7F3vQDae2EOcZELOWBoQIaX+QK2SCPOy1mBJOrP42kPX+jvzRqGSJP6yvbjL67ymEVcdgsQJmOSBybRSjLyfyUqL5YmrskCCe/99mBM1Jp/bDIaNNGfTn/nxp+rM/PxfBzhS3sRPLxzH2pUT2yn+hTUcJ2XfUzSNXknjuCv7bRyj4g389qosrp0zkpzCWv7vn/s7fNa102/FETGM4TsfAtnznjhdvasD6pYFRXVmjKae1xrtC2a7i2F1OwGwpC0OGjG1vuC1iEhSNhqbEa2p6xBMK4mRYVw9eyQ7i4xdatp4dXgqDyGnTOsQLusvFCPfD4xOiGD9ldPQa9Xc95/D5Fc2d39TNwRLXF4+U9WqPzhc1sjd7x3C6RasvzKLlVPO8dJlF2lbf4GsMVCx8HG/hGm6QqNW8Z15o3niqixkIbj73UN8XvD1dhCh0VM191fojUeIO/61iFlPQzZuWVBcZxoQDaPdxUYuUOVSHzOVsFj/zn4CDW8hG0vymcXXHoZsAFZnjyA1OpyXt57s9MHdweEx1UJLBbbEaT0fsI8oRr6fGBajZ/2VWcTqtTzw/hEOlvVMwa4zgkVf3tJPomRfnqzjgfePkhAZxtNrpnvNbkk69BKG2oNUnPcILsPA7Z6elBrN76/OZkJKJL/fVMir24raZl5NYy7DnDKblD2/ReVoAXoWshHCk944UN7ewWMnmKE6gWPsRV493VDC2+uzxU/CrTFgqN7b43Z0GhU/Pn8sZQ1W/rmn1Os1rTLTbVR5KlGZ4gZuYVsx8v1IUlQY66/MIiUqnIc/zONweVOf2wrEjTTe6I/1g/8dreKJjQWMS47kiSunkRjZscpSWH0Bybm/pzFjlafwxwATa9DxyOWZXJo1jPcPVrB+Y77nxy1JVCxYh8ZmJOnA80DPZmWVTbYBk4Uw2VwkVuagQtAyannIxuNbCdeqO+5kVmmxpM4hsnJnr9qaNTqOFZNTeDe3jMLqFq/XtJuJVXp2W9dHT+xVP76gGPl+Ji5Cx2+umEZKVBiPfJhHQR9DN5YgESvzd2bNp3lVPP/FCbJHxvHI5ZneJSRkJyNzfoGsi6bivEf92n9v0KhV3LxkLDcvHsPuonp+/Z8jNFud2BKzaBy3msSjf0FjrmqrA9oZTVZnv8fgz2ZnUR0XSPuwhqdgS5galDIavcWbN29OnUd4w7FOReY644ZFGcRH6Hjq044L8HDOzK3qEHLMKJzantWM9QeKkR8AYvRaHl09jViDlnUfHO3TYmzrRppARgjh17WDzwuqef7zE8wcFcevV03utA5v8oHn0RuPUL7ocdz6BL/131cunT6cuy+exMlaE7989xC1LXaqZ60FIZOS+/u2OqDecLplyhusAzrenYWVLFEfxpK+nDCdFy83BDGEeTHywxYAEHFWNlRPiAzTcOfKiVQ323hm8/EOzli7UGvlIRxJmb0fsA8oRn6AiI/Q8ejqTCLCNDzw3yOcqut9emWgSxx4PFT/tJVTWMszm48zfWQsv/rmpE53EYfXHSZ5/3M0jl1Nc/rF/uncDywcl8gjl2fSYHFw778PUSaSqZ/8PeIK/0VY44lOw1qVjbYBzaSqNzswVHxJBDZPqCbACtr3F95qsFqSspA1eiIqd/W6vanDY/jhwgx2Fhl5e+/pdudaxcqwNUN9Eeb4gd1ophj5ASQ5KpxHV2ei1ai4/79HqGjsnccW6JWi/PUQ2lVk5HefHWPq8Bju++bkTtP5JLedtJy1uPTxVCx42C99+5Opw2N45PJMTHYX9/77MIfH3oSs0ZOy90mvYa1mm7PTXbT9xRfHarhQlYtbHY5p+MKQ3QR1Lnqt2kulKC3mlN7H5Vu5fPpwLpiYxJu7S/nkyLk7nd1Qvg8QNMdP79ug+4hi5AeYYTF6Hl2diVsIHnj/CPU90KZuJdA9eX88hAqqmnnyf8cYnxzF/aumdBqiAUjOfQZ9QwHli9bjDg/MCkYTUqL4zepp2Jxu1n5UTtH4HxJz6hPk03vaTeuFEFQ2DsD2x7MQQrApr4qLdQcwj1iM0ISHfGZNK5IkeY/LD5vfp7h8a5v/d+F4Zo+O48UtJ/k0r6rtnMXhgrI9nv+f0coZKBQjPwiMjDOw7tKpNFo8Ozd7mjnjcp+TjhVg+PoQqmyy8siHeSRE6rj/kildZnnoaw+SdOiP1I9fQ8uowC5uMTYpksdWT0OWBdfnz8UelkDKV+uxneXN15kcvdok5Q+OVbUQ33SUFLmW5vSLUKk8tVCHCt5CNn2Ny7eiUau4++JJzBgVy3Ofn+Ct3SV8tXM7N3zvO+x4+/eUWCP4av9hn8bdW4bOJxpgTEiJ4lffmExpvYXffJzf4x94oIZsbE43LnffY8lNVifr3j+KANZdOrVLrW7JZSMt5+c4DSlUzn+gz30OJOmJETy6OpNmEcYzziuIrNyJo/AzwJNLXdMysF48wGf51Vyu3Y2s0tI0euWZEEboL7q24i005UtcvpVwrZr7V01h2aRk/vLux6xd+wsqKivJineQW6fjqYfvY++u7b4MvVcoRn4QmTk6jp8tG8/h8iae+vRYjxbcBkKatC/4slnL7nLzm4/yqDXZuX/VFIbH6ru8PmXf04Q3nqB88W+RwwYuFc1XRidE8MjlmbzluoBSVxx1b/yIObNnsfrKq/jqy4H70YNH/yensIbV2q8wjViMHBY7ZOLxrRh0XcXlv/SpbY1axR3LxpNyegsOSYda2IjSuMi3xBFuiOAff33Vp/Z7g2LkB5mlE5P50SLPqvxLOSe7zYUP1J2vfd2sJQvB7z8rpKCqhbUrJjJ5WNcVtgxVX5F4+BWMk76DKe38PvU5mIxNimT1CCv3bXEz1mDmqolQWVU94N7d/45WM9l9nAR3DU1jLgG8y/CGMp3F5U0jFhHeUIjGXOXlrt6172qqZlp6CkuHedbe3ikUlJkEhwuO8+XJOnJLG9h7qp4PD1Ww91S9T/11xtB6dAcoq7NH0Ghx8m5uGfEROq6dO6rTax0uGadb9mthEn/Q13j8aztOseOkkR8tyugo2XsOKoeJkTk/xxmVRtXcX/epv0Dgyw/epMw8jFyjg+uGl/Bp1QwE8I+/vsrs+Yv6vX+XW+bDQxXcH7Mf2aGjedQKAAxDJH3ybCLDNR12FrekLWXYV48RVbaFhonf9qn9tFGjMdbVcsEIJy0uNc1EYzU34g6L4/FPCtpd22x1MTs93qf+vKEY+QDhBwtG02hx8NZXpcTotXxz2rBOr7XY3cQYem/k3WfEwwSeBTZ/PSgcLhmnq/fx+A8PVfCfA+VckjWMy6cP7/b6YbsfRttSRtElG5B1wVt/tKy0hMSkJF46Ba/MOsai6BI2y6MpLy0ZkP53nDTSYLaxImYHprQlyGExhGlVnRedDmEiwzRUY293zB43Each1S9G/trrb+Kph+9jckQTx0zRxGrdhEWpuP3eX5I6MRunW0aSYEScnnkZ/bORTzHyAYIkSfz0wvE025y8lHOSGL22U8/W5HARY+i5FrXd5aam2U6T1dlOPEyvU5MSHeZdKqAX9CVUs6vIyKvbipiXEc+Ni8Z0u+AXVfIp8cf+Sc30n2BJndvXoQYErd7dKV0SO+qq+NmkWt74bxhjRo/p977dsmDD3tNcHlVIhL2GkvHfArxv8x8K6LVqVCrab+KTJFrSlhJz6mOQXaDqu5mcPX8R9/3qbsYV/Jg/FkSSlJzM1d+/scOMTd+PO42H3qM7gFGrJH550SQmDYvmqU+Pdapc2RsRsEaLg+PVJhotzg7qkFaHm1N1nvJxvuji9DZUU1jdwpOfHmNcciR3rpzY7Zdbr4KbwAAAHuBJREFUba1jxLZ7sMZPoWbmL/o8zkDh2utvwmYxYzO38PfKDGJ1MrdPqMUyfnm/yTS3su14LSX1Fn4SuxtXWCwtozxlmCOGgF6NNyRJIiqso5PTMnIpakczhppcn/tYONzzBPnJk2/z2hv/GJCQ3NkoRj7ACNeqeeBMhslvPsr3qnNja90m3Q01LTZO11u7lf6tNzkoMVr6bOh7o5ZY1WTj4Q/ziDfout3sBIAQpG37JWpHM6eXPoNQd144OViYPX8RDz/+W1JSUthTYmazMZX/y3aijojm0Y/y+m0vhMst89ZXpWQmCMbWb6Fx7GqE2qPo6U3LZagQFd7xAWcavhAhqYk6vcXn9mOrd4IuCobPGJT3WTHyAUhkuIaHL5tKZLiGhz446lX+oDu1xzqTneome5fXnE2LzcXp+t4LYzlcco9z/Jutns1fsixYd+lUYg3dG+zEw68SXbqJqrn3Yo8fOHnW/uaSi5axYcMG9u7dy9S7PkCtVvPKyE85VNbEY73YN9Eb/pdXTWWTjXvT8lC57TRM8IRq1CoppCtBdUekFyMvh8VgTplDdOlnPrcfUbED0heCWjMoMybFyAcoCZFhPHTZ1E7lD7qKgzfbnH3aIt9kdVLd3Lv7ehqPt7vcPPpxPjUtNu5bNZkRcV3nwgMYqvaQuudxmtK/gXHqDb0aVyATo9e2M6r6xHSMU65ncs1HPDRPkFvayOOf+NfQN1gcvLHzFFnDo5lZ+x7W+MnYEjzVibzt/BxKaNUq9LqOprA54xuENxwjrPFE39s2laNpKIKMJYDngeqtr/5EMfIBTKv8QZO1o/xBZ8bV4ZI5XW/pc581zXZabD0XyepJPN4tC5769BgFlc38YsVEpg7vfgOT2mpk1Oe34YhKo2zJk/1eym8gSYhsP4MxhKmpyb4Nd1g0V9S+yO1Lx7K3pIF1Hxz1i3SzEIKXc05id8ncl1mPvj4f45Tr297ToRyqaSXaS/JBU/o3POeKP+5zu/EVX3j+M25F27GB3nSmGPkAZ0JKFPd+YzKn6y08+lFem3fXWVz+dIPFZ7nfsgZrj2L+0P0OXCEEL289ya6iem5cPIZF3eTCAyC7GPnFT1HbGyhd9hKyrusNUsGEXqfq8CMP06hRR8RRM3MtURXbWROxn7UrJpBX2cy9/z5Mg8W3AiKb8qvZcdLId+aOYlLJW7jC4mgcd0Xb+aG66Ho20V5kNFwRqZiTZxHjg5GPPb0Z4sdA4vi2Y4qRV+jAzFFx/Gz5BI5UNPP4J/k4zxjgcxc860x2v2jbuNyCyqbuwzZ2l7vb/PgN+8r45EgVV80cwWU9yIVHCIbvfJCoiu1ULPwNtoSpPR12UJAQ0bF0IXgMrXHyd7HGT2HYroe4YEwkv/7mZMoarPziXwc6LS3XHQVVzbyUU0TWiBi+PV4QXfop9ZO+g9CEA6BSQbhWMQPhWjU6L+JsTWNWoa/PQ9dU3Os2VS4L2tLtMOHidjPRgX6oKp9ukHD+hCR+cmYa/8TGAlxuGdNZU3mHS6aqB4a5pzRanN166d1l1WzKr+aNXSUsnZjE9xek96jfhKN/ISH/DWqzfkzDhKt7OtygQK2SOhVeiwzTgEpDxXmPoDNXknzgWWanx/PElVlIksQ97x3if0erepUBdarOzKMf5RMfoeOXF08i5eirgArjlO+3XROh0wwpUbKuiNZ3NL7NZ0I2MUUf9rq9pJodSG47TLio3fGBjssrRj6I+EbmMH68ZAy7i+t58tNjbNr8OWvWrGH27NlcceVV7NnpX+2Tym7y5022zh8CX56s47nPj5M9Mpb/u3A8qh4Ykujijxm2+xGaRl9M1Zx7+jTmQCY+Qoeqkz0BrVN4S+ocGsZ/i8TDrxJW79lL8Purs5kyLJrnvzjBQx/m9WhxPLe0gXv/fRi1SmLdpVNJdNcQV/AP6idejSvi693USjz+a2L1HbO9nJEjMKXOJ+74v+g2F/kc4oreB0MijO6YFz+QIRvFyAcZq7KGc+OiDDZ/sYWf/2ItlVXV/9/encfHeZWHHv8977wz72yaRaPFshZvsZ3FSRyuE2eBQBJCzJZQlhJ6y4cL5eYT1txy+VBCLk1pSaEUwlKaQgiU0OSmcFMoLpRsDiUXbkNiJyHEW2zHm2xZkm3to9nP/WPGtiyNpNEsmpl3zvcfjWZGM2cWPe95n3POc4i0tnJsYKDsRa5iycycm5rM1tN/Zv9J/vbR3bnxhNm37puq6dAWep78KNHWSzj8uq+B2O+r2eybfcqoyzSwcmmTvsvuIOMK0PXUJyCTJOhx8rkb1/HfX7OS7UdHuPWBbXzzyT0cOD4x4yB8dHiSb2zZw52btxP2ufibd1xEZ9hD2/PfAGBw/cfPun+jz6yZyuPKn7IZWvturNGDCyo/bGXGMfc+CuveDo6Z7/Fivu/6E64jko5jRvt5d9cou4Y3M7QkRdA9QsSbYrnbYGDE5Cc/+FZZV9T1j8YJeV0zVqVOJtJ5SyM/d3CIL/xiJ8tbfPzFWy/AW0D+0d/7FD1bbmUycj4HNt2PcnrL1v5aEfCYeQPIVD7LJJ5MkPZEOHLVXSzbcittv7uHgUtuw2EIN168lCtWRvg/2w7zxM5+Ht3RT4vfojvswWEI/aMxDg9NYhrC29Yv5Y8vX4ZlOnCN7Cf88o84cd57SfrPjIsYBg2zp2uhwl4n/aNnry8ZWfEmlv7nnTTvfoiJpVcU9DhLjjyOpGJwYf6UYzZNtuCTg6LoIF9rVAbX6KHs/Nyh3biHXsYaeQXnRB9m7MyWZA+cLt8yOO0B9pD+wToS/i5i4bXEmtcSi6wj2vaqoop6pTOKE+Nx2gLus67P14t/9sBJvviLXfQ0e/nLGy8o6JQ0uG8zXb/6BPHQKg5sesBWM2mmmqsXf4rfMjk5nj1zGl3xJoZX3UTbc19nvONKoksuBaC1yeLDrzuH91zWw3/uO8H2o6P0jUySVoqOoIfrz2/n6tWtRPy5Ad7cQHbGdDO4/iNnPZ/Ox88U8rpmBHllehha/U4iO37AsUs/fdaBMh8R8L/0ADSvgq4Nee9jGILH5ViUTYB0kK8yIzGOZ/AFfP1b8Q5sw9v/HI7kmZkUiaZu4sFziLZeRMrXQdK7hLQV4lv3fJPBoVGUw+LY8AQBS1jZlGJli5ub3nA11ugBfMeeJrzvJwAocTDZso6JJRsZ77yaiY6Np5e0z2dwPE6zz3VWlcLpQf5XLw/y1SdeZnnEy+duXDd/0TOlaPn9vXQ8cxfjSzZy8PrvkLFqc5/WUrlMo6AicH7r7N7dkavuwjP4Aj1Pfpg9f/AL0p4z00/DXhdvurBjzmqlAIH9P6ep9z84evmfk/K2n3Vbo20SUgiXaeB3mzPGm46v+yCRHfcTeem7HLv8s3M+RuvwixhHnoU3zr2+w2+ZOsjbjlI4x4/g7d+Kb2Ab3v6tuE/uRFQGhRALr2V41Y1Mtl5MLHwu8fBqMk5f3oda83YfP//8ZwkH3YwqH7/cOwjJNH925x30XXlm4YURH8Y7+CK+Y8/gO/Y0ke3fp/X395J2+hnrei1jPa9nrPta0u7wrM3OZODERIL2XG8+k1GnF2Mppdj8u6N899f7WdcZ5H+9+bx5UzRGYozOX99O6JXNDK94C72vvfv0lD47CvsKq/LpmNa7y7gCHLruW6za/DZ6tnyIA5v+aUHvkznRR+dvPkO05aLs4qdp8tVs0bKL1aYH+WRTF8Mrb6R514Mcv+iWGQfMqVpe+g5YQVj/R3M+j98yGaDw0iPF0p9yJWWSeE7swNv/LN7+bfj6t+KM9gOQdvqYbF3PwPqPEW3fQLR1/YK2srv0ilfz1a9+hW/dcw/De/dxyZplnFx2DQ8c8hLa1c+152a/hBkrxHjX1Yx3ZZdVS2oS/9HfEDj4OE2HniC0/+cocTCxZCOjy29gdNkNeU9Hj4/HafFbOAxhPJFCKUimM/zDr/bx+I5+rlgZ4ZNvWDt/3vnob+j89adxjR3m2IZPMXjxh205yHqKCDQXUKPnlKZpvbtY5AJ6r/4y3b/8OD1PfoSDr/92QaVvJRWjZ8uHkXSCw9f83Yy/cRgyf3G4BtVkZcdPppeVGHjVnxLc/zPan/0SR177lbx/2zLyIo6dP4XXfBKsudOjp7YfrHReXkopMVvQE4hsAr4OOID7lFJfnO2+GzZsUFu3bi3qeY4MT57OZ1aFyuAaPYDnxHbcx1/CO/A83sEXMNLZ6W4JfxfR9g1MtP8Xou0biIXXllSnuqXJRUcwW/9lPJ5i/+AEI5NJvvTILl48MsL157XzwdesmLtXrTJ4Bl8kcPAxAgcfwZ2r0RFtuYjR5ZsYXXYD8fCZlXr7XnyGB//xXnbv2UeovZPY6usZ8K/i3Ru6+aONPXNOk3SNHKD9ubsJ7ftX4oFl9L7my0Q7Nhb9+utFyOuku7nwgeRoIsW+gYkZ1zfvuJ/O//dZRpa/MVuNc44evaQTdP/yYwQOPMKh6+5hdMWbS25Xozk5keDI0MyCfUt+exetv/82r7zpISaWXnX2jZkUFzzyLozRXvjYNrCa5n2eA8cnGIul8LgcnNNW/EY4IrJNKZV3AKCiQV5EHMDLwPVAL/As8B6l1I5896+HIC+pGK6xg7hGD2KN7Mc1uh/3yd24T+7EkcrWjFFiMhk5PxfUNxBt30DKt6R8bRBYu6Tp9NREpRTbj46i1Jlysg9v66UtYHHbdWu4sLOwMwTX8D6CBx8hcOBRvIMvABALrmJ0+Q08O9bGHV+5n0AwwMmEwSt9J8gkYnzozz7HB945M4hkG5bB1/c0zbsfIvjKz1CGk+PrPsjAJR+3dXpmqpWtvgXnvnccHc07cyny0n10PP1XTLZcSO/rvkY8dM6M+5jRAbr/4zb8R3/D0cvv5MS6P8n7HF1hD+ECBoMblVKK3f1jM1Z0G8kJVv30rZixIfbetJlkU/fp25Zv/SuaXvgOvP0+uOhdBT3P8fE4fcOxug7yVwB/oZS6Iff77QBKqS/ku3/RQT4Zo2/gGMPjcURlQGWATO4yUy5nQKnsZbK/i8ogmSRGMoqRHMdITmCkohjJCRzxEZyTg5iTg5jR7E/n5NmzWVJWmHjoHCYjFxBrWcdk8/nEw6sLHtQsRsTvYmno7CqOB09MMDp5Jo+4/egIdz/+MgNjca5e3cL7rlxOW9NC8rnHsj38A4/g73saUSniaeGFIQ9PD1gcmvQwnHDiCLTxic/dDWLgiI9gxk5iDe/Fc/x3+Hv/L87YcdKuAENr/pDBi24l5W0r2/tQ6yynwZr2+Xtz0x0+GWU4mr9IXNPBx+h66pMYyQmGz3kHIyvfQjywDDM2RNPhLUR2fB8jFePIVX/N8JrZA825HU01t09wrRmOJvKW37aG9rDq3/6AjOHi6Ku/QCx0Di077yey/fuw8UPwxlmTFTPEkmn29I/XdZB/J7BJKfXB3O/vBTYqpT6a7/5FB/mXfgwPv7+UpuaVMZykPK2kvK3Zn55Wkr4O4sEVJALLiQeWLfqMEJFs0bLpue98p5exZJp/ea6XHz93BIBrzm3jbeuX0hVe2Gn6sf4+HvzEJi5phyvaJrk4HMdyzP29SbpbmOh8NaPd1zK6fFPD9Nyn6gi5afEv/GA/Ek1yaI5KomZ0gLbnv0H45R9ipM8M3CmEse5r6Nv4WRKhVbP+vdtpsLqIg08j2n98Iu/KbmtoDz1P3IJ7ZB+Qfe9l463whs/nXfw0l13HRjENo2JBvuoDryJyC3ALQE9PT3EPsnQ9Q9d+iYlEJjuIJwYKyU1fMlC56xDJXiZ3HxFAUA4XGaePjOkj7fRmLzv92d54jc0jDnmdeQc3882UcDsd/NeNy7j+vHZ+uPUwT+7q59Htx7iwM8iVqyJc2Bmku9k7I5eulKJ3aJIXe4d5as9xdvSNcmKgm8cGopzX044DRdCMEVEnWdrs4/0f+ACiMqRdAdJWiHhoFSlPa829d4tJJDvNsRh+99wLZVLeNo5e9Xn6LvsMvv6tmNF+Mq7A6Wm28yl1T99G0hnysHdgfEb6LB5ezZ53PIb/yK8JpIeInHc1RGY/sM7Fb5nEkuXfJOYUe6RrqIGB10WyZol/1l189g6MMznHjlHD0QSPbD/GU3uOn64573YatPotAh4nGZXdP7Z/LHb6S9cV9nDN2jZax/fx7S/dSUs4iMfrY3BohFh0gk/++V2LvmdlPSh1YPOVwfEFbau4EMWMEzSy8XgqV0Ji5m1+t8myZu+sNYkKMRJNMjger9ue/LPAahFZARwBbgbmnjyqzWr6jkLTBTzmnEE+5HVx86U93HxpD0eHJ9nZN8q+wXFOTCQYi6VwGLAk6Obi7hDLI17WdQZZEnDnVkV20xH8G370g/vYv38/bUu78+46r2VN3xhkoQIeZ0WCvMMQvC49dXIh/JbJylYfh09Onp5WKZJdxdwRdJe8atjvNjk+Ubn58hUN8kqplIh8FHiU7BTK7ymltlfyOe2stWnu/G7A7Sx4X9elIQ9LQx6uO2/2RR3TveG6a3nXW7NlU+fLGzcyt9MoqGbPXAJuJ32Ur3T0KU1uXcqgGF6XyZp2P9FEmlRG4XU5yjZwXekDb8XP2ZRS/w4Uv7WKBoDPcuCZ54vgdjqwnAbxCuT3RM7O+wc8Jk5T5t00pBEVUqdmPi7TwONyzHlmVox8OyBphRGRiqW5KjlOoudQ1Yn5evGnzLYpRakCbudZeUcRKUswsxvDKH7Adbpyf5Yi2dWcWu3xVbAnr4N8HXA7CytwBZUL8vket9nrauQJNHmFvLNvDLJQ5f4spx+otdpRyRSaDvJ1oNBePGRTNuXes3N6quYU02EQ8urT/6kiZTy7cZlGWXduCurPqiHpIF/jnObs+4LOptz/zEHP7D3AYhb72JXXcpS96Fe5Uj+GoVM1jUoH+RoX8VkLPpXLt1dlKeaqceJ2OvDpfUIBaPGV/4AX9DjLkhKb60Ct2ZsO8jXMMIqbqXFq44NycJoy736UEd2bx3QIAU/5e8oOY+FncvmU64xAqz86yNewiM+asbdqoRZSw7zUxwm4s9MpG1nE56rY4Fmps5gsp6FXuDYwHeRrlEhpqyYDHrPoA8TUNhRSjrbRp1MW+j4Vy2eZJQ2mN/Jno+kgX7NCXmdJK+pEpOSl9UFP4W1o5OmUC3mfilXsAHc55+1r9UkH+Rq1kGmTs2n2lRZ4FxJYGnk65WLMMAp5nUWlxJp9rpLP6LT6poN8DZqvEFmhnA6j6DSC323OW0ZhukgFZpfUukLKTZSDiNC6wIOJiJ7iqukgX5PK0Ys//Vh+q6jefFsRbfC4HGVdvFMPFnNmUbPPhbWA3HyL39K7P2k6yNeapiJ60HNxmcaCc/MBj1n0bIxG6jlaTqNiZSTyERE6goXtsGU6pKydBa1+6SBfY9oC5f/HbGtyYzoK686LZGvKFyvgNvPuXGVH1TigNbmdBY19dIY9OhevATrI1xSf5Si5Dnk+DkNmbPw9m/aAu6TxgEaZTmk6hHCVBpo7Qx48rtn/dVubLAJ6iz8tRwf5GtIeqNxm10GPk5amuYNvwGOWbVaPYfNvVot/4eUmysUwhOURX960XsTvKulMTLMfvQyuRvgsR8VXJXYEPaTSiuFocsZtfrdJd7j4PUmnchjZ3vzxMXvuueswpKzVJothOgxWtfoYiiYZiyUxRAj7XPOWoNAaj/5G1IhK9uKn6m724rMSDI7FSaQymI7soqnWMvdMIz6LE+OJvJsf17sWf/lqxpfiVGqsEdJjWvF0kK8BTe7iZ7MU41RgUEpVLOXgMrMzT/KdNdQzw9AF2bT6YvPMaX2oVg610jllO07ha20qvmicplWDDvJVFvI6y77RRK1wOx15d5SqVw5DKlIzXtMqSQf5KhJZvFx8tdipN98WsGoiF69pC6GDfBW1+C3bLxzyWaYtdo5ymUbVZ9RoWjHsHWFqmOmQourD1KM2G5ytLAm4qzYvXtNKoYN8lSwNehrm1N9vmXVduMxnOcq+ObqmLRYd5KvA7zYbLmjU69iDCAWXhNC0WqSD/CLLBo36DHil8Ftm2TYXX0wRv8u2s5+0xqCD/CLrCJZWAKyeLamz3rzTFNqb6qvNmjadDvKLyO82G3q1pMflqKstAjtDjTNuotmXDvKLxGEIXWGd220PuOtiw++wz0mTLter2YAO8oukJ+LVW7GRnW9e61NHXabB0qA+IGv2oKPOIugIuXUJ2ClqeRGYCPQ0e3WaRrON2vxPs5GI39VQ+54WwjCEzhpNXXUE3WXdY1fTqk0H+QoKeZ16jvUs/JZJ2FdbOe+wz9nQA+OaPekgXyEhr1MPtM6jI+ipmbSNz3LQqQ/Img2V9B8mIn8rIrtE5EUR+YmIhKbcdruI7BWR3SJyQ+lNrR9tAYvuZq+udTIPhyF0N3uqPtvG4zJYFvHpz0uzpVK7UY8D65RSFwEvA7cDiMj5wM3ABcAm4B4RsX2i0zCys2jqdQl/NXhdZlU3nnY7DZZHfHojEM22SgrySqnHlFKp3K9PA125yzcB/6yUiiul9gN7gctKea5aF/Q4WdPeRNBTW3nmetDit6qySMrjMljR4sPUU1s1GyvnvL4PAD/MXe4kG/RP6c1dN4OI3ALcAtDT01P0k7f4szvVp9IZUhlFIpUhmc6QTCuS6UzFNpRucpu0BSy8Lj1FshRdYQ+pjGI8lpr/zmXgd5v0NHt1D16zvXkjk4g8ASzJc9MdSqmf5u5zB5ACHlxoA5RS9wL3AmzYsKHoUGyZjllrwiilSKYViXSGZCpDIp0hkcoQzx0IUunCn1YEvC4HTW4nQY+zZgYO652IsKzZy4ETE0zE0xV9rpYml64PrzWMeYO8Uur1c90uIv8NeAtwnVKn+8tHgO4pd+vKXVcVIoLLlGxAzjNDTqnsASCdyR4MMhlFRikUIGQHCB1G9u9dDkMHhwoxDGF5xMehk1HGKtCjdxhCV7OHgC5XoDWQknIMIrIJ+BTwWqVUdMpNm4H/LSJ3A0uB1cAzpTxXJYlIw1aGrDWGISyLeOkfjTM4Fi/b44a8TjqCbp1/1xpOqYnkb5LtGz+e690+rZS6VSm1XUR+BOwgm8b5iFKqsufgmm2ICEuCbvxukyNDkyRSmaIfy+82addjJloDK+mbr5Q6Z47b7gLuKuXxtcbmt0zWtPs5OZHg+Hii4GBvGNnZThGfpUsUaA1Pd2+0miYiRPwWEb/FRDzFeDxFNJEmmRtDgWyu3eUw8Lgc+CwTn8uhx000LUcHea1u+CwTn67mqWkLokehNE3TbEwHeU3TNBvTQV7TNM3GdJDXNE2zMR3kNU3TbEwHeU3TNBvTQV7TNM3GdJDXNE2zMR3kNU3TbExUpXbTKIKIDAIHi/zzFuB4GZtTD/Rrbgz6NTeGUl7zMqVUa74bairIl0JEtiqlNlS7HYtJv+bGoF9zY6jUa9bpGk3TNBvTQV7TNM3G7BTk7612A6pAv+bGoF9zY6jIa7ZNTl7TNE2byU49eU3TNG0aHeQ1TdNszBZBXkQ2ichuEdkrIp+udnsqTUS6ReSXIrJDRLaLyG3VbtNiEBGHiDwvIj+rdlsWi4iERORhEdklIjtF5Ipqt6mSRORPc9/pl0TkIRFxV7tNlSAi3xORARF5acp1zSLyuIjsyf0Ml+O56j7Ii4gD+HvgjcD5wHtE5PzqtqriUsD/VEqdD1wOfKQBXjPAbcDOajdikX0deEQpdS5wMTZ+/SLSCXwc2KCUWgc4gJur26qK+T6wadp1nwa2KKVWA1tyv5es7oM8cBmwVyn1ilIqAfwzcFOV21RRSqk+pdRzuctjZP/xO6vbqsoSkS7gzcB91W7LYhGRIHA18F0ApVRCKTVc3VZVnAl4RMQEvMDRKrenIpRSTwEnp119E3B/7vL9wNvK8Vx2CPKdwOEpv/di84A3lYgsBy4BflvdllTc14BPAZlqN2QRrQAGgX/MpanuExFftRtVKUqpI8CXgUNAHzCilHqsuq1aVO1Kqb7c5WNAezke1A5BvmGJiB/4F+B/KKVGq92eShGRtwADSqlt1W7LIjOBVwH/oJS6BJigTKfwtSiXg76J7MFtKeATkT+ubquqQ2XntpdlfrsdgvwRoHvK712562xNRJxkA/yDSqkfV7s9FXYVcKOIHCCbjrtWRB6obpMWRS/Qq5Q6dZb2MNmgb1evB/YrpQaVUkngx8CVVW7TYuoXkQ6A3M+BcjyoHYL8s8BqEVkhIi6yAzWbq9ymihIRIZun3amUurva7ak0pdTtSqkupdRysp/vk0op2/fwlFLHgMMisjZ31XXAjio2qdIOAZeLiDf3Hb8OGw8057EZeF/u8vuAn5bjQc1yPEg1KaVSIvJR4FGyo/HfU0ptr3KzKu0q4L3A70Xkhdx1n1FK/XsV26RVxseAB3MdmFeA91e5PRWjlPqtiDwMPEd2Btnz2LS8gYg8BLwOaBGRXuBO4IvAj0TkT8iWXP/DsjyXLmugaZpmX3ZI12iapmmz0EFe0zTNxnSQ1zRNszEd5DVN02xMB3lN0zQb00Fe0zTNxnSQ1zRNs7H/D6b9EByI3YmnAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "new_campaign.run()\n", - "plt = plot_state(x,y,new_campaign)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Agents confidence near the maximum of the function improved, and it acquired two highly relevant points.\n", - "Let's run again." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign 2 state: Getting new results\n", - "Campaign 2 state: Analyzing results\n", - "Campaign 2 state: Agent GenericGPUCB hypothesizing\n", - "Campaign 2 state: Running experiments\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "new_campaign.run()\n", - "plt = plot_state(x,y,new_campaign)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see now another set of points close to the actual maximum are acquired. Let's run again." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign 3 state: Getting new results\n", - "Campaign 3 state: Analyzing results\n", - "Campaign 3 state: Agent GenericGPUCB hypothesizing\n", - "Campaign 3 state: Running experiments\n" - ] - }, - { - "data": { - "image/png": "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lHIrtRdYd1A7fVIHLAcWA38WQhyldhrlita25W4t7c0riqK4i1PmyUspHwEeOefmY8BIZ5y/vTjdmzfoW74vrKIoSlvyqrIGbUH15hVF8SQqyTeTROJw09j83/72t2Y/5s0332ThwoUuiEZRFE/gVUne55WX0f286azbdD9vwueVl53WhpQSq93htPM1R0uSvKIoHZtXJXlt6FB877qrPtHrft6E7113oQ0d2qrz5mRnk5w4mNvn30rqsGT+9uQTpKamMnToUB555H+XIp588kn69evHZZddxg033MCzzz4LwIQJEzi9yKu4uLi+lo7D4eC+++6rP9err74KQF5eHuPGjSM5OZnExER+/PFHlixZQk1NDcnJydx0000AvPvuu4wcOZLk5GR++9vf4nDUvvi88cYb9OvXj5EjR7Jp09kveoqidCxeVYVSu3QM1hdfxPeuu7Df/Ct83n0H64svol06ptXnPno0g+UrVlBZWcWajz/ix02b8fXRcfXVV7Nx40aCgoL44IMP2LVrF3a7nZSUFIYPH37Rc65YsYKwsDC2bduGxWJhzJgxTJ06lY8//php06bxl7/8BYfDgclkYuzYsSxbtqy+ONnBgwf58MMP2bRpEwaDgTvvvJOVK1cyZcoUHnnkEbZv305YWBiXX345w4YNa/XzVxTFM3lVkofaRG+/+Vf4vPAf7H+42ykJHqBHj56MvGQUDyy+n++/X8fI1OHohMBoNJKRkUFVVRVz5swhMDAQgKuvvrrRc65du5Y9e/bUbxhSUVFBRkYGqampzJ8/H5vNxuzZs0lOTj7vsd9//z3bt28nNTUVgJqaGmJiYti6dSsTJkzg9MKyefPmceTIEaf8DBRF8Txel+R1P2/C5913sP/hbnzefQdt9CinJPrAM8r/3nvf/fxmwR346HT4+tSOeP373/++4GPPLDN8ZolhKSUvvPAC06ZNO+8xGzdu5Msvv+TWW2/lz3/+M7/+9a/Pul9KyS233MJTTz111u1r1qxp2RNUFMUredWY/OkxeOuLL2JftKh+6Obci7GtMXnKVN5+602MRiMOTSM3N5fCwkLGjRvHmjVrqKmpoaqqis8//7z+MfHx8Wzfvh2gvtcOMG3aNF5++WVsNhsAR44cobq6mpycHGJjY1mwYAG33347O3bsAGorWZ4+dtKkSaxevZrCwkKgttRxTk4O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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "new_campaign.run()\n", - "plt = plot_state(x,y,new_campaign)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This time we observe that the agent took a `risk` and tried acquiring near where it is highly uncertain." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign 4 state: Getting new results\n", - "Campaign 4 state: Analyzing results\n", - "Campaign 4 state: Agent GenericGPUCB hypothesizing\n", - "Campaign 4 state: Running experiments\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "new_campaign.run()\n", - "plt = plot_state(x,y,new_campaign)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that now it \"learned\" from its mistake as its uncertainty is minimized near the previous \"bad\" acqusitions. The new set of acquisitions are close to ideal again." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Conclusion**: In the context of materials science experiments, for many \"low-throughput\" campaigns (e.g. up to a few thousand examples) and/or low-dimensional search spaces (e.g. up to ten or twenty parameters to control), this approach is often worth trying." - ] - }, - { - "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.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/main_tutorial.ipynb b/examples/main_tutorial.ipynb new file mode 100644 index 00000000..f0b38224 --- /dev/null +++ b/examples/main_tutorial.ipynb @@ -0,0 +1,2625 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# Sequential learning with CAMD\n", + "\n", + "**Objective:** imagine you want to design a procedure in order to discover materials which are resistant to deformation. Such efforts have been the subject of at least one [paper](https://pubs.acs.org/doi/10.1021/jacs.8b02717) which used machine learning to predict and verify a new superhard material, (ReWC0.8). How would we design a method that iterated on each of its past experiments in order to improve itself? We might want to compare various machine learning methods and their performance in a active learning procedure. This notebook describes how to do that with CAMD." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install camd" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import pandas as pd" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Preprocessing data\n", + "The dataset we'll be using in the tutorial will be the elastic tensor dataset from Maarten de Jong's 2015 paper, [Charting the complete elastic properties of inorganic crystalline compounds](https://www.nature.com/articles/sdata20159). We'll be using the [MatMiner](https://hackingmaterials.lbl.gov/matminer/) API to fetch the data, which we've written a function for in the helper code pre-installed on your SageMaker instance. In order to make our data compatible with some machine learning functionality later on, we'll be featurizing it, also using MatMiner. Lastly, we'll lay the groundwork for the simulation of our sequential learning procedure by separating the featurized data into **seed_data** which we will assume we have full knowledge of a-priori, and **candidate_data** which we will assume we know nothing about." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupbulk_modulusshear_modulus
0Nb4CoSi124194.397.1
1Al(CoSi)2164175.496.3
2SiOs221295.1130.1
3Ga6349.115.1
4SiRu262256.8101.9
\n", + "
" + ], + "text/plain": [ + " formula space_group bulk_modulus shear_modulus\n", + "0 Nb4CoSi 124 194.3 97.1\n", + "1 Al(CoSi)2 164 175.4 96.3\n", + "2 SiOs 221 295.1 130.1\n", + "3 Ga 63 49.1 15.1\n", + "4 SiRu2 62 256.8 101.9" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from matminer.datasets.convenience_loaders import load_elastic_tensor\n", + "data = load_elastic_tensor()\n", + "data = data[['formula', 'space_group', 'K_VRH', 'G_VRH']].round(1)\n", + "data = data.rename(columns={\"K_VRH\": \"bulk_modulus\", \"G_VRH\": \"shear_modulus\"})\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupbulk_modulusshear_modulus
1015C194435.7522.9
853Os194401.3258.7
413WC187385.2279.0
1103Re194365.1173.1
726Ir3W194351.3193.3
\n", + "
" + ], + "text/plain": [ + " formula space_group bulk_modulus shear_modulus\n", + "1015 C 194 435.7 522.9\n", + "853 Os 194 401.3 258.7\n", + "413 WC 187 385.2 279.0\n", + "1103 Re 194 365.1 173.1\n", + "726 Ir3W 194 351.3 193.3" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Sort the data and inspect again, these are the highest bulk modulus materials\n", + "# roughly equivalent to the hardest\n", + "data = data.sort_values('bulk_modulus', ascending=False)\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupbulk_modulusshear_modulus
909Ba1948.03.4
224Na2297.53.2
1016CsI2257.43.9
957Hg2297.22.7
992CsBr2256.54.6
\n", + "
" + ], + "text/plain": [ + " formula space_group bulk_modulus shear_modulus\n", + "909 Ba 194 8.0 3.4\n", + "224 Na 229 7.5 3.2\n", + "1016 CsI 225 7.4 3.9\n", + "957 Hg 229 7.2 2.7\n", + "992 CsBr 225 6.5 4.6" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Sort the data and inspect again, these are the lowest bulk modulus materials\n", + "# roughly equivalent to the softest\n", + "data.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4001aa2eeb294e11a82d2df1f8cfa427", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "ElementProperty: 0%| | 0/1181 [00:00\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupbulk_modulusshear_moduluscompositionMagpieData minimum NumberMagpieData maximum NumberMagpieData range NumberMagpieData mean NumberMagpieData avg_dev Number...MagpieData range GSmagmomMagpieData mean GSmagmomMagpieData avg_dev GSmagmomMagpieData mode GSmagmomMagpieData minimum SpaceGroupNumberMagpieData maximum SpaceGroupNumberMagpieData range SpaceGroupNumberMagpieData mean SpaceGroupNumberMagpieData avg_dev SpaceGroupNumberMagpieData mode SpaceGroupNumber
1015C194435.7522.9(C)6.06.00.06.000.000...0.00.00.00.0194.0194.00.0194.00.0194.0
853Os194401.3258.7(Os)76.076.00.076.000.000...0.00.00.00.0194.0194.00.0194.00.0194.0
413WC187385.2279.0(W, C)6.074.068.040.0034.000...0.00.00.00.0194.0229.035.0211.517.5194.0
1103Re194365.1173.1(Re)75.075.00.075.000.000...0.00.00.00.0194.0194.00.0194.00.0194.0
726Ir3W194351.3193.3(Ir, W)74.077.03.076.251.125...0.00.00.00.0225.0229.04.0226.01.5225.0
\n", + "

5 rows × 137 columns

\n", + "" + ], + "text/plain": [ + " formula space_group bulk_modulus shear_modulus composition \\\n", + "1015 C 194 435.7 522.9 (C) \n", + "853 Os 194 401.3 258.7 (Os) \n", + "413 WC 187 385.2 279.0 (W, C) \n", + "1103 Re 194 365.1 173.1 (Re) \n", + "726 Ir3W 194 351.3 193.3 (Ir, W) \n", + "\n", + " MagpieData minimum Number MagpieData maximum Number \\\n", + "1015 6.0 6.0 \n", + "853 76.0 76.0 \n", + "413 6.0 74.0 \n", + "1103 75.0 75.0 \n", + "726 74.0 77.0 \n", + "\n", + " MagpieData range Number MagpieData mean Number \\\n", + "1015 0.0 6.00 \n", + "853 0.0 76.00 \n", + "413 68.0 40.00 \n", + "1103 0.0 75.00 \n", + "726 3.0 76.25 \n", + "\n", + " MagpieData avg_dev Number ... MagpieData range GSmagmom \\\n", + "1015 0.000 ... 0.0 \n", + "853 0.000 ... 0.0 \n", + "413 34.000 ... 0.0 \n", + "1103 0.000 ... 0.0 \n", + "726 1.125 ... 0.0 \n", + "\n", + " MagpieData mean GSmagmom MagpieData avg_dev GSmagmom \\\n", + "1015 0.0 0.0 \n", + "853 0.0 0.0 \n", + "413 0.0 0.0 \n", + "1103 0.0 0.0 \n", + "726 0.0 0.0 \n", + "\n", + " MagpieData mode GSmagmom MagpieData minimum SpaceGroupNumber \\\n", + "1015 0.0 194.0 \n", + "853 0.0 194.0 \n", + "413 0.0 194.0 \n", + "1103 0.0 194.0 \n", + "726 0.0 225.0 \n", + "\n", + " MagpieData maximum SpaceGroupNumber MagpieData range SpaceGroupNumber \\\n", + "1015 194.0 0.0 \n", + "853 194.0 0.0 \n", + "413 229.0 35.0 \n", + "1103 194.0 0.0 \n", + "726 229.0 4.0 \n", + "\n", + " MagpieData mean SpaceGroupNumber MagpieData avg_dev SpaceGroupNumber \\\n", + "1015 194.0 0.0 \n", + "853 194.0 0.0 \n", + "413 211.5 17.5 \n", + "1103 194.0 0.0 \n", + "726 226.0 1.5 \n", + "\n", + " MagpieData mode SpaceGroupNumber \n", + "1015 194.0 \n", + "853 194.0 \n", + "413 194.0 \n", + "1103 194.0 \n", + "726 225.0 \n", + "\n", + "[5 rows x 137 columns]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "featurized_data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "Here we partition the data by choosing every other member of our known data for the seed data and the remainder for our candidate data. Note that this partitioning can have a **significant** impact on how the sequential learning procedure progresses, just as test-train splits can in conventional machine learning. As an exercise, you might try seeing how the notebook compares if you use the alternative commented option where the seed is the bottom half of the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupcompositionMagpieData minimum NumberMagpieData maximum NumberMagpieData range NumberMagpieData mean NumberMagpieData avg_dev NumberMagpieData mode NumberMagpieData minimum MendeleevNumber...MagpieData range GSmagmomMagpieData mean GSmagmomMagpieData avg_dev GSmagmomMagpieData mode GSmagmomMagpieData minimum SpaceGroupNumberMagpieData maximum SpaceGroupNumberMagpieData range SpaceGroupNumberMagpieData mean SpaceGroupNumberMagpieData avg_dev SpaceGroupNumberMagpieData mode SpaceGroupNumber
853Os194(Os)76.076.00.076.000.00076.057.0...0.00.00.00.0194.0194.00.0194.00.0194.0
1103Re194(Re)75.075.00.075.000.00075.054.0...0.00.00.00.0194.0194.00.0194.00.0194.0
571MoC187(Mo, C)6.042.036.024.0018.0006.050.0...0.00.00.00.0194.0229.035.0211.517.5194.0
139MoIr3194(Mo, Ir)42.077.035.068.2513.12577.050.0...0.00.00.00.0225.0229.04.0226.01.5225.0
725IrW51(Ir, W)74.077.03.075.501.50074.051.0...0.00.00.00.0225.0229.04.0227.02.0225.0
..................................................................
1160Sr229(Sr)38.038.00.038.000.00038.08.0...0.00.00.00.0225.0225.00.0225.00.0225.0
564KI225(K, I)19.053.034.036.0017.00019.03.0...0.00.00.00.064.0229.0165.0146.582.564.0
10Na194(Na)11.011.00.011.000.00011.02.0...0.00.00.00.0229.0229.00.0229.00.0229.0
224Na229(Na)11.011.00.011.000.00011.02.0...0.00.00.00.0229.0229.00.0229.00.0229.0
957Hg229(Hg)80.080.00.080.000.00080.071.0...0.00.00.00.0166.0166.00.0166.00.0166.0
\n", + "

590 rows × 135 columns

\n", + "
" + ], + "text/plain": [ + " formula space_group composition MagpieData minimum Number \\\n", + "853 Os 194 (Os) 76.0 \n", + "1103 Re 194 (Re) 75.0 \n", + "571 MoC 187 (Mo, C) 6.0 \n", + "139 MoIr3 194 (Mo, Ir) 42.0 \n", + "725 IrW 51 (Ir, W) 74.0 \n", + "... ... ... ... ... \n", + "1160 Sr 229 (Sr) 38.0 \n", + "564 KI 225 (K, I) 19.0 \n", + "10 Na 194 (Na) 11.0 \n", + "224 Na 229 (Na) 11.0 \n", + "957 Hg 229 (Hg) 80.0 \n", + "\n", + " MagpieData maximum Number MagpieData range Number \\\n", + "853 76.0 0.0 \n", + "1103 75.0 0.0 \n", + "571 42.0 36.0 \n", + "139 77.0 35.0 \n", + "725 77.0 3.0 \n", + "... ... ... \n", + "1160 38.0 0.0 \n", + "564 53.0 34.0 \n", + "10 11.0 0.0 \n", + "224 11.0 0.0 \n", + "957 80.0 0.0 \n", + "\n", + " MagpieData mean Number MagpieData avg_dev Number \\\n", + "853 76.00 0.000 \n", + "1103 75.00 0.000 \n", + "571 24.00 18.000 \n", + "139 68.25 13.125 \n", + "725 75.50 1.500 \n", + "... ... ... \n", + "1160 38.00 0.000 \n", + "564 36.00 17.000 \n", + "10 11.00 0.000 \n", + "224 11.00 0.000 \n", + "957 80.00 0.000 \n", + "\n", + " MagpieData mode Number MagpieData minimum MendeleevNumber ... \\\n", + "853 76.0 57.0 ... \n", + "1103 75.0 54.0 ... \n", + "571 6.0 50.0 ... \n", + "139 77.0 50.0 ... \n", + "725 74.0 51.0 ... \n", + "... ... ... ... \n", + "1160 38.0 8.0 ... \n", + "564 19.0 3.0 ... \n", + "10 11.0 2.0 ... \n", + "224 11.0 2.0 ... \n", + "957 80.0 71.0 ... \n", + "\n", + " MagpieData range GSmagmom MagpieData mean GSmagmom \\\n", + "853 0.0 0.0 \n", + "1103 0.0 0.0 \n", + "571 0.0 0.0 \n", + "139 0.0 0.0 \n", + "725 0.0 0.0 \n", + "... ... ... \n", + "1160 0.0 0.0 \n", + "564 0.0 0.0 \n", + "10 0.0 0.0 \n", + "224 0.0 0.0 \n", + "957 0.0 0.0 \n", + "\n", + " MagpieData avg_dev GSmagmom MagpieData mode GSmagmom \\\n", + "853 0.0 0.0 \n", + "1103 0.0 0.0 \n", + "571 0.0 0.0 \n", + "139 0.0 0.0 \n", + "725 0.0 0.0 \n", + "... ... ... \n", + "1160 0.0 0.0 \n", + "564 0.0 0.0 \n", + "10 0.0 0.0 \n", + "224 0.0 0.0 \n", + "957 0.0 0.0 \n", + "\n", + " MagpieData minimum SpaceGroupNumber \\\n", + "853 194.0 \n", + "1103 194.0 \n", + "571 194.0 \n", + "139 225.0 \n", + "725 225.0 \n", + "... ... \n", + "1160 225.0 \n", + "564 64.0 \n", + "10 229.0 \n", + "224 229.0 \n", + "957 166.0 \n", + "\n", + " MagpieData maximum SpaceGroupNumber MagpieData range SpaceGroupNumber \\\n", + "853 194.0 0.0 \n", + "1103 194.0 0.0 \n", + "571 229.0 35.0 \n", + "139 229.0 4.0 \n", + "725 229.0 4.0 \n", + "... ... ... \n", + "1160 225.0 0.0 \n", + "564 229.0 165.0 \n", + "10 229.0 0.0 \n", + "224 229.0 0.0 \n", + "957 166.0 0.0 \n", + "\n", + " MagpieData mean SpaceGroupNumber MagpieData avg_dev SpaceGroupNumber \\\n", + "853 194.0 0.0 \n", + "1103 194.0 0.0 \n", + "571 211.5 17.5 \n", + "139 226.0 1.5 \n", + "725 227.0 2.0 \n", + "... ... ... \n", + "1160 225.0 0.0 \n", + "564 146.5 82.5 \n", + "10 229.0 0.0 \n", + "224 229.0 0.0 \n", + "957 166.0 0.0 \n", + "\n", + " MagpieData mode SpaceGroupNumber \n", + "853 194.0 \n", + "1103 194.0 \n", + "571 194.0 \n", + "139 225.0 \n", + "725 225.0 \n", + "... ... \n", + "1160 225.0 \n", + "564 64.0 \n", + "10 229.0 \n", + "224 229.0 \n", + "957 166.0 \n", + "\n", + "[590 rows x 135 columns]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Partition data into seed and candidate data\n", + "k_seed_data = featurized_data.iloc[::2]\n", + "k_candidate_data = featurized_data.iloc[1::2]\n", + "k_candidate_data.drop(['bulk_modulus', 'shear_modulus'], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "# Alternative: choose bottom half\n", + "# half = int(len(featurized_data) / 2)\n", + "# k_seed_data = featurized_data.iloc[half:]\n", + "# k_candidate_data = featurized_data.iloc[:half]\n", + "# k_candidate_data.drop(['bulk_modulus', 'shear_modulus'], axis=1)\n", + "\n", + "# Alternative: choose randomly\n", + "# half = int(len(featurized_data) / 2)\n", + "# k_seed_data = featurized_data.sample(half)\n", + "# k_candidate_data = featurized_data.loc[~k_seed_data]\n", + "# k_candidate_data.drop(['bulk_modulus', 'shear_modulus'], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "# test to ensure no overlap\n", + "assert not set(k_seed_data.index).intersection(k_candidate_data.index) " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### Agents\n", + "In CAMD, Hypothesis *Agents* are python objects which select candidates on which to perform experiments. Almost all of the \"AI\" components, including ML algorithms, simpler regression, and even random selection, within CAMD are contained in logic implemented within Agents. \n", + "\n", + "\n", + "To implement a CAMD-compatible Agent, we use the *HypothesisAgent* abstract class, which basically will issue an error if we don't fulfill all of the things we need to in order to ensure that our Agent is compatible with the sequential learning process implemented in a CAMD *Campaign* (more on Campaigns later)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from camd.agent.base import HypothesisAgent\n", + "from sklearn.linear_model import LinearRegression\n", + "\n", + "def get_magpie_features(dataframe):\n", + " magpie_columns = [column for column in dataframe \n", + " if column.startswith(\"MagpieData\")]\n", + " return dataframe[magpie_columns]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "class LinearHardnessAgent(HypothesisAgent):\n", + " def get_hypotheses(self, candidate_data, seed_data):\n", + " # Fit on known data\n", + " x_known = get_magpie_features(seed_data)\n", + " y_known = seed_data['bulk_modulus']\n", + " regressor = LinearRegression()\n", + " regressor.fit(x_known, y_known)\n", + " \n", + " # Predict unknown data\n", + " x_unknown = get_magpie_features(candidate_data)\n", + " y_predicted = regressor.predict(x_unknown)\n", + " \n", + " # Pick top 5 candidates\n", + " candidate_data['bulk_modulus_pred'] = y_predicted\n", + " candidate_data = candidate_data.sort_values(\n", + " 'bulk_modulus_pred', ascending=False)\n", + " top_candidates = candidate_data.head(5)\n", + " return top_candidates" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "Let's play with the Agent a bit to see what it recommends." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/ipykernel_launcher.py:14: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " \n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulabulk_modulusbulk_modulus_pred
853Os401.3367.238820
571MoC349.8358.426101
1148W303.9350.504942
1103Re365.1335.387171
55TaC323.9328.280252
\n", + "
" + ], + "text/plain": [ + " formula bulk_modulus bulk_modulus_pred\n", + "853 Os 401.3 367.238820\n", + "571 MoC 349.8 358.426101\n", + "1148 W 303.9 350.504942\n", + "1103 Re 365.1 335.387171\n", + "55 TaC 323.9 328.280252" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lh_agent = LinearHardnessAgent()\n", + "hypotheses = lh_agent.get_hypotheses(k_candidate_data, k_seed_data)\n", + "hypotheses[['formula', 'bulk_modulus', 'bulk_modulus_pred']]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupbulk_modulusshear_moduluscomposition
1015C194435.7522.9(C)
853Os194401.3258.7(Os)
413WC187385.2279.0(W, C)
1103Re194365.1173.1(Re)
726Ir3W194351.3193.3(Ir, W)
571MoC187349.8239.8(Mo, C)
7Ir225346.3216.5(Ir)
139MoIr3194337.0187.7(Mo, Ir)
919W2C162335.8165.7(W, C)
725IrW51334.2182.8(Ir, W)
\n", + "
" + ], + "text/plain": [ + " formula space_group bulk_modulus shear_modulus composition\n", + "1015 C 194 435.7 522.9 (C)\n", + "853 Os 194 401.3 258.7 (Os)\n", + "413 WC 187 385.2 279.0 (W, C)\n", + "1103 Re 194 365.1 173.1 (Re)\n", + "726 Ir3W 194 351.3 193.3 (Ir, W)\n", + "571 MoC 187 349.8 239.8 (Mo, C)\n", + "7 Ir 225 346.3 216.5 (Ir)\n", + "139 MoIr3 194 337.0 187.7 (Mo, Ir)\n", + "919 W2C 162 335.8 165.7 (W, C)\n", + "725 IrW 51 334.2 182.8 (Ir, W)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data.head(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### Preset agents\n", + "To implement the linear hardness agent more simply, we can use some pre-built agents\n", + "that will use simple logic to select the maximum prediction by a machine learning model." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# Regressor presets\n", + "from camd.agent.generic import RegressorAgent\n", + "n_query = 3\n", + "linear_agent = RegressorAgent.from_linear(\n", + " features=featurizer.feature_labels(), \n", + " target='bulk_modulus',\n", + " n_query=n_query\n", + ")\n", + "rf_agent = RegressorAgent.from_random_forest(\n", + " features=featurizer.feature_labels(),\n", + " target='bulk_modulus',\n", + " n_query=n_query\n", + ")\n", + "mlp_agent = RegressorAgent.from_mlp(\n", + " features=featurizer.feature_labels(),\n", + " target='bulk_modulus',\n", + " n_query=n_query\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# Generic Model preset\n", + "from sklearn.svm import SVR\n", + "svr = SVR()\n", + "svr_agent = RegressorAgent(\n", + " model=svr,\n", + " features=featurizer.feature_labels(),\n", + " target='bulk_modulus',\n", + " n_query=n_query\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " formula bulk_modulus\n", + "413 WC 385.2\n", + "571 MoC 349.8\n", + "853 Os 401.3\n", + " formula bulk_modulus\n", + "853 Os 401.3\n", + "413 WC 385.2\n", + "1103 Re 365.1\n", + " formula bulk_modulus\n", + "413 WC 385.2\n", + "455 W2C 334.2\n", + "1045 W2C 333.4\n", + " formula bulk_modulus\n", + "725 IrW 334.2\n", + "726 Ir3W 351.3\n", + "968 Nb2C 227.7\n" + ] + } + ], + "source": [ + "### Test agent here\n", + "print(linear_agent.get_hypotheses(featurized_data, featurized_data)[['formula', 'bulk_modulus']])\n", + "print(rf_agent.get_hypotheses(featurized_data, featurized_data)[['formula', 'bulk_modulus']])\n", + "print(mlp_agent.get_hypotheses(featurized_data, featurized_data)[['formula', 'bulk_modulus']])\n", + "print(svr_agent.get_hypotheses(featurized_data, featurized_data)[['formula', 'bulk_modulus']])" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupbulk_modulusshear_moduluscomposition
1015C194435.7522.9(C)
853Os194401.3258.7(Os)
413WC187385.2279.0(W, C)
1103Re194365.1173.1(Re)
726Ir3W194351.3193.3(Ir, W)
571MoC187349.8239.8(Mo, C)
7Ir225346.3216.5(Ir)
139MoIr3194337.0187.7(Mo, Ir)
919W2C162335.8165.7(W, C)
725IrW51334.2182.8(Ir, W)
455W2C60334.2167.5(W, C)
1045W2C58333.4169.4(W, C)
635TaIr3221324.7212.5(Ta, Ir)
55TaC225323.9214.6(Ta, C)
485Cr3C262319.8168.6(Cr, C)
54VIr3221319.7214.9(V, Ir)
269NbIr3221313.4204.1(Nb, Ir)
970Cr3C263308.9147.1(Cr, C)
792Ru194307.5192.8(Ru)
181CrIr3221307.2214.0(Cr, Ir)
\n", + "
" + ], + "text/plain": [ + " formula space_group bulk_modulus shear_modulus composition\n", + "1015 C 194 435.7 522.9 (C)\n", + "853 Os 194 401.3 258.7 (Os)\n", + "413 WC 187 385.2 279.0 (W, C)\n", + "1103 Re 194 365.1 173.1 (Re)\n", + "726 Ir3W 194 351.3 193.3 (Ir, W)\n", + "571 MoC 187 349.8 239.8 (Mo, C)\n", + "7 Ir 225 346.3 216.5 (Ir)\n", + "139 MoIr3 194 337.0 187.7 (Mo, Ir)\n", + "919 W2C 162 335.8 165.7 (W, C)\n", + "725 IrW 51 334.2 182.8 (Ir, W)\n", + "455 W2C 60 334.2 167.5 (W, C)\n", + "1045 W2C 58 333.4 169.4 (W, C)\n", + "635 TaIr3 221 324.7 212.5 (Ta, Ir)\n", + "55 TaC 225 323.9 214.6 (Ta, C)\n", + "485 Cr3C2 62 319.8 168.6 (Cr, C)\n", + "54 VIr3 221 319.7 214.9 (V, Ir)\n", + "269 NbIr3 221 313.4 204.1 (Nb, Ir)\n", + "970 Cr3C2 63 308.9 147.1 (Cr, C)\n", + "792 Ru 194 307.5 192.8 (Ru)\n", + "181 CrIr3 221 307.2 214.0 (Cr, Ir)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data.head(20)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Experiments\n", + "\n", + "In CAMD, *Experiments* are objects that are used to generate new data corresponding to the output of the *Agent.get_hypotheses* method. In other words, *Agents* pick the candidates on which you want to do experiments, and *Experiments* actually do those experiments. As of today, only two experiments are implemented in CAMD, one of which is a AWS-based density functional theory computation of an input crystal structure. The other, which we'll demonstrate below, is an *after-the-fact sampler*, which basically fetches the result of an experiment we already did that corresponds to the input.\n", + "\n", + "Why is the ATFSampler useful? We'll discuss simulation in more detail in a bit, but let's just say we use the ATFSampler to help us evaluate the performance of an Agent when we're trying to pick which agent is the best!" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from camd.experiment.base import ATFSampler" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "k_atf_experiment = ATFSampler(dataframe=featurized_data)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "Note that experiments are *stateful* meaning that their state is explicitly controlled by the user using the `submit` method. When a new set of experiments are submitted, the previous experiments are appended to an internal history attribute and the new ones are set as the current experiments." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
formulaspace_groupbulk_modulusshear_moduluscompositionMagpieData minimum NumberMagpieData maximum NumberMagpieData range NumberMagpieData mean NumberMagpieData avg_dev Number...MagpieData range GSmagmomMagpieData mean GSmagmomMagpieData avg_dev GSmagmomMagpieData mode GSmagmomMagpieData minimum SpaceGroupNumberMagpieData maximum SpaceGroupNumberMagpieData range SpaceGroupNumberMagpieData mean SpaceGroupNumberMagpieData avg_dev SpaceGroupNumberMagpieData mode SpaceGroupNumber
853Os194401.3258.7(Os)76.076.00.076.00.0...0.00.00.00.0194.0194.00.0194.00.0194.0
571MoC187349.8239.8(Mo, C)6.042.036.024.018.0...0.00.00.00.0194.0229.035.0211.517.5194.0
1148W229303.9147.3(W)74.074.00.074.00.0...0.00.00.00.0229.0229.00.0229.00.0229.0
1103Re194365.1173.1(Re)75.075.00.075.00.0...0.00.00.00.0194.0194.00.0194.00.0194.0
55TaC225323.9214.6(Ta, C)6.073.067.039.533.5...0.00.00.00.0194.0229.035.0211.517.5194.0
\n", + "

5 rows × 137 columns

\n", + "
" + ], + "text/plain": [ + " formula space_group bulk_modulus shear_modulus composition \\\n", + "853 Os 194 401.3 258.7 (Os) \n", + "571 MoC 187 349.8 239.8 (Mo, C) \n", + "1148 W 229 303.9 147.3 (W) \n", + "1103 Re 194 365.1 173.1 (Re) \n", + "55 TaC 225 323.9 214.6 (Ta, C) \n", + "\n", + " MagpieData minimum Number MagpieData maximum Number \\\n", + "853 76.0 76.0 \n", + "571 6.0 42.0 \n", + "1148 74.0 74.0 \n", + "1103 75.0 75.0 \n", + "55 6.0 73.0 \n", + "\n", + " MagpieData range Number MagpieData mean Number \\\n", + "853 0.0 76.0 \n", + "571 36.0 24.0 \n", + "1148 0.0 74.0 \n", + "1103 0.0 75.0 \n", + "55 67.0 39.5 \n", + "\n", + " MagpieData avg_dev Number ... MagpieData range GSmagmom \\\n", + "853 0.0 ... 0.0 \n", + "571 18.0 ... 0.0 \n", + "1148 0.0 ... 0.0 \n", + "1103 0.0 ... 0.0 \n", + "55 33.5 ... 0.0 \n", + "\n", + " MagpieData mean GSmagmom MagpieData avg_dev GSmagmom \\\n", + "853 0.0 0.0 \n", + "571 0.0 0.0 \n", + "1148 0.0 0.0 \n", + "1103 0.0 0.0 \n", + "55 0.0 0.0 \n", + "\n", + " MagpieData mode GSmagmom MagpieData minimum SpaceGroupNumber \\\n", + "853 0.0 194.0 \n", + "571 0.0 194.0 \n", + "1148 0.0 229.0 \n", + "1103 0.0 194.0 \n", + "55 0.0 194.0 \n", + "\n", + " MagpieData maximum SpaceGroupNumber MagpieData range SpaceGroupNumber \\\n", + "853 194.0 0.0 \n", + "571 229.0 35.0 \n", + "1148 229.0 0.0 \n", + "1103 194.0 0.0 \n", + "55 229.0 35.0 \n", + "\n", + " MagpieData mean SpaceGroupNumber MagpieData avg_dev SpaceGroupNumber \\\n", + "853 194.0 0.0 \n", + "571 211.5 17.5 \n", + "1148 229.0 0.0 \n", + "1103 194.0 0.0 \n", + "55 211.5 17.5 \n", + "\n", + " MagpieData mode SpaceGroupNumber \n", + "853 194.0 \n", + "571 194.0 \n", + "1148 229.0 \n", + "1103 194.0 \n", + "55 194.0 \n", + "\n", + "[5 rows x 137 columns]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "k_atf_experiment.submit(hypotheses)\n", + "results = k_atf_experiment.get_results()\n", + "results" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Analyzers\n", + "\n", + "**Analyzers** are a bit tricky to explain because they're not necessary for every sequential learning process. We're not going to spend much time on them here other than to say that, after you've performed an experiment, sometimes you want to postprocess the data in order to summarize the results of the current iteration and to augment the **seed data** which is being used to provide the **Agent** with the information it needs to make its next decision on which candidates to select for further experiments. Analyzers act on \"campaigns\", which are effectively state machines for the iterative loop between the agent and experiments." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Failed to import pyspglib.Download at: http://sourceforge.net/projects/spglib/ andfollow instructions for installing python API\n" + ] + } + ], + "source": [ + "from camd.analysis import AnalyzerBase" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "# Here's an example of an analyzer\n", + "class BulkModulusAnalyzer(AnalyzerBase):\n", + " def analyze(self, campaign):\n", + " new_experimental_results = campaign.experiment.get_results()\n", + " new_seed = pd.concat(\n", + " [campaign.seed_data, new_experimental_results],\n", + " axis=0)\n", + " # Create a summary\n", + " average_new_bulk_modulus = new_experimental_results.bulk_modulus.mean()\n", + " average_dataset_bulk_modulus = new_seed.bulk_modulus.mean()\n", + " new_result_ranks = new_seed.bulk_modulus.rank(pct=True).loc[\n", + " new_experimental_results.index\n", + " ]\n", + " summary = pd.DataFrame({\n", + " \"average_new_bulk_modulus\": [average_new_bulk_modulus],\n", + " \"average_dataset_bulk_modulus\": [average_dataset_bulk_modulus],\n", + " \"average_rank\": [new_result_ranks.mean()]\n", + " })\n", + " return summary" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "# Here's invoking a generic analyzer\n", + "from camd.analysis import GenericMaxAnalyzer, GenericATFAnalyzer\n", + "k_analyzer = GenericMaxAnalyzer(threshold=275.0, target='bulk_modulus')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Data, Campaigns, and Simulations\n", + "\n", + "Now that we've got all of the building blocks in place, let's try putting everything together!" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign initialization state: Agent RegressorAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent RegressorAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent RegressorAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent RegressorAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent RegressorAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent RegressorAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent RegressorAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent RegressorAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent RegressorAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent RegressorAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent RegressorAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent RegressorAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign initialization state: Agent RegressorAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent RegressorAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent RegressorAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent RegressorAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent RegressorAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent RegressorAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent RegressorAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent RegressorAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent RegressorAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent RegressorAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent RegressorAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent RegressorAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign initialization state: Agent RegressorAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent RegressorAgent hypothesizing\n", + " /Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/sklearn/neural_network/_multilayer_perceptron.py:696: ConvergenceWarning:Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent RegressorAgent hypothesizing\n", + " /Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/sklearn/neural_network/_multilayer_perceptron.py:696: ConvergenceWarning:Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent RegressorAgent hypothesizing\n", + " /Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/sklearn/neural_network/_multilayer_perceptron.py:696: ConvergenceWarning:Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent RegressorAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent RegressorAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent RegressorAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent RegressorAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent RegressorAgent hypothesizing\n", + " /Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/sklearn/neural_network/_multilayer_perceptron.py:696: ConvergenceWarning:Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent RegressorAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent RegressorAgent hypothesizing\n", + " /Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/sklearn/neural_network/_multilayer_perceptron.py:696: ConvergenceWarning:Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent RegressorAgent hypothesizing\n", + " /Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/sklearn/neural_network/_multilayer_perceptron.py:696: ConvergenceWarning:Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign initialization state: Agent RegressorAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent RegressorAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent RegressorAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent RegressorAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent RegressorAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent RegressorAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent RegressorAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent RegressorAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent RegressorAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent RegressorAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent RegressorAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent RegressorAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n" + ] + } + ], + "source": [ + "import os, shutil\n", + "from monty.os import cd\n", + "from camd.campaigns.base import Campaign\n", + "# Set up folders\n", + "shutil.rmtree(os.path.join(os.getcwd(), \"agent_comp\"), ignore_errors=True)\n", + "# Reinitialize experiment to clear history\n", + "k_atf_experiment = ATFSampler(dataframe=featurized_data)\n", + "campaigns = []\n", + "for n, agent in enumerate([linear_agent, rf_agent, mlp_agent, svr_agent]):\n", + " path = os.path.join(os.getcwd(), \"agent_comp\", str(n))\n", + " os.makedirs(path)\n", + " campaign = Campaign(\n", + " candidate_data=k_candidate_data, \n", + " seed_data=k_seed_data,\n", + " agent=agent,\n", + " experiment=k_atf_experiment,\n", + " analyzer=k_analyzer,\n", + " path=path\n", + " )\n", + " campaign.auto_loop(initialize=True)\n", + " campaigns.append(campaign)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'Materials with\\n$K_{VRH} > 275$ GPa')" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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vMql+XiW2FhO9etP/JEjuFWqmImR355H06Z4d+1CvgYNssUyUSpzJScns2rVKl588UUAKleuzIYNG3BxcWHlypWMGTOGgIAAwHoGtnPnTtzc3KhUqRKvv/46Li4ujBs3juDgYLy8vGjevPn1VT9ef/11evfuTe/evZk1axaDBg1i8eLFAERERLB582aWLFlCx44d2bRpEzNmzODhhx++XhMWrKuTODs74+bmxtatW+94zNDQUP755x+cnZ1p2bIl06dPp0KFCmzdupUBAwawevVq3nvvPf7++29KlixJZGQkefLk4b333rth6Dhlsj558iRxcXHUrFmTAwcO8Ouvv7Jp0yZcXV0ZMGAA8+bNo1WrVrz//vvs2LEDT09PWrRowUMPPXT9GOmJC2D69OkMHjyYnj17kpCQQHJyMsHBwcyePZutW7dijKF+/fo8+uij+Pj43HEptHuhyVLdk3OhR/FY+Ayxkpf8fReR6Ab9V/Zn85nNPPHAE4xtMJZ8rvnSfDyTnEzYsOHE7d+P35QpeLZononRq2zjHs4AM9LVq1epVasWYWFhVKlShdatWwPWpal69+7N4cOHERESUxTAb9myJV62+r5Vq1blxIkTXLhwgWbNmuHr6wtYhy///fdfADZv3syiRYsA6NWrF2+++eb1Yz3xxBOICDVq1KBo0aLUqFEDgGrVqnH8+PHryfLmYdg7HfPpp5/G2dmZmJgY/vnnnxuW1IqPjwegcePG9OnTh27dul1fZis1v/76K+vXr+fgwYNMmTIFd3d3Vq1aRXBwMA8//PD1z7BIkSJs27aNRx99lIIFC16P49pncD9xNWzYkA8//JDQ0FC6du1KhQoV2LhxI126dLm+2krXrl3ZsGEDHTt2THUptPTQZKnSLDrqEldmdaWIucr5p3/jcp5ohv3+CpFxkYxrOI4nKzx5Tze/GGM49+GHxKxZQ9F33tZEqRzu2jXL2NhY2rZty9SpUxk0aBBvv/02zZs3JzAwkOPHj9OsWbPrba4Nr8L9L0117VhOTk43HNfJySndx72WQCwWC97e3qnWj50+fTpbt27lzz//pG7dugQHB6d6rGvXLIOCgmjTpg0dO3bEGEPv3r35+OOPb9j32pltRsf17LPPUr9+ff7880/at2/PN998k6Z+7peON9mBiJQSkTUisl9E9onIYNv2z0TkoIjsFpFA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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Pull up some results\n", + "from matplotlib import pyplot as plt\n", + "plt.subplot(1, 1, 1)\n", + "ax = plt.gca()\n", + "for campaign in campaigns:\n", + " campaign.history.score.plot(\n", + " ax=ax, label=campaign.agent.model.__class__.__name__)\n", + "ax.legend(loc='lower right')\n", + "ax.set_xlabel(\"iteration\")\n", + "ax.set_ylabel(\"Materials with\\n$K_{VRH} > 275$ GPa\", rotation=0, ha='right')\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Final thoughts\n", + "There's a lot more that we can do to improve our postprocessing analysis of how well the campaign proceeded, but this should get you started. A few exercises you might find interesting to try:\n", + "\n", + "* Test more regressors from scikit learn, this [documentation of their supervised learning methods](https://scikit-learn.org/stable/supervised_learning.html) points to many of them.\n", + "* Test the agent on using multiple random seeds and determine the spread on discovery rate.\n", + "* Develop an explore/exploit strategy where you choose some candidates from the regressor prediction and some randomly.\n", + "* Try different datasets, see the datasets folder or the [matminer datasets documentation](https://hackingmaterials.lbl.gov/matminer/dataset_summary.html).\n", + "* Try different featurizers, see [the matminer featurizer documentation](https://hackingmaterials.lbl.gov/matminer/featurizer_summary.html)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Glossary\n", + "* **Agent** - decision making object in camd, must implement `get_hypotheses` in order to work properly in the loop\n", + "* **Experiment** - object which performs some action in order to determine unknowns about an input dataset\n", + "* **Analyzer** - object which acts on the campaign to summarize the results of an iteration\n", + "* **seed_data** - Data which is \"known\" either before the start of a given **Campaign** or prior to any iteration. Is used to inform the **Agent** of the data it should be using to make a decision about how to select from the **Candidate data**.\n", + "* **candidate_data** - data which represents the information about the set of \"unknowns\" at a given point of time for a **Campaign**.\n", + "* **Campaign** - the iterative procedure by which an **Agent** suggests experiments from the **candidate data**, the **Experiment** performs them, the **Analyzer** analyzes the state and feeds a new **seed data** and set of **candidate data** back to the **Agent** to start a new iteration. " + ] + } + ], + "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.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/meta_agent.ipynb b/examples/meta_agent.ipynb new file mode 100644 index 00000000..22dfd536 --- /dev/null +++ b/examples/meta_agent.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## CAMD Meta agent example" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## NOTE: This notebook is still in development, Meta Agent functionality is still beta" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Failed to import pyspglib.Download at: http://sourceforge.net/projects/spglib/ andfollow instructions for installing python API\n" + ] + } + ], + "source": [ + "from camd.campaigns.meta_agent import MetaAgentCampaign, \\\n", + " StabilityCampaignAnalyzer\n", + "from camd.analysis import StabilityAnalyzer\n", + "from camd.utils.data import load_dataframe, partition_intercomp, \\\n", + " get_oqmd_data_by_chemsys\n", + "from camd.experiment.agent_simulation import LocalAgentSimulation\n", + "from camd.agent.base import RandomAgent\n", + "from taburu.table import ParameterTable\n", + "from camd import CAMD_S3_BUCKET\n", + "# Note that env variable must be set\n", + "if CAMD_S3_BUCKET is None:\n", + " raise ValueError(\"Set CAMD_S3_BUCKET to enable notebook\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define the agents and agent_pool" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Define agent parameters\n", + "agent_params = [\n", + " {\n", + " \"@class\": [\"camd.agent.base.RandomAgent\"],\n", + " \"n_query\": [2, 3],\n", + " },\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Construct the dataframe using Fe-O data" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "data = get_oqmd_data_by_chemsys(\"Fe-O\", drop_duplicates=True)\n", + "candidate_data, seed_data = partition_intercomp(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Construct core campaign objects" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:00<00:00, 4092.00it/s]\n" + ] + } + ], + "source": [ + "experiment = LocalAgentSimulation(\n", + " atf_candidate_data=candidate_data, seed_data=seed_data,\n", + " analyzer=StabilityAnalyzer(), iterations=10,\n", + ")\n", + "analyzer = StabilityCampaignAnalyzer(\n", + " checkpoint_indices=[2, 5, 10])\n", + "agent_pool = ParameterTable(agent_params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Reserve campaign in S3" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "MetaAgentCampaign.reserve(\n", + " name=\"test_meta_agent_python\", experiment=experiment,\n", + " agent_pool=agent_pool, analyzer=analyzer\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run via Python API" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "MetaAgentCampaign initialization state: Agent RandomAgent hypothesizing\n", + "MetaAgentCampaign 0 state: Running experiments\n", + "Iteration: 0\n", + "MetaAgentCampaign 0 state: Getting new results\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "/Users/josephmontoya/miniconda3/envs/camd/lib/python3.7/site-packages/pymatgen/analysis/phase_diagram.py:1968: UserWarning: linestyle is redundantly defined by the 'linestyle' keyword argument and the fmt string \"ko-\" (-> linestyle='-'). The keyword argument will take precedence.\n", + " plt.plot(x, y, \"ko-\", **self.plotkwargs)\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "MetaAgentCampaign 0 state: Analyzing results\n" + ] + }, + { + "ename": "PicklingError", + "evalue": "logger cannot be pickled", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mPicklingError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/var/folders/ty/mv_mnwg56csbrw__g26c5nf80000gq/T/ipykernel_75234/3681173893.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 6\u001b[0m campaign = MetaAgentCampaign.from_reserved_name(\n\u001b[1;32m 7\u001b[0m \"test_meta_agent_python\", meta_agent=meta_agent)\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mcampaign\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautorun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchdir\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'..'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/PycharmProjects/camd/camd_public/camd/campaigns/meta_agent.py\u001b[0m in \u001b[0;36mautorun\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 179\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 180\u001b[0m \"\"\"\n\u001b[0;32m--> 181\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_loop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn_iterations\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitialize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 182\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 183\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/PycharmProjects/camd/camd_public/camd/campaigns/base.py\u001b[0m in \u001b[0;36mauto_loop\u001b[0;34m(self, n_iterations, monitor, initialize, save_iterations)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0mn_iterations\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miteration\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Iteration: {}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miteration\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\" Waiting for next round ...\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/PycharmProjects/camd/camd_public/camd/campaigns/base.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, finalize)\u001b[0m\n\u001b[1;32m 172\u001b[0m \u001b[0mnew_seed_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mseed_data\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnew_experimental_results\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 173\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mseed_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnew_seed_data\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 174\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msave\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"seed_data\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"pickle\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 175\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 176\u001b[0m \u001b[0;31m# Remove candidates from candidate space\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/PycharmProjects/camd/camd_public/camd/campaigns/base.py\u001b[0m in \u001b[0;36msave\u001b[0;34m(self, data_holder, custom_name, method)\u001b[0m\n\u001b[1;32m 411\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Unknown data save method\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 412\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 413\u001b[0;31m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdump\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattribute__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_holder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 414\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 415\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0ms3_prefix\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/camd/lib/python3.7/logging/__init__.py\u001b[0m in \u001b[0;36m__reduce__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1659\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgetLogger\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1660\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpickle\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1661\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mpickle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPicklingError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'logger cannot be pickled'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1662\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgetLogger\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1663\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mPicklingError\u001b[0m: logger cannot be pickled" + ] + } + ], + "source": [ + "import os, shutil\n", + "shutil.rmtree(\"python\", ignore_errors=True)\n", + "os.makedirs(\"python\")\n", + "os.chdir(\"python\")\n", + "meta_agent = RandomAgent(n_query=1)\n", + "campaign = MetaAgentCampaign.from_reserved_name(\n", + " \"test_meta_agent_python\", meta_agent=meta_agent)\n", + "campaign.autorun()\n", + "os.chdir('..')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run via CLI" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def create_test(name):\n", + " data = get_oqmd_data_by_chemsys(\"Fe-O\", drop_duplicates=True)\n", + " candidate_data, seed_data = partition_intercomp(data)\n", + " experiment = LocalAgentSimulation(\n", + " atf_candidate_data=candidate_data, seed_data=seed_data,\n", + " analyzer=StabilityAnalyzer(), iterations=10,\n", + " )\n", + " analyzer = StabilityCampaignAnalyzer(\n", + " checkpoint_indices=[2, 5, 10])\n", + " agent_pool = ParameterTable(agent_params)\n", + " MetaAgentCampaign.reserve(\n", + " name=name, experiment=experiment,\n", + " agent_pool=agent_pool, analyzer=analyzer\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "create_test(\"test_meta_agent_cli\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run s3 backed campaign\n", + "!rm -rf test && \\\n", + "mkdir test && \\\n", + "cd test && \\\n", + "camd_runner meta_agent/test_meta_agent_cli" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Summary figure\n", + "![title](test/campaign_summary.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run on AWS Batch" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "create_test(\"test_meta_agent_aws_batch\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "submit_output = \\\n", + "!aws batch submit-job \\\n", + "--job-name test_meta_agent_aws_batch \\\n", + "--job-queue camd_dev \\\n", + "--job-definition camd_dev_batch \\\n", + "--parameters name=meta_agent/test_meta_agent_aws_batch" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json, time\n", + "job_data = json.loads(''.join(submit_output))\n", + "job_id = job_data['jobId']\n", + "while True:\n", + " describe_output = !aws batch describe-jobs --jobs $job_id\n", + " job_data = json.loads(''.join(describe_output))\n", + " job_status = job_data['jobs'][0]['status']\n", + " print(\"Job status is {}\".format(job_status))\n", + " if job_status not in ['PENDING', 'RUNNABLE', 'RUNNING']:\n", + " break\n", + " time.sleep(20)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/meta_agent/camd_meta_agent_example.ipynb b/examples/meta_agent/camd_meta_agent_example.ipynb deleted file mode 100644 index 8f3f0546..00000000 --- a/examples/meta_agent/camd_meta_agent_example.ipynb +++ /dev/null @@ -1,501 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## CAMD Meta agent example" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from camd.campaigns.meta_agent import MetaAgentCampaign, \\\n", - " StabilityCampaignAnalyzer\n", - "from camd.analysis import StabilityAnalyzer\n", - "from camd.utils.data import load_dataframe, partition_intercomp, \\\n", - " get_oqmd_data_by_chemsys\n", - "from camd.experiment.agent_simulation import LocalAgentSimulation\n", - "from camd.agent.base import RandomAgent\n", - "from taburu.table import ParameterTable\n", - "from camd import CAMD_S3_BUCKET\n", - "# Note that env variable must be set\n", - "if CAMD_S3_BUCKET is None:\n", - " raise ValueError(\"Set CAMD_S3_BUCKET to enable notebook\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Define the agents and agent_pool" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Define agent parameters\n", - "agent_params = [\n", - " {\n", - " \"@class\": [\"camd.agent.base.RandomAgent\"],\n", - " \"n_query\": [2, 3],\n", - " },\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Construct the dataframe using Fe-O data" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "data = get_oqmd_data_by_chemsys(\"Fe-O\", drop_duplicates=True)\n", - "candidate_data, seed_data = partition_intercomp(data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Construct core campaign objects" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 2/2 [00:00<00:00, 5966.29it/s]\n" - ] - } - ], - "source": [ - "experiment = LocalAgentSimulation(\n", - " atf_candidate_data=candidate_data, seed_data=seed_data,\n", - " analyzer=StabilityAnalyzer(), iterations=10,\n", - ")\n", - "analyzer = StabilityCampaignAnalyzer(\n", - " checkpoint_indices=[2, 5, 10])\n", - "agent_pool = ParameterTable(agent_params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reserve campaign in S3" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "MetaAgentCampaign.reserve(\n", - " name=\"test_meta_agent_python\", experiment=experiment,\n", - " agent_pool=agent_pool, analyzer=analyzer\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Run via Python API" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MetaAgentCampaign initialization state: Agent RandomAgent hypothesizing\n", - "MetaAgentCampaign 0 state: Running experiments\n", - "Iteration: 0\n", - "MetaAgentCampaign 0 state: Getting new results\n", - "Campaign initialization state: Agent RandomAgent hypothesizing\n", - "Campaign 0 state: Running experiments\n", - "Iteration: 0\n", - "Campaign 0 state: Getting new results\n", - "Campaign 0 state: Analyzing results\n", - "Campaign 0 state: Agent RandomAgent hypothesizing\n", - "Campaign 0 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 1\n", - "Campaign 1 state: Getting new results\n", - "Campaign 1 state: Analyzing results\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/josephmontoya/miniconda3/envs/camd/lib/python3.6/site-packages/pandas/core/frame.py:6692: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version\n", - "of pandas will change to not sort by default.\n", - "\n", - "To accept the future behavior, pass 'sort=False'.\n", - "\n", - "To retain the current behavior and silence the warning, pass 'sort=True'.\n", - "\n", - " sort=sort)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign 1 state: Agent RandomAgent hypothesizing\n", - "Campaign 1 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 2\n", - "Campaign 2 state: Getting new results\n", - "Campaign 2 state: Analyzing results\n", - "Campaign 2 state: Agent RandomAgent hypothesizing\n", - "Campaign 2 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 3\n", - "Campaign 3 state: Getting new results\n", - "Campaign 3 state: Analyzing results\n", - "Campaign 3 state: Agent RandomAgent hypothesizing\n", - "Campaign 3 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 4\n", - "Campaign 4 state: Getting new results\n", - "Campaign 4 state: Analyzing results\n", - "Campaign 4 state: Agent RandomAgent hypothesizing\n", - "Campaign 4 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 5\n", - "Campaign 5 state: Getting new results\n", - "Campaign 5 state: Analyzing results\n", - "Campaign 5 state: Agent RandomAgent hypothesizing\n", - "Campaign 5 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 6\n", - "Campaign 6 state: Getting new results\n", - "Campaign 6 state: Analyzing results\n", - "Campaign 6 state: Agent RandomAgent hypothesizing\n", - "Campaign 6 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 7\n", - "Campaign 7 state: Getting new results\n", - "Campaign 7 state: Analyzing results\n", - "Campaign 7 state: Agent RandomAgent hypothesizing\n", - "Campaign 7 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 8\n", - "Campaign 8 state: Getting new results\n", - "Campaign 8 state: Analyzing results\n", - "Campaign 8 state: Agent RandomAgent hypothesizing\n", - "Campaign 8 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 9\n", - "Campaign 9 state: Getting new results\n", - "Campaign 9 state: Analyzing results\n", - "Campaign 9 state: Agent RandomAgent hypothesizing\n", - "Campaign 9 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 10\n", - "Campaign 10 state: Getting new results\n", - "Campaign 10 state: Analyzing results\n", - "Campaign 10 state: Agent RandomAgent hypothesizing\n", - "Campaign 10 state: Running experiments\n", - " Waiting for next round ...\n", - "Finalizing campaign.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/josephmontoya/PycharmProjects/camd/camd_public/camd/analysis.py:365: UserWarning: No prior data, prior phase diagram cannot be constructed\n", - " warnings.warn(\"No prior data, prior phase diagram cannot be constructed\")\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MetaAgentCampaign 0 state: Analyzing results\n", - "MetaAgentCampaign 0 state: Agent RandomAgent hypothesizing\n", - "MetaAgentCampaign 0 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 1\n", - "MetaAgentCampaign 1 state: Getting new results\n", - "Campaign initialization state: Agent RandomAgent hypothesizing\n", - "Campaign 0 state: Running experiments\n", - "Iteration: 0\n", - "Campaign 0 state: Getting new results\n", - "Campaign 0 state: Analyzing results\n", - "Campaign 0 state: Agent RandomAgent hypothesizing\n", - "Campaign 0 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 1\n", - "Campaign 1 state: Getting new results\n", - "Campaign 1 state: Analyzing results\n", - "Campaign 1 state: Agent RandomAgent hypothesizing\n", - "Campaign 1 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 2\n", - "Campaign 2 state: Getting new results\n", - "Campaign 2 state: Analyzing results\n", - "Campaign 2 state: Agent RandomAgent hypothesizing\n", - "Campaign 2 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 3\n", - "Campaign 3 state: Getting new results\n", - "Campaign 3 state: Analyzing results\n", - "Campaign 3 state: Agent RandomAgent hypothesizing\n", - "Campaign 3 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 4\n", - "Campaign 4 state: Getting new results\n", - "Campaign 4 state: Analyzing results\n", - "Campaign 4 state: Agent RandomAgent hypothesizing\n", - "Campaign 4 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 5\n", - "Campaign 5 state: Getting new results\n", - "Campaign 5 state: Analyzing results\n", - "Campaign 5 state: Agent RandomAgent hypothesizing\n", - "Campaign 5 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 6\n", - "Campaign 6 state: Getting new results\n", - "Campaign 6 state: Analyzing results\n", - "Campaign 6 state: Agent RandomAgent hypothesizing\n", - "Campaign 6 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 7\n", - "Campaign 7 state: Getting new results\n", - "Campaign 7 state: Analyzing results\n", - "Campaign 7 state: Agent RandomAgent hypothesizing\n", - "Campaign 7 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 8\n", - "Campaign 8 state: Getting new results\n", - "Campaign 8 state: Analyzing results\n", - "Campaign 8 state: Agent RandomAgent hypothesizing\n", - "Campaign 8 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 9\n", - "Campaign 9 state: Getting new results\n", - "Campaign 9 state: Analyzing results\n", - "Campaign 9 state: Agent RandomAgent hypothesizing\n", - "Campaign 9 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 10\n", - "Campaign 10 state: Getting new results\n", - "Campaign 10 state: Analyzing results\n", - "Campaign 10 state: Agent RandomAgent hypothesizing\n", - "Campaign 10 state: Running experiments\n", - " Waiting for next round ...\n", - "Finalizing campaign.\n", - "MetaAgentCampaign 1 state: Analyzing results\n", - "Candidate data exhausted. Stopping loop.\n", - "Finalizing campaign.\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import os\n", - "os.mkdir('python')\n", - "os.chdir('python')\n", - "meta_agent = RandomAgent(n_query=1)\n", - "campaign = MetaAgentCampaign.from_reserved_name(\n", - " \"test_meta_agent_python\", meta_agent=meta_agent)\n", - "campaign.autorun()\n", - "os.chdir('..')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Run via CLI" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def create_test(name):\n", - " data = get_oqmd_data_by_chemsys(\"Fe-O\", drop_duplicates=True)\n", - " candidate_data, seed_data = partition_intercomp(data)\n", - " experiment = LocalAgentSimulation(\n", - " atf_candidate_data=candidate_data, seed_data=seed_data,\n", - " analyzer=StabilityAnalyzer(), iterations=10,\n", - " )\n", - " analyzer = StabilityCampaignAnalyzer(\n", - " checkpoint_indices=[2, 5, 10])\n", - " agent_pool = ParameterTable(agent_params)\n", - " MetaAgentCampaign.reserve(\n", - " name=name, experiment=experiment,\n", - " agent_pool=agent_pool, analyzer=analyzer\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "create_test(\"test_meta_agent_cli\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run s3 backed campaign\n", - "!rm -rf test && \\\n", - "mkdir test && \\\n", - "cd test && \\\n", - "camd_runner meta_agent/test_meta_agent_cli" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Summary figure\n", - "![title](test/campaign_summary.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Run on AWS Batch" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 2/2 [00:00<00:00, 3545.48it/s]\n" - ] - } - ], - "source": [ - "create_test(\"test_meta_agent_aws_batch\")" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "submit_output = \\\n", - "!aws batch submit-job \\\n", - "--job-name test_meta_agent_aws_batch \\\n", - "--job-queue camd_dev \\\n", - "--job-definition camd_dev_batch \\\n", - "--parameters name=meta_agent/test_meta_agent_aws_batch" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Job status is RUNNABLE\n", - "Job status is RUNNABLE\n", - "Job status is RUNNABLE\n", - "Job status is STARTING\n" - ] - } - ], - "source": [ - "import json, time\n", - "job_data = json.loads(''.join(submit_output))\n", - "job_id = job_data['jobId']\n", - "while True:\n", - " describe_output = !aws batch describe-jobs --jobs $job_id\n", - " job_data = json.loads(''.join(describe_output))\n", - " job_status = job_data['jobs'][0]['status']\n", - " print(\"Job status is {}\".format(job_status))\n", - " if job_status not in ['PENDING', 'RUNNABLE', 'RUNNING']:\n", - " break\n", - " time.sleep(20)" - ] - }, - { - "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.6.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/mnist_active_learning/mnist_active_learning_example.ipynb b/examples/mnist_active_learning/mnist_active_learning_example.ipynb deleted file mode 100644 index a9ce7ac3..00000000 --- a/examples/mnist_active_learning/mnist_active_learning_example.ipynb +++ /dev/null @@ -1,757 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Active-learning example with MNIST data for digit recognition" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The goal of this notebook is to show how a simple active-learning campaign can be designed for digit recognition using CAMD." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from sklearn.datasets import fetch_openml\n", - "from camd.agent.base import RandomAgent, HypothesisAgent\n", - "from camd.analysis import AnalyzerBase\n", - "from camd.experiment import ATFSampler\n", - "from camd.loop import Campaign\n", - "from scipy.stats import entropy\n", - "from sklearn.datasets import fetch_openml\n", - "from sklearn.linear_model import LogisticRegression\n", - "from sklearn.model_selection import cross_val_score, KFold\n", - "path = os.getcwd()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's fetch the MNIST dataset by LeCun et al. from OpenML" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "X, y = fetch_openml('mnist_784', version=1, return_X_y=True)\n", - "df = pd.DataFrame(X)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Dataset has 70,000 labeled instances of hand written digits in 28x28 resolution. We'll use 10,000 for testing our active-learning model. Use 60000 for active-learning itself." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(70000, 784)\n" - ] - } - ], - "source": [ - "print(df.shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is what a typical example looks like:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.imshow(X[2].reshape(28,28))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's prepare our data for camd.Campaign" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(60000, 785)\n" - ] - } - ], - "source": [ - "df.index = [str(i) for i in list(df.index)] # indices need to be strings\n", - "y = y.astype(int) # we need target as int\n", - "df['target'] = y # and target needs to be part of df\n", - "test_df = df.sample(10000, random_state=42) # setting aside 10,000 examples for testing\n", - "df = df.drop(test_df.index, axis=0)\n", - "print(df.shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's construct an Agent that tries to choose the next set of examples to be labeled by a human oracle (which corresponds to the Experiment class in camd). Note we have 10 digits (hence classes), this for each example we get 10 probability estimations for belonging to digit classes\n", - "\n", - "Our Agents strategy is to pick the examples where the class assignments are the most undecided, which we quantify by calculating the entropy of the predicted probabilities from 0 to 9. The larger the entropy, the closer the point is to the ``decision boundaries`` of the classifier.\n", - "\n", - "Given the seed data and candidate data, our Agent will simply return a list of N_query number of examples to send for labeling." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "class DecisionBoundary(HypothesisAgent):\n", - " def __init__(self, candidate_data=None, seed_data=None, N_query=None, clf=None, clf_params=None):\n", - " self.candidate_data = candidate_data\n", - " self.seed_data = seed_data\n", - " self.N_query = N_query if N_query else 1\n", - " self.cv_score = np.nan\n", - " self.clf = clf(**clf_params)\n", - " super(DecisionBoundary).__init__()\n", - " \n", - " def get_hypotheses(self, candidate_data, seed_data=None):\n", - " self.candidate_data = candidate_data.drop(columns=['target'], axis=1)\n", - " self.seed_data = seed_data\n", - " X_train = seed_data.drop(columns=['target'], axis=1)\n", - " y_train = seed_data['target']\n", - " \n", - " # CV score from acquired data\n", - " cv_score = cross_val_score(self.clf, X_train, y_train,\n", - " cv=KFold(3, shuffle=True))\n", - " self.cv_score = np.mean(cv_score)\n", - " \n", - " # Fit the main model, make predictions and request new experiments based on entorpy\n", - " self.clf.fit(X_train, y_train)\n", - " probabs = self.clf.predict_proba(self.candidate_data)\n", - " self.probabs = probabs\n", - " neg_entropies = [-1*entropy(probabs[i]) for i in range(len(probabs))]\n", - " selected = np.argsort(neg_entropies)[:self.N_query]\n", - " selected = candidate_data.index.values[selected].tolist()\n", - " return selected" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We design an `Analyzer` to test the active-learning model against the independent test set and stores the scores. It trains a classifier with acquired data, and evaluates the performance on test data. Note some arguments in the analyzer below are not used and are there for the `Campaign` not to break when it tries to pass such arguments to `MnistAnalyze`." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "class MnistAnalyze(AnalyzerBase):\n", - " def __init__(self, test_df, clf, clf_params, *args, **kwargs):\n", - " self.score = []\n", - " self.test_df = test_df\n", - " self.clf = clf\n", - " self.clf_params = clf_params\n", - " super(MnistAnalyze, self).__init__()\n", - " def analyze(self, df, submitted_experiment_request, consumed):\n", - " X = df.drop(columns=['target'], axis=1)\n", - " y = df['target']\n", - " clf = self.clf(**clf_params)\n", - " clf.fit(X, y)\n", - " self.score.append(clf.score(test_df.drop('target', axis=1), test_df['target']))\n", - " print('score', self.score)\n", - " return [], []\n", - " def present(self, a, b, c, *args, **kwargs):\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will now setup our active learning campaign simulation by specifying the hyperparameters of the loop, such as the maximum number of queries allowed in each iteration, number of examples to pick as the seed, `Agent`, `Analyzer` and `Experiment` classes (and their parameters) to be used. Here we are using the `ATFSampler` class as the experiment, which helps perform an after-the-fact simulation by simply looking up the results from the provided dataframe. " - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "N_query = 500 # This many new examples are asked to oracle in each iteration (labeled by humans in reality)\n", - "N_seed = 5000 # This many samples are randomly acquired in the beginning to form a seed.\n", - "clf = LogisticRegression\n", - "clf_params = {'C': 50./X.shape[1], 'multi_class':'multinomial', 'penalty':'l1', 'solver':'saga', 'tol':0.1}\n", - "agent = DecisionBoundary\n", - "agent_params = {'N_query': N_query, 'clf':clf, 'clf_params': clf_params}\n", - "analyzer = MnistAnalyze\n", - "analyzer_params = {'test_df': test_df, 'clf':clf, 'clf_params': clf_params}\n", - "experiment = ATFSampler\n", - "experiment_params = {'dataframe': df}\n", - "candidate_data = df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's construct a `Campaign`, which will execute the entire campaign. You should make a folder named 'mnist-entropy' in the working directory of this notebook." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "new_loop = Campaign(candidate_data, agent, experiment, analyzer,\n", - " agent_params=agent_params, analyzer_params=analyzer_params, \n", - " experiment_params=experiment_params,path=os.path.join(path, 'mnist-entropy'),create_seed=N_seed)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initialize the loop to create an initial seed with N_seed examples randomly picked from the candidate data. It actually runs the RandomAgent under the hood." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign initialization state: Agent RandomAgent hypothesizing\n", - "Campaign 0 state: Running experiments\n" - ] - } - ], - "source": [ - "new_loop.initialize()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now our loop is initialized, and a set of new \"experiments\" are submitted. We will ask the Campaign to start runing with 10 iterations:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Iteration: 0\n", - "Campaign 0 state: Getting new results\n", - "Campaign 0 state: Analyzing results\n", - "score [0.8962]\n", - "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 0 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 1\n", - "Campaign 1 state: Getting new results\n", - "Campaign 1 state: Analyzing results\n", - "score [0.8962, 0.9003]\n", - "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 1 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 2\n", - "Campaign 2 state: Getting new results\n", - "Campaign 2 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015]\n", - "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 2 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 3\n", - "Campaign 3 state: Getting new results\n", - "Campaign 3 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018]\n", - "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 3 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 4\n", - "Campaign 4 state: Getting new results\n", - "Campaign 4 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028]\n", - "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 4 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 5\n", - "Campaign 5 state: Getting new results\n", - "Campaign 5 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001]\n", - "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 5 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 6\n", - "Campaign 6 state: Getting new results\n", - "Campaign 6 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036]\n", - "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 6 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 7\n", - "Campaign 7 state: Getting new results\n", - "Campaign 7 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028]\n", - "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 7 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 8\n", - "Campaign 8 state: Getting new results\n", - "Campaign 8 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049]\n", - "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 8 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 9\n", - "Campaign 9 state: Getting new results\n", - "Campaign 9 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068]\n", - "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 9 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 10\n", - "Campaign 10 state: Getting new results\n", - "Campaign 10 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062]\n", - "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 10 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 11\n", - "Campaign 11 state: Getting new results\n", - "Campaign 11 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087]\n", - "Campaign 11 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 11 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 12\n", - "Campaign 12 state: Getting new results\n", - "Campaign 12 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094]\n", - "Campaign 12 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 12 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 13\n", - "Campaign 13 state: Getting new results\n", - "Campaign 13 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096]\n", - "Campaign 13 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 13 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 14\n", - "Campaign 14 state: Getting new results\n", - "Campaign 14 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103]\n", - "Campaign 14 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 14 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 15\n", - "Campaign 15 state: Getting new results\n", - "Campaign 15 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117]\n", - "Campaign 15 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 15 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 16\n", - "Campaign 16 state: Getting new results\n", - "Campaign 16 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139]\n", - "Campaign 16 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 16 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 17\n", - "Campaign 17 state: Getting new results\n", - "Campaign 17 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151]\n", - "Campaign 17 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 17 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 18\n", - "Campaign 18 state: Getting new results\n", - "Campaign 18 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156]\n", - "Campaign 18 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 18 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 19\n", - "Campaign 19 state: Getting new results\n", - "Campaign 19 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181]\n", - "Campaign 19 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 19 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 20\n", - "Campaign 20 state: Getting new results\n", - "Campaign 20 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918]\n", - "Campaign 20 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 20 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 21\n", - "Campaign 21 state: Getting new results\n", - "Campaign 21 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195]\n", - "Campaign 21 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 21 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 22\n", - "Campaign 22 state: Getting new results\n", - "Campaign 22 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203]\n", - "Campaign 22 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 22 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 23\n", - "Campaign 23 state: Getting new results\n", - "Campaign 23 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208]\n", - "Campaign 23 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 23 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 24\n", - "Campaign 24 state: Getting new results\n", - "Campaign 24 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196]\n", - "Campaign 24 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 24 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 25\n", - "Campaign 25 state: Getting new results\n", - "Campaign 25 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196, 0.92]\n", - "Campaign 25 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 25 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 26\n", - "Campaign 26 state: Getting new results\n", - "Campaign 26 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196, 0.92, 0.9207]\n", - "Campaign 26 state: Agent DecisionBoundary hypothesizing\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Campaign 26 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 27\n", - "Campaign 27 state: Getting new results\n", - "Campaign 27 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196, 0.92, 0.9207, 0.9198]\n", - "Campaign 27 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 27 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 28\n", - "Campaign 28 state: Getting new results\n", - "Campaign 28 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196, 0.92, 0.9207, 0.9198, 0.9198]\n", - "Campaign 28 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 28 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 29\n", - "Campaign 29 state: Getting new results\n", - "Campaign 29 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196, 0.92, 0.9207, 0.9198, 0.9198, 0.9202]\n", - "Campaign 29 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 29 state: Running experiments\n", - " Waiting for next round ...\n", - "Iteration: 30\n", - "Campaign 30 state: Getting new results\n", - "Campaign 30 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196, 0.92, 0.9207, 0.9198, 0.9198, 0.9202, 0.92]\n", - "Campaign 30 state: Agent DecisionBoundary hypothesizing\n", - "Campaign 30 state: Running experiments\n", - " Waiting for next round ...\n", - "Campaign 31 state: Getting new results\n", - "Campaign 31 state: Analyzing results\n", - "score [0.8962, 0.9003, 0.9015, 0.9018, 0.9028, 0.9001, 0.9036, 0.9028, 0.9049, 0.9068, 0.9062, 0.9087, 0.9094, 0.9096, 0.9103, 0.9117, 0.9139, 0.9151, 0.9156, 0.9181, 0.918, 0.9195, 0.9203, 0.9208, 0.9196, 0.92, 0.9207, 0.9198, 0.9198, 0.9202, 0.92, 0.9203]\n", - "Finalizing campaign.\n" - ] - } - ], - "source": [ - "new_loop.auto_loop(n_iterations=30, timeout=0)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Text(0.5, 0, 'iteration')" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(new_loop.analyzer.score)\n", - "plt.xlabel('iteration')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Next: Simulations, confidence intervals and baselines" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "How confident are we with this simulation? In the next step, we will run multiple versions of this simulation with different seeds to build a confidence bound around our learning curve. In addition, we will simulate a strong baseline to compare: i.e. random acquisition. Random acquisition is not easy when the goal is to improve prediction accuracy at minimal acquisition cost. \n", - "\n", - "We parallelize this a little bit." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "from joblib import Parallel, delayed" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def entropy_campaign(i):\n", - " loop_dir = os.path.join(path,'mnist-entropy-{}'.format(i))\n", - " os.mkdir(loop_dir)\n", - " loop = Campaign(candidate_data, agent, experiment, analyzer,\n", - " agent_params=agent_params, analyzer_params=analyzer_params, \n", - " experiment_params=experiment_params,path=loop_dir,create_seed=N_seed)\n", - " loop.initialize(random_state=np.random.randint(0,100000))\n", - " loop.auto_loop(n_iterations=30, timeout=0)\n", - " return loop" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "entropy_results = Parallel(n_jobs=-1)(delayed(entropy_campaign)(i) for i in range(8))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's also run similar simulations with a randomly aquiring agent: `RandomAgent`" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "agent_random = RandomAgent\n", - "agent_random_params = {'N_query': N_query}" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def random_campaign(i):\n", - " loop_dir = os.path.join(path,'mnist-random-{}'.format(i))\n", - " os.mkdir(loop_dir)\n", - " loop = Campaign(candidate_data, agent_random, experiment, analyzer,\n", - " agent_params=agent_random_params, analyzer_params=analyzer_params, \n", - " experiment_params=experiment_params,path=loop_dir,create_seed=N_seed)\n", - " loop.initialize(random_state=np.random.randint(0,100000))\n", - " loop.auto_loop(n_iterations=30, timeout=0)\n", - " return loop" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "random_results = Parallel(n_jobs=-1)(delayed(random_campaign)(i) for i in range(8))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Putting our results together." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "merged1 = np.array([i.analyzer.score for i in entropy_results])\n", - "merged2 = np.array([i.analyzer.score for i in random_results])\n", - "mu1 = np.mean(merged1, axis=0)\n", - "mu2 = np.mean(merged2, axis=0)\n", - "std1 = np.std(merged1, axis=0)\n", - "std2 = np.std(merged2, axis=0)\n", - "\n", - "plt.fill_between(range(len(mu1)), mu1+2*std1, mu1-2*std1, alpha=0.2)\n", - "plt.plot(mu1, label='Entropy')\n", - "plt.fill_between(range(len(mu2)), mu2+2*std2, mu2-2*std2, alpha=0.2)\n", - "plt.plot(mu2, label='Random')\n", - "plt.title('MNIST Logistic Regression Seed:5000 Query:500/iter.')\n", - "plt.xlabel('iteration')\n", - "plt.ylabel('score')\n", - "plt.legend()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We find that the accuracy of a Logistic Regression classifier for MNIST data can be improved more quickly with active-learning via entropy-based acquisition, compared to random acquisition of new labels." - ] - } - ], - "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.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/mnist_max_entropy.ipynb b/examples/mnist_max_entropy.ipynb new file mode 100644 index 00000000..530c0e49 --- /dev/null +++ b/examples/mnist_max_entropy.ipynb @@ -0,0 +1,1914 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Active-learning example with MNIST data for digit recognition" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The goal of this notebook is to show how a simple active-learning campaign can be designed for digit recognition using CAMD." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Failed to import pyspglib.Download at: http://sourceforge.net/projects/spglib/ andfollow instructions for installing python API\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "import shutil\n", + "from sklearn.datasets import fetch_openml\n", + "from camd.agent.base import RandomAgent, HypothesisAgent\n", + "from camd.analysis import AnalyzerBase\n", + "from camd.experiment.base import ATFSampler\n", + "from camd.campaigns.base import Campaign\n", + "from scipy.stats import entropy\n", + "from sklearn.datasets import fetch_openml\n", + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.model_selection import cross_val_score, KFold\n", + "from sklearn.exceptions import NotFittedError\n", + "path = os.getcwd()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's fetch the MNIST dataset by LeCun et al. from OpenML" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "X, y = fetch_openml('mnist_784', version=1, return_X_y=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.DataFrame(X)\n", + "df['target'] = y.values\n", + "df = df.astype(int)\n", + "df.index = df.index.map(str)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dataset has 70,000 labeled instances of hand written digits in 28x28 resolution. We'll use 10,000 for testing our active-learning model. Use 60000 for active-learning itself." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(70000, 785)\n" + ] + } + ], + "source": [ + "print(df.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is what a typical example looks like:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(X.values[2, :-1].astype(int).reshape(28,28))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's prepare our data for camd.Campaign" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(60000, 785)\n" + ] + } + ], + "source": [ + "test_df = df.sample(10000, random_state=42) # setting aside 10,000 examples for testing\n", + "learn_df = df.drop(test_df.index, axis=0)\n", + "print(learn_df.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's construct an Agent that tries to choose the next set of examples to be labeled by a human oracle (which corresponds to the Experiment class in camd). Note we have 10 digits (hence classes), this for each example we get 10 probability estimations for belonging to digit classes\n", + "\n", + "Our Agents strategy is to pick the examples where the class assignments are the most undecided, which we quantify by calculating the entropy of the predicted probabilities from 0 to 9. The larger the entropy, the closer the point is to the ``decision boundaries`` of the classifier.\n", + "\n", + "Given the seed data and candidate data, our Agent will simply return a list of N_query number of examples to send for labeling." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "class DecisionBoundary(HypothesisAgent):\n", + " def __init__(self, candidate_data=None, seed_data=None, \n", + " N_query=None, classifier=None):\n", + " self.candidate_data = candidate_data\n", + " self.seed_data = seed_data\n", + " self.N_query = N_query if N_query else 1\n", + " self.cv_score = np.nan\n", + " self.classifier = classifier\n", + " super(DecisionBoundary).__init__()\n", + " \n", + " def get_hypotheses(self, candidate_data, seed_data=None):\n", + " self.candidate_data = candidate_data.drop(columns=['target'], axis=1)\n", + " self.seed_data = seed_data\n", + " X_train = seed_data.drop(columns=['target'], axis=1)\n", + " y_train = seed_data['target']\n", + " \n", + " # CV score from acquired data\n", + " # cv_score = cross_val_score(self.classifier, X_train, y_train,\n", + " # cv=KFold(3, shuffle=True))\n", + " # self.cv_score = np.mean(cv_score)\n", + " \n", + " # Fit the main model, make predictions and request new experiments based on entropy\n", + " self.classifier.fit(X_train, y_train)\n", + " probabs = self.classifier.predict_proba(self.candidate_data)\n", + " self.probabs = probabs\n", + " neg_entropies = [-1*entropy(probabs[i]) for i in range(len(probabs))]\n", + " selected = np.argsort(neg_entropies)[:self.N_query]\n", + " selected = candidate_data.iloc[selected]\n", + " return selected" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We design an `Analyzer` to test the active-learning model against the independent test set and stores the scores. It evaluates the performance of the latest fit classifier on the test data. Note that the analyzer acts on the `Campaign` object, which has both an experiment and agent to conduct iteration and campaign-wide retrospectives." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "class MnistAnalyze(AnalyzerBase):\n", + " def __init__(self, test_df):\n", + " self.score = []\n", + " self.test_df = test_df\n", + " \n", + " def analyze(self, campaign, finalize=False):\n", + " new_results = campaign.experiment.get_results()\n", + " X = new_results.drop(columns=['target'], axis=1)\n", + " y = new_results['target']\n", + " try:\n", + " iteration_score = campaign.agent.classifier.score(X, y)\n", + " overall_score = campaign.agent.classifier.score(\n", + " test_df.drop('target', axis=1), test_df['target']\n", + " )\n", + " except NotFittedError as e:\n", + " iteration_score = None\n", + " overall_score = None\n", + " summary = pd.DataFrame(\n", + " {\"iteration_score\": [iteration_score],\n", + " \"overall_score\": [overall_score]\n", + " }\n", + " )\n", + " return summary" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will now setup our active learning campaign simulation by specifying the hyperparameters of the loop, such as the maximum number of queries allowed in each iteration, number of examples to pick as the seed, `Agent`, `Analyzer` and `Experiment` classes (and their parameters) to be used. Here we are using the `ATFSampler` class as the experiment, which helps perform an after-the-fact simulation by simply looking up the results from the provided dataframe. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "N_query = 500 # This many new examples are asked to oracle in each iteration (labeled by humans in reality)\n", + "N_seed = 2000 # This many samples are randomly acquired in the beginning to form a seed.\n", + "N_iter = 10\n", + "candidate_data = learn_df\n", + "classifier = LogisticRegression(\n", + " C=50./X.shape[1],\n", + " multi_class='multinomial',\n", + " penalty='l1',\n", + " solver='saga',\n", + " tol=0.1\n", + ")\n", + "\n", + "agent = DecisionBoundary(\n", + " N_query=N_query,\n", + " classifier=classifier\n", + ")\n", + "analyzer = MnistAnalyze(\n", + " test_df=test_df\n", + ")\n", + "experiment = ATFSampler(dataframe=df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's construct a `Campaign`, which will execute the entire campaign." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "campaign_path = os.path.join(path, 'mnist-entropy')\n", + "shutil.rmtree(campaign_path, ignore_errors=True)\n", + "os.makedirs(campaign_path)\n", + "campaign = Campaign(\n", + " candidate_data, agent, experiment, analyzer,\n", + " path=campaign_path,\n", + " create_seed=N_seed\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n" + ] + } + ], + "source": [ + "campaign.auto_loop(n_iterations=N_iter, initialize=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iteration_scoreoverall_score
0NaNNaN
10.3340.8861
20.3560.8863
30.4400.8884
40.4400.8903
50.4480.8942
60.5320.8956
70.5660.8948
80.5700.8945
90.5860.8967
100.6100.8981
\n", + "
" + ], + "text/plain": [ + " iteration_score overall_score\n", + "0 NaN NaN\n", + "1 0.334 0.8861\n", + "2 0.356 0.8863\n", + "3 0.440 0.8884\n", + "4 0.440 0.8903\n", + "5 0.448 0.8942\n", + "6 0.532 0.8956\n", + "7 0.566 0.8948\n", + "8 0.570 0.8945\n", + "9 0.586 0.8967\n", + "10 0.610 0.8981" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "campaign.history" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Next: Simulations, confidence intervals and baselines" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "How confident are we with this simulation? In the next step, we will run multiple versions of this simulation with different seeds to build a confidence bound around our learning curve. In addition, we will simulate a strong baseline to compare: i.e. random acquisition. Random acquisition is not easy when the goal is to improve prediction accuracy at minimal acquisition cost. \n", + "\n", + "We parallelize this a little bit." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "from multiprocessing import Pool" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "61814" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.random.randint(1, 100000)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "def entropy_campaign(i):\n", + " loop_dir = os.path.join(path,'mnist-entropy-{}'.format(i))\n", + " shutil.rmtree(loop_dir, ignore_errors=True)\n", + " os.mkdir(loop_dir)\n", + " classifier = LogisticRegression(\n", + " C=50./X.shape[1],\n", + " multi_class='multinomial',\n", + " penalty='l1',\n", + " solver='saga',\n", + " tol=0.1\n", + " )\n", + " agent = DecisionBoundary(\n", + " N_query=N_query,\n", + " classifier=classifier\n", + " )\n", + " loop = Campaign(\n", + " candidate_data, agent, experiment, analyzer,\n", + " path=loop_dir, create_seed=N_seed)\n", + " loop.initialize(random_state=i)\n", + " loop.auto_loop(n_iterations=N_iter, initialize=False)\n", + " return loop.history" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Analyzing results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Iteration: 0\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Getting new results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 0 state: Running experiments\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 0 state: Running experiments\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + " Waiting for next round ...\n", + "Campaign 1 state: Getting new results\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 0 state: Running experiments\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Campaign 2 state: Analyzing results\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 1 state: Running experiments\n", + "Campaign 2 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 2 state: Running experiments\n", + "Campaign 3 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 2 state: Running experiments\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Running experiments\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Running experiments\n", + "Campaign 3 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 3 state: Running experiments\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 3 state: Running experiments\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + " Waiting for next round ...\n", + "Campaign 4 state: Getting new results\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Analyzing results\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Running experiments\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Running experiments\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 10 state: Agent DecisionBoundary hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Running experiments\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Running experiments\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Finalizing campaign.\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n" + ] + } + ], + "source": [ + "with Pool() as pool:\n", + " entropy_results = pool.map(entropy_campaign, range(8))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's also run similar simulations with a randomly aquiring agent: `RandomAgent`" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "agent_random = RandomAgent(n_query=N_query)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "class MnistAnalyzeRandom(AnalyzerBase):\n", + " def __init__(self, test_df):\n", + " self.score = []\n", + " self.test_df = test_df\n", + " \n", + " def analyze(self, campaign, finalize=False):\n", + " new_results = campaign.experiment.get_results()\n", + " seed_data = campaign.seed_data\n", + " if len(seed_data) > 0:\n", + " X = new_results.drop(columns=['target'], axis=1)\n", + " y = new_results['target']\n", + " classifier = LogisticRegression(\n", + " C=50./70000,\n", + " multi_class='multinomial',\n", + " penalty='l1',\n", + " solver='saga',\n", + " tol=0.1\n", + " )\n", + " classifier.fit(seed_data.drop(columns=['target'], axis=1),\n", + " seed_data['target']\n", + " )\n", + " iteration_score = classifier.score(X, y)\n", + " overall_score = classifier.score(\n", + " test_df.drop('target', axis=1), test_df['target']\n", + " ) \n", + " else:\n", + " iteration_score = None\n", + " overall_score = None\n", + " summary = pd.DataFrame(\n", + " {\"iteration_score\": [iteration_score],\n", + " \"overall_score\": [overall_score]\n", + " }\n", + " )\n", + " return summary" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "def random_campaign(i):\n", + " loop_dir = os.path.join(path,'mnist-random-{}'.format(i))\n", + " shutil.rmtree(loop_dir, ignore_errors=True)\n", + " os.mkdir(loop_dir)\n", + " analyzer_random = MnistAnalyzeRandom(test_df=test_df)\n", + " loop = Campaign(candidate_data, agent_random, experiment, analyzer_random,\n", + " create_seed=N_seed, path=loop_dir)\n", + " loop.initialize(random_state=i)\n", + " loop.auto_loop(n_iterations=N_iter)\n", + " return loop.history" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign initialization state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Iteration: 0\n", + "Iteration: 0\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Getting new results\n", + "Iteration: 0\n", + "Campaign 0 state: Getting new results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Analyzing results\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 0 state: Running experiments\n", + "Campaign 1 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + " Waiting for next round ...\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + "Iteration: 1\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Campaign 1 state: Getting new results\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Agent RandomAgent hypothesizing\n", + "Campaign 0 state: Running experiments\n", + "Campaign 0 state: Running experiments\n", + "Campaign 1 state: Analyzing results\n", + " Waiting for next round ...\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 1\n", + "Campaign 1 state: Getting new results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Analyzing results\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 1 state: Running experiments\n", + "Campaign 2 state: Getting new results\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 1 state: Running experiments\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Iteration: 2\n", + " Waiting for next round ...\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Getting new results\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 1 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 1 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 2\n", + "Campaign 2 state: Getting new results\n", + "Campaign 2 state: Analyzing results\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 2 state: Agent RandomAgent hypothesizing\n", + "Campaign 2 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 3\n", + "Campaign 3 state: Getting new results\n", + "Campaign 3 state: Analyzing results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + "Campaign 4 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Analyzing results\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 3 state: Agent RandomAgent hypothesizing\n", + "Campaign 3 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 4\n", + "Campaign 4 state: Getting new results\n", + "Campaign 4 state: Analyzing results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + "Campaign 5 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 4 state: Agent RandomAgent hypothesizing\n", + "Campaign 4 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 5\n", + "Campaign 5 state: Getting new results\n", + "Campaign 5 state: Analyzing results\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + "Campaign 6 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + "Campaign 5 state: Agent RandomAgent hypothesizing\n", + "Campaign 5 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + " Waiting for next round ...\n", + "Iteration: 6\n", + "Campaign 6 state: Getting new results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Analyzing results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + "Campaign 7 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 6 state: Agent RandomAgent hypothesizing\n", + "Campaign 6 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 7\n", + "Campaign 7 state: Getting new results\n", + "Campaign 7 state: Analyzing results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 7 state: Agent RandomAgent hypothesizing\n", + "Campaign 7 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 8\n", + "Campaign 8 state: Getting new results\n", + "Campaign 8 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 8 state: Agent RandomAgent hypothesizing\n", + "Campaign 8 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 9\n", + "Campaign 9 state: Getting new results\n", + "Campaign 9 state: Analyzing results\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + "Campaign 10 state: Analyzing results\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 9 state: Agent RandomAgent hypothesizing\n", + "Campaign 9 state: Running experiments\n", + " Waiting for next round ...\n", + "Iteration: 10\n", + "Campaign 10 state: Getting new results\n", + "Campaign 10 state: Analyzing results\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n", + "Campaign 10 state: Agent RandomAgent hypothesizing\n", + "Campaign 10 state: Running experiments\n", + " Waiting for next round ...\n", + "Finalizing campaign.\n" + ] + } + ], + "source": [ + "with Pool() as pool:\n", + " random_results = pool.map(random_campaign, range(8))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Putting our results together." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEWCAYAAAB8LwAVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAABKTklEQVR4nO2deZhcZZX/P6eW7uqu3pfs6XQCSSAEQiDssgnIJqDOOCyCQXQYfg6gjI7iMoqMC66jIuKCAURGREXFEURFUWQRkhACAQIha2dPJ713dddyfn+8t7orneqkk3T1reo+n+epp6rueu6tW+/3Pee8i6gqhmEYhjGQgN8GGIZhGPmJCYRhGIaRFRMIwzAMIysmEIZhGEZWTCAMwzCMrJhAGIZhGFkxgRgjiMipIrLyAPd9VEQWDrdN+YyIvEdE/uC3HQeDiDwhIh/w2w5j3+Tr8zbmBEJE1opIr4jUDVj+goioiDR63+/xvh+fsc2hIqIZ33f7A4rIJ0VkjYh0iEiTiPzMW77CW9YhIkkRiWV8/2QWG28RkZ8M53Wr6pOqOntf22U7t6qer6r37u85vfvX6V3nRhH5hogE9/c4fqCq96vq23Jx7MGek5FERM4QkVTGc9iRWQkQkRoR+ZX3+60TkSsG7H+Ft7xTRH4tIjVD3TeLLXNE5GERaRWRdhH5s4icOPxXvf94//HM/+vKAesP6j6IyPdF5NqBz5v33zk0t1e3b8acQHisAS5PfxGRI4HSLNvtBD4/lAN6f66rgLNVtQxYADwOoKpHqGqZt/xJ4Pr0d1X94sFdSt4zz7vu04FLgWuG+wQiEhruY+aKvT0nPrAp4zksG1AJuAPoBcYD7wHuFJEjALz37+OuYzzQBXx3KPsOREQOAZ4CXgKmA5OAXwN/zKycDRcH+Kxk/l/7KlnDdB/OBx45AJsGZVj/D6o6pl7AWuDTwPMZy74GfApQoNFbdg/wDWALcLq37FB3y/r2ewL4gPf5O8A3h3D+vn32ss0twE8GWXcxsAJo8Y51eMa6Y4AXgHbg58DPgM97684AmjK2/Tiw0dt2JXAWcB7ugY4DHcCL2WwG/hV41dv3FeCYQWxV4NCM7w8Cd2R8fzuwzLuWp4Gj9udavGvYAtyHq+zcDLwJNHvnqvG2jwA/8Za3AM8D4711VwOrvfOsAd6TsfzvGfac7O3X6r2fPOA3/W9cQdcO/AGoG+Se7PU5ASqBHwGbvd/n80AwY/013r3fBTwGTMtYdw7wmmfjd4C/MsizNvB5GLAu6j0HszKW3Qfc5n3+IvC/GesO8bYv39e+Wc51H/BIluV3An8ZzFbc//hs7/PefvtG3HP4fmA98Dfgd8ANA463HHjn/vxfD/Y+AEcBywc+b56NCnTi/oeXDuH/shb3f1gO9AChfZVFQ3mNVQ/iWaBCRA73Qh6X4QqQgXThHoIvDPGY7xWR/xSRBbkIpYjILOCnwIeBelzN47ciUiQiRcCvcMJW4233zkGOMxu4HjhOVcuBc4G1qvp73PX+TF1taV6Wfd+NE7D3AhU4wWoegu2HAacCq7zv84FFwL8Btbia2MMiUjzEa5ngrZsGXAvcALwD56lMwhWgd3jbLsQVvFO9c10HdItIFPg2cL53H07G/QEH2l6DK1S+7e3/DeB3IlKbsdkVwPuAcUAR8NFBbsW+npN7gASuMjIfeBvwAc+OS4BPAu/C/f5PevcGL2T6EK7yU4crLE/JuIYGEWkRkYaMc40Tka1euOt/vPsBMAtIqOrrGdu+CKRrv0d43wFQ1TfxCsMh7DuQc3AVgIE8CJwqIpFB9stkb799mtOBw3HP+r3AlekVIjIPmIz7Tb8rIt8dsO+XRGSHiDwlImdkLD/Y+3AB7rnaDVU9zfs4z/sf/mxv/5eMXS8HLgSqVDUx8LgHwlgVCHBq/l7cA/oqrraWje8DDSJy/t4Opqo/wT2o5+JqbttE5OPDZy7gQjS/U9U/qmoc5/mU4Aq2E4EQ8G1VjavqQ8BzgxwnCRQDc0QkrKprvYd7KHwA+IqqPq+OVaq6bi/bLxWRTtw9foJ+F/xa4Puq+g9VTaoLb/R41zGUa0kBn1XVHlXtxhX6n1LVJlXtwYnYP3vudhz3pzrUO9cSVW3LOM5cESlR1c2quiLLNVwIvKGq96lqQlV/iqupX5Sxzd2q+rpny4PA0dluxt6eExEZjys0Pqyqnaq6DfgfXAUG7xq/pKqvegXAF4GjRWSat98KVf2F92x8E+ddpc+7XlWrVHW9t+g1z8aJwFuBY3HCB1AGpO9PmlZczTi9vnWQ9fvadyB1OG9pIJuBIK4SsC/29tunucW7p93Aw8AsEZnprbsKVynqVdUPquoHM/b7ODADJyA/wFXIDvHWHex9uJChh5f29n9J821V3eBd47Aw1gXiCpxr9+PBNvIeuP/2XntFXaLpbKAK99D+t4icOxzGekwC+gpjVU0BG3AP7yRgo3r+pseGQexchfNCbsEVUA+IyKQh2jAVVzsdKsfg/iyXAifgXG9wNf+PeLXaFhFp8Y49aYjXsl1VYxnfpwG/yjjWqzghHI/7rR8DHhCRTSLyFU8YOz27rgM2i8jvPE9nILvdd491uPueZkvG5y7vmrOyl+dkGhD2bElfx/dxXkn6Gr+VsW4nIPT//hsyzqEM8vt767eo6iuqmlLVNcDHgH/yVnfgvMNMKnDhs32t39e+A9mBE6mBTMSFWfbpnbL33z5N5r2J4UKWV4pIAFfzvi/bgb0Cud2riNyLCyNe4K0+4PsgIlXAYbhQ0VDY2/9lj2scLsasQHi13jW4H/uhfWx+N+7P/K4hHjuuqj/HxQPnHoSZA9mEe1AAEBHBPSQbcTWuyd6yNFP3YuP/qupbvOMp8OX0qn3YsAEXax0ynqfxIPAM8JmM43zBq9WmX6Ve7Xwo1zLQzg24UFHm8SKqutH7PT6nqnNw3tbbcd4jqvqYqp6DK5BeA36Y5RJ2u+8eDQzudQ6JLM/JBlytsC7jGipUNR2W2AD824BrLFHVp3H3rO8eZTwbQzaH/vLgdSCUUcMGmIfLfeG994UfRWQGziN9fQj7DuRPwLuzLP8X4FmvgtZJRiMSLyxXn7HtoL/9gOvL5F5c4vgsoEtVnxnEvoEoTpTh4O7DucCfVTU5xPPu7f+SaduwMmYFwuP9wFu9muSgeO78Z3HuZlZE5GoRuVBEykUk4IWkjgD+cYC2BUQkkvEqxoUuLhSRs0QkDHwEV6A8jSt8k8D1IhLy4tVZW4GIyGwReat3zBjQjQu1AGwFGr2aVTbuAj4qIseK41AvxDEUbgP+VUQm4Ari60TkBO840fT9259ryeB7wBfStohIvbcfInKmiBzpFSxtuJBTSkTGi8glXuy9B1frS2U59iO4kMQVnj2XAnOA/xvidfext+dEVTfjEtxfF5EKb/0hInJ6xjV+QvpbE1WKywmBi2UfISLv8kIrN+LyNIPZcaaITPPu/VTcb/MbAO//8BBwq/e7nAJcQn8t+37gInF9a6LArcBDXk17X/sO5HPAySLyBXHNQstF5AZcPiddmXgdiHj3LYzLs2TG3gf97QfDE4QU8PXBbBORKhE51/v/hUTkPcBpwO+H4T5kzT9ksBUX2kqzt/9L7tBhyHQX0ouM1g8DlofYsxXT5zPWB4CXGbwV07tw7ucuXCH0EnB1lvP07bMXG2/xbMl8NXnr3olrOdSKi2EfkbHfAlyStQOX+HsI+C9v3RkZxzgKF9Nvx4Up/g+Y5K2rBf7uXcfSbDbjwiIrvfO8DMwf5Dp2a8XkLXsU+Lr3+Txci6AWXA3450D5/lzLgN/nPzy72nFhsC966y73lnfi/njf9n7vid49bKW/Vdgcb5+r2b0V01uAJd62S4C3DPabDtx3gJ17fU5wyfQ7ca20WnEtuS7LWH+Vt08brla5KGPdebjCdI9WTDiPpwNo8L7/B84D6vKO8+30vffW1+Cam3biWv9cMeA6rvCWd+KEpWao+2a5J3Nxz2AbrmLQgvMIGHBPNwPbcA0A1rJ7K6bBfvtG3HO4R6senNAoMCNj2feA73mf63HPZ7tn07PAOQd7H3AeyBZg3GDPDF7Y0zvvvwzh/9J3P7zvK/Ba5B3MS7yDGaMQEfkH7mG/229bDpbRdC3G4IjIFFxB/FlV/VGOz/Ve4Fp1odYRQ1z/ju+o6rD38xhuxnqIaVQhIqeLyATPHV6I8xR+v6/98pHRdC3G0FHVJlznsYkiMmii/2ARkVLgg7iWSX7wWZ/Ou18UTA9UY0jMxuUporjOX/+sLq5diIymazH2A1V9CRdGywniWow9hEuQ/2+uzjMYqjpY8/O8w0JMhmEYRlYsxGQYhmFkZdSEmOrq6rSxsdFvMwzDMAqKJUuW7FDV+mzrRo1ANDY2snjxYr/NMAzDKChEZNChcizEZBiGYWTFBMIwDMPIigmEYRiGkZVRk4PIRjwep6mpiVgstu+NDSKRCFOmTCEcDvttimEYecCoFoimpibKy8tpbGxk94FBjYGoKs3NzTQ1NTF9+nS/zTEMIw8Y1SGmWCxGbW2ticMQEBFqa2vN2zIMo49RLRCAicN+YPfKMIxMcioQInKeiKwUkVUicnOW9dNE5HERWS4iT3gjOabXLRSRN7zXwlzaaRiGYexJznIQ3uQsd+DmfG4CnheRh1X1lYzNvgb8WFXvFZG3Al8CrhI3SfxncXMCKLDE23dXruzNFcFgkCOPPLLv+2WXXcbNN++hlX088cQTFBUVcfLJJ4+EeYZhFAiqSjypxJMp4skUvckU8aSSSilTa0r3fYADIJdJ6uOBVaq6GkBEHsDNqJQpEHNwE30A/AU3uQa46fj+qKo7vX3/iJssI3N6vYKgpKSEZcuWDXn7J554grKysqwCkUgkCIVGdbsCwxizpFLqFfqpPiHoTaT6liWSSraxVUPB3IWGc1naTGb3SbSbcJPWZ/Iiboatb+FmSisXkdpB9p3MKKKxsZGFCxfy29/+lng8zs9//nMikQjf+973CAaD/OQnP+H222/nRz/6EZFIhBdeeIFTTjmF9773vVx33XV0dXVxyCGHsGjRIqqrqznjjDOYN28ef/3rX0kkEixatIgFCxYwe/Zsnn76aerr60mlUsyaNYtnnnmG+vqsQ68YhpEjEl7B3y8CKeIJpTeZpDehJFP5N7K239XRjwLfEZGrgb/hpkAc6iTeiMi1wLUADQ0Ne932c79dwSub2g7Y0GzMmVTBZy86Yq/bdHd3c/TRR/d9/8QnPsGll14KQF1dHUuXLuW73/0uX/va17jrrru47rrrKCsr46Mf/SgAP/rRj2hqauLpp58mGAxy1FFHcfvtt3P66afzmc98hs997nN885vfBKCrq4tly5bxt7/9jWuuuYaXX36ZK6+8kvvvv58Pf/jD/OlPf2LevHkmDkbBoar0JFLE4klicVezFsF7CQEBwb0jEBBB8N69bSRjefozGdv07RPY/xr5YOGfuOcB9CZSWWv/+U4uBWIjMDXj+xRvWR+qugnnQeDNHvVPqtoiIhtx8w5n7vvEwBOo6g/wZoRasGBBXt7+vYWY3vWudwFw7LHH8tBDDw16jHe/+90Eg0FaW1tpaWnh9NPdHPYLFy7k3e9+d992l19+OQCnnXYabW1ttLS0cM0113DJJZfw4Q9/mEWLFvG+971vmK7MMHJDbyJFLJEkFk/SE3ei0DPCBWy6Qd9u4iE4AfKESERQdR7BYOGfQieXAvE8MFNEpuOE4TLcBN99iEgdsFNVU8AngEXeqseAL4pItff9bd76A2ZfNX0/KC4uBlwiO5FIDLpdNBod0vEGNlMVEaZOncr48eP585//zHPPPcf9999/4AYbxjCSTKnnESSJ9XkHSVIpvy2jr7BP9pX6o7D0HwI5a+aqqgngelxh/yrwoKquEJFbReRib7MzgJUi8jowHviCt+9O4L9xIvM8cGs6YT3aKS8vp729Peu6yspKqqurefLJJwG47777+rwJgJ/97GcA/P3vf6eyspLKykoAPvCBD3DllVf2eSKGMZKoOiFo6eplS2uMtTs6eW1LG69samP19k42tcTY2dFLV09+iIPRT05zEKr6CPDIgGWfyfj8C+AXg+y7iH6PomAZmIM477zzuO222wbd/qKLLuKf//mf+c1vfsPtt9++x/p77723L0k9Y8YM7r777r51kUiE+fPnE4/HWbSo/9ZdfPHFvO9977PwkpFz8iE8ZAwffiepRz3JZPac+9q1a/s+L1iwgCeeeAKAWbNmsXz58r51p5566m77HX300Tz77LNZj3nllVf2JawzefHFF5k3bx6HHXbY/hlvGIOQTCk9CZcw7k6HifIkPDTaUVVau+Nsao2xaVc3m9u6WdBYw78smLrvnfcTE4hRzm233cadd95puQfjoEillLZYnLbuBN3xJL0JU4Jc09mTYFNLNxtbutncGmNj+nNLN529/RXPYEAIiOREIERHie+3YMECHTjl6Kuvvsrhhx/uk0WFid0zI42q0t6ToLUrTmt33MJEOaAnkWRziyv8N7V2s6mlm00tMTa1dNPSHe/bToD68mImVZUwuaqESVURJlWVMKmyhMnVJcydXHnANojIElVdkG2deRCGYfShqnT2uoRya3fcQkbDQCKZYmtbD5tanQewyXttbImxo6Nnt22rS8NMqirhuOk1nhCUMKkywsTKEopC2dsUBQ+g38ZQMYEwDIPu3iQt3b20dMVJJM1V2F9Squxo72GTFwrqF4FutrbFyOwkHS0OMqWqlLmTK5wIVHpCUBWhtCi/iuT8ssYwjBEjFk/S2h2npStuOYUhoqpsbe9h9fYOVm/vZP3OLjZ5OYLeZP89LA4FmFRVwoz6Mk6dWc+kygiTq0qYWFVCRSRUMEPrm0AYxhiiN5GitTtOa3cv3b0mCnsjmVKadnWxekcnb27rYPWOTlbv6KCzxyWIAwITK0uYUl3C/IZqJleVMNnLDdREiwpGBPaGCUSOSQ/3nUgkmD59Ovfddx9VVVUHfdx77rmHxYsX853vfOfgjTRGNYmkE4WW7jhdPUMe6mxM0ZNIsq65izc9z2D1jg7W7ujq8wqKggGm1ZZy6qH1zKiPckh9GdNqSykO5UHH0xy2HjCByDGZYzEtXLiQO+64g0996lP+GmWMepIppT3mwkcdPQlrgZRBR0+CNds7eHNHZ1+oaMOurr48QbQoyIz6Ms6fO4EZ9WUcUh9lSnVpTpPB+00qSbC3jWBvK2HtgUnH5+Q0JhAjyEknndTXCe65557jQx/6ELFYjJKSEu6++25mz57NPffcw8MPP0xXVxdvvvkm73znO/nKV74CwN13382XvvQlqqqqmDdvXt9YTmvXruWaa65hx44d1NfXc/fdd9PQ0MDVV19NSUkJL7zwAtu2bWPRokX8+Mc/5plnnuGEE07gnnvu8etWGDlAVWmLuWapbTFrlgqws7PX8wo6eNPzDLa29bccqiktYkZ9lBNm1DKjznkG4yuK8zM8lIoT7Gkj2NtGIN5B3/hQoaKcnXLsCMSjN8OWl4b3mBOOhPMHHzYjk2QyyeOPP8773/9+AA477DCefPJJQqEQf/rTn/jkJz/JL3/5SwCWLVvGCy+8QHFxMbNnz+aGG24gFArx2c9+liVLllBZWcmZZ57J/PnzAbjhhhtYuHAhCxcuZNGiRdx44438+te/BmDXrl0888wzPPzww1x88cU89dRT3HXXXRx33HEsW7Zst2FAjMJDVenoSXh5hbHbLDWlypbWmMsTZIhBS1d/X4KJlREOrS/j3DnOM5hRH6W6NHeF63AgiZjnKbQhiS7ATRBUXBSkKBSgOBSgqKg4Z+cfOwLhE+mxmDZu3Mjhhx/OOeecA0BraysLFy7kjTfeQESIx/sf5LPOOqtvoL05c+awbt06duzYwRlnnNE3l8Oll17K66+/DsAzzzzTN1z4VVddxcc+9rG+Y1100UWICEceeSTjx4/vm/70iCOOYO3atSYQBUpXb4IWrwPbWGuWmkwp63d2ufDQjs6+vEF3vD953FBTyjEN1RxSH2VGXRnT66JEiwujuJN4F8HeNoribRRL3IlASZCiUAlFwSDBgd0hAjkbc3UMCcQQa/rDTToH0dXVxbnnnssdd9zBjTfeyH/9139x5pln8qtf/Yq1a9dyxhln9O2TDh3BvocC3xfpYwUCgd2OGwgEDuq4xsgzVpulJlPKmh2dvLSxhZc2trJiUxtd3lATRaEA02ujnDG7nkPqy5hRF2VabXTQTmX5iKCUEKM01U4k2UGxJAlHA4QDIfwuoseOQPhMaWkp3/72t3nHO97BBz/4QVpbW5k82c2iOpRcwAknnMCHPvQhmpubqaio4Oc//znz5s0D4OSTT+aBBx7gqquu4v77799jgD/DX1TddJIpdaGQVOb3lPdd3YQzSe97KkXGciWRUuKJseEp7E0QJleVcOrMeo6YVMGh9WVMqirJr+TxXhBx/SMi4SDFQShOdRFJtlMcbwdNuskXQgB50DLKwwRiBJk/fz5HHXUUP/3pT/nYxz7GwoUL+fznP8+FF164z30nTpzILbfcwkknnURVVdVuoaHbb7+d973vfXz1q1/tS1IbB096mstkyiuoU24CGVeAuwI+2ffZ+55yBXoyo5C3ZPHeGYogHDm5krmTKqgty128fTgJh4RIKEgkHCQS9kQhkEJ62iG2HbraQfPfC7TB+ozdGIv3LC0EPfFU3xDWPQmbxyBX7EsQ5k6uLBhBCAakTwDSYlAcCvZ7Nck4xFqhuwV6M1oeDSeBMEyYe8C722B9hsHuQhBLJPveC3VC+UJhtHkIIlBZEqYmWpQ98R2PQWerE4Z458gbOIyYQBijDhMCfxltgpCmKBSgOhqmprSI0MCmRL2dThBirZCI+WNgDhj1AqGq+dnpJQ8ptHBjKqX0JvuntUy/mxCMLKNVENKUR0LUlBVRXjxgkL2eDuje5UQhFR/8AAXMqBaISCRCc3MztbW1JhL7QFVpbm4mEon4bcoeDCYEPfH8T/KNRgYKwiub2vpmOBsNggAut1ATLaImWrR7k9l4txOF7l2Q7PXPwBFiVAvElClTaGpqYvv27X6bUhBEIhGmTJnitxnE4knaYnG6e13CeCy1+c9HunuTrNrWzmtb2nl1S9segvCWUSAIaUqLg9RGi6gsCfdXKhO9/aKQ6PbXwBFmVAtEOBxm+vTpfpth7INUSunsTdAWS9Aei4+Z9v75SEqVTS3dvLalnZVb2lm5tZ11zZ19A9mNNkEAl3SujhZRGy0iEvb6ICQTEGtxotDb4at9fjKqBcLIX3oTKTp6ErR122ijftLRk+B1Twhe29LO61vb6ehxPeyjRUFmjS/nhAVTmT2hnFnjyqkoCfts8fARCQeoiRZRVVrkmqWmUtC104lCTzs5aZJaYJhAGCOCqtIdT9Iec6IQs/zBiJMew8h5Bm2s3NLOhl0uZCLAtNpSTjmkltkTypk9oYIp1SUERlnubo8mqqrQ09afbC6AzmsjiQmEkTOSKaUjlqAtFqc9liCZshrZSNLaHWflljYXLtrazhtbO/oGtKuIhJg9oZzTZ4/jsPHlzBxflnfzIQ8n4ZBLOleXFhEOBlwLpJZdLoyUsjHJBmP0PhGGL8TSXoKXZLbQ0ciQSKZYs6OTlVv7cwebW117/GBAmF4b5a2HjWP2hHIOm1DOhIrImGjZt1sT1UQMOreMrhZIsRZoaYJED0w5dtgPbwJhHBTpBHNaFCzBPDI0d/T0eQYrt7SzaltH3/SYNaVFzJ5QznlHTGD2hHIOqS/rT76OAdJNVKujYYpJQvdOaCvwFkg97bBrDexcC7vSrzVO7AAOvwgu/cmwn9YEwthv4skU7V6Lo/aYJZhzSW8ixc6uXna097BqWwevbW1n5ZY2dnS4GnAoIBw6rowLjpzA7AkVzB5fTl1Z0ZjwDgZSUuQ1US0WAj2tLoRUaC2Qejv7C/9MMeja0b9NuASqG6HhJPdeeygctu8BPw8EEwificWTdPQk6Igl6I4nCYgQDHgvEYJB7z2w+ysUEALived4uOPMBHN7LE53ryXyDpZYPMnOzl52dfWys7O373NzZy+7OnvZ2RVnZ2cPnT3J3fYbV17MnImVfaGi6XVRF1Mfo4hAVWmY2tIwJcl26N4KrQXQAineBbvW9YvBrrVOEDq39W8TikD1NBc6qp7uxKC6EcrGuwtPEwhD+YScmGkCMcIkkq55Z3ssQWdvIktIZv8fbBGcWAT7RSPoCcdAIQkFdhegwcTFEswHRldvwhX2GYX8zs74HkKQHooik1CgP5E6paqEoyZXuvb5pa5H7/S6KNXR/J4ic6QoDgeoKQ1THYwR7NkGO/O0BVIiliEEa/vFoH1L/zbBMFRNg4nzoKaxXwzKJ4D4K/4mEDkmHaPv7EnS0ZOb2rd68xIcSCEuwu7eiQhJVUswZ6CqdPYmMwr+3Qv7zM/Zmu8WhQLUlBZRHS2isS7KMaVVfR2zqr3Cv7q0iPJIaEyGhoZEKk5JIEVFkVJRBBF6obMlf1ogJXqgZX2GEKx1YtC2mb5KXyAMVVNh3ByYfWG/GJRPhEB+5ohyKhAich7wLdwUSXep6m0D1jcA9wJV3jY3q+ojIlIEfB9YAKSAD6nqE7m0dThJt+Tp6EnQmeedwFQhkdQxN6/xYMS91kCvb3Wdxt7Y1sG2tp6+BHAmkXB/wX/ouDJq0oW9N4ZPel20KGgF/2CoIqleJJVAknEk5V6k4gRScSLBJGVh12mvOBCABO7ls83sXA0b/gHbXnVi0Lax34ORoBOCulkw821OBGoaoWIyBAqrTp4za0UkCNwBnAM0Ac+LyMOq+krGZp8GHlTVO0VkDvAI0Aj8K4CqHiki44BHReQ41Xz0IV2h0uEJgoVjCoeUKhtbunljazuvb+3g9a3trNnRScL7/apLw8wcV85xjTV9hX1/wR8e1f0GhoVUsq/Al1QiuxDonqV9JBykrDhEWXGIcDBPem73dkDTEicKTc9Bp5c0rpzqBOCQM70cwXSonOLCRqOAXD7hxwOrVHU1gIg8AFwCZAqEAhXe50pgk/d5DvBnAFXdJiItOG/iuRzaO2RSKaWjN9EnCjaqaGHQ3NHD69s6PEFw3kE6F1ASDjJzXBmXHD2ZWePLmDW+nNro2GwNNCRSif7CP6PAzxQCdM88SzaEflGIRkKE82GOaVXY+aYThA3PwZaX3fWEoy5pPPUEmHo8ROv9tjSn5FIgJgMbMr43AScM2OYW4A8icgMQBc72lr8IXCwiPwWmAsd677sJhIhcC1wL0NDQMMzm95NuxZMWhC6Lz+c9nT0JVm1zXsHr25yHsLPTNQ1Ndxw7fVY9s8aXM2t8OZOrSvqniTT2JJUk2LOLUKwZSfZysK2EBCgtcl5CaXGQUD7c+5522LgE1nteQlezW157CMy71InC+CMKLkx0MPh9pZcD96jq10XkJOA+EZkLLAIOBxYD64CngT2qI6r6A+AH4OakHk7D0oPJdcQStPfESZmTkLek8wZ9oaJt7TTt6u8UNakywlGTK5k5vpxZ48uYUVe2+xj/xuCk4oS6mwnFmofsEQyGAFEvdFRaFML31rmq0LzK8xL+AVtXuDxCURSmHOcEYcpxEK3z2VD/yKVAbMTV+tNM8ZZl8n7gPABVfUZEIkCdqm4DbkpvJCJPA6/n0FbXrLMn0ScKNgdBfpIejvr1rV6oaFs7q7f35w2qSsPMGlfOGbPqmTm+nJnjyiiPjI548EgiiRih7u0Ee1o4GG8hIE4UosUhouEQAb9Foacdmhb3h466d7rltTNh3hXQcLxrZTSGvIS9kcu78DwwU0Sm44ThMuCKAdusB84C7hGRw4EIsF1ESgFR1U4ROQdIDEhuDys7O3vZ1NJtYaM8ZGdn724tit7Y2t43WU1JOMih48q45OhJzBznQkVjtRfxcBHo7SDUvZ1AvP2AjxEUIVocJBpxouDrz6Ep5yWs97yEba94XkKZ5yUc716ltT4amb/kTCBUNSEi1wOP4ZqwLlLVFSJyK7BYVR8GPgL8UERuwlVTrlZV9VouPSYiKZy4XJUrO8F5DyYO+cHOzl6Wrt/F0vW7eHVz/5ASwYDQWFvKabPqmTXOjT46pbrU8gbDgSrBnlZC3duR5IGNVxQKSF/4qCQc9FcUYm2wMdNL8MYrqpsFR1/hQkfjDjcvYQjk9A6p6iO4pquZyz6T8fkV4JQs+60FZufSNiM/iCdTvLq5jaXrW1i6fhdrdnQCronpkZOrmD2hjFnjypleH6U4lJ+diQqWVJJQbCfBWDOS2v/RTUNBcc1Ri0KUFPn422gKdrzRLwhpL6G4HCYvgAYvl2Bewn5jEmqMOFvaYixd57yE5U2tdMeThALCnIkVLDypkWOnVdFYG7VQUY6QZC/BWDOh2M79SjwHBMLBQF/ro0jYx4RCrNXLJTznWhylvYT62TD/Sucl1M82L+Egsbtn5JxYPMnLG1tZsn4XL6xvYWOLC2OMKy/mjNn1HDutmiMnV1rHsxwjiW4v8dxKOvEcEAgFA30DQvaP1RUgEHCho1Ag4I3b5aPxXc2w5SX32rzc9VHQFBRX9Lc4mnoclFT7aOTow/6RxrCjqmzY1c3SdbtYsn4XKza1Ek8qRaEAR02u5IIjJ3JsQzWTqsbGpDUjTTAghIPivQcIxTso6tlBONlJoARCpSV9YpCXt18VWtf3C8KWl6DN60MbLIbxc2D+VS65XH9Y3o5jNBowgTCGhY6eBC9uaOlLMKeTy1NrSrnwyIkc01DNEZMqrf/BAZIu9EPBgKvVB13NPv05HAxkFPriCtnuXdCxDZLd7p8eytO/eyrhcghblvcLQqzVrYtUwoQjYc473HvdTAsbjSB2p40DIqXKm9s6PEFo4bUtbaQUSouCHD21isuOq2Z+QxXjyiN+m1owiLgBAItDQYpCAYpDAe89OPTWWqkkdOyAzu2QiufW4AOlt8slkre85ERh6yuQ7HHrKiZBw4lODCYc5cY6yks3Z2xgAmEMmZauXl7Y0MLSdbt4YUMLrd2uADq0vox/PnYqxzRUMXt8OSHfu8jmLyJu+O+iYIDiAWJwUBP/JHrdZDNdzfk3L0Jm/mDLS65fgqbcXAe1h8Lhb/cE4cjCamkULHIhr7SAiXjzN4i3bJD3rNswhG32dpzcYAJhDEoimWLl1naWrHPJ5VXb3fSNlSVh5k+t4php1cyfWkVVqU1iM5BwSDwRCGZ4Ak4YhjXv0tvlhKG7hbyYRU0VWpt2Dxe1eQMo9OUPrnRiMO4IKCr1196hIgE3UF9RKYRL3XAco2TE1r1hAmHsRktXL/9Ys5Ml63bxYlMLXb1JAgKHTajgyhOncWxDNTPqowTM7ScQgOKQE4BiLxRUHHYikOtpYIm1Qsd26D3wHs/DQioBO1b1h4u2vASxFreuL39wsZc/mFU4+YNQiScGniiEImMy1FUgv5aRS1SVFZvaePTlzTz9ZjOJlFJXVsRbDq3jmIZq5k2toqx4bD4qIvQV/mkvIC0CIx5KS6Vc4rlzm5vK0g/iXS5nkPYOtr3Sb0v5JNcpLR0uqmwojEI1EHIeQaaHYC2jABOIMU1nT4K/rNzGoy9vYf3OLqJFQc6fO4Fz5kygsbZ0TDZBFYGSIm9uguIQpeFg7r2BwUilXPI23u1e3btGPvGcSsD2ldD0vOuYlu6lLAGoOQRmXwATj4Lxcwtk1FPxQkSl/aIQshDpYJhAjEHe3N7Boy9t5q9vbCcWT3FofRk3vPVQTptZTyQ89mpOJUUByorDboC5otDIC4Kqq4XHu93cxon0u09eQttGJwZNi2HTUujtBMT1TJ53OUyc5+ZFKIr6Y9/+ECzePVQULi0MryZPMIEYI/Qkkvz9jR08+vIWVm5tpygU4LSZdZw/dyKzxpf7bd6IEgkH+oagLisOjdyAf2khSMQgHuv/nOjB1wRzTztsesHzEpZAu9cprWw8zDgTpiyASfNdTiGfkaDnFZT2i0LQiriDwe7eKGdTSzePvryZx1/dRntPgslVJXzgLdM567DxlEXGxs9f7AlCWVGIaHEw97kD1d09gXh3fghBmlTChYqaljhR2P6aCxuFS2DSMXDUu90gd5VT8ri2Lc7edIuicCmErc/NcDM2SogxRjKl/GNNM4++vIVlG1oIBoQTZ9RywdwJHDm5ctTnFopCAaLF/XmEg+pfsDf6hCA2IEQUIy+EII2qFzby8gibXnDJZgm4oSrmX+m8hEKYKKe4wo23FKnC/9mHRj95/jQY+0NzRw+PrdjCY69sZWdnL3VlxVx5QgPnzJlATXT0JuL6hp32BGHYh/NQhWRvhieQESLKJyHIJNbm8gdNi50wdGx1y8snwKFnOQ9h8jFuSOx8J1zqRKGkekz0PcgnTCAKnJQqL25o4dGXt/CPNc2owvyGaj54xiEsmFYzKifUCQY8QYi4kFFO5ono7XKx+Z42V9vOt97JA0nGvbCRl0fY/hqgLg4/eb6bKGfysVAxOY/DRhkEwlBa40QhXOK3NWMWE4gCpa07zuOvbeXRl7ewuTVGRSTEO+dP5rwjJjKhcnTFYgMB+rwDNw9BDgQhlewXhFhb/o5jlCY94mk6j7DpBefRSMCFio55rxsGe9xh+R82SiMBFzoqrXFTghaCkI1yCuTJMcB1aFu5tZ1HX9rCk6u2E08qh0+s4IrjGzjl0LrcxdpHGJGBgjDMw1Okicf6BaG3g7wNF6WJtcDGpf1NUDu3ueUVk2DWuf2tjYrKfDVzv7G8Qt5iAlEAdPcmeeL1bfz+5S2s3tFJSTjIOXMmcP4RE2isK4C26EOgpChIecQJQmlRMDeCkEq5oSlibU4Ykvs/zeaI07YZVv8F1vzNdVhDnQBMOqY/uVwxyW8r9x/LKxQEJhB5zLrmTh55eQt/eW0b3fEk0+uifPCMQzh9Vn3Bz77W1/Q0130REj39gtDbkf+5BHAJ5Tf/Aquf8HIJuNZGC97n8giFOpWm5RUKjgJ8ykY38WSKp1a5Dm2vbG4jHBTecmgdF8ydyOwJ5QXbRDUYkD4PISctjdKoerkEL5/gV2/k/aVjG6z+q/MWtr3iltXNguP/DWacARUTfTXvgEnnFUqqXYupAn1+xyomED6jqrR0xVnb3MmLTa386dWttHbHmVgZ4X0nN3LW4eOpLCk8F1yEPg+hPJKjxHKaRK8nCK3uvRC8BIDOHbDmr85b2PqyW1Z7KBz3r3DIGa7FUaFieYVRgQnECBKLJ1m/s4t1zZ2sbe5ibXMn65q7+ibeCQgcP72G8+dO5OipVQU3pHY6jxAtDhHNVR4BnJfQ29mfYE505+Y8uaBrpxOF1U/A5uWAQs0MWHCNG9aiaqrfFh44oZL+EJLlFUYFJhA5IKXKltZYnwCs2dHJuuZONrfG+trJFIUCTKsp5fjGGhrrSplWG2V6bZSKAvIWRiyPAK6df0+7mwehpx00mbtzDTfdLS7JvPovsPlF5+FUTYNjFzpRqJ7mt4UHTiDsBKG0xvIKoxATiIOktTvueQTOK1jniUJPwoU5BJhYGWFabZTTZ9XTWBelsTbK+IpIwXViS+cR0qKQszwCOC8h3uUlmNsh3pm7c+WCWCusfdKFjza94EShcqpreTTjDOc1FCqWVxgzmEAMkd5Eig27nACs2dEvBDu7+ptKVkRCNNZFOfeICUyrLaWxNkpDTWnBDqE9onkEcENZ9HS4pqg9HYXlJYATsrV/d55C0xJnf8UkmHeFyynUHFI4hWkg5LyDQMiNiJr+HCqC4krLK4wRTCAGoKpsa+/ZzSNYu6OTjS3dpLz4UDgoTK0p5eipVbuFh6pKwwXbyiiNCNSVFbthLHKZRwDXUa23wxWsvR1ulNFCo7cD1j7licJidw3lE+Gof4FDzoTamfkjCulCPxh2M6b1fQ55QpDxOV9sNnxlzAvEtvYYv1jcxPKm1r7kcXe8v+Y6vqKYxtooJx1SR6PnFUyqKim48NBQCASgsTZKNFfTiyZ6dvcQ8n04i8Ho7YR1T7tE84bn3HWUjYe5/+RyCvWzR66A7avlD1LQW6FvHAQmEG09fOWxlUSLgzTWRnnrYeOYVlvK9NooDbWlBd8hbaiEQ0JjbXR4w0iJ3t09hELouTwY8S5Y94wnCs+6pHm0Do54h8spjJuTuwJYgu5coUhGoe95AVboGzlkbJR+e2H2hHL+74a3kEppwYeHDpRIOEBjXfTgx3JKtzTq7XAeQrJneAz0i1TCicKqP8H6Z931lNbC4Rc7URh/hEvY5goJQHQcROttZjTDF3L61InIecC3gCBwl6reNmB9A3AvUOVtc7OqPiIiYeAu4BjPxh+r6pdyYWM4GGB8RYQtrQXS43aYiRYHmVYbPbCQWTLRHy7q7SicXsv7ItYCr/0OVvzGDYhXUg2zz3c5hfFzXc09l0jAiUJ0nAmD4Ss5e/pEJAjcAZwDNAHPi8jDqvpKxmafBh5U1TtFZA7wCNAIvBsoVtUjRaQUeEVEfqqqa3Nl71ikqjTMlOqSoXtO6SGx0x5CIXVQGwrNq+Dlh5zHkOx1A+KdciM0nJR7UQAnDKV1Lp9hwmDkAbl8Co8HVqnqagAReQC4BMgUCAUqvM+VwKaM5VERCQElQC/QlkNbxxz15cX7njcilewXg94OF4cfbaQSrhXSiodcJ7ZgsRs6+4h3jlxfhT5hGGc9kI28IpcCMRnYkPG9CThhwDa3AH8QkRuAKHC2t/wXODHZDJQCN6nqzoEnEJFrgWsBGhoahtP2Uc2kqgi1ZcXZVyZ6oKvZiUK8i7yfI+FAibV6YaRfuzBS2Xg44TqYfQFEKva5+/AgLvlcNt6EwchL/PZjLwfuUdWvi8hJwH0iMhfnfSSBSUA18KSI/CntjaRR1R8APwBYsGDBKC3Jhg8RmFpTmn3wv95ON6JorGXE7RpR/A4jASAu2V023nU8M4w8JZcCsRHIHHlsircsk/cD5wGo6jMiEgHqgCuA36tqHNgmIk8BC4DVGAdEMCBMqy3ds49DrM0JQ2+7P4aNBNnCSDPPhbkjGEYCnDDUQNkEEwajIMilQDwPzBSR6ThhuAxX8GeyHjgLuEdEDgciwHZv+VtxHkUUOBH4Zg5tHdXs0cdBFbp3OWEYbYnmTPIijAQmDEahkjOBUNWEiFwPPIZrwrpIVVeIyK3AYlV9GPgI8EMRuQkX7L5aVVVE7gDuFpEVuPHu7lbV5bmydTRTUhRgWq3XxyGVcvmFzm2F3WltXzS/6byFN/7ohZHmwyk3QMPJIxhG8iipgfIJEBok52MYeUxOcxCq+giu6Wrmss9kfH4FOCXLfh24pq7GQVAWCdFQU0pQk9C2FTq3F94AeEMllXDDX7z8S5/DSB4l1c5jCO+jpZhh5DF+J6mNHFFVGmZKeQBpa3Jew2hvjfTKb9xczr6FkTxMGIxRxJAFQkRKgAZVXZlDe4xhYFxJgvHshO0tfpuSO7KFkU6+3p8wErj5Econ2KQ5xqhiSAIhIhcBXwOKgOkicjRwq6penEPbjP0k0NvGpFA71T0FPgbSYKTHRnr5l7B5mRdGehvMfZd/E/BEKt3w3iYMxihkqB7ELbi+CU8AqOoyr3WS4TeqBHtaCMe2MyEK5aFRGDWMtXlhpF/nRxgJnDCUTYCiUn/ObxgjwFBLk7iqtg4Ys2eUBrULhFSSUGwnwVgzIY0zsSpCSYHOXDcoO1e7Tm1v/NGNpDrxaDjpeph2khv22g+KK5zHYMJgjAGG+i9bISJXAEERmQncCDydO7OMQUnFCXU3E4o1gyYJBwNMrCqhOJfzQ480W5bDkh/DxsUQLHJhpCPeCbWH+GdTcYXLMRRF/bPBMEaYoQrEDcCngB7gf3F9Gz6fK6OMPZFEjFD3DoI9u0g7b5FQgAlVJYRHy+x2m5fD0ntg41LXGuj4a+GwC104xy+Kyp0wFJf5Z4Nh+MQ+BcIbtvt3qnomTiSMEUTiXYS7txHo3X0w22hRiAkVkdExd/zm5bDkHtjkCcOJH4Q5F7sZ1PwiWAwVk6Ckyj8bDMNn9ikQqpoUkZSIVKpq60gYZbgWSaHuHQTiHXusK4+EGF8eKfzZJje/6AnDC54w/DvMuchfYZCg8xii9TadpzHmGWqIqQN4SUT+CHSmF6rqjTmxaqzitUgKdW9HktlnZ6uJFlEbLfDxfDYtg6X39gvDSf8Oh/ssDODmZCifaJP1GIbHUP8JD3kvI0dIsoei1rVIKnsfBgHqyyNUlhRw4bVpmfMYNi9zYxTlizAUV7hwkvVlMIzdGFJpo6r3ikgRMMtbtNIbitsYBiQRo7h1DQxySwWYUBWhrKhAxWHTC54wvOgJw/WeMPg8gF0o4oTBzyS4YeQxQ+1JfQZwL7AWV15NFZGFqvq3nFk2RpBEtycOiazrgyKF28chUxhKa+HkG+Cwt/svDBJ0oaRoneUZDGMvDLVK+nXgbelxmERkFvBT4NhcGTYWCMQ7KWpbO+gIq+FggElVEYqCBdRUSdWFkPJRGPqm+JxgeQbDGAJD/ZeEMwfpU9XXRcQm0T0IAr1tFLWtB1JZ10dCQSZWRQgVSh8H1X6PYctyTxhudP0YfBcGvDzDZBtl1TD2g6EKxGIRuQv4iff9PcDi3Jg0+gn0tFDUvoHBRispqD4OewhDXX4JQ6jEyzP4NGaTYRQwQxWI/wf8O26IDYAnge/mxKJRTjC2k3BH06DrKyIhxhVCHwdV17FtyT2w5SUXusknYQiEXJ6htNbyDIZxgAxVIELAt1T1G9DXuzoPSoHCIti9g3Dnpr7vAYFgIEAwIIQCQiQcpLo0zyN3qrBxiROGrS87YTjlQ25k1XwQBsR1ciuf4M+8EIYxihiqQDwOnI3rMAdQAvwBODkXRhUqIi6xnC7wQ0Eh5AlAUfdWQomdBKtLCQaFkEhhVWyzCsOHYfb5eSIMuOaqFZPzxx7DKHCGKhARb55owM0ZLSKjfrxjEfoK+VBACAZkNwEIBoVwhgcQGCyh3LoREs1QiP0Y9hCGenjLTU4YgnnSoztc6vIMxeV+W2IYo4qhllidInKMqi4FEJEFQHfuzBpZyopDTKkucTX7QL8gDFrgDxVVaN3gzQldYKi64baX3ANbV+SnMATCXp6hxvIMhpEDhioQHwJ+LiLpAPpE4NLcmDTylBQFKSka5ni1KuxaC7GW4T1urtlDGMblnzAgUDbOzSxneQbDyBlDFYjpwHygAXgXcAI2o9zgpFKwaw30tO1723yiZT089U03H0N0HLzlP2D2eXkkDECkyssz5JFNhjFKGapA/Jeq/lxEqoAzga8Bd+KEwsgklYSda6C33W9Lhk6iB5b9r3uFilxz1cPfnl/CEI56eQabuMcwRoqhCkR6LIgLgR+q6u9ExGaUG0gyATvfhHiX35YMnabF8Pf/gbaNcOjZcOL/c30H8oVA2AlDaY3flhjGmGOoArFRRL4PnAN8WUSKgULo5ztyJOPQ/CYkCiR339UMz3wX3nwcKqfABV+DKQv8tioD8SbuGUdhdCk3jNHHUAXiX4DzgK+paouITAT+M3dmFRiJHicOyexzOeQVqSS8+lt4/oeQ6IVjr4Z5l+dX34FQBKobbX4Gw/CZoc4H0UXGhEGquhnYnCujCop4DJpXQaoApsfY8QY8+Q3Y/ipMPgZOuQmqpvpt1e6UVEPlVGudZBh5QAH23MojertcziGVfS6HvKG3CxYvghUPud7Gb/00HHJWfvUdkIATBss1GEbeYAJxoPR0wM7Vg87lkBeowpq/wTO3Q2czzLkYjvtA/vU4DpV4ISUbitsw8omcZv9E5DwRWSkiq0Tk5izrG0TkLyLygogsF5ELvOXvEZFlGa+UiBydS1v3i1ib8xzyWRzaNsPvPwF/+qzrO/COO1yHt3wTh9JaqJtl4mAYeUjOPAhvxNc7cC2fmoDnReRhVX0lY7NPAw+q6p0iMgd4BGhU1fuB+73jHAn8WlWX5crW/aJ7F+xaR972E0zG4aUHYcmPXQjpxH+Hue90w1/nExZSMoy8J5elxvHAKlVdDSAiDwCXAJkCoUB6JpdKYBN7cjnwQA7tHDpdO6Flnd9WDM7m5fD3b7ghPhpPg5Ovd0NS5BvhUhdSyqeWU4Zh7EEuBWIysCHjexN79ry+BfiDiNwARHFDig/kUpyw+EvHdmgbfKIfX4m1wLPfh9cfdeMTnftFmJanI7FH691QGfmUIDcMIyt+xx0uB+5R1a+LyEnAfSIyV1VTACJyAtClqi9n21lErgWuBWhoaMidle1boD0PW/Wqwuu/h2fvhN5O15/hmPfmZ/8BCUJVA5RU+W2JYRhDJJcCsRHIbGQ/xVuWyftxHfBQ1WdEJALUAdu89ZcBPx3sBKr6A+AHAAsWLMhNUqB1I3Ru2/d2I83ONW6IjC3LYcKRLgFdM8Nvq7ITjnohpTwa28kwjH2SS4F4HpgpItNxwnAZcMWAbdYDZwH3iMjhQATYDiAiAVwP7lNzaOPgqEJrE3Tt8OX0g5KIwdIfw4s/g6IonPYxN+Kq5OlwFNFxbiwlCykZRsGRM4FQ1YSIXA88BgSBRaq6QkRuBRar6sPAR4AfishNuIT11aqa9gROAzakk9wjiqpLRnfvGvFT75X1z7rhuNu3wKzz4MTrXBPWfCQQciGlSKXflhiGcYBIf3lc2CxYsEAXL1588AfKx7kcOrbBM99xnd6qprlw0qSj/bZqcIrKnJ0WUjKMvEdElqhq1pE6/U5S5xf5NpdDKgErfuWGyUgl4bh/haP+BYJhvy0bnLIJbhRWCykZRsFjApEmmXBDZ8Q7/bbEse1VN7Be8xsw9QQ45UMulp+vBMJeSKli39sahlEQmEBAfs3l0NMOz/8IXvmNG4bi7Ftg+un5XSMvKofqafnt2RiGsd+YQIBLRvstDqrw5p9driHWCnPfBQuucS2V8pnyia5zXj4LmGEYB4QJRD6gKXjqW85rqJ8N53/ZDWCXzwTCrm+DzRFtGKMWEwi/SSXhya/DykfgqMvg+H/N/8lyiitcK6WgPT6GMZqxf7ifpBLwxJdh1R/hmIVu+s+8DtWIS5Tn4wCAhmEMOyYQfpFKwJ8/D6ufcJP4zL/Sb4v2TrDIhZTyPSdiGMawYQLhB8le+NPnYN1TcOIHXd+GfCZS6UJK+R76MgxjWDGBGGkSPfDHz8KGZ13fhiPe6bdFe0GgcgpE6/w2xDAMHzCBGEkSMXjsU7BxKZz6UTj87X5bNDjBYi+kVOq3JYZh+IQJxEjR2wWPfQK2vARnfNwNtpevlFS76UAtpGQYYxoTiJGgtwMe/bgbPuPMT8GhZ/lt0SBYSMkwjH5MIHJNrA0e/U83lMfZt8D00/y2KDvWSskwjAGYQOSSWAv87qOwax2cc2v+zhNtHd8Mw8iClQi5omsn/O4/oG0TnPdFmHKc3xZlx4bnNgxjEEwgckHndvi//3Dv538ZJs3326I9kaAbgdVmfDMMYxBMIIabjq3wfzdBdwtc8BWYcJTfFu1JuNTlG0LFfltiGEYeYwIxnLRtcuLQ2wEXfg3GzfHboj0prYWKKRAI+G2JYRh5jgnEcNGyAX53EyR64e3/k4fDdYvr2xCt9dsQwzAKBBOI4WDXWpdz0BRc9E2omeG3RbtjvaINwzgATCAOluY34Xcfcb2O3/4tl/jNJ4ornDhYr2jDMPYTE4iDYftKeOQ/XbL37f/jeiHnE+UTXRNWwzCMA8AE4kDZugIe/RgUl8OF/wMVE/22qJ9AyHV8i1T4bYlhGAWMCcSBsHk5/P7jUFIDb/8GlI3326J+wlGvCWuR35YYhlHgmEDsLxuXwmOfdNNuXviN/BrYrrTOhbmsV7RhGMOACcT+sOE5+MOnoWIyXPh1KK3x2yKHBFwT1nyxxzCMUYEJxFBZ97SbCa66ES78KkSq/LbIESyGmukQLvHbEsMwRhkmEENh9V/h8Vtd57cLvuIS0/mAzRVtGEYOMYHYF6seh798wQ2bcf6X82S+BIGKSS4PYhiGkSNMIPbGykfhr1+BifPckN3hPOiJHAi7znj54sUYhjFqyemIbSJynoisFJFVInJzlvUNIvIXEXlBRJaLyAUZ644SkWdEZIWIvCQikVzaugev/hb++mWYfCycf1t+iENRGdTPNnEwDGNEyJkHISJB4A7gHKAJeF5EHlbVVzI2+zTwoKreKSJzgEeARhEJAT8BrlLVF0WkFojnytY9ePkhePrb0HAinP25/BgWOzrOhZWsCathGCNELkNMxwOrVHU1gIg8AFwCZAqEAunuvpXAJu/z24DlqvoigKo259DO3XnxAfjH96DxVDjrMxAMj9ipsyIBqGqAkmp/7TAMY8yRyxDTZGBDxvcmb1kmtwBXikgTznu4wVs+C1AReUxElorIx7KdQESuFZHFIrJ4+/btB2/x0vucOMw4E87+rP/iEIpA3WwTB8MwfMHvWWMuB+5R1SnABcB9IhLAeTZvAd7jvb9TRM4auLOq/kBVF6jqgvr6+gO3QhUWL4LFP4KZb4O3ftqNZ+QnkSonDuGRTb0YhmGkyaVAbASmZnyf4i3L5P3AgwCq+gwQAepw3sbfVHWHqnbhvItjcmKlKvztq7D0x3DYhXD6x33uVyBuxrea6Tbrm2EYvpLLEuh5YKaITBeRIuAy4OEB26wHzgIQkcNxArEdeAw4UkRKvYT16eyeuxg+Vj8Bz/8Q5rwDTv2Iv+IQCEPtoVB2EN6QYRjGMJGzOIqqJkTkelxhHwQWqeoKEbkVWKyqDwMfAX4oIjfhEtZXq6oCu0TkGziRUeARVf1dTgw95Ez4p0Wuxu5nC6FgMdTN9D/vYRiG4SGuPC58FixYoIsXLz6wnTu2QdvA6NcIYuJgGIZPiMgSVV2QbZ0Fuf3GxMEwjDzFBMJPTBwMw8hjTCD8wsTBMIw8xwTCD0wcDMMoAEwgRhoTB8MwCgQTiJHExMEwjALCBGKkMHEwDKPAMIEYCUwcDMMoQEwgco2Jg2EYBYoJRC4xcTAMo4AxgcgVJg6GYRQ4JhC5wMTBMIxRgAnEcGPiYBjGKMEEYjgxcTAMYxRhAjFcmDgYhjHKMIEYDkwcDMMYhZhAHCwmDoZhjFJMIA4GEwfDMEYxJhAHiomDYRijHBOIA8HEwTCMMYAJxP5i4mAYxhjBBGJ/MHEwDGMMYQIxVEwcDMMYY5hADAUTB8MwxiAmEPvCxMEwjDGKCcTeMHEwDGMMYwIxGCYOhmGMcUwgsmHiYBiGYQKxByYOhmEYgAnE7pg4GIZh9JFTgRCR80RkpYisEpGbs6xvEJG/iMgLIrJcRC7wljeKSLeILPNe38ulnYCJg2EYxgBCuTqwiASBO4BzgCbgeRF5WFVfydjs08CDqnqniMwBHgEavXVvqurRubJvN4JFJg6GYRgDyKUHcTywSlVXq2ov8ABwyYBtFKjwPlcCm3Joz+CUVJk4GIZhDCCXAjEZ2JDxvclblsktwJUi0oTzHm7IWDfdCz39VUROzXYCEblWRBaLyOLt27cPo+mGYRiG30nqy4F7VHUKcAFwn4gEgM1Ag6rOB/4D+F8RqRi4s6r+QFUXqOqC+vr6ETXcMAxjtJNLgdgITM34PsVblsn7gQcBVPUZIALUqWqPqjZ7y5cAbwKzcmirYRiGMYBcCsTzwEwRmS4iRcBlwMMDtlkPnAUgIofjBGK7iNR7SW5EZAYwE1idQ1sNwzCMAeSsFZOqJkTkeuAxIAgsUtUVInIrsFhVHwY+AvxQRG7CJayvVlUVkdOAW0UkDqSA61R1Z65sNQzDMPZEVNVvG4aFBQsW6OLFi/02wzAMo6AQkSWquiDbOr+T1IZhGEaeYgJhGIZhZMUEwjAMw8jKqMlBiMh2YJ3fdhwkdcAOv43II+x+7I7dj37sXuzOwdyPaaqatSPZqBGI0YCILB4sWTQWsfuxO3Y/+rF7sTu5uh8WYjIMwzCyYgJhGIZhZMUEIr/4gd8G5Bl2P3bH7kc/di92Jyf3w3IQhmEYRlbMgzAMwzCyYgJhGIZhZMUEIg8Qkane3NyviMgKEfmQ3zb5jYgEvQmj/s9vW/xGRKpE5Bci8pqIvCoiJ/ltk5+IyE3e/+RlEfmpiET8tmkkEZFFIrJNRF7OWFYjIn8UkTe89+rhOJcJRH6QAD6iqnOAE4F/9+boHst8CHjVbyPyhG8Bv1fVw4B5jOH7IiKTgRuBBao6FzdS9GX+WjXi3AOcN2DZzcDjqjoTeNz7ftCYQOQBqrpZVZd6n9txBcDA6VnHDCIyBbgQuMtvW/xGRCqB04AfAahqr6q2+GqU/4SAEhEJAaX4NZe9T6jq34CB0x9cAtzrfb4XeMdwnMsEIs8QkUZgPvAPn03xk28CH8PNBTLWmQ5sB+72Qm53iUjUb6P8QlU3Al/DTTa2GWhV1T/4a1VeMF5VN3uftwDjh+OgJhB5hIiUAb8EPqyqbX7b4wci8nZgmzfVrOFqy8cAd3pztHcyTOGDQsSLrV+CE85JQFRErvTXqvxCXd+FYem/YAKRJ4hIGCcO96vqQ37b4yOnABeLyFrgAeCtIvITf03ylSagSVXTHuUvcIIxVjkbWKOq21U1DjwEnOyzTfnAVhGZCOC9bxuOg5pA5AEiIrgY86uq+g2/7fETVf2Eqk5R1UZc8vHPqjpma4iqugXYICKzvUVnAa/4aJLfrAdOFJFS739zFmM4aZ/Bw8BC7/NC4DfDcVATiPzgFOAqXG15mfe6wG+jjLzhBuB+EVkOHA180V9z/MPzpH4BLAVewpVhY2rYDRH5KfAMMFtEmkTk/cBtwDki8gbOy7ptWM5lQ20YhmEY2TAPwjAMw8iKCYRhGIaRFRMIwzAMIysmEIZhGEZWTCAMwzCMrJhAGEYWRORp771RRK4Y5mN/Mtu5DCPfsGauhrEXROQM4KOq+vb92Cekqom9rO9Q1bJhMM8wcop5EIaRBRHp8D7eBpzqdV68yZun4qsi8ryILBeRf/O2P0NEnhSRh/F6OovIr0VkiTd3wbXesttwI5EuE5H7M88ljq968xy8JCKXZhz7iYw5Ie73ehEbRk4J+W2AYeQ5N5PhQXgFfauqHicixcBTIpIeTfQYYK6qrvG+X6OqO0WkBHheRH6pqjeLyPWqenSWc70L11N6HlDn7fM3b9184Ajc0NZP4Xrf/324L9YwMjEPwjD2j7cB7xWRZbgh2WuBmd665zLEAeBGEXkReBaYmrHdYLwF+KmqJlV1K/BX4LiMYzepagpYBjQOw7UYxl4xD8Iw9g8BblDVx3Zb6HIVnQO+nw2cpKpdIvIEcDBTY/ZkfE5i/11jBDAPwjD2TjtQnvH9MeD/ecOzIyKzBpnApxLY5YnDYbipZNPE0/sP4EngUi/PUY+bSe65YbkKwzgArBZiGHtnOZD0QkX34OaHbgSWeoni7WSf3vH3wHUi8iqwEhdmSvMDYLmILFXV92Qs/xVwEvAibsKXj6nqFk9gDGPEsWauhmEYRlYsxGQYhmFkxQTCMAzDyIoJhGEYhpEVEwjDMAwjKyYQhmEYRlZMIAzDMIysmEAYhmEYWfn/jFBmzF9ljMsAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "merged1 = np.array([history.overall_score for history in entropy_results])\n", + "merged2 = np.array([history.overall_score for history in random_results])\n", + "mu1 = np.mean(merged1, axis=0)\n", + "mu2 = np.mean(merged2, axis=0)\n", + "std1 = np.std(merged1, axis=0)\n", + "std2 = np.std(merged2, axis=0)\n", + "\n", + "plt.fill_between(range(len(mu1)), mu1+2*std1, mu1-2*std1, alpha=0.2)\n", + "plt.plot(mu1, label='Entropy')\n", + "plt.fill_between(range(len(mu2)), mu2+2*std2, mu2-2*std2, alpha=0.2)\n", + "plt.plot(mu2, label='Random')\n", + "plt.title('MNIST Logistic Regression Seed:5000 Query:500/iter.')\n", + "plt.xlabel('iteration')\n", + "plt.ylabel('score')\n", + "plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We find that the accuracy of a Logistic Regression classifier for MNIST data can be improved more quickly with active-learning via entropy-based acquisition, compared to random acquisition of new labels." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/requirements.txt b/requirements.txt index 74fd6e41..ab097ad4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ Django==3.1.14 python-dateutil==2.8.2 networkx==2.5.1 -matminer==0.7.4 +matminer==0.7.8 boto3==1.24.12 docopt==0.6.2 taburu==2020.5.9 @@ -16,4 +16,4 @@ spglib==1.16.3 scipy==1.7.3 # Temporary qmpy3 dependency -qmpy-tri>=2021.6.11 +qmpy-tri>=2021.7.21 diff --git a/setup.py b/setup.py index e3cd2680..5697caf1 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( name='camd', url="https://github.com/TRI-AMDD/CAMD", - version="2022.1.24", + version="2022.7.21", packages=find_packages(), description=DESCRIPTION, long_description=LONG_DESCRIPTION, @@ -35,17 +35,19 @@ install_requires=["python-dateutil==2.8.2", "networkx==2.5.1", "matplotlib==3.5.2", - "matminer==0.7.4", + "matminer==0.7.8", "awscli", "boto3==1.24.12", "docopt==0.6.2", "taburu==2020.5.9", "GPy==1.10.0", "watchtower==2.1.1", - "qmpy-tri>=2021.6.11" + "qmpy-tri>=2021.7.21" ], extras_require={ "proto_dft": ["protosearch==2020.5.10"], + "m3gnet": ["m3gnet"], + "atomate": ["atomate"], "tests": ["pytest", "pytest-cov", "coveralls"