alchemist-nrel 0.2.1__py3-none-any.whl → 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alchemist_core/__init__.py +14 -7
- alchemist_core/acquisition/botorch_acquisition.py +15 -6
- alchemist_core/audit_log.py +594 -0
- alchemist_core/data/experiment_manager.py +76 -5
- alchemist_core/models/botorch_model.py +6 -4
- alchemist_core/models/sklearn_model.py +74 -8
- alchemist_core/session.py +788 -39
- alchemist_core/utils/doe.py +200 -0
- alchemist_nrel-0.3.1.dist-info/METADATA +185 -0
- alchemist_nrel-0.3.1.dist-info/RECORD +66 -0
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.1.dist-info}/entry_points.txt +1 -0
- api/example_client.py +7 -2
- api/main.py +21 -4
- api/models/requests.py +95 -1
- api/models/responses.py +167 -0
- api/routers/acquisition.py +25 -0
- api/routers/experiments.py +134 -6
- api/routers/sessions.py +438 -10
- api/routers/visualizations.py +10 -5
- api/routers/websocket.py +132 -0
- api/run_api.py +56 -0
- api/services/session_store.py +285 -54
- api/static/NEW_ICON.ico +0 -0
- api/static/NEW_ICON.png +0 -0
- api/static/NEW_LOGO_DARK.png +0 -0
- api/static/NEW_LOGO_LIGHT.png +0 -0
- api/static/assets/api-vcoXEqyq.js +1 -0
- api/static/assets/index-DWfIKU9j.js +4094 -0
- api/static/assets/index-sMIa_1hV.css +1 -0
- api/static/index.html +14 -0
- api/static/vite.svg +1 -0
- ui/gpr_panel.py +7 -2
- ui/notifications.py +197 -10
- ui/ui.py +1117 -68
- ui/variables_setup.py +47 -2
- ui/visualizations.py +60 -3
- alchemist_core/models/ax_model.py +0 -159
- alchemist_nrel-0.2.1.dist-info/METADATA +0 -206
- alchemist_nrel-0.2.1.dist-info/RECORD +0 -54
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.1.dist-info}/WHEEL +0 -0
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.1.dist-info}/top_level.txt +0 -0
ui/variables_setup.py
CHANGED
|
@@ -2,6 +2,7 @@ import customtkinter as ctk
|
|
|
2
2
|
from customtkinter import filedialog
|
|
3
3
|
import json, csv, os
|
|
4
4
|
from skopt.space import Real, Integer, Categorical
|
|
5
|
+
from alchemist_core.data.search_space import SearchSpace
|
|
5
6
|
from tkinter import StringVar
|
|
6
7
|
import tkinter.messagebox as messagebox
|
|
7
8
|
from tksheet import Sheet # requires tksheet package
|
|
@@ -427,7 +428,52 @@ class SpaceSetupWindow(ctk.CTkToplevel):
|
|
|
427
428
|
|
|
428
429
|
# Update the main UI if it has the necessary methods
|
|
429
430
|
if hasattr(self.master, 'search_space'):
|
|
431
|
+
# Store the skopt-compatible dimensions for legacy UI code
|
|
430
432
|
self.master.search_space = self.search_space
|
|
433
|
+
# Always create/update the SearchSpace manager on the master so the
|
|
434
|
+
# OptimizationSession and ExperimentManager get a consistent object.
|
|
435
|
+
try:
|
|
436
|
+
ss = SearchSpace().from_dict(self.master.variable_space_data)
|
|
437
|
+
self.master.search_space_manager = ss
|
|
438
|
+
# Keep the skopt-compatible reference too
|
|
439
|
+
try:
|
|
440
|
+
self.master.search_space = ss.to_skopt()
|
|
441
|
+
except Exception:
|
|
442
|
+
self.master.search_space = ss
|
|
443
|
+
# Ensure experiment manager is updated
|
|
444
|
+
if hasattr(self.master, 'experiment_manager'):
|
|
445
|
+
try:
|
|
446
|
+
self.master.experiment_manager.set_search_space(ss)
|
|
447
|
+
except Exception:
|
|
448
|
+
pass
|
|
449
|
+
# If there's an active OptimizationSession, update it as well
|
|
450
|
+
if hasattr(self.master, 'session') and self.master.session is not None:
|
|
451
|
+
try:
|
|
452
|
+
self.master.session.search_space = ss
|
|
453
|
+
self.master.session.experiment_manager.set_search_space(ss)
|
|
454
|
+
# emit an event so other UI components can react
|
|
455
|
+
try:
|
|
456
|
+
self.master.session.events.emit('search_space_loaded', {'source': 'ui'})
|
|
457
|
+
except Exception:
|
|
458
|
+
pass
|
|
459
|
+
except Exception:
|
|
460
|
+
pass
|
|
461
|
+
except Exception:
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
# Let the main app know the search space changed so it can sync to session
|
|
465
|
+
if hasattr(self.master, '_sync_data_to_session'):
|
|
466
|
+
try:
|
|
467
|
+
self.master._sync_data_to_session()
|
|
468
|
+
except Exception:
|
|
469
|
+
pass
|
|
470
|
+
|
|
471
|
+
if hasattr(self.master, '_update_ui_state'):
|
|
472
|
+
try:
|
|
473
|
+
self.master._update_ui_state()
|
|
474
|
+
except Exception:
|
|
475
|
+
pass
|
|
476
|
+
|
|
431
477
|
if hasattr(self.master, 'update_variables_display'):
|
|
432
478
|
self.master.update_variables_display()
|
|
433
479
|
elif hasattr(self.master, 'var_sheet'):
|
|
@@ -469,8 +515,7 @@ class SpaceSetupWindow(ctk.CTkToplevel):
|
|
|
469
515
|
self.master.load_exp_button.configure(state='normal')
|
|
470
516
|
if hasattr(self.master, 'gen_template_button'):
|
|
471
517
|
self.master.gen_template_button.configure(state='normal')
|
|
472
|
-
|
|
473
|
-
self.master.cluster_switch.configure(state='normal')
|
|
518
|
+
# Clustering UI removed; no cluster_switch configuration needed
|
|
474
519
|
|
|
475
520
|
except Exception as e:
|
|
476
521
|
print(f"Error updating main UI variables: {e}")
|
ui/visualizations.py
CHANGED
|
@@ -93,11 +93,53 @@ class Visualizations:
|
|
|
93
93
|
def initialize_figure(self):
|
|
94
94
|
"""Initialize the Matplotlib figure and axes."""
|
|
95
95
|
if self.fig is None or self.ax is None:
|
|
96
|
-
|
|
96
|
+
# Use current rcParam DPI when creating the figure so text sizes
|
|
97
|
+
# are consistent with the active window's DPI settings.
|
|
98
|
+
dpi = plt.rcParams.get('figure.dpi', 100)
|
|
99
|
+
self.fig, self.ax = plt.subplots(figsize=(6, 4), dpi=dpi)
|
|
97
100
|
|
|
98
101
|
def open_window(self):
|
|
99
102
|
"""Open the visualization window."""
|
|
100
|
-
|
|
103
|
+
# Probe DPI from the parent window (avoids creating a new Tk root).
|
|
104
|
+
try:
|
|
105
|
+
dpi = int(self.parent.winfo_fpixels('1i'))
|
|
106
|
+
except Exception:
|
|
107
|
+
dpi = plt.rcParams.get('figure.dpi', 100)
|
|
108
|
+
|
|
109
|
+
# Apply DPI to matplotlib runtime settings so text and markers scale correctly
|
|
110
|
+
plt.rcParams['figure.dpi'] = dpi
|
|
111
|
+
|
|
112
|
+
# Cap font sizes to avoid oversized rendering on HiDPI displays
|
|
113
|
+
try:
|
|
114
|
+
fs = plt.rcParams.get('font.size', 10)
|
|
115
|
+
if fs > 14:
|
|
116
|
+
plt.rcParams['font.size'] = 10
|
|
117
|
+
if plt.rcParams.get('xtick.labelsize', 10) > 12:
|
|
118
|
+
plt.rcParams['xtick.labelsize'] = 8
|
|
119
|
+
if plt.rcParams.get('ytick.labelsize', 10) > 12:
|
|
120
|
+
plt.rcParams['ytick.labelsize'] = 8
|
|
121
|
+
except Exception:
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
self.initialize_figure() # Ensure the figure is initialized with corrected DPI
|
|
125
|
+
# Enforce additional rcParams to keep the figure compact and fonts reasonable
|
|
126
|
+
try:
|
|
127
|
+
plt.rcParams['axes.titlesize'] = min(12, plt.rcParams.get('axes.titlesize', 12))
|
|
128
|
+
plt.rcParams['axes.labelsize'] = min(10, plt.rcParams.get('axes.labelsize', 10))
|
|
129
|
+
plt.rcParams['legend.fontsize'] = min(9, plt.rcParams.get('legend.fontsize', 9))
|
|
130
|
+
plt.rcParams['xtick.labelsize'] = min(9, plt.rcParams.get('xtick.labelsize', 9))
|
|
131
|
+
plt.rcParams['ytick.labelsize'] = min(9, plt.rcParams.get('ytick.labelsize', 9))
|
|
132
|
+
plt.rcParams['lines.markersize'] = min(6, plt.rcParams.get('lines.markersize', 6))
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
# Ensure the figure uses a compact size (in inches) and the detected DPI.
|
|
137
|
+
try:
|
|
138
|
+
# Prefer a slightly smaller figure to avoid huge white margins
|
|
139
|
+
self.fig.set_size_inches(5, 4, forward=True)
|
|
140
|
+
self.fig.set_dpi(dpi)
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
101
143
|
|
|
102
144
|
if self.search_space is None:
|
|
103
145
|
print("Error: Search space is not defined.")
|
|
@@ -122,6 +164,15 @@ class Visualizations:
|
|
|
122
164
|
visualization_window.focus_force()
|
|
123
165
|
visualization_window.grab_set()
|
|
124
166
|
|
|
167
|
+
# Fix macOS Retina scaling by probing the new window's DPI and applying it
|
|
168
|
+
try:
|
|
169
|
+
dpi = int(visualization_window.winfo_fpixels('1i'))
|
|
170
|
+
except Exception:
|
|
171
|
+
dpi = plt.rcParams.get('figure.dpi', 100)
|
|
172
|
+
|
|
173
|
+
# Apply DPI to matplotlib runtime settings so text and markers scale correctly
|
|
174
|
+
plt.rcParams['figure.dpi'] = dpi
|
|
175
|
+
|
|
125
176
|
# Top control frame for other visualizations (metrics, parity, hyperparameters)
|
|
126
177
|
# Use two rows to avoid crowding
|
|
127
178
|
control_container = ctk.CTkFrame(visualization_window)
|
|
@@ -191,8 +242,14 @@ class Visualizations:
|
|
|
191
242
|
self.plot_frame = ctk.CTkFrame(main_container, height=450)
|
|
192
243
|
self.plot_frame.pack(side="left", fill='both', expand=True, padx=5, pady=5)
|
|
193
244
|
self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)
|
|
245
|
+
# Draw and tighten layout before packing to reduce excess margins
|
|
246
|
+
try:
|
|
247
|
+
self.fig.tight_layout(pad=0.6)
|
|
248
|
+
except Exception:
|
|
249
|
+
pass
|
|
194
250
|
self.canvas.draw()
|
|
195
|
-
|
|
251
|
+
# Use smaller padding so the canvas doesn't create excess whitespace
|
|
252
|
+
self.canvas.get_tk_widget().pack(fill='both', expand=True, padx=2, pady=2)
|
|
196
253
|
self.metrics_toolbar = NavigationToolbar2Tk(self.canvas, self.plot_frame)
|
|
197
254
|
self.metrics_toolbar.config(background='#2E2E2E')
|
|
198
255
|
self.metrics_toolbar._message_label.config(background='#2E2E2E')
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
from ax.service.ax_client import AxClient
|
|
2
|
-
from .base_model import BaseModel
|
|
3
|
-
import pandas as pd
|
|
4
|
-
import numpy as np
|
|
5
|
-
from skopt.space import Real, Integer, Categorical
|
|
6
|
-
|
|
7
|
-
class AxModel(BaseModel):
|
|
8
|
-
def __init__(self, search_space, experiment_name="experiment", random_state=42):
|
|
9
|
-
"""
|
|
10
|
-
Initialize the AxModel.
|
|
11
|
-
|
|
12
|
-
Args:
|
|
13
|
-
search_space: A list of skopt.space objects (Real, Integer, or Categorical).
|
|
14
|
-
experiment_name: A name for the Ax experiment.
|
|
15
|
-
random_state: Random seed for reproducibility.
|
|
16
|
-
"""
|
|
17
|
-
self.experiment_name = experiment_name
|
|
18
|
-
self.search_space = search_space
|
|
19
|
-
self.random_state = random_state
|
|
20
|
-
self.ax_client = AxClient(random_seed=random_state)
|
|
21
|
-
self.trained = False
|
|
22
|
-
|
|
23
|
-
def _build_parameters(self):
|
|
24
|
-
"""
|
|
25
|
-
Build the Ax parameters list from the search_space.
|
|
26
|
-
"""
|
|
27
|
-
parameters = []
|
|
28
|
-
for dim in self.search_space:
|
|
29
|
-
if isinstance(dim, Real):
|
|
30
|
-
# For Real dimensions, use a continuous range.
|
|
31
|
-
parameters.append({
|
|
32
|
-
"name": dim.name,
|
|
33
|
-
"type": "range",
|
|
34
|
-
"bounds": list(dim.bounds),
|
|
35
|
-
"value_type": "float",
|
|
36
|
-
})
|
|
37
|
-
elif isinstance(dim, Integer):
|
|
38
|
-
# For Integer dimensions, use a range and specify value type as int.
|
|
39
|
-
parameters.append({
|
|
40
|
-
"name": dim.name,
|
|
41
|
-
"type": "range",
|
|
42
|
-
"bounds": list(dim.bounds),
|
|
43
|
-
"value_type": "int",
|
|
44
|
-
})
|
|
45
|
-
elif isinstance(dim, Categorical):
|
|
46
|
-
# For categorical dimensions, use "choice" and list the categories.
|
|
47
|
-
# Here we assume that the categories are strings; if numeric, adjust "value_type" accordingly.
|
|
48
|
-
parameters.append({
|
|
49
|
-
"name": dim.name,
|
|
50
|
-
"type": "choice",
|
|
51
|
-
"values": list(dim.categories),
|
|
52
|
-
"value_type": "str",
|
|
53
|
-
})
|
|
54
|
-
else:
|
|
55
|
-
raise ValueError(f"Unsupported search space dimension type: {type(dim)}")
|
|
56
|
-
return parameters
|
|
57
|
-
|
|
58
|
-
def train(self, exp_df, **kwargs):
|
|
59
|
-
"""Train the Ax model using the raw experiment DataFrame."""
|
|
60
|
-
X = exp_df.drop(columns="Output")
|
|
61
|
-
y = exp_df["Output"]
|
|
62
|
-
parameters = self._build_parameters()
|
|
63
|
-
self.ax_client.create_experiment(
|
|
64
|
-
name=self.experiment_name,
|
|
65
|
-
parameters=parameters,
|
|
66
|
-
)
|
|
67
|
-
for i, row in X.iterrows():
|
|
68
|
-
params = row.to_dict()
|
|
69
|
-
outcome = float(y.iloc[i])
|
|
70
|
-
self.ax_client.complete_trial(trial_index=i, raw_data={"objective": outcome})
|
|
71
|
-
self.trained = True
|
|
72
|
-
|
|
73
|
-
def predict(self, X, return_std=False, **kwargs):
|
|
74
|
-
"""
|
|
75
|
-
For Ax, prediction means asking for the next candidate.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
X: Not used (the next candidate is computed based on the experiment history).
|
|
79
|
-
return_std: Not applicable; always returns just the candidate.
|
|
80
|
-
|
|
81
|
-
Returns:
|
|
82
|
-
A dictionary with parameter names and suggested values.
|
|
83
|
-
"""
|
|
84
|
-
if not self.trained:
|
|
85
|
-
raise ValueError("The Ax experiment has not been trained with past data yet.")
|
|
86
|
-
parameters, trial_index = self.ax_client.get_next_trial()
|
|
87
|
-
return parameters
|
|
88
|
-
|
|
89
|
-
def predict_with_std(self, X):
|
|
90
|
-
"""
|
|
91
|
-
Make predictions with standard deviation.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
X: Input features (DataFrame or array)
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
Tuple of (predictions, standard deviations)
|
|
98
|
-
"""
|
|
99
|
-
if not self.is_trained:
|
|
100
|
-
raise ValueError("Model is not trained yet")
|
|
101
|
-
|
|
102
|
-
# Convert to DataFrame if needed
|
|
103
|
-
if not isinstance(X, pd.DataFrame):
|
|
104
|
-
if hasattr(self, 'feature_names') and self.feature_names:
|
|
105
|
-
X = pd.DataFrame(X, columns=self.feature_names)
|
|
106
|
-
else:
|
|
107
|
-
raise ValueError("Cannot convert input to DataFrame - feature names unknown")
|
|
108
|
-
|
|
109
|
-
# Prepare the observations in Ax format
|
|
110
|
-
obs = []
|
|
111
|
-
for _, row in X.iterrows():
|
|
112
|
-
arm_parameters = row.to_dict()
|
|
113
|
-
obs.append(arm_parameters)
|
|
114
|
-
|
|
115
|
-
# Get the predictions
|
|
116
|
-
means, covariances = self.surrogate.predict(obs)
|
|
117
|
-
|
|
118
|
-
# Extract standard deviations from covariances
|
|
119
|
-
stds = np.sqrt(np.diag(covariances))
|
|
120
|
-
|
|
121
|
-
return means, stds
|
|
122
|
-
|
|
123
|
-
def evaluate(self, X, y, **kwargs):
|
|
124
|
-
"""
|
|
125
|
-
Evaluate the Ax model's performance using stored outcomes.
|
|
126
|
-
In a more complete implementation, you could compute metrics such as RMSE across trials.
|
|
127
|
-
|
|
128
|
-
Returns:
|
|
129
|
-
A dictionary with evaluation metrics (here empty as a placeholder).
|
|
130
|
-
"""
|
|
131
|
-
# Example: Extract and compute statistics from the experiment.
|
|
132
|
-
return {}
|
|
133
|
-
|
|
134
|
-
def get_hyperparameters(self):
|
|
135
|
-
"""
|
|
136
|
-
Get model hyperparameters.
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
A dictionary with hyperparameter names and values.
|
|
140
|
-
"""
|
|
141
|
-
if not self.is_trained:
|
|
142
|
-
return {"status": "Model not trained"}
|
|
143
|
-
|
|
144
|
-
try:
|
|
145
|
-
params = {}
|
|
146
|
-
# For Ax models, we can extract some basic info
|
|
147
|
-
if hasattr(self, 'surrogate') and hasattr(self.surrogate, 'model'):
|
|
148
|
-
model_type = type(self.surrogate.model).__name__
|
|
149
|
-
params['model_type'] = model_type
|
|
150
|
-
|
|
151
|
-
# Try to get some GPEI-specific attributes if available
|
|
152
|
-
if hasattr(self.surrogate.model, 'model'):
|
|
153
|
-
inner_model = self.surrogate.model.model
|
|
154
|
-
if hasattr(inner_model, 'covar_module'):
|
|
155
|
-
params['covar_module'] = str(inner_model.covar_module)
|
|
156
|
-
|
|
157
|
-
return params
|
|
158
|
-
except Exception as e:
|
|
159
|
-
return {"error": str(e)}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: alchemist-nrel
|
|
3
|
-
Version: 0.2.1
|
|
4
|
-
Summary: Active learning and optimization toolkit for chemical and materials research
|
|
5
|
-
Author-email: Caleb Coatney <caleb.coatney@nrel.gov>
|
|
6
|
-
License: BSD-3-Clause
|
|
7
|
-
Project-URL: Homepage, https://github.com/NREL/ALchemist
|
|
8
|
-
Project-URL: Documentation, https://nrel.github.io/ALchemist/
|
|
9
|
-
Project-URL: Source, https://github.com/NREL/ALchemist
|
|
10
|
-
Project-URL: Bug Tracker, https://github.com/NREL/ALchemist/issues
|
|
11
|
-
Project-URL: Changelog, https://github.com/NREL/ALchemist/releases
|
|
12
|
-
Keywords: active learning,bayesian optimization,gaussian processes,materials science,chemistry
|
|
13
|
-
Classifier: Development Status :: 4 - Beta
|
|
14
|
-
Classifier: Intended Audience :: Science/Research
|
|
15
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
-
Classifier: Operating System :: OS Independent
|
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Topic :: Scientific/Engineering
|
|
21
|
-
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
22
|
-
Requires-Python: >=3.11
|
|
23
|
-
Description-Content-Type: text/markdown
|
|
24
|
-
License-File: LICENSE
|
|
25
|
-
Requires-Dist: numpy
|
|
26
|
-
Requires-Dist: pandas
|
|
27
|
-
Requires-Dist: scipy
|
|
28
|
-
Requires-Dist: matplotlib
|
|
29
|
-
Requires-Dist: mplcursors
|
|
30
|
-
Requires-Dist: scikit-learn
|
|
31
|
-
Requires-Dist: scikit-optimize
|
|
32
|
-
Requires-Dist: botorch
|
|
33
|
-
Requires-Dist: torch
|
|
34
|
-
Requires-Dist: gpytorch
|
|
35
|
-
Requires-Dist: ax-platform
|
|
36
|
-
Requires-Dist: customtkinter
|
|
37
|
-
Requires-Dist: tksheet
|
|
38
|
-
Requires-Dist: tabulate
|
|
39
|
-
Requires-Dist: ctkmessagebox
|
|
40
|
-
Requires-Dist: joblib
|
|
41
|
-
Requires-Dist: fastapi>=0.109.0
|
|
42
|
-
Requires-Dist: uvicorn[standard]>=0.27.0
|
|
43
|
-
Requires-Dist: pydantic>=2.5.0
|
|
44
|
-
Requires-Dist: python-multipart>=0.0.6
|
|
45
|
-
Provides-Extra: test
|
|
46
|
-
Requires-Dist: pytest>=8.0.0; extra == "test"
|
|
47
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
48
|
-
Requires-Dist: pytest-anyio>=0.0.0; extra == "test"
|
|
49
|
-
Requires-Dist: httpx>=0.25.0; extra == "test"
|
|
50
|
-
Requires-Dist: requests>=2.31.0; extra == "test"
|
|
51
|
-
Provides-Extra: dev
|
|
52
|
-
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
53
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
54
|
-
Requires-Dist: pytest-anyio>=0.0.0; extra == "dev"
|
|
55
|
-
Requires-Dist: httpx>=0.25.0; extra == "dev"
|
|
56
|
-
Requires-Dist: requests>=2.31.0; extra == "dev"
|
|
57
|
-
Dynamic: license-file
|
|
58
|
-
|
|
59
|
-
<img src="docs/assets/logo.png" alt="ALchemist" width="50%" />
|
|
60
|
-
|
|
61
|
-
**ALchemist: Active Learning Toolkit for Chemical and Materials Research**
|
|
62
|
-
|
|
63
|
-
ALchemist is a modular Python toolkit that brings active learning and Bayesian optimization to experimental design in chemical and materials research. It is designed for scientists and engineers who want to efficiently explore or optimize high-dimensional variable spaces—without writing code—using an intuitive graphical interface.
|
|
64
|
-
|
|
65
|
-
**NREL Software Record:** SWR-25-102
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## 📖 Documentation
|
|
70
|
-
|
|
71
|
-
Full user guide and documentation:
|
|
72
|
-
[https://nrel.github.io/ALchemist/](https://nrel.github.io/ALchemist/)
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## 🚀 Overview
|
|
77
|
-
|
|
78
|
-
ALchemist accelerates discovery and optimization by combining:
|
|
79
|
-
|
|
80
|
-
- **Flexible variable space definition:** Real, integer, and categorical variables with bounds or discrete values.
|
|
81
|
-
- **Probabilistic surrogate modeling:** Gaussian process regression via BoTorch or scikit-optimize backends.
|
|
82
|
-
- **Advanced acquisition strategies:** Efficient sampling using qEI, qPI, qUCB, and qNegIntegratedPosteriorVariance.
|
|
83
|
-
- **Modern web interface:** React-based UI with FastAPI backend for seamless active learning workflows.
|
|
84
|
-
- **Experiment tracking:** CSV logging, reproducible random seeds, and error tracking.
|
|
85
|
-
- **Extensibility:** Abstract interfaces for models and acquisition functions enable future backend and workflow expansion.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## 🧭 Quick Start
|
|
90
|
-
|
|
91
|
-
### Web Application (Recommended)
|
|
92
|
-
|
|
93
|
-
**Development Mode:**
|
|
94
|
-
```bash
|
|
95
|
-
# Option 1: Manual start
|
|
96
|
-
python run_api.py # Terminal 1: Backend (port 8000)
|
|
97
|
-
cd alchemist-web && npm run dev # Terminal 2: Frontend (port 5173)
|
|
98
|
-
|
|
99
|
-
# Option 2: Automated start
|
|
100
|
-
scripts\dev_start.bat # Windows
|
|
101
|
-
./scripts/dev_start.sh # Linux/Mac
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Production Mode:**
|
|
105
|
-
```bash
|
|
106
|
-
# Build and run
|
|
107
|
-
scripts\build_production.bat # Windows
|
|
108
|
-
./scripts/build_production.sh # Linux/Mac
|
|
109
|
-
|
|
110
|
-
# Start production server
|
|
111
|
-
python run_api.py --production
|
|
112
|
-
|
|
113
|
-
# Access at: http://localhost:8000
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
**Docker Deployment:**
|
|
117
|
-
```bash
|
|
118
|
-
# Build frontend first
|
|
119
|
-
cd alchemist-web && npm run build && cd ..
|
|
120
|
-
|
|
121
|
-
# Run with Docker Compose
|
|
122
|
-
cd docker
|
|
123
|
-
docker-compose up --build
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Python Package Installation
|
|
127
|
-
|
|
128
|
-
Requirements: Python 3.9 or higher
|
|
129
|
-
|
|
130
|
-
We recommend using [Anaconda](https://www.anaconda.com/products/distribution) to manage your Python environments.
|
|
131
|
-
|
|
132
|
-
**1. Create a new environment:**
|
|
133
|
-
```bash
|
|
134
|
-
conda create -n alchemist-env python=3.12
|
|
135
|
-
conda activate alchemist-env
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
**2. Install ALchemist:**
|
|
139
|
-
|
|
140
|
-
*Option A: Install directly from GitHub:*
|
|
141
|
-
```bash
|
|
142
|
-
python -m pip install git+https://github.com/NREL/ALchemist.git
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
*Option B: Clone and install (recommended for development):*
|
|
146
|
-
```bash
|
|
147
|
-
git clone https://github.com/NREL/ALchemist.git
|
|
148
|
-
cd ALchemist
|
|
149
|
-
python -m pip install -e .
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
All dependencies are specified in `pyproject.toml` and will be installed automatically.
|
|
153
|
-
|
|
154
|
-
For step-by-step instructions, see the [Getting Started](https://nrel.github.io/ALchemist/) section of the documentation.
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## 📁 Project Structure
|
|
159
|
-
|
|
160
|
-
```
|
|
161
|
-
ALchemist/
|
|
162
|
-
├── alchemist_core/ # Core Python library
|
|
163
|
-
├── alchemist-web/ # React frontend application
|
|
164
|
-
├── api/ # FastAPI backend
|
|
165
|
-
├── docker/ # Docker configuration files
|
|
166
|
-
├── scripts/ # Build and development scripts
|
|
167
|
-
├── tests/ # Test suite
|
|
168
|
-
├── docs/ # Documentation (MkDocs)
|
|
169
|
-
├── memory/ # Development notes and references
|
|
170
|
-
└── run_api.py # API server entry point
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
## 🛠️ Development Status
|
|
176
|
-
|
|
177
|
-
ALchemist is under active development at NREL as part of the DataHub project within the ChemCatBio consortium. It is designed to be approachable for non-ML researchers and extensible for advanced users. Planned features include:
|
|
178
|
-
|
|
179
|
-
- Enhanced initial sampling and DoE methods
|
|
180
|
-
- Additional model types and acquisition strategies
|
|
181
|
-
- Improved visualization tools
|
|
182
|
-
- GUI reimplementation in PySide6 for broader compatibility
|
|
183
|
-
- Support for multi-output models and multi-objective optimization
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
## 🐞 Issues & Troubleshooting
|
|
188
|
-
|
|
189
|
-
If you encounter any issues or have questions, please [open an issue on GitHub](https://github.com/NREL/ALchemist/issues) or contact ccoatney@nrel.gov.
|
|
190
|
-
|
|
191
|
-
For the latest known issues and troubleshooting tips, see the [Issues & Troubleshooting Log](docs/ISSUES_LOG.md).
|
|
192
|
-
|
|
193
|
-
We appreciate your feedback and bug reports to help improve ALchemist!
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## 📄 License
|
|
198
|
-
|
|
199
|
-
This project is licensed under the BSD 3-Clause License. See the [LICENSE](LICENSE) file for details.
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## 🔗 Repository
|
|
204
|
-
|
|
205
|
-
[https://github.com/NREL/ALchemist](https://github.com/NREL/ALchemist)
|
|
206
|
-
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
main.py,sha256=3sAO2QZxxibs4WRT82i2w6KVBBFmYEMNUoGiMYFowOw,126
|
|
2
|
-
alchemist_core/__init__.py,sha256=YA4a1INzAl6iuumPEXGGrorX9weogVN20b-w-UbB9bY,1813
|
|
3
|
-
alchemist_core/config.py,sha256=Sk5eM1okktO5bUMlMPv9yzF2fpuiyGr9LUtlCWIBDc8,3366
|
|
4
|
-
alchemist_core/events.py,sha256=ty9nRzfZGHzk6b09dALIwrMY_5PYSv0wMaw94JLDjSk,6717
|
|
5
|
-
alchemist_core/session.py,sha256=bZTpa5vatg3w4zSg6qaXDQbpV-WrGwaRqD_sNc4Qas8,23889
|
|
6
|
-
alchemist_core/acquisition/__init__.py,sha256=3CYGI24OTBS66ETrlGFyHCNpfS6DBMP41MZDhvjFEzg,32
|
|
7
|
-
alchemist_core/acquisition/base_acquisition.py,sha256=s51vGx0b0Nt91lSCiVwYP9IClugVg2VJ21dn2n_4LIs,483
|
|
8
|
-
alchemist_core/acquisition/botorch_acquisition.py,sha256=9ofyetj6gyBzO_3y8Y50EfCxP6BiHKYXTGIHT3fx6eY,30947
|
|
9
|
-
alchemist_core/acquisition/skopt_acquisition.py,sha256=YRdANqgiN3GWd4sn16oruN6jVnI4RLmvLhBMUfYyLp4,13115
|
|
10
|
-
alchemist_core/data/__init__.py,sha256=wgEb03x0RzVCi0uJXOzEKXkbA2oNHom5EgSB3tKgl1E,256
|
|
11
|
-
alchemist_core/data/experiment_manager.py,sha256=kS63mVmuTNxhI3A9io52VKGcZOcgcTIyJqrzUQuZgkw,5910
|
|
12
|
-
alchemist_core/data/search_space.py,sha256=oA9YEF3JRWpRklHzSo_Uxlmfy7bHwZfLZFDf4_nl4ew,6230
|
|
13
|
-
alchemist_core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
alchemist_core/models/ax_model.py,sha256=Fnu19Et376WxLXpApZSalIbWHQE9OQnMk41_PW69XnQ,6040
|
|
15
|
-
alchemist_core/models/base_model.py,sha256=gIpC2eoTZcp4ozI0Rctcxt_4bLhgaE6ALYCzvuIrMZw,3145
|
|
16
|
-
alchemist_core/models/botorch_model.py,sha256=t-zqLRpCIGwSeIyveqJTjr8MQ9zWecnJIueaWg3Geck,42026
|
|
17
|
-
alchemist_core/models/sklearn_model.py,sha256=ID0anz8BNGKDMpQlLlzKuyey1CvaJQkf0C79Q_S3tGo,34735
|
|
18
|
-
alchemist_core/utils/__init__.py,sha256=oQsvUqukRng8GgiZSPMM-xmB-Lv46XveJzYQr2MdkOc,99
|
|
19
|
-
alchemist_nrel-0.2.1.dist-info/licenses/LICENSE,sha256=wdIWWEj59ztfQViDuT_9wG3L1K8afUpRSygimXw36wY,1511
|
|
20
|
-
api/__init__.py,sha256=ODc6pq4OSImgK4xvhX_zMhqUjIc7JvLfqxKF_-Ubw7g,49
|
|
21
|
-
api/dependencies.py,sha256=sF1YYjnFRaw7nj7Y7URKLF2Ek-EfaXqjOyV1Zbktz2g,1090
|
|
22
|
-
api/example_client.py,sha256=aZMNuJmt00hpNEkijn5RnVEV7ZPfKzwQxucSasTrrdw,6645
|
|
23
|
-
api/main.py,sha256=Y2oPwxPK_jfOMNkT9qLQnM6lNzaM9sdJhOybJVFf8UM,3403
|
|
24
|
-
api/middleware/__init__.py,sha256=WM4JEg3DibymvEvQ6hx_FJkv8lKLjHD48qNouSORGxA,313
|
|
25
|
-
api/middleware/error_handlers.py,sha256=k5hNo6W5QDjGRHUY8Le-t7ubWkwSZBFpsTKcEF0nweI,4545
|
|
26
|
-
api/models/__init__.py,sha256=YFtp8989mH9Zjzvd8W6pXklQJXTf8zWj4I2YWnLegDQ,1204
|
|
27
|
-
api/models/requests.py,sha256=wy8KCgf3cwJ2Yz2wEzQjCqkUT6x0VHQHFTQDVMfbUMU,7213
|
|
28
|
-
api/models/responses.py,sha256=c_okZVP0A8MF7aH199Bl6zjoJgede9lBrH5i8R8oPXc,8445
|
|
29
|
-
api/routers/__init__.py,sha256=Mhg62NdA6iaPy-L5HLVp_dd9aUHmJ72KtMSRyO2kusA,180
|
|
30
|
-
api/routers/acquisition.py,sha256=7c9ifuCFeDj8osWRHgCjAwbXjhd3iEBlVLW23FOAZXI,5710
|
|
31
|
-
api/routers/experiments.py,sha256=q7Y1y5-pIIu0hRZAQKTpllL3AGstjbCP-dUqYAnQ-hI,4946
|
|
32
|
-
api/routers/models.py,sha256=32Ln0MtlnCEjfN3Q6Io_EBwwwGoJXb73UbQEMIcVGjI,3651
|
|
33
|
-
api/routers/sessions.py,sha256=k4gJe8qgb-8INT04_buYAfZIfMGBxfOOxn5tcXfr6LI,4523
|
|
34
|
-
api/routers/variables.py,sha256=TiByX1ITabBDdTSMGAPa1lGd0LBipNgDfmPsbvTEAdE,10108
|
|
35
|
-
api/routers/visualizations.py,sha256=qP__NhhflNKEAEcLV0c_WMQvofoNWSORMXbEMQCuVgI,23783
|
|
36
|
-
api/services/__init__.py,sha256=0jw0tkL-8CtChv5ytdXRFeIz1OTVz7Vw1UaaDo86PQs,106
|
|
37
|
-
api/services/session_store.py,sha256=Jot2S75Qrqaw_dbfNbeJt5rwlu25abexP4BMMCIgfdE,9605
|
|
38
|
-
ui/__init__.py,sha256=H4kWlVey7KKf3iPQi74zuM7FSOg5Gh-ii3UwSTuIp8A,1203
|
|
39
|
-
ui/acquisition_panel.py,sha256=zF-mQDrs-Y7sf2GXYF-bPlO9UXZMTzYRMDN-Wn5FyWw,39647
|
|
40
|
-
ui/custom_widgets.py,sha256=UXNv4DiTw3tFC0VaN1Qtcf_-9umX34uDn46-cEA6cs0,3812
|
|
41
|
-
ui/experiment_logger.py,sha256=dP3IGaQ31sURyz7awd_VrZBWaKLH2xXEeRalWZpvVcQ,8366
|
|
42
|
-
ui/gpr_panel.py,sha256=cXZbYndkZ7E6GowkorYqBHVStpxEKDVJv8S-ZPzGGRQ,26230
|
|
43
|
-
ui/notifications.py,sha256=evVd9r7OMqaZyx2WWXFnL-moIjrDFmRoMKrX9hvKB_g,28003
|
|
44
|
-
ui/pool_viz.py,sha256=RwjggEfRSSEe-4nGjxc-I-1e5_aH64DgypT_YoubLIU,8765
|
|
45
|
-
ui/ui.py,sha256=tJZoiBRACjY07NPrDdqYJc8we1Ze4fqnxJVUCHVkFZ8,54945
|
|
46
|
-
ui/ui_utils.py,sha256=yud2-9LvT4XBcjTyfwUX5tYGNZRlAUVlu2YpcNY1HKA,658
|
|
47
|
-
ui/utils.py,sha256=m19YFFkEUAY46YSj6S5RBmfUFjIWOk7F8CB4oKDRRZw,1078
|
|
48
|
-
ui/variables_setup.py,sha256=4pu91MNRZlsK-DwRNd7U619wKDdSknI5Cf744o5Lxqo,21673
|
|
49
|
-
ui/visualizations.py,sha256=X3Arl1k2UoQK2DCQ8YBnoJ4fscvinAlPJ1hInYkKvGo,67686
|
|
50
|
-
alchemist_nrel-0.2.1.dist-info/METADATA,sha256=AAMdcL8KyjGcyjH9Ci2pqJOLenJSZ29b_CDigeK99Ak,6892
|
|
51
|
-
alchemist_nrel-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
52
|
-
alchemist_nrel-0.2.1.dist-info/entry_points.txt,sha256=zTuR1EjHMjeeAtUOACu0njiTCBMoaCobFqLlQ7IPYs8,40
|
|
53
|
-
alchemist_nrel-0.2.1.dist-info/top_level.txt,sha256=dwh-oxj7H6oAGYchcUDyfiu9UxxzCn4hUDx1oeM2k-8,27
|
|
54
|
-
alchemist_nrel-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|