From 4eb8837b789ca36591a367911d9bcf436e201d9e Mon Sep 17 00:00:00 2001 From: Yuting Xu <12775874+xuyuting@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:00:07 -0400 Subject: [PATCH 001/136] Update 2_Analysis_and_Visualization.md update docs (in progress) --- docs/wiki/2_Analysis_and_Visualization.md | 29 +++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/docs/wiki/2_Analysis_and_Visualization.md b/docs/wiki/2_Analysis_and_Visualization.md index 5ef53b9..67bb167 100644 --- a/docs/wiki/2_Analysis_and_Visualization.md +++ b/docs/wiki/2_Analysis_and_Visualization.md @@ -22,7 +22,7 @@ To simplify the description, assuming the preferred direction of any response va If there are multiple experimental outcomes ($J \geq 1$), the $j^{th}$ outcome in vector $Y_i$ is denoted as $Y_i^{j}$. -### Evaluating the Measured Experimental Conditions +#### Evaluating the Measured Experimental Conditions A direct assessment of input features is assigning a binary indicator (True/False) to identify the best performer $X_{opt}$ or an opimal solution set $\chi_{opt}$. @@ -58,7 +58,7 @@ For multi-objective optimization, the evaluation is subjective to user preferenc -### The Overall Optimization Performance Metrics +#### The Overall Optimization Performance Metrics To monitor the progress of SMBO workflow, we need to define a scalar evaluation metric to summarize performance over all the $N$ data points. @@ -75,14 +75,29 @@ To monitor the progress of SMBO workflow, we need to define a scalar evaluation --- ## Surrogate Model Interpretation +In this session, we assume the input feature $X$ is a $K$-dimensional vector $(X_1, X_2, ..., X_K)$, and all the model explanation techniques are applied to each surrogate model outcome individually. -### SHAP (SHapley Additive exPlanations) +#### SHAP (SHapley Additive exPlanations) -### Partial Dependence Plot +We use the Kernel SHAP algorithm to estimate the Shapley values, which is a feature attribution method that quantifies the contribution of each feature towards the surrogate model's prediction for any input data, providing insights into variable importance and model explanation. -### Individual Conditional Expectation +The Shapley value is a concept from game theory that aims to fairly allocate the total gains among the players in a coalitional game. In the original definition of the Shapley value, the contribution of each player is the difference in gains when including or excluding this player, averaged over all possible permutations of players. Let $v(S)$ be the gain of any player subset $S$, the Shapley value $\varphi_k(v)$ for the $k^{th}$ player is defined as: -### Sensitivity Analysis +\begin{equation*} +\varphi_k(v) = \frac{1}{K!} \sum_{S \subseteq K \setminus {k}} |S|! \times (K-|S|-1)! \times \big(v(S \cup \{k\}) - v(S)\big) +\end{equation*} + +It can be used to explain the outputs of a machine learning model, where the input features are considered as the players and model prediction is interpreted as the total gains achieved through the collaborative effort of these features. + +Calculating the exact Shapley values is not feasible due to the large number of $2^K$ possible subsets and the need to train a new prediction model for each possible subset of features for obtaining $v(S)$. +The Kernel SHAP algorithm implemented in [SHAP](https://github.com/shap/shap) package provides a model-agnostic and computationally efficient approach to estimate Shapley values. + + +#### Partial Dependence Plot + +#### Individual Conditional Expectation + +#### Sensitivity Analysis @@ -93,7 +108,7 @@ To monitor the progress of SMBO workflow, we need to define a scalar evaluation -### Prediction Uncertainty +#### Prediction Uncertainty (TBA...) From a35ba74fb6950ebd1fcc626e1048798fbaade784 Mon Sep 17 00:00:00 2001 From: Yuting Xu <12775874+xuyuting@users.noreply.github.com> Date: Tue, 13 Aug 2024 01:13:15 -0400 Subject: [PATCH 002/136] Update 2_Analysis_and_Visualization.md First draft in multiple sub-sessions --- docs/wiki/2_Analysis_and_Visualization.md | 33 +++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/wiki/2_Analysis_and_Visualization.md b/docs/wiki/2_Analysis_and_Visualization.md index 67bb167..453bc33 100644 --- a/docs/wiki/2_Analysis_and_Visualization.md +++ b/docs/wiki/2_Analysis_and_Visualization.md @@ -95,11 +95,35 @@ The Kernel SHAP algorithm implemented in [SHAP](https://github.com/shap/shap) pa #### Partial Dependence Plot +Partial Dependence Plot (PDP) is a powerful visualization tool used in the interpretation and explanation of complex machine learning models. +It helps to visualize the relationship between a specific feature and the target variable while holding all other features constant. +PDPs are particularly valuable when working with sophisticated models like deep neural networks, random forests, and gradient boosting machines, which are often considered "black boxes" due to their complexity. +By isolating the effect of a single feature, PDPs can reveal whether the relationship with the target variable is linear, monotonic, or more intricate. +They also have the ability to uncover interactions between features, providing deeper insights into the model's behavior. +One of the key advantages of PDP is their relative ease of computation and interpretation, making them an effective means of communicating model insights to both technical and non-technical audiences. +This versatility has made PDPs an essential technique in the field of explainable AI (XAI), allowing stakeholders to gain trust in and understanding of complex predictive models across various domains. + #### Individual Conditional Expectation -#### Sensitivity Analysis +Individual Conditional Expectation (ICE) plots serve as a powerful complement to Partial Dependence Plots (PDPs), offering a granular, instance-level perspective that reveals how predictions change for individual data points as a feature value varies, thereby uncovering heterogeneous effects and non-linear relationships that might be obscured in the aggregate view provided by PDPs. +While Partial Dependence Plots (PDPs) provide a global view of a feature's impact on model predictions, ICE plots offer a more granular, instance-level perspective. +These plots illustrate how the prediction for a specific data point changes as the value of a particular feature is varied, while keeping all other features constant. +This approach allows us to observe the model's behavior at a local level, providing crucial insights into how the model makes predictions for individual instances. +ICE plots are particularly valuable when dealing with complex, non-linear relationships or when there are significant interactions between features that might be obscured in aggregate visualizations. +By displaying a separate line for each instance in the dataset, ICE plots can reveal heterogeneity in feature effects that might be averaged out in PDPs. +This makes them especially useful for identifying subgroups within the data that may be affected differently by changes in a feature. +#### Sensitivity Analysis + +Sensitivity analysis around the optimal solution, particularly after the suggestions have stabilized over several iterations, serves as a critical step in validating and understanding the robustness of the identified solution. +As the optimization process converges, it's essential to examine how small perturbations in the input variables affect the output, ensuring that the algorithm hasn't fallen into a local optimum or prematurely converged. +This analysis helps quantify the trade-off between exploration and exploitation, a key consideration in Bayesian optimization. +By systematically varying the parameters around the suggested optimal point, we can gauge the stability of the solution and identify any regions of high sensitivity. +This process not only provides insights into the model's behavior but also helps in assessing the reliability of the optimization results. +Moreover, sensitivity analysis can reveal potential areas for further refinement or highlight the need for additional iterations if the optimal point proves to be unstable. +In cases where the analysis indicates a robust optimal solution, it strengthens confidence in the APO outcome and provides valuable information about the parameter space surrounding the optimum. +This understanding is particularly crucial in complex, high-dimensional problems where visualizing the entire optimization landscape may not be feasible. @@ -109,8 +133,13 @@ The Kernel SHAP algorithm implemented in [SHAP](https://github.com/shap/shap) pa #### Prediction Uncertainty -(TBA...) +Adding prediction intervals as an uncertainty metric to suggested candidates is crucial for enhancing both the performance and interpretability of the optimization process. +These intervals provide a quantifiable measure of uncertainty around predicted values, enabling a balanced approach between exploration of uncertain areas and exploitation of promising regions. +This balance is key to avoiding premature convergence to local optima and making more informed decisions about where to sample next. +Furthermore, they greatly enhance the explainability of the process by visually and numerically representing the model's confidence across the parameter space. +This additional context allows for clearer communication of potential risks and rewards associated with different candidate points. +By incorporating prediction intervals, APO becomes a more transparent and interpretable tool, which is crucial for its effective real-world applications where understanding the rationale behind suggestions is as important as the suggestions themselves. From 4d9541a221b8fa13b45bef37faa77b48b3716bf7 Mon Sep 17 00:00:00 2001 From: Yuting Xu <12775874+xuyuting@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:31:20 -0400 Subject: [PATCH 003/136] update wiki in progress... --- .gitignore | 3 - docs/wiki/1_APO_Workflow_and_CodeStructure.md | 4 +- docs/wiki/2_Analysis_and_Visualization.md | 6 +- docs/wiki/3_Data.md | 115 +++++++++++++++++- 4 files changed, 119 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index c2e89ce..a5fae96 100644 --- a/.gitignore +++ b/.gitignore @@ -85,9 +85,6 @@ instance/ # Scrapy stuff: .scrapy -# Sphinx documentation -docs/_build/ - # PyBuilder .pybuilder/ target/ diff --git a/docs/wiki/1_APO_Workflow_and_CodeStructure.md b/docs/wiki/1_APO_Workflow_and_CodeStructure.md index 47f39c2..2b8c3d7 100644 --- a/docs/wiki/1_APO_Workflow_and_CodeStructure.md +++ b/docs/wiki/1_APO_Workflow_and_CodeStructure.md @@ -48,7 +48,9 @@ The data structure in a typical APO work flow includes: See section [Data](3_Data.md) and submodule -[`parameters`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/parameters) +[`parameters`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/parameters), +and submodule +[`objectives`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/objectives) for more details. diff --git a/docs/wiki/2_Analysis_and_Visualization.md b/docs/wiki/2_Analysis_and_Visualization.md index 453bc33..f157d03 100644 --- a/docs/wiki/2_Analysis_and_Visualization.md +++ b/docs/wiki/2_Analysis_and_Visualization.md @@ -3,9 +3,9 @@ ![APO Workflow](https://github.com/MSDLLCpapers/obsidian/blob/main/docs/_static/APO_workflow.png?raw=true) -This section introduces some additional components, such as retrospective analysis and visualization methods, that are not essential steps in a Sequential Model-Based Optimization (SMBO) algorithm but are valuable in real-world applications for several reasons. +This section introduces some additional components, such as retrospective analysis and visualization methods, that are not essential steps in a Algorithmic Process Optimization (APO) algorithm but are valuable in real-world applications for several reasons. -* The technical details involved in SMBO algorithms, such as surrogate models and acquisition functions, may seem complicated to users with non-quantitative background. As a result, the entire workflow of suggesting new experimental conditions may appear to be a black box for users, which hampers the adoption of this this powerful process optimization technique. Various performance metrics and model interpretation methods help to bridge this gap by providing users with a better intuitive understanding of the underlying algorithms and revealing the decision-making processes involved. +* The technical details involved in APO algorithms, such as surrogate models and acquisition functions, may seem complicated to users with non-quantitative background. As a result, the entire workflow of suggesting new experimental conditions may appear to be a black box for users, which hampers the adoption of this this powerful process optimization technique. Various performance metrics and model interpretation methods help to bridge this gap by providing users with a better intuitive understanding of the underlying algorithms and revealing the decision-making processes involved. * The variable importance analysis and/or model interpretation tools can provide critical insights into the optimization process, aiding in a deeper understanding of the variables that influenced the selection of optimal solution and the relationships between input variables, which could be confirmed with additional experiments or scientific domain experts. @@ -60,7 +60,7 @@ For multi-objective optimization, the evaluation is subjective to user preferenc #### The Overall Optimization Performance Metrics -To monitor the progress of SMBO workflow, we need to define a scalar evaluation metric to summarize performance over all the $N$ data points. +To monitor the progress of APO workflow, we need to define a scalar evaluation metric to summarize performance over all the $N$ data points. * Single-objective optimization: The optimal value (either max or min, depends on target specification) of measured experimental outcome. diff --git a/docs/wiki/3_Data.md b/docs/wiki/3_Data.md index c8daa1d..b906f5d 100644 --- a/docs/wiki/3_Data.md +++ b/docs/wiki/3_Data.md @@ -1,3 +1,114 @@ -# Data +# Data Structure -(TBA...) \ No newline at end of file +## Experimental design space $X_{space}$ + + +### Basic Syntax + +Each of the input varible is defined according to the variable type and domain. +Continuous variable is specified by variable name, followed by lower and upper bounds. +> Param_Continuous('varName', lower_bound, upper_bound) + +Discrete varaible is specified by variable name, followed by the (ordered) list of possible values in string format. +> Param_Categorical('varName', ['level 1', 'level 2', 'level 3',...]) + +An example list of input parameter specifications including commonly used variable types: continuous, categorical and ordinal: + +```python +from obsidian.parameters import Param_Continuous, Param_Categorical, Param_Ordinal + +params = [ + Param_Continuous('Temperature', -10, 30), + Param_Continuous('Concentration', 10, 150), + Param_Continuous('Enzyme', 0.01, 0.30), + Param_Categorical('Variant', ['MRK001', 'MRK002', 'MRK003']), + Param_Ordinal('StirRate', ['Low', 'Medium', 'High']), +] +``` +Then the $X_{space}$ is specified as a `ParamSpace` class object, initialized by the list of parameters. + +```python +from obsidian import ParamSpace +X_space = ParamSpace(params) +``` + +The `ParamSpace` class object can be exported into dictionary format to facilite save and reload for future usage: + +```python +X_space_dict = X_space.save_state() +X_space_reload = ParamSpace.load_state(X_space_dict) +``` + +### Additional Variable Types + +* Continuous observatioal variable + + For example, an entire time course was measured during the experiment, and data at all the different timepoints ranging from 0 to 10 are used for fitting. + But during optimization, we are only interested in improving the results for a certain fixed time point at 6. + + ```python + from obsidian.parameters import Param_Discrete_Numeric + Param_Observational(name = 'Time', min = 0, max = 10, design_point = 6) + ``` + + +* Discrete numerical variable + + ```python + from obsidian.parameters import Param_Discrete_Numeric + Param_Discrete_Numeric('LightStage', [1, 2, 3, 4, 5]) + ``` + +* Task variable + + ... + +## Initial experimental conditions, or seed experiments $X_0$ + +When we start the APO workflow from scratch, the initial experimental conditions are usually generated by random sampling or design-of-experiments algorithms. +For example, generate six input conditions $X_0$ according to previously specified $X_{space}$ using Latin hypercube sampling (LHS) method: + +```python +from obsidian.experiment import ExpDesigner + +designer = ExpDesigner(X_space, seed = 0) +X0 = designer.initialize(m_initial = 6, method='LHS') +``` + + +| | Temperature | Concentration | Enzyme | Variant | StirRate | +|---:|--------------:|----------------:|----------:|:----------|:-----------| +| 0 | 13.3333 | 68.3333 | 0.2275 | MRK003 | High | +| 1 | 6.66667 | 115 | 0.0825 | MRK003 | Low | +| 2 | 26.6667 | 45 | 0.0341667 | MRK002 | Medium | +| 3 | 20 | 91.6667 | 0.275833 | MRK001 | Low | +| 4 | -6.66667 | 21.6667 | 0.179167 | MRK002 | Medium | +| 5 | 0 | 138.333 | 0.130833 | MRK001 | High | + + +The `designer` returns experimental conditions as a pandas dataframe, which is the default data format in various `obsidian` functions. + + + +## Experimental outcome variable(s) $Y$ + +... + +```python +from obsidian import Target +target = Target('Yield', aim='max') +``` + +```python +from obsidian import Target +target = [ + Target('Yield', aim='max'), + Target('Cost', aim='min') +] +``` + +## Use campaign object to manage data + +The campaign class object serves as a convinient portal to access all components in APO workflow, including data management. + +... From 574702d816d07c7efbc7dc7df5be7248b5eff8fd Mon Sep 17 00:00:00 2001 From: Yuting Xu <12775874+xuyuting@users.noreply.github.com> Date: Wed, 14 Aug 2024 01:12:55 -0400 Subject: [PATCH 004/136] finish wiki/3_Data.md --- docs/wiki/3_Data.md | 137 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 121 insertions(+), 16 deletions(-) diff --git a/docs/wiki/3_Data.md b/docs/wiki/3_Data.md index b906f5d..08d0798 100644 --- a/docs/wiki/3_Data.md +++ b/docs/wiki/3_Data.md @@ -2,7 +2,6 @@ ## Experimental design space $X_{space}$ - ### Basic Syntax Each of the input varible is defined according to the variable type and domain. @@ -32,13 +31,22 @@ from obsidian import ParamSpace X_space = ParamSpace(params) ``` -The `ParamSpace` class object can be exported into dictionary format to facilite save and reload for future usage: +The `ParamSpace` class object can be exported into dictionary format to facilite save to json files and reload for future usage: ```python -X_space_dict = X_space.save_state() -X_space_reload = ParamSpace.load_state(X_space_dict) +import json + +with open('X_space.json', 'w') as f: + X_space_dict = X_space.save_state() + json.dump(X_space_dict, f) + +with open('X_space.json', 'r') as f: + X_space_dict = json.load(f) + X_space_reload = ParamSpace.load_state(X_space_dict) ``` +In addition, the `ParamSpace` class contains various instance methods for input variable transformation, which are implicitly called during the optimization but no need for direct access by the user. + ### Additional Variable Types * Continuous observatioal variable @@ -51,7 +59,6 @@ X_space_reload = ParamSpace.load_state(X_space_dict) Param_Observational(name = 'Time', min = 0, max = 10, design_point = 6) ``` - * Discrete numerical variable ```python @@ -61,11 +68,18 @@ X_space_reload = ParamSpace.load_state(X_space_dict) * Task variable - ... + Only one special 'task' categorical variable is allowed for encoding multiple tasks. + Distinct response will be predicted for each task. + + ```python + from obsidian.parameters import Task + Task('TaskVar', ['Task_A', 'Task_B', 'Task_C', 'Task_D']) + ``` ## Initial experimental conditions, or seed experiments $X_0$ When we start the APO workflow from scratch, the initial experimental conditions are usually generated by random sampling or design-of-experiments algorithms. + For example, generate six input conditions $X_0$ according to previously specified $X_{space}$ using Latin hypercube sampling (LHS) method: ```python @@ -73,9 +87,9 @@ from obsidian.experiment import ExpDesigner designer = ExpDesigner(X_space, seed = 0) X0 = designer.initialize(m_initial = 6, method='LHS') +print(X0.to_markdown()) ``` - | | Temperature | Concentration | Enzyme | Variant | StirRate | |---:|--------------:|----------------:|----------:|:----------|:-----------| | 0 | 13.3333 | 68.3333 | 0.2275 | MRK003 | High | @@ -92,23 +106,114 @@ The `designer` returns experimental conditions as a pandas dataframe, which is t ## Experimental outcome variable(s) $Y$ -... +### Basic Syntax + +Similar to the `ParamSpace` object for input variables, there is `Target` class object which handles the specification and preprocessing for experimental outcome variables. + +For each outcome measurement, there are three essential arguments to be specified: +* name: Variable name, which is a required input by user +* f_transform: Transformation function for preprocessing the raw response values, to facilitate the numerical computations during optimization. + - 'Identity': (default) No transformation + - 'Standard': Normalization into zero mean and unit standard deviation + - 'Logit_MinMax': Logit transofrmation with the range or scale automatically calculated based on data + - 'Logit_Percentage': Assuming input response is a percentage ranging between 0 to 100, apply logit transofrmation with scale 1/100. +* aim: Either 'max'(default) or 'min', which specifies the desirable direction for improvement. Currently it only handles continuous outcome values. + + +Depend on the number of outcomes, define one `Target` object or a list of multiple objects: ```python from obsidian import Target -target = Target('Yield', aim='max') + +target = Target(name = 'Yield', f_transform = 'Logit_Percentage', aim='max') + +target_multiple = [ + Target(name = 'Yield', f_transform = 'Logit_Percentage', aim='max'), + Target(name = 'Cost', f_transform = 'Standard', aim='min') +] ``` +### Example + +To demonstrate the usage of `Target` class, we simulate a single task experimental outcome $y_0$ using the previously generated $X_0$ and an analytical function 'shifted_parab'. + ```python -from obsidian import Target -target = [ - Target('Yield', aim='max'), - Target('Cost', aim='min') -] +from obsidian.experiment import Simulator +from obsidian.experiment.benchmark import shifted_parab + +simulator = Simulator(X_space, shifted_parab, name='Yield') +y0 = simulator.simulate(X0) +print(y0.to_markdown()) +``` + +| | Yield | +|---:|--------:| +| 0 | 47.8147 | +| 1 | 62.5599 | +| 2 | 60.7972 | +| 3 | 39.1121 | +| 4 | 83.0833 | +| 5 | 52.2631 | + +If manually input $y_0$, it should be a pandas dataframe with the same variable name 'Yield' as specifed in the `target` definition. + +When the 'transform_f' function is called with 'fit=True' during the optimization workflow, the raw response will be saved as an attribute to `target` object +```python +y_transformed = target.transform_f(y0, fit = True) +type(target.f_raw) # torch.Tensor +``` + +The `Target` class object, as well as the input response 'f_raw' (if exists), can be exported into dictionary format to facilite save to json files and reload for future usage: + +```python +import json + +with open('target.json', 'w') as f: + target_dict = target.save_state() + json.dump(target_dict, f) + +with open('target.json', 'r') as f: + target_dict = json.load(f) + target_reload = Target.load_state(target_dict) ``` ## Use campaign object to manage data -The campaign class object serves as a convinient portal to access all components in APO workflow, including data management. +The `Campaign` class object acts as the central hub, seamlessly connecting all components within the APO workflow, including data management, optimizer, and experimental designer. +It is the recommended approach that offers a more streamlined workflow compared to utilizing each component separately. + + + +Here is an example of creating a `Campaign` class object and adding the initial dataset to its 'data' attribute: + +```python +from obsidian.campaign import Campaign + +data_Iter0 = pd.concat([X0, y0], axis=1) +my_campaign = Campaign(X_space, target, seed=0) +my_campaign.add_data(data_Iter0) +``` + +The 'add_data' method will append each new batch of data to one pandas dataframe with incremental integer 'Iteration'. The new data should be a dataframe contains both the input experimental conditions and the target outcomes. + + +There are various ways to retrieve data from `Campaign`: -... +```python +print(my_campaign.data.to_markdown()) +``` + +| Observation ID | Temperature | Concentration | Enzyme | Variant | StirRate | Yield | Iteration | +|-----------------:|--------------:|----------------:|----------:|:----------|:-----------|--------:|------------:| +| 0 | 13.3333 | 68.3333 | 0.2275 | MRK003 | High | 47.4471 | 0 | +| 1 | 6.66667 | 115 | 0.0825 | MRK003 | Low | 61.3989 | 0 | +| 2 | 26.6667 | 45 | 0.0341667 | MRK002 | Medium | 63.6213 | 0 | +| 3 | 20 | 91.6667 | 0.275833 | MRK001 | Low | 43.4116 | 0 | +| 4 | -6.66667 | 21.6667 | 0.179167 | MRK002 | Medium | 84.5542 | 0 | +| 5 | 0 | 138.333 | 0.130833 | MRK001 | High | 51.8577 | 0 | + +and +```python +my_campaign.X +my_campaign.y +``` \ No newline at end of file From 6f2e8836a5a042596be3683136632fa3993a4037 Mon Sep 17 00:00:00 2001 From: Yuting Xu <12775874+xuyuting@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:31:15 -0400 Subject: [PATCH 005/136] fix the demo ipynb files and remove outputs to reduce file sizes. Outputs could be found in gh pages --- demo/Constrained multi objective.ipynb | 51 +- ...int optimization with constant param.ipynb | 32 +- demo/Simple single objective.ipynb | 3417 +---------------- 3 files changed, 82 insertions(+), 3418 deletions(-) diff --git a/demo/Constrained multi objective.ipynb b/demo/Constrained multi objective.ipynb index c83070b..56814c2 100644 --- a/demo/Constrained multi objective.ipynb +++ b/demo/Constrained multi objective.ipynb @@ -87,6 +87,27 @@ "Z0.plot(x='Response 1', y='Response 2', kind='scatter', figsize=(4,3))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define the Target" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from obsidian.parameters import Target\n", + "\n", + "target = [\n", + " Target('Response 1', aim='max'),\n", + " Target('Response 2', aim='min')\n", + "]" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -109,7 +130,7 @@ "metadata": {}, "outputs": [], "source": [ - "my_campaign = Campaign(X_space)\n", + "my_campaign = Campaign(X_space, target)\n", "my_campaign.add_data(Z0)\n", "my_campaign.data" ] @@ -127,22 +148,7 @@ "metadata": {}, "outputs": [], "source": [ - "from obsidian.parameters import Target" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "target = [\n", - " Target('Response 1', aim='max'),\n", - " Target('Response 2', aim='min')\n", - "]\n", - "\n", - "my_campaign.set_target(target)\n", - "my_campaign.fit()\n" + "my_campaign.fit()" ] }, { @@ -168,10 +174,10 @@ "outputs": [], "source": [ "# # X1 + X2 >= 2\n", - "# optim_kwargs = {'m_batch':2, 'acquisition':[{'qNEHVI':{'ref_point':[-350,0]}}], 'ineq_constraints':[InConstraint_Generic(X_space, indices=[0,1], coeff=[1,1], rhs=2)]}\n", + "# optim_kwargs = {'m_batch':2, 'acquisition':[{'NEHVI':{'ref_point':[-350,0]}}], 'ineq_constraints':[InConstraint_Generic(X_space, indices=[0,1], coeff=[1,1], rhs=2)]}\n", "\n", "# X1 + X2 <= 6 aka -X1 - X2 >= -6\n", - "optim_kwargs = {'m_batch':2, 'acquisition':[{'qNEHVI':{'ref_point':[-350,0]}}], 'ineq_constraints':[InConstraint_Generic(X_space, indices=[0,1], coeff=[-1,-1], rhs=-6)]}\n", + "optim_kwargs = {'m_batch':2, 'acquisition':[{'NEHVI':{'ref_point':[-350,0]}}], 'ineq_constraints':[InConstraint_Generic(X_space, indices=[0,1], coeff=[-1,-1], rhs=-6)]}\n", "\n", "X_suggest, eval_suggest = my_campaign.optimizer.suggest(**optim_kwargs)" ] @@ -244,6 +250,13 @@ "my_campaign.data" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, diff --git a/demo/Joint optimization with constant param.ipynb b/demo/Joint optimization with constant param.ipynb index b6b620e..8174785 100644 --- a/demo/Joint optimization with constant param.ipynb +++ b/demo/Joint optimization with constant param.ipynb @@ -88,6 +88,23 @@ "Z0.plot(x='Temperature', y='Yield', kind='scatter', figsize=(4,3))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define the Target" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from obsidian.parameters import Target\n", + "target = Target('Yield', aim='max')" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -110,7 +127,7 @@ "metadata": {}, "outputs": [], "source": [ - "my_campaign = Campaign(X_space)\n", + "my_campaign = Campaign(X_space,target)\n", "my_campaign.add_data(Z0)\n", "my_campaign.data" ] @@ -128,18 +145,7 @@ "metadata": {}, "outputs": [], "source": [ - "from obsidian.parameters import Target" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "target = Target('Yield', aim='max')\n", - "my_campaign.set_target(target)\n", - "my_campaign.fit()\n" + "my_campaign.fit()" ] }, { diff --git a/demo/Simple single objective.ipynb b/demo/Simple single objective.ipynb index dd31bfd..51f2b2c 100644 --- a/demo/Simple single objective.ipynb +++ b/demo/Simple single objective.ipynb @@ -2,17 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "obsidian version: 0.7.10\n" - ] - } - ], + "outputs": [], "source": [ "import obsidian\n", "print(f'obsidian version: ' + obsidian.__version__)\n", @@ -32,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -42,141 +34,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "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", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
TemperatureConcentrationEnzymeVariantStir Rate
08.017.00.1405MRK001Medium
112.0143.00.1695MRK003Medium
24.0101.00.2855MRK002High
328.087.00.1115MRK002Low
4-4.0115.00.2275MRK001Low
5-8.073.00.0825MRK002Medium
620.0129.00.0535MRK001High
724.031.00.2565MRK002Medium
816.059.00.1985MRK003High
90.045.00.0245MRK003Low
\n", - "
" - ], - "text/plain": [ - " Temperature Concentration Enzyme Variant Stir Rate\n", - "0 8.0 17.0 0.1405 MRK001 Medium\n", - "1 12.0 143.0 0.1695 MRK003 Medium\n", - "2 4.0 101.0 0.2855 MRK002 High\n", - "3 28.0 87.0 0.1115 MRK002 Low\n", - "4 -4.0 115.0 0.2275 MRK001 Low\n", - "5 -8.0 73.0 0.0825 MRK002 Medium\n", - "6 20.0 129.0 0.0535 MRK001 High\n", - "7 24.0 31.0 0.2565 MRK002 Medium\n", - "8 16.0 59.0 0.1985 MRK003 High\n", - "9 0.0 45.0 0.0245 MRK003 Low" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "params = [\n", " Param_Continuous('Temperature', -10, 30),\n", @@ -203,127 +63,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "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", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
TemperatureConcentrationEnzymeVariantStir RateYieldIteration
Observation ID
5-8.073.00.0825MRK002Medium86.1963330
24.0101.00.2855MRK002High46.2704220
328.087.00.1115MRK002Low60.2889190
4-4.0115.00.2275MRK001Low63.0824170
112.0143.00.1695MRK003Medium44.2801310
\n", - "
" - ], - "text/plain": [ - " Temperature Concentration Enzyme Variant Stir Rate \\\n", - "Observation ID \n", - "5 -8.0 73.0 0.0825 MRK002 Medium \n", - "2 4.0 101.0 0.2855 MRK002 High \n", - "3 28.0 87.0 0.1115 MRK002 Low \n", - "4 -4.0 115.0 0.2275 MRK001 Low \n", - "1 12.0 143.0 0.1695 MRK003 Medium \n", - "\n", - " Yield Iteration \n", - "Observation ID \n", - "5 86.196333 0 \n", - "2 46.270422 0 \n", - "3 60.288919 0 \n", - "4 63.082417 0 \n", - "1 44.280131 0 " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from obsidian.experiment import Simulator\n", "from obsidian.experiment.benchmark import shifted_parab\n", @@ -338,20 +80,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "from obsidian.plotting import visualize_inputs\n", "\n", @@ -367,808 +98,18 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GP model has been fit to data with a train-score of: 1 for response: Yield\n" - ] - } - ], + "outputs": [], "source": [ "campaign.fit()" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - " \n", - " " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "error_y": { - "array": [ - 0.018810962788350594, - 0.01881091456185402, - 0.01881095275962963, - 0.018810935337775447, - 0.01881094664046401, - 0.018810935584212984, - 0.01881096416672534, - 0.018810903284204983, - 0.018810922088640325, - 0.018810992548722538 - ], - "arrayminus": [ - 0.01881095972854041, - 0.018810911502050942, - 0.01881094969981234, - 0.018810932277965264, - 0.018810943580660933, - 0.0188109325244028, - 0.01881096110691516, - 0.01881090022440901, - 0.018810919028830142, - 0.018810989488898144 - ], - "color": "gray", - "thickness": 0.5 - }, - "marker": { - "color": [ - 6.296058387458202e-13, - 5.7407106676363984e-15, - 1.3157485598457538e-13, - 3.531533078572637e-14, - 1.1309012440804044e-13, - 6.467660058121535e-14, - 1.283618563323006e-13, - 2.8661340807074577e-13, - 4.787149064302353e-13, - 2.981768401867673e-14 - ], - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 15 - }, - "mode": "markers", - "name": "Observations", - "type": "scatter", - "x": [ - 92.33980672490216, - 44.28013139056551, - 46.27042165792395, - 60.288918896712964, - 63.08241717950659, - 86.19633278740932, - 47.62503445818602, - 48.96614297388498, - 49.87657408385417, - 81.22881027324594 - ], - "y": [ - 92.33973345538415, - 44.28013474555937, - 46.270438441709, - 60.28890756699405, - 63.082395965599275, - 86.19635470851127, - 47.62505152110378, - 48.966169188540064, - 49.87660859306999, - 81.22879624681924 - ] - }, - { - "line": { - "color": "black", - "dash": "dot" - }, - "mode": "lines", - "name": "Parity", - "showlegend": false, - "type": "scatter", - "x": [ - 40.25466490051409, - 101.57378739739238 - ], - "y": [ - 40.25466490051409, - 101.57378739739238 - ] - } - ], - "layout": { - "autosize": false, - "height": 400, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "rgb(51,51,51)" - }, - "error_y": { - "color": "rgb(51,51,51)" - }, - "marker": { - "line": { - "color": "rgb(237,237,237)", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "rgb(237,237,237)", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "rgb(51,51,51)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(51,51,51)" - }, - "baxis": { - "endlinecolor": "rgb(51,51,51)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(51,51,51)" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "rgb(237,237,237)" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "rgb(217,217,217)" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "colorscale": { - "sequential": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "sequentialminus": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ] - }, - "colorway": [ - "#F8766D", - "#A3A500", - "#00BF7D", - "#00B0F6", - "#E76BF3" - ], - "font": { - "color": "rgb(51,51,51)" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "rgb(237,237,237)", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "paper_bgcolor": "white", - "plot_bgcolor": "rgb(237,237,237)", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "bgcolor": "rgb(237,237,237)", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "fillcolor": "black", - "line": { - "width": 0 - }, - "opacity": 0.3 - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "bgcolor": "rgb(237,237,237)", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - } - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white" - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white" - } - } - }, - "title": { - "text": "Parity Plot" - }, - "width": 600, - "xaxis": { - "title": { - "text": "Actual Response (Yield)" - } - }, - "yaxis": { - "title": { - "text": "Predicted Response (Yield)" - } - } - } - }, - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "from obsidian.plotting import parity_plot, factor_plot\n", "\n", @@ -1177,1256 +118,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "fill": "toself", - "line": { - "color": "#00857C" - }, - "name": "95% Pred Band", - "opacity": 0.3, - "showlegend": true, - "type": "scatter", - "x": [ - -10, - -9.595959595959597, - -9.191919191919192, - -8.787878787878789, - -8.383838383838386, - -7.979797979797982, - -7.575757575757578, - -7.171717171717175, - -6.767676767676771, - -6.363636363636367, - -5.959595959595964, - -5.55555555555556, - -5.151515151515156, - -4.747474747474753, - -4.34343434343435, - -3.9393939393939448, - -3.5353535353535417, - -3.1313131313131386, - -2.7272727272727337, - -2.3232323232323306, - -1.9191919191919276, - -1.5151515151515227, - -1.1111111111111196, - -0.7070707070707165, - -0.30303030303031164, - 0.10101010101008967, - 0.5050505050504945, - 0.9090909090908994, - 1.3131313131313007, - 1.7171717171717056, - 2.1212121212121104, - 2.5252525252525118, - 2.9292929292929166, - 3.3333333333333215, - 3.737373737373723, - 4.141414141414128, - 4.5454545454545325, - 4.949494949494934, - 5.353535353535339, - 5.757575757575744, - 6.161616161616145, - 6.56565656565655, - 6.969696969696955, - 7.373737373737356, - 7.777777777777761, - 8.181818181818166, - 8.585858585858567, - 8.989898989898972, - 9.393939393939377, - 9.797979797979778, - 10.20202020202018, - 10.606060606060588, - 11.010101010100989, - 11.41414141414139, - 11.818181818181799, - 12.2222222222222, - 12.626262626262601, - 13.03030303030301, - 13.434343434343411, - 13.838383838383812, - 14.242424242424221, - 14.646464646464622, - 15.050505050505024, - 15.454545454545432, - 15.858585858585833, - 16.262626262626235, - 16.666666666666643, - 17.070707070707044, - 17.474747474747446, - 17.878787878787854, - 18.282828282828255, - 18.686868686868657, - 19.090909090909065, - 19.494949494949466, - 19.898989898989868, - 20.303030303030276, - 20.707070707070677, - 21.11111111111108, - 21.515151515151487, - 21.91919191919189, - 22.32323232323229, - 22.727272727272698, - 23.1313131313131, - 23.5353535353535, - 23.93939393939391, - 24.34343434343431, - 24.747474747474712, - 25.15151515151512, - 25.55555555555552, - 25.959595959595923, - 26.36363636363633, - 26.767676767676733, - 27.171717171717134, - 27.575757575757542, - 27.979797979797944, - 28.383838383838345, - 28.787878787878753, - 29.191919191919155, - 29.595959595959556, - 29.999999999999957, - 29.999999999999957, - 29.595959595959556, - 29.191919191919155, - 28.787878787878753, - 28.383838383838345, - 27.979797979797944, - 27.575757575757542, - 27.171717171717134, - 26.767676767676733, - 26.36363636363633, - 25.959595959595923, - 25.55555555555552, - 25.15151515151512, - 24.747474747474712, - 24.34343434343431, - 23.93939393939391, - 23.5353535353535, - 23.1313131313131, - 22.727272727272698, - 22.32323232323229, - 21.91919191919189, - 21.515151515151487, - 21.11111111111108, - 20.707070707070677, - 20.303030303030276, - 19.898989898989868, - 19.494949494949466, - 19.090909090909065, - 18.686868686868657, - 18.282828282828255, - 17.878787878787854, - 17.474747474747446, - 17.070707070707044, - 16.666666666666643, - 16.262626262626235, - 15.858585858585833, - 15.454545454545432, - 15.050505050505024, - 14.646464646464622, - 14.242424242424221, - 13.838383838383812, - 13.434343434343411, - 13.03030303030301, - 12.626262626262601, - 12.2222222222222, - 11.818181818181799, - 11.41414141414139, - 11.010101010100989, - 10.606060606060588, - 10.20202020202018, - 9.797979797979778, - 9.393939393939377, - 8.989898989898972, - 8.585858585858567, - 8.181818181818166, - 7.777777777777761, - 7.373737373737356, - 6.969696969696955, - 6.56565656565655, - 6.161616161616145, - 5.757575757575744, - 5.353535353535339, - 4.949494949494934, - 4.5454545454545325, - 4.141414141414128, - 3.737373737373723, - 3.3333333333333215, - 2.9292929292929166, - 2.5252525252525118, - 2.1212121212121104, - 1.7171717171717056, - 1.3131313131313007, - 0.9090909090908994, - 0.5050505050504945, - 0.10101010101008967, - -0.30303030303031164, - -0.7070707070707165, - -1.1111111111111196, - -1.5151515151515227, - -1.9191919191919276, - -2.3232323232323306, - -2.7272727272727337, - -3.1313131313131386, - -3.5353535353535417, - -3.9393939393939448, - -4.34343434343435, - -4.747474747474753, - -5.151515151515156, - -5.55555555555556, - -5.959595959595964, - -6.363636363636367, - -6.767676767676771, - -7.171717171717175, - -7.575757575757578, - -7.979797979797982, - -8.383838383838386, - -8.787878787878789, - -9.191919191919192, - -9.595959595959597, - -10 - ], - "y": [ - 97.99033166985825, - 97.60527273527329, - 97.22590899996217, - 96.85252088627426, - 96.48536317696069, - 96.12465889729555, - 95.77059302467268, - 95.42330623713447, - 95.08288894321856, - 94.74937585808158, - 94.42274140447233, - 94.10289621843111, - 93.78968502248713, - 93.48288608733418, - 93.182212433715, - 92.88731483232006, - 92.5977865494512, - 92.31316967263845, - 92.03296274735447, - 91.75662937616224, - 91.48360738399349, - 91.21331814158837, - 90.94517566188213, - 90.67859513520942, - 90.41300063972427, - 90.14783184344653, - 89.8825495946724, - 89.61664037076866, - 89.34961961672147, - 89.08103405179305, - 88.81046305492193, - 88.5375192582143, - 88.26184848515032, - 87.98312916840035, - 87.70107137393781, - 87.41541554567704, - 87.12593107003427, - 86.83241474410185, - 86.53468921563406, - 86.23260144850993, - 85.92602125427969, - 85.61483991906327, - 85.2989689455365, - 84.97833892202726, - 84.65289852468771, - 84.32261365418715, - 83.98746670516817, - 83.64745596464387, - 83.30259513439604, - 82.95291297208384, - 82.5984530460644, - 82.23927359971857, - 81.8754475222744, - 81.50706242464608, - 81.13422082058841, - 80.75704041544707, - 80.37565450690822, - 79.99021250436537, - 79.60088057576684, - 79.20784243300602, - 78.81130026900257, - 78.41147586147339, - 78.00861185990922, - 77.60297327330417, - 77.19484917654903, - 76.78455465291967, - 76.37243298847166, - 75.95885813109888, - 75.54423742209553, - 75.12901460074215, - 74.71367307202631, - 74.29873941326845, - 73.88478707611229, - 73.47244021490528, - 73.06237753976559, - 72.65533605148056, - 72.25211446521328, - 71.85357607092904, - 71.46065071217848, - 71.07433549518461, - 70.69569377401999, - 70.3258519057838, - 69.96599324709793, - 69.61734888866768, - 69.28118471856143, - 68.95878458547435, - 68.65142961040503, - 68.36037406291076, - 68.08681864779318, - 67.8318824854453, - 67.59657543875689, - 67.3817726591286, - 67.18819322775443, - 67.0163845335482, - 66.86671359610031, - 66.73936598938833, - 66.634352364072, - 66.55152174366822, - 66.49057995995146, - 66.45111120356633, - 52.66702819667758, - 53.08727517868487, - 53.49685994566989, - 53.895342663834995, - 54.28239745477552, - 54.65783372470225, - 55.021614335542196, - 55.37386855662996, - 55.7148981465827, - 56.04517576524461, - 56.36533575621257, - 56.67615798821764, - 56.978545981180176, - 57.27350096686345, - 57.56209376501758, - 57.845436350724995, - 58.12465476831817, - 58.40086467716356, - 58.67515037693409, - 58.94854773008008, - 59.22203103127031, - 59.49650359626567, - 59.77279166198365, - 60.05164109556811, - 60.333716384885605, - 60.6196014055512, - 60.90980151161315, - 61.20474656336711, - 61.50479457564841, - 61.81023573639286, - 62.121296604403, - 62.43814434541261, - 62.760890906482835, - 63.08959706111415, - 63.42427628225521, - 63.76489841884802, - 64.11139316482898, - 64.46365331867811, - 64.8215378376273, - 65.18487469423137, - 65.55346354483217, - 65.9270782200038, - 66.30546904670443, - 66.68836501092451, - 67.07547576828728, - 67.46649350850672, - 67.86109467795595, - 68.25894156292522, - 68.65968373450968, - 69.06295935453059, - 69.46839634046624, - 69.87561338611467, - 70.28422083363908, - 70.69382139181934, - 71.10401069476237, - 71.51437769509513, - 71.92450488581835, - 72.33396834562562, - 72.74233760368914, - 73.14917532180486, - 73.55403679449198, - 73.95646927134183, - 74.3560111107434, - 74.75219078026996, - 75.1445257266141, - 75.53252114715758, - 75.91566870606539, - 76.29344525015831, - 76.66531159353318, - 77.03071145453706, - 77.38907064354765, - 77.73979661407756, - 78.08227850153202, - 78.41588778168062, - 78.73997968235416, - 79.05389547449555, - 79.35696575005713, - 79.6485147621572, - 79.92786585623314, - 80.19434795993308, - 80.44730302662208, - 80.68609424748011, - 80.91011476765254, - 81.11879657195713, - 81.31161915526503, - 81.48811757078764, - 81.64788946226128, - 81.79060073501073, - 81.91598960235567, - 82.02386884892496, - 82.11412626849514, - 82.18672334694244, - 82.24169235798048, - 82.27913211145747, - 82.2992026373816, - 82.3021191050683, - 82.2881452713049, - 82.25758673058377, - 82.21078420932112, - 82.14810710755556 - ] - }, - { - "line": { - "color": "#00857C" - }, - "mode": "lines", - "name": "Mean", - "type": "scatter", - "x": [ - -10, - -9.595959595959597, - -9.191919191919192, - -8.787878787878789, - -8.383838383838386, - -7.979797979797982, - -7.575757575757578, - -7.171717171717175, - -6.767676767676771, - -6.363636363636367, - -5.959595959595964, - -5.55555555555556, - -5.151515151515156, - -4.747474747474753, - -4.34343434343435, - -3.9393939393939448, - -3.5353535353535417, - -3.1313131313131386, - -2.7272727272727337, - -2.3232323232323306, - -1.9191919191919276, - -1.5151515151515227, - -1.1111111111111196, - -0.7070707070707165, - -0.30303030303031164, - 0.10101010101008967, - 0.5050505050504945, - 0.9090909090908994, - 1.3131313131313007, - 1.7171717171717056, - 2.1212121212121104, - 2.5252525252525118, - 2.9292929292929166, - 3.3333333333333215, - 3.737373737373723, - 4.141414141414128, - 4.5454545454545325, - 4.949494949494934, - 5.353535353535339, - 5.757575757575744, - 6.161616161616145, - 6.56565656565655, - 6.969696969696955, - 7.373737373737356, - 7.777777777777761, - 8.181818181818166, - 8.585858585858567, - 8.989898989898972, - 9.393939393939377, - 9.797979797979778, - 10.20202020202018, - 10.606060606060588, - 11.010101010100989, - 11.41414141414139, - 11.818181818181799, - 12.2222222222222, - 12.626262626262601, - 13.03030303030301, - 13.434343434343411, - 13.838383838383812, - 14.242424242424221, - 14.646464646464622, - 15.050505050505024, - 15.454545454545432, - 15.858585858585833, - 16.262626262626235, - 16.666666666666643, - 17.070707070707044, - 17.474747474747446, - 17.878787878787854, - 18.282828282828255, - 18.686868686868657, - 19.090909090909065, - 19.494949494949466, - 19.898989898989868, - 20.303030303030276, - 20.707070707070677, - 21.11111111111108, - 21.515151515151487, - 21.91919191919189, - 22.32323232323229, - 22.727272727272698, - 23.1313131313131, - 23.5353535353535, - 23.93939393939391, - 24.34343434343431, - 24.747474747474712, - 25.15151515151512, - 25.55555555555552, - 25.959595959595923, - 26.36363636363633, - 26.767676767676733, - 27.171717171717134, - 27.575757575757542, - 27.979797979797944, - 28.383838383838345, - 28.787878787878753, - 29.191919191919155, - 29.595959595959556, - 29.999999999999957 - ], - "y": [ - 90.06921836669864, - 89.90802747917313, - 89.74174689964158, - 89.57033213921747, - 89.3937402260298, - 89.21192987543537, - 89.02486169770847, - 88.83249844718962, - 88.6348053131274, - 88.43175024816736, - 88.22330432682674, - 88.00944212419574, - 87.79014210466802, - 87.56538701130239, - 87.3351642478458, - 87.09946624702515, - 86.85829082017541, - 86.61164148451545, - 86.35952776541193, - 86.10196547180868, - 85.83897694367437, - 85.57059127086745, - 85.29684448325327, - 85.01777971225611, - 84.73344732431514, - 84.4439050269608, - 84.14921794844307, - 83.84945869204945, - 83.5447073664305, - 83.23505159340233, - 82.9205864947981, - 82.60141465997786, - 82.27764609555271, - 81.9493981587409, - 81.61679547553427, - 81.27996984453004, - 80.93906012690154, - 80.59421212254905, - 80.24557843204605, - 79.89331830358563, - 79.53759746378668, - 79.17858793094965, - 78.81646780918729, - 78.45142106179824, - 78.08363726230358, - 77.71331132172068, - 77.34064319089858, - 76.96583753705694, - 76.58910339405841, - 76.21065378636642, - 75.83070532710028, - 75.44947779107218, - 75.06719366417634, - 74.68407767097692, - 74.30035628281972, - 73.91625720924667, - 73.53200887592654, - 73.14783989270933, - 72.76397851576556, - 72.38065210805239, - 71.99808660255357, - 71.61650597284083, - 71.23613171548584, - 70.85718234870228, - 70.47987293131004, - 70.10441460569363, - 69.73101416789504, - 69.35987366736802, - 68.9911900382582, - 68.62515476342304, - 68.26195357178736, - 67.90176616909974, - 67.54476600172919, - 67.19112005282248, - 66.8409886699542, - 66.49452542329489, - 66.15187699331784, - 65.81318308710483, - 65.47857638239503, - 65.14818249861558, - 64.82211999422208, - 64.50050038975279, - 64.18342821604504, - 63.8710010870801, - 63.56330979690406, - 63.26043844002647, - 62.96246455462513, - 62.6694592877848, - 62.3814875818847, - 62.09860838110292, - 61.82087485680072, - 61.54833465020615, - 61.281030130030445, - 61.01899866074261, - 60.76227287278611, - 60.51088091846215, - 60.264846692138775, - 60.02419000249109, - 59.78892670464867, - 59.559068810887815 - ] - } - ], - "layout": { - "autosize": false, - "height": 400, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "rgb(51,51,51)" - }, - "error_y": { - "color": "rgb(51,51,51)" - }, - "marker": { - "line": { - "color": "rgb(237,237,237)", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "rgb(237,237,237)", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "rgb(51,51,51)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(51,51,51)" - }, - "baxis": { - "endlinecolor": "rgb(51,51,51)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(51,51,51)" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "rgb(237,237,237)" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "rgb(217,217,217)" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "colorscale": { - "sequential": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "sequentialminus": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ] - }, - "colorway": [ - "#F8766D", - "#A3A500", - "#00BF7D", - "#00B0F6", - "#E76BF3" - ], - "font": { - "color": "rgb(51,51,51)" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "rgb(237,237,237)", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "paper_bgcolor": "white", - "plot_bgcolor": "rgb(237,237,237)", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "bgcolor": "rgb(237,237,237)", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "fillcolor": "black", - "line": { - "width": 0 - }, - "opacity": 0.3 - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "bgcolor": "rgb(237,237,237)", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - } - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white" - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white" - } - } - }, - "title": { - "text": "Factor Effect Plot for Temperature" - }, - "width": 600, - "xaxis": { - "title": { - "text": "Temperature" - } - }, - "yaxis": { - "title": { - "text": "Yield" - } - } - } - }, - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "factor_plot(campaign.optimizer, feature_id=0)" ] @@ -2440,7 +134,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -2449,111 +143,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "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", - "
TemperatureConcentrationEnzymeVariantStir RateYield (pred)Yield lbYield ubf(Yield)aq Valueaq Value (joint)aq Method
0-10.00000010.00.131014MRK001Low104.65557597.694464111.6166872.349349-0.380345-0.292665NEI
1-10.00000010.00.135985MRK001High101.87053394.441592109.2994742.195901-0.612096-0.292665NEI
2-1.22787910.00.114474MRK002Low101.24326697.004850105.4816842.161341-0.709565-0.292665NEI
\n", - "
" - ], - "text/plain": [ - " Temperature Concentration Enzyme Variant Stir Rate Yield (pred) \\\n", - "0 -10.000000 10.0 0.131014 MRK001 Low 104.655575 \n", - "1 -10.000000 10.0 0.135985 MRK001 High 101.870533 \n", - "2 -1.227879 10.0 0.114474 MRK002 Low 101.243266 \n", - "\n", - " Yield lb Yield ub f(Yield) aq Value aq Value (joint) aq Method \n", - "0 97.694464 111.616687 2.349349 -0.380345 -0.292665 NEI \n", - "1 94.441592 109.299474 2.195901 -0.612096 -0.292665 NEI \n", - "2 97.004850 105.481684 2.161341 -0.709565 -0.292665 NEI " - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "df_suggest = pd.concat([X_suggest, eval_suggest], axis=1)\n", "df_suggest" @@ -2568,184 +160,9 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "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", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
TemperatureConcentrationEnzymeVariantStir RateYieldIterationYield (pred)Yield lbYield ubf(Yield)aq Valueaq Value (joint)aq Method
Observation ID
816.00000059.00.198500MRK003High49.8765740NaNNaNNaNNaNNaNNaNNaN
90.00000045.00.024500MRK003Low81.2288100NaNNaNNaNNaNNaNNaNNaN
10-10.00000010.00.131014MRK001Low92.1968491104.65557597.694464111.6166872.349349-0.380345-0.292665NEI
11-10.00000010.00.135985MRK001High74.4994681101.87053394.441592109.2994742.195901-0.612096-0.292665NEI
12-1.22787910.00.114474MRK002Low95.0004671101.24326697.004850105.4816842.161341-0.709565-0.292665NEI
\n", - "
" - ], - "text/plain": [ - " Temperature Concentration Enzyme Variant Stir Rate \\\n", - "Observation ID \n", - "8 16.000000 59.0 0.198500 MRK003 High \n", - "9 0.000000 45.0 0.024500 MRK003 Low \n", - "10 -10.000000 10.0 0.131014 MRK001 Low \n", - "11 -10.000000 10.0 0.135985 MRK001 High \n", - "12 -1.227879 10.0 0.114474 MRK002 Low \n", - "\n", - " Yield Iteration Yield (pred) Yield lb Yield ub \\\n", - "Observation ID \n", - "8 49.876574 0 NaN NaN NaN \n", - "9 81.228810 0 NaN NaN NaN \n", - "10 92.196849 1 104.655575 97.694464 111.616687 \n", - "11 74.499468 1 101.870533 94.441592 109.299474 \n", - "12 95.000467 1 101.243266 97.004850 105.481684 \n", - "\n", - " f(Yield) aq Value aq Value (joint) aq Method \n", - "Observation ID \n", - "8 NaN NaN NaN NaN \n", - "9 NaN NaN NaN NaN \n", - "10 2.349349 -0.380345 -0.292665 NEI \n", - "11 2.195901 -0.612096 -0.292665 NEI \n", - "12 2.161341 -0.709565 -0.292665 NEI " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "y_iter1 = pd.DataFrame(simulator.simulate(X_suggest), columns = ['Yield'])\n", "Z_iter1 = pd.concat([X_suggest, y_iter1, eval_suggest], axis=1)\n", @@ -2762,19 +179,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GP model has been fit to data with a train-score of: 1 for response: Yield\n", - "GP model has been fit to data with a train-score of: 1 for response: Yield\n", - "GP model has been fit to data with a train-score of: 1 for response: Yield\n" - ] - } - ], + "outputs": [], "source": [ "for iter in range(3):\n", " campaign.fit()\n", @@ -2786,782 +193,20 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "hovertemplate": "Observation ID=%{x}
Yield=%{y}
aq Value=%{marker.color}", - "legendgroup": "", - "marker": { - "color": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - -0.38034538065202295, - -0.6120958124027052, - -0.7095649136708557, - -2.05692078994696, - -3.1516288285054745, - -2.473037637673595, - -2.798014989204011, - -3.57020802379168, - -3.088292727987067, - -2.245755141023286, - -3.426519149943271, - -2.4302932293385533 - ], - "coloraxis": "coloraxis", - "symbol": "circle" - }, - "mode": "markers", - "name": "", - "orientation": "v", - "showlegend": false, - "type": "scatter", - "x": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21 - ], - "xaxis": "x", - "y": [ - 92.33980672490216, - 44.28013139056551, - 46.27042165792395, - 60.288918896712964, - 63.08241717950659, - 86.19633278740932, - 47.62503445818602, - 48.96614297388498, - 49.87657408385417, - 81.22881027324594, - 92.19684872892208, - 74.49946768778311, - 95.0004674747163, - 99.35898338419939, - 91.60949770163496, - 91.31362421740042, - 99.99794882558741, - 92.89284062164445, - 92.23896061375875, - 100.95704001123406, - 92.7739266193329, - 99.65361433177773 - ], - "yaxis": "y" - } - ], - "layout": { - "coloraxis": { - "colorbar": { - "title": { - "text": "aq Value" - } - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "height": 300, - "legend": { - "tracegroupgap": 0 - }, - "margin": { - "t": 60 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "rgb(51,51,51)" - }, - "error_y": { - "color": "rgb(51,51,51)" - }, - "marker": { - "line": { - "color": "rgb(237,237,237)", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "rgb(237,237,237)", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "rgb(51,51,51)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(51,51,51)" - }, - "baxis": { - "endlinecolor": "rgb(51,51,51)", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "rgb(51,51,51)" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - }, - "colorscale": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "rgb(237,237,237)" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "rgb(217,217,217)" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "tickcolor": "rgb(237,237,237)", - "ticklen": 6, - "ticks": "inside" - } - }, - "colorscale": { - "sequential": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ], - "sequentialminus": [ - [ - 0, - "rgb(20,44,66)" - ], - [ - 1, - "rgb(90,179,244)" - ] - ] - }, - "colorway": [ - "#F8766D", - "#A3A500", - "#00BF7D", - "#00B0F6", - "#E76BF3" - ], - "font": { - "color": "rgb(51,51,51)" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "rgb(237,237,237)", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "paper_bgcolor": "white", - "plot_bgcolor": "rgb(237,237,237)", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "bgcolor": "rgb(237,237,237)", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "rgb(237,237,237)", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "fillcolor": "black", - "line": { - "width": 0 - }, - "opacity": 0.3 - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - }, - "bgcolor": "rgb(237,237,237)", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside" - } - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white" - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "showgrid": true, - "tickcolor": "rgb(51,51,51)", - "ticks": "outside", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white" - } - } - }, - "width": 400, - "xaxis": { - "anchor": "y", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Observation ID" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Yield" - } - } - } - }, - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = px.scatter(campaign.data, x=campaign.data.index, y='Yield', color='aq Value')\n", "fig.update_layout(height=300, width=400, template='ggplot2')" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 033b461fe3ca8e002da62ee910650e6089a95743 Mon Sep 17 00:00:00 2001 From: Kevin Stone Date: Wed, 14 Aug 2024 21:01:21 -0400 Subject: [PATCH 006/136] Improved reactivity of readme to light/dark theme --- readme.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index d0ed0a4..d506237 100644 --- a/readme.md +++ b/readme.md @@ -3,15 +3,27 @@ obsidian ReadMe --> -
-

- obsidian logo -

+ + + + + +
+
+
-# obsidian +

obsidian

![Supports Python](https://img.shields.io/badge/Python-3.10-teal) [![License](https://img.shields.io/badge/license-GPLv3-teal.svg)](https://github.com/MSDLLCpapers/obsidian/blob/main/LICENSE) From d5401e3addc0d78e236f7853ab67f1b3bb9f3a20 Mon Sep 17 00:00:00 2001 From: Kevin Stone Date: Wed, 14 Aug 2024 21:36:02 -0400 Subject: [PATCH 007/136] Upgraded settings for parity_plot --- obsidian/plotting/plotly.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/obsidian/plotting/plotly.py b/obsidian/plotting/plotly.py index 8a34307..e3ab0b6 100644 --- a/obsidian/plotting/plotly.py +++ b/obsidian/plotting/plotly.py @@ -55,23 +55,36 @@ def parity_plot(optimizer: Optimizer, y_true = y_true[y_name].values RMSE = ((y_true-y_pred)/y_true)**2 + NRMSE = RMSE/(y_true.max()-y_true.min()) y_min = np.min([y_true.min(), y_pred.min()]) y_max = np.max([y_true.max(), y_pred.max()]) abs_margin = 0.1 y_abs = [y_min/(1+abs_margin), y_max*(1+abs_margin)] + error_y = y_ub - y_pred + error_y_minus = y_pred - y_lb + fig = go.Figure() fig.add_trace(go.Scatter(x=y_true, y=y_pred, - error_y={'array': y_ub - y_pred, - 'arrayminus': y_pred - y_lb, + error_y={'array': [f'{y:.3G}' for y in error_y], + 'arrayminus': [f'{y:.3G}' for y in error_y_minus], 'color': 'gray', 'thickness': 0.5}, mode='markers', name='Observations', - marker={'color': RMSE, 'colorscale': 'Viridis', 'size': 15}, + marker={'color': NRMSE, 'size': 15, + 'cmax': 0.5, 'cmin': 0, + 'colorscale': [[0, obsidian_colors.rich_blue], + [0.5, obsidian_colors.teal], + [1, obsidian_colors.lemon]], + 'colorbar': dict(title=dict(text='NRMSE', font=dict(size=10))) + }, + showlegend=False )) + fig.update_traces(hovertemplate="(%{x:.3G}, %{y:.3G}) +%{error_y.array:.3G}/-%{error_y.arrayminus:.3G}") + fig.add_trace(go.Scatter(x=y_abs, y=y_abs, mode='lines', name='Parity', @@ -81,7 +94,7 @@ def parity_plot(optimizer: Optimizer, fig.update_xaxes(title_text=f'Actual Response ({y_name})') fig.update_yaxes(title_text=f'Predicted Response ({y_name})') fig.update_layout(template='ggplot2', title='Parity Plot', - autosize=False, height=400, width=600) + autosize=False, height=400, width=500) return fig From cee5d623abb59bac44225a65d2889ac9891cef47 Mon Sep 17 00:00:00 2001 From: Kevin Stone Date: Wed, 14 Aug 2024 21:49:08 -0400 Subject: [PATCH 008/136] Fixed hoverdata for non-numeric params --- obsidian/plotting/plotly.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/obsidian/plotting/plotly.py b/obsidian/plotting/plotly.py index e3ab0b6..f8c9cb9 100644 --- a/obsidian/plotting/plotly.py +++ b/obsidian/plotting/plotly.py @@ -3,6 +3,7 @@ from obsidian.campaign import Campaign from obsidian.optimizer import Optimizer from obsidian.exceptions import UnfitError, UnsupportedError +from obsidian.parameters import Param_Continuous from .branding import obsidian_colors import plotly.graph_objects as go @@ -407,8 +408,9 @@ def optim_progress(campaign: Campaign, customdata=campaign.data[X_names], name='Data')) - template = [""+str(name)+": "+" %{customdata["+str(i)+"]:.3G}
" - for i, name in enumerate(X_names)] + template = [""+str(param.name)+": "+" %{customdata["+str(i)+"]" + + (":.3G}"if isinstance(param, Param_Continuous) else "}") + "
" + for i, param in enumerate(campaign.X_space)] fig.update_traces(hovertemplate=''.join(template) + out_names[0] + ": %{x:.3G}
" + out_names[1] + ": %{y:.3G}
") From bfe94d1d626e5263f31b71df5ed3e52a9cfe438d Mon Sep 17 00:00:00 2001 From: Yuting Xu <12775874+xuyuting@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:00:07 -0400 Subject: [PATCH 009/136] enable auto adjust table width for the markdown files in docs/wiki --- docs/_static/mycss.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/_static/mycss.css b/docs/_static/mycss.css index 182d881..cf6433b 100644 --- a/docs/_static/mycss.css +++ b/docs/_static/mycss.css @@ -7,4 +7,8 @@ .bd-page-width { max-width: 100%; /* default is 88rem */ -} \ No newline at end of file +} + +.pst-scrollable-table-container table.table { + width: auto; +} From 94b2b03b01f894535599858f29b2993a2775a050 Mon Sep 17 00:00:00 2001 From: Yuting Xu <12775874+xuyuting@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:23:25 -0400 Subject: [PATCH 010/136] template of surrogate model and acquisition function user guide, to be modified --- docs/wiki/4_SurrogateModel.md | 135 ++++++++++++++++++++++++++++- docs/wiki/5_AcquisitionFunction.md | 130 ++++++++++++++++++++++++++- 2 files changed, 263 insertions(+), 2 deletions(-) diff --git a/docs/wiki/4_SurrogateModel.md b/docs/wiki/4_SurrogateModel.md index fec2840..d0f37d0 100644 --- a/docs/wiki/4_SurrogateModel.md +++ b/docs/wiki/4_SurrogateModel.md @@ -1,3 +1,136 @@ # Surrogate Model -(TBA...) \ No newline at end of file +## 1. Introduction + +The `obsidian.surrogates` submodule is a key component of the Obsidian Bayesian optimization library. It provides a collection of surrogate models used to approximate the objective function in the optimization process. These surrogate models are essential for efficient exploration of the parameter space and for making informed decisions about which points to evaluate next. + +## 2. Available Surrogate Models + +The `obsidian.surrogates` submodule offers several types of surrogate models: + +1. **Gaussian Process (GP)**: The default surrogate model, suitable for most optimization tasks. +2. **Mixed Gaussian Process (MixedGP)**: A GP model that can handle mixed continuous and categorical input spaces. +3. **Deep Kernel Learning GP (DKL)**: A GP model with a neural network feature extractor. +4. **Flat GP**: A GP model with non-informative or no prior distributions. +5. **Prior GP**: A GP model with custom prior distributions. +6. **Multi-Task GP (MTGP)**: A GP model for multi-output optimization. +7. **Deep Neural Network (DNN)**: A dropout neural network model. + +## 3. How to Use Surrogate Models + +To use a surrogate model in your optimization process, you typically don't need to interact with it directly. The Obsidian optimizer will handle the creation and management of the surrogate model. However, if you need to create a surrogate model manually, you can do so using the `SurrogateBoTorch` class: + +```python +from obsidian.surrogates import SurrogateBoTorch +from obsidian.parameters import ParamSpace, Target + +# Define your parameter space +param_space = ParamSpace([...]) # Define your parameters here + +# Create a surrogate model (default is GP) +surrogate = SurrogateBoTorch(model_type='GP') + +# Fit the model to your data +surrogate.fit(X, y) + +# Make predictions +mean, std = surrogate.predict(X_new) +``` + +## 4. Customization Options + +### 4.1 Model Selection + +You can choose different surrogate models by specifying the `model_type` parameter when creating a `SurrogateBoTorch` instance. Available options are: + +- `'GP'`: Standard Gaussian Process +- `'MixedGP'`: Mixed input Gaussian Process +- `'DKL'`: Deep Kernel Learning GP +- `'GPflat'`: Flat (non-informative prior) GP +- `'GPprior'`: Custom prior GP +- `'MTGP'`: Multi-Task GP +- `'DNN'`: Dropout Neural Network + +### 4.2 Hyperparameters + +You can pass custom hyperparameters to the surrogate model using the `hps` parameter: + +```python +surrogate = SurrogateBoTorch(model_type='GP', hps={'your_custom_param': value}) +``` + +### 4.3 Custom GP Models + +The submodule provides several custom GP implementations: + +- `PriorGP`: A GP with custom prior distributions +- `FlatGP`: A GP with non-informative or no prior distributions +- `DKLGP`: A GP with a neural network feature extractor + +### 4.4 Custom Neural Network Model + +The `DNN` class provides a customizable dropout neural network model. You can adjust parameters such as dropout probability, hidden layer width, and number of hidden layers. + +## 5. Examples + +### 5.1 Using a standard GP surrogate + +```python +from obsidian.surrogates import SurrogateBoTorch +from obsidian.parameters import ParamSpace, Target +import pandas as pd + +# Define your parameter space +param_space = ParamSpace([...]) # Define your parameters here + +# Assume X and y are your input features and target variables +X = pd.DataFrame(...) +y = pd.Series(...) + +surrogate = SurrogateBoTorch(model_type='GP') +surrogate.fit(X, y) + +# Make predictions +X_new = pd.DataFrame(...) +mean, std = surrogate.predict(X_new) +``` + +### 5.2 Using a Mixed GP for categorical and continuous variables + +```python +surrogate = SurrogateBoTorch(model_type='MixedGP') +# cat_dims should be a list of indices for categorical variables in your input space +surrogate.fit(X, y, cat_dims=[0, 2]) # Assuming columns 0 and 2 are categorical +``` + +### 5.3 Using a DNN surrogate + +```python +# The 'hps' parameter allows you to customize the DNN architecture +surrogate = SurrogateBoTorch(model_type='DNN', hps={'p_dropout': 0.1, 'h_width': 32, 'h_layers': 3}) +surrogate.fit(X, y) +``` + +## 6. Advanced Usage + +### 6.1 Saving and Loading Models + +You can save and load surrogate models using the `save_state()` and `load_state()` methods: + +```python +# Save model state +state = surrogate.save_state() + +# Load model state +loaded_surrogate = SurrogateBoTorch.load_state(state) +``` + +### 6.2 Model Evaluation + +You can evaluate the performance of a surrogate model using the `score()` method: + +```python +loss, r2_score = surrogate.score(X_test, y_test) +``` + +This concludes the user guide for the `obsidian.surrogates` submodule. For more detailed information, please refer to the source code and docstrings in the individual files. \ No newline at end of file diff --git a/docs/wiki/5_AcquisitionFunction.md b/docs/wiki/5_AcquisitionFunction.md index 9b32ee3..389b17d 100644 --- a/docs/wiki/5_AcquisitionFunction.md +++ b/docs/wiki/5_AcquisitionFunction.md @@ -1,3 +1,131 @@ # Acquisition Function -(TBA...) \ No newline at end of file +## 1. Introduction + +The `obsidian.acquisition` submodule is a crucial component of the Obsidian Bayesian optimization library. It provides acquisition functions that guide the optimization process by determining which points in the parameter space should be evaluated next. These acquisition functions balance exploration of uncertain areas and exploitation of promising regions, which is key to efficient optimization. + +## 2. Key Components + +The acquisition submodule includes several acquisition functions, both standard and custom implementations: + +### 2.1 Standard Acquisition Functions + +- Expected Improvement (EI) +- Probability of Improvement (PI) +- Upper Confidence Bound (UCB) +- Noisy Expected Improvement (NEI) +- Expected Hypervolume Improvement (EHVI) +- Noisy Expected Hypervolume Improvement (NEHVI) + +### 2.2 Custom Acquisition Functions + +- qMean: Optimizes for the maximum value of the posterior mean +- qSpaceFill: Optimizes for the maximum value of minimum distance between a point and the training data + +## 3. Understanding Acquisition Functions + +### 3.1 Expected Improvement (EI) + +EI calculates the expected amount by which we will improve upon the current best observed value. + +Mathematical formulation: +``` +EI(x) = E[max(f(x) - f(x+), 0)] +``` +where f(x+) is the current best observed value. + +Example usage: +```python +from obsidian.optimizer import BayesianOptimizer + +optimizer = BayesianOptimizer(X_space=param_space) +X_suggest, eval_suggest = optimizer.suggest(acquisition=['EI']) +``` + +### 3.2 Upper Confidence Bound (UCB) + +UCB balances exploration and exploitation by selecting points with high predicted values or high uncertainty. + +Mathematical formulation: +``` +UCB(x) = μ(x) + β * σ(x) +``` +where μ(x) is the predicted mean, σ(x) is the predicted standard deviation, and β is a parameter that controls the exploration-exploitation trade-off. + +Example usage: +```python +X_suggest, eval_suggest = optimizer.suggest(acquisition=[{'UCB': {'beta': 2.0}}]) +``` + +### 3.3 Noisy Expected Improvement (NEI) + +NEI is a variant of EI that accounts for noise in the observations, making it more suitable for real-world problems with measurement uncertainty. + +Example usage: +```python +X_suggest, eval_suggest = optimizer.suggest(acquisition=['NEI']) +``` + +## 4. Advanced Usage + +### 4.1 Multi-Objective Optimization + +For multi-objective optimization problems, you can use specialized acquisition functions: + +```python +X_suggest, eval_suggest = optimizer.suggest(acquisition=['NEHVI']) +``` + +### 4.2 Customizing Acquisition Functions + +Some acquisition functions accept parameters to customize their behavior. These can be specified in the `suggest` method: + +```python +X_suggest, eval_suggest = optimizer.suggest( + acquisition=[{'EI': {'inflate': 0.01}}] +) +``` + +### 4.3 Custom Acquisition Functions + +If you need to implement a custom acquisition function, you can extend the `MCAcquisitionFunction` class from BoTorch: + +```python +from botorch.acquisition import MCAcquisitionFunction +import torch + +class CustomAcquisition(MCAcquisitionFunction): + def forward(self, X): + posterior = self.model.posterior(X) + mean = posterior.mean + std = posterior.variance.sqrt() + return (mean + 0.1 * std).sum(dim=-1) # Example custom acquisition logic +``` + +## 5. Comparing Acquisition Functions + +Different acquisition functions have different strengths: + +- EI and PI are good for exploiting known good regions but may underexplore. +- UCB provides a tunable exploration-exploitation trade-off. +- NEI and NEHVI are robust to noisy observations. +- qMean is purely exploitative and can be useful in the final stages of optimization. +- qSpaceFill is purely explorative and can be useful for initial space exploration. + +## 6. Best Practices + +1. Choose appropriate acquisition functions based on your problem characteristics (e.g., noise level, number of objectives). +2. For noisy problems, consider using noise-aware acquisition functions like NEI or NEHVI. +3. Experiment with different acquisition functions to find the best performance for your specific problem. +4. When using UCB, carefully tune the beta parameter to balance exploration and exploitation. +5. For multi-objective problems, EHVI and NEHVI are often good choices. +6. Consider using a sequence of acquisition functions, starting with more exploratory ones and moving to more exploitative ones as the optimization progresses. + +## 7. Common Pitfalls + +1. Using EI or PI in noisy problems, which can lead to overexploitation of noisy observations. +2. Setting UCB's beta parameter too high (over-exploration) or too low (over-exploitation). +3. Using single-objective acquisition functions for multi-objective problems. +4. Not accounting for constraints when selecting acquisition functions. + +This concludes the user guide for the `obsidian.acquisition` submodule. For more detailed information, please refer to the source code and docstrings in the individual files. \ No newline at end of file From 8ac2bbc11680240e8ed94171c76cc251e5de03f1 Mon Sep 17 00:00:00 2001 From: Kevin Stone Date: Wed, 14 Aug 2024 23:27:16 -0400 Subject: [PATCH 011/136] Completed first two tutorial notebooks and HTML --- ...=> Constrained multi-output min-max.ipynb} | 139 +- demo/Simple single objective.ipynb | 59 +- .../Constrained multi-output min-max.html | 8508 +++++++++++++++++ .../tutorials/Simple single objective.html | 172 +- docs/stubs/tutorials.rst | 3 +- .../Constrained multi-output min-max.rst | 6 + 6 files changed, 8773 insertions(+), 114 deletions(-) rename demo/{Constrained multi objective.ipynb => Constrained multi-output min-max.ipynb} (58%) create mode 100644 docs/_static/tutorials/Constrained multi-output min-max.html create mode 100644 docs/stubs/tutorials/Constrained multi-output min-max.rst diff --git a/demo/Constrained multi objective.ipynb b/demo/Constrained multi-output min-max.ipynb similarity index 58% rename from demo/Constrained multi objective.ipynb rename to demo/Constrained multi-output min-max.ipynb index 56814c2..7347362 100644 --- a/demo/Constrained multi objective.ipynb +++ b/demo/Constrained multi-output min-max.ipynb @@ -7,34 +7,47 @@ "outputs": [], "source": [ "import obsidian\n", - "obsidian.__version__" + "print(f'obsidian version: ' + obsidian.__version__)\n", + "\n", + "import pandas as pd\n", + "import plotly.express as px\n", + "import plotly.io as pio\n", + "pio.renderers.default = \"plotly_mimetype+notebook\"" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "import pandas as pd\n", - "import plotly.express as px" + "## Introduction" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "from obsidian.parameters import ParamSpace, Param_Continuous\n", - "from obsidian.experiment import ExpDesigner" + "In this tutorial, we will see how to use _obsidian_ for multi-output optimization. To demonstrate the versatility of the approach, we will seek to maximize one response while minimizing the other.\n", + "\n", + "$$\\underset{X}{argmax} HV\\left(+f\\left(y_1\\right) -f\\left(y_2\\right)\\right)$$\n", + "\n", + "Furthermore, we will apply a linear constraint on the input variables; requiring that the $X_1 + X_2 \\leq 6 $." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Set up parameter space and initialize a design" + "## Set up parameter space and initialize a design" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from obsidian import Campaign, Target, ParamSpace, BayesianOptimizer\n", + "from obsidian.parameters import Param_Continuous" ] }, { @@ -49,8 +62,12 @@ " ]\n", "\n", "X_space = ParamSpace(params)\n", - "designer = ExpDesigner(X_space, seed=0)\n", - "X0 = designer.initialize(4, 'LHS')\n", + "target = [\n", + " Target('Response 1', aim='max'),\n", + " Target('Response 2', aim='min')\n", + "]\n", + "campaign = Campaign(X_space, target, seed=0)\n", + "X0 = campaign.designer.initialize(4, 'LHS')\n", "\n", "X0" ] @@ -59,7 +76,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Collect results (e.g. from a simulation)" + "## Collect results (e.g. from a simulation)" ] }, { @@ -75,23 +92,15 @@ "y0 = simulator.simulate(X0)\n", "Z0 = pd.concat([X0, y0], axis=1)\n", "\n", - "Z0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Z0.plot(x='Response 1', y='Response 2', kind='scatter', figsize=(4,3))" + "campaign.add_data(Z0)\n", + "campaign.data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Define the Target" + "### Fit an optimizer and visualize results" ] }, { @@ -100,19 +109,7 @@ "metadata": {}, "outputs": [], "source": [ - "from obsidian.parameters import Target\n", - "\n", - "target = [\n", - " Target('Response 1', aim='max'),\n", - " Target('Response 2', aim='min')\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Create a campaign to track optimization" + "campaign.fit()" ] }, { @@ -121,7 +118,7 @@ "metadata": {}, "outputs": [], "source": [ - "from obsidian.campaign import Campaign" + "from obsidian.plotting import surface_plot, optim_progress" ] }, { @@ -130,16 +127,14 @@ "metadata": {}, "outputs": [], "source": [ - "my_campaign = Campaign(X_space, target)\n", - "my_campaign.add_data(Z0)\n", - "my_campaign.data" + "surface_plot(campaign.optimizer)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Fit an optimizer" + "## Optimize new experiment suggestions" ] }, { @@ -148,23 +143,16 @@ "metadata": {}, "outputs": [], "source": [ - "my_campaign.fit()" + "from obsidian.constraints import InConstraint_Generic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Make new experiment suggestions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from obsidian.constraints import InConstraint_Generic" + "_Note:_ It is a good idea to balance a set of acquisition functions with those that prefer design-space exploration. This helps to ensure that the optimizer is not severely misled by deficiencies in the dataset, particularly for small data. It also helps to ascertain a global optimum.\n", + "\n", + "A simple choice is __Space Filling (SF)__ although __Negative Integrated Posterior Variance (NIPV)__ is available for single-output optimizations; and there are various other acquisiiton functions whose hyperparameters can be tuned to manage the \"explore-exploit\" balance." ] }, { @@ -173,13 +161,9 @@ "metadata": {}, "outputs": [], "source": [ - "# # X1 + X2 >= 2\n", - "# optim_kwargs = {'m_batch':2, 'acquisition':[{'NEHVI':{'ref_point':[-350,0]}}], 'ineq_constraints':[InConstraint_Generic(X_space, indices=[0,1], coeff=[1,1], rhs=2)]}\n", - "\n", - "# X1 + X2 <= 6 aka -X1 - X2 >= -6\n", - "optim_kwargs = {'m_batch':2, 'acquisition':[{'NEHVI':{'ref_point':[-350,0]}}], 'ineq_constraints':[InConstraint_Generic(X_space, indices=[0,1], coeff=[-1,-1], rhs=-6)]}\n", - "\n", - "X_suggest, eval_suggest = my_campaign.optimizer.suggest(**optim_kwargs)" + "X_suggest, eval_suggest = campaign.optimizer.suggest(acquisition = [{'NEHVI':{'ref_point':[-350, -20]}}, 'SF'],\n", + " # X1 + X2 <= 6, written as -X1 - X2 >= -6\n", + " ineq_constraints = [InConstraint_Generic(X_space, indices=[0,1], coeff=[-1,-1], rhs=-6)])" ] }, { @@ -195,7 +179,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Collect data at new suggestions" + "## Collect data at new suggestions" ] }, { @@ -206,15 +190,15 @@ "source": [ "y_iter1 = pd.DataFrame(simulator.simulate(X_suggest))\n", "Z_iter1 = pd.concat([X_suggest, y_iter1, eval_suggest], axis=1)\n", - "my_campaign.add_data(Z_iter1)\n", - "my_campaign.data" + "campaign.add_data(Z_iter1)\n", + "campaign.data.tail()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Repeat as desired" + "## Repeat as desired" ] }, { @@ -224,11 +208,12 @@ "outputs": [], "source": [ "for iter in range(5):\n", - " my_campaign.fit()\n", - " X_suggest, eval_suggest = my_campaign.optimizer.suggest(**optim_kwargs)\n", + " campaign.fit()\n", + " X_suggest, eval_suggest = campaign.optimizer.suggest(acquisition = [{'NEHVI':{'ref_point':[-350, -20]}}, 'SF'],\n", + " ineq_constraints = [InConstraint_Generic(X_space, indices=[0,1], coeff=[-1,-1], rhs=-6)])\n", " y_iter = pd.DataFrame(simulator.simulate(X_suggest))\n", " Z_iter = pd.concat([X_suggest, y_iter, eval_suggest], axis=1)\n", - " my_campaign.add_data(Z_iter)" + " campaign.add_data(Z_iter)" ] }, { @@ -237,8 +222,7 @@ "metadata": {}, "outputs": [], "source": [ - "fig = px.scatter(my_campaign.data, x='Response 1', y='Response 2', color='Iteration')\n", - "fig.update_layout(height=300, width=400, template='ggplot2')" + "optim_progress(campaign)" ] }, { @@ -247,7 +231,7 @@ "metadata": {}, "outputs": [], "source": [ - "my_campaign.data" + "surface_plot(campaign.optimizer, response_id = 0)" ] }, { @@ -255,14 +239,9 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "surface_plot(campaign.optimizer, response_id = 1)" + ] } ], "metadata": { diff --git a/demo/Simple single objective.ipynb b/demo/Simple single objective.ipynb index 51f2b2c..cef399f 100644 --- a/demo/Simple single objective.ipynb +++ b/demo/Simple single objective.ipynb @@ -49,7 +49,7 @@ "X_space = ParamSpace(params)\n", "target = Target('Yield', aim='max')\n", "campaign = Campaign(X_space, target, seed=0)\n", - "X0 = campaign.designer.initialize(10, 'LHS')\n", + "X0 = campaign.initialize(m_initial = 10, method = 'LHS')\n", "\n", "X0" ] @@ -111,8 +111,15 @@ "metadata": {}, "outputs": [], "source": [ - "from obsidian.plotting import parity_plot, factor_plot\n", - "\n", + "from obsidian.plotting import parity_plot, factor_plot, optim_progress" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "parity_plot(campaign.optimizer)" ] }, @@ -138,7 +145,7 @@ "metadata": {}, "outputs": [], "source": [ - "X_suggest, eval_suggest = campaign.optimizer.suggest(m_batch=3)" + "X_suggest, eval_suggest = campaign.suggest(m_batch=3)" ] }, { @@ -185,7 +192,7 @@ "source": [ "for iter in range(3):\n", " campaign.fit()\n", - " X_suggest, eval_suggest = campaign.optimizer.suggest(m_batch=3)\n", + " X_suggest, eval_suggest = campaign.suggest(m_batch=3)\n", " y_iter = pd.DataFrame(simulator.simulate(X_suggest))\n", " Z_iter = pd.concat([X_suggest, y_iter, eval_suggest], axis=1)\n", " campaign.add_data(Z_iter)" @@ -197,8 +204,14 @@ "metadata": {}, "outputs": [], "source": [ - "fig = px.scatter(campaign.data, x=campaign.data.index, y='Yield', color='aq Value')\n", - "fig.update_layout(height=300, width=400, template='ggplot2')" + "optim_progress(campaign, color_feature_id = 'aq Value')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Analyze using Explainer" ] }, { @@ -206,7 +219,37 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "from obsidian.campaign import Explainer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exp = Explainer(campaign.optimizer)\n", + "exp.shap_explain(n=500)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exp.shap_summary()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exp.shap_pdp_ice(ind = 0, ice_color_var = 2)" + ] } ], "metadata": { diff --git a/docs/_static/tutorials/Constrained multi-output min-max.html b/docs/_static/tutorials/Constrained multi-output min-max.html new file mode 100644 index 0000000..a0f63a0 --- /dev/null +++ b/docs/_static/tutorials/Constrained multi-output min-max.html @@ -0,0 +1,8508 @@ + + + + + +Constrained multi-output min-max + + + + + + + + + + + + +
+ + + + + + + + + +
+ + diff --git a/docs/_static/tutorials/Simple single objective.html b/docs/_static/tutorials/Simple single objective.html index 4fdd762..f15cef3 100644 --- a/docs/_static/tutorials/Simple single objective.html +++ b/docs/_static/tutorials/Simple single objective.html @@ -7536,7 +7536,7 @@ @@ -7587,7 +7587,7 @@

Set up parameter space a X_space = ParamSpace(params) target = Target('Yield', aim='max') campaign = Campaign(X_space, target, seed=0) -X0 = campaign.designer.initialize(10, 'LHS') +X0 = campaign.initialize(m_initial = 10, method = 'LHS') X0

@@ -7873,7 +7873,7 @@

Collect results (e.g. from a s

@@ -7916,7 +7916,7 @@

Fit the optimizer and visualize -