Metadata-Version: 2.4
Name: synbo
Version: 0.6.1
Summary: A multi-objective reaction optimization framework based on Bayesian Optimization
Author-email: Zhenzhi Tan <tanzhenzhi8@gmail.com>
Maintainer-email: Zhenzhi Tan <tanzhenzhi8@gmail.com>
License: MIT License
        
        Copyright (c) 2025 ReactionOpt
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
Project-URL: Bug Tracker, https://github.com/yourusername/reactionopt/issues
Project-URL: Documentation, https://reactionopt.readthedocs.io
Project-URL: Homepage, https://github.com/yourusername/reactionopt
Project-URL: Repository, https://github.com/yourusername/reactionopt
Keywords: chemistry,reaction optimization,bayesian optimization,machine learning,catalysis
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.20.0
Requires-Dist: pandas>=1.3.0
Requires-Dist: scikit-learn>=1.0.0
Requires-Dist: matplotlib>=3.3.0
Requires-Dist: seaborn>=0.11.0
Requires-Dist: openpyxl>=3.0.0
Requires-Dist: tqdm>=4.60.0
Requires-Dist: rich>=13.0.0
Requires-Dist: typer>=0.9.0
Requires-Dist: loguru>=0.7.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: torch>=2.8.0
Requires-Dist: botorch>=0.15.0
Requires-Dist: ax-platform>=0.2.0
Requires-Dist: rdkit>=2024.9.1
Requires-Dist: pyDOE>=0.3.8
Requires-Dist: lxml>=6.0.2
Requires-Dist: epam.indigo>=1.36.1
Requires-Dist: xgboost>=2.0.0
Requires-Dist: hyperopt>=0.2.7
Requires-Dist: openai>=2.20.0
Requires-Dist: tabulate>=0.10.0
Provides-Extra: dev
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: pytest-cov>=2.0; extra == "dev"
Requires-Dist: black>=21.0; extra == "dev"
Requires-Dist: flake8>=3.8; extra == "dev"
Requires-Dist: mypy>=0.800; extra == "dev"
Requires-Dist: pre-commit>=2.10; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=4.0; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
Requires-Dist: myst-parser>=0.15; extra == "docs"
Provides-Extra: test
Requires-Dist: pytest>=6.0; extra == "test"
Requires-Dist: pytest-cov>=2.0; extra == "test"
Requires-Dist: pytest-mock>=3.0; extra == "test"
Dynamic: license-file

# SynBO: Synthetic Bayesian Optimization for Reaction Condition Screening

[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**SynBO** (Synthetic Bayesian Optimization) is an intelligent reaction optimization framework that uses Bayesian Optimization to find optimal reaction conditions with minimal experimental effort.

---

## 🔬 Why SynBO?

Optimizing a chemical reaction typically involves screening dozens to hundreds of condition combinations (catalysts, solvents, bases, temperature, etc.). Traditional OFAT (One-Factor-At-A-Time) quickly becomes impractical — e.g., **5 catalysts × 5 solvents × 4 bases × 4 temperatures = 400 combinations**.

SynBO learns from previous experiments and intelligently recommends the next batch of conditions to test. Typically, only **50–80 experiments** are needed to find optimal conditions.

---

## 📁 Example Project: Cobalt-Catalyzed Asymmetric Reaction

The [`examples/`](examples/) directory contains a complete, runnable example of a cobalt-catalyzed reaction optimization with **5 reagent types** and **2 objectives** (yield + ee):

```
examples/
├── optimization_settings.json          # Optimization goals & settings
├── rxn_space/                          # Reaction space definitions
│   ├── alkali.csv                      #   9 alkali/additive options
│   ├── cobalt_catalyst.csv             #   8 Co-catalyst candidates
│   ├── organo_catalyst.csv             #   9 organocatalyst candidates
│   ├── oxidant.csv                     #   9 oxidant options
│   └── solvent.csv                     # 10 solvent options
├── descriptors/                        # RDKit molecular descriptors
│   ├── alkali_RDKit.csv
│   ├── cobalt_catalyst_RDKit.csv
│   ├── organo_catalyst_RDKit.csv
│   ├── oxidant_RDKit.csv
│   └── solvent_RDKit.csv
└── results/                            # Example optimization outputs
    ├── batch-0_20260420.csv            # Initial sampling results
    ├── batch-0_20260420.xlsx
    ├── batch-1_20260420.csv            # 1st optimization round results
    └── batch-1_20260420.xlsx
```

**Reaction space size**: 9 × 8 × 9 × 9 × 10 = **58,320 possible combinations**

### Step-by-Step Workflow

#### 1. Define Your Reaction Space

Create CSV files for each reagent/condition type under `rxn_space/`. Each file must contain `SMILES` and `name` columns:

```csv
# rxn_space/solvent.csv
SMILES,name
ClCCl,DCM
CC#N,CH3CN
C1CCOC1,THF
...
```

#### 2. Generate Molecular Descriptors

```bash
python scripts/get_desc.py --input rxn_space/solvent.csv --smiles-col 'SMILES' --name-col 'name'
```

Repeat for each reagent type. Outputs go to `descriptors/{reagent}_RDKit.csv`.

#### 3. Define Optimization Goals

Create `optimization_settings.json`:

```json
{
    "reagent_types": ["alkali", "cobalt_catalyst", "organo_catalyst", "oxidant", "solvent"],
    "opt_metrics": ["yield", "ee"],
    "opt_direct_info": [
        {"opt_direct": "max", "opt_range": [0, 100], "metric_weight": 1.0},
        {"opt_direct": "max", "opt_range": [0, 100], "metric_weight": 1.0}
    ]
}
```

#### 4. Initialize — Generate First Batch

**CLI:**
```bash
python scripts/initialize.py --project-dir examples --batch-size 8 --sampling-method lhs
```

**Python API:**
```python
from synbo import ReactionOptimizer
from synbo.utils import load_desc_dict

desc_dict, condition_dict = load_desc_dict(
    reagent_types=["alkali", "cobalt_catalyst", "organo_catalyst", "oxidant", "solvent"],
    desc_dir="examples/descriptors",
    name_suffix="_RDKit",
    index_col="name",
    return_condition_dict=True,
)

optimizer = ReactionOptimizer(
    opt_metrics=["yield", "ee"],
    opt_type="init",
    random_seed=42,
    save_dir="examples/results",
)
optimizer.load_rxn_space(condition_dict)
optimizer.load_desc(desc_dict)
optimizer.initialize(batch_size=8, sampling_method="lhs")
optimizer.save_results(filetype="excel")
```

#### 5. Run Experiments & Record Results

Run the recommended experiments in the lab. Fill in the `yield` and `ee` columns in the output file (replace `[exp_data]` with actual measurements).

#### 6. Optimize — Get the Next Batch

**CLI:**
```bash
python scripts/optimize.py --project-dir examples --batch-size 5
```

**Python API:**
```python
from synbo.utils import get_prev_rxn

prev_data = get_prev_rxn("examples/results", "batch-*.csv")

optimizer = ReactionOptimizer(
    opt_metrics=["yield", "ee"],
    opt_type="auto",
    random_seed=42,
    save_dir="examples/results",
)
optimizer.load_rxn_space(condition_dict)
optimizer.load_desc(desc_dict)
optimizer.load_prev_rxn(prev_data)
optimizer.optimize(batch_size=5)
optimizer.save_results(filetype="excel")
```

#### 7. Repeat Steps 5–6 Until Satisfactory Results

---

## 📊 Jupyter Notebook Demo

An interactive Jupyter notebook demonstrating the full optimization workflow with visualizations is available at **[examples/demo_optimization.ipynb](examples/demo_optimization.ipynb)**. It covers:

- Loading the example reaction space and descriptors
- Running initialization and optimization rounds
- Visualizing the Pareto front (yield vs ee trade-off)
- Tracking optimization progress with Hypervolume metrics
- Interpreting explore vs exploit recommendations

> **Run it**: `jupyter notebook examples/demo_optimization.ipynb`

---

## 🚀 Quick Start

### Installation

```bash
pip install synbo
```

### Minimal Python Example

```python
from synbo import ReactionOptimizer

optimizer = ReactionOptimizer(
    opt_metrics=['yield', 'ee'],
    opt_type='auto',
    random_seed=42
)

# Load reaction space
optimizer.load_rxn_space({
    'catalyst': ['Pd(OAc)2', 'Pd(PPh3)4', 'Pd2(dba)3'],
    'solvent': ['THF', 'Dioxane', 'Toluene', 'DMF', 'MeCN'],
    'base': ['Cs2CO3', 'K2CO3', 'NaOEt', 'DBU'],
    'temperature': [25, 50, 80, 100]
})

# Use OneHot encoding (auto-generated when no descriptors provided)
optimizer.load_desc()

# Initial sampling
optimizer.run(batch_size=8)
optimizer.save_results(filetype='csv')

# After experiments, load results and optimize
# optimizer.load_prev_rxn(pd.read_csv('results.csv'))
# optimizer.run(batch_size=5)
```

---

## 🧪 Python API Reference

### ReactionOptimizer

```python
optimizer = ReactionOptimizer(
    opt_metrics=["yield", "ee"],
    opt_metric_settings=[
        {"opt_direct": "max", "opt_range": [0, 100], "metric_weight": 1.0},
        {"opt_direct": "max", "opt_range": [0, 100], "metric_weight": 1.0},
    ],
    opt_type="auto",    # "init" | "opt" | "auto"
    random_seed=42,
    save_dir="./results",
)
```

### Key Methods

| Method | Description |
|--------|-------------|
| `load_rxn_space(condition_dict)` | Load reaction space (all possible reagent combinations) |
| `load_desc(desc_dict=None)` | Load molecular descriptors (OneHot encoding used if None) |
| `load_prev_rxn(df)` | Load previous experimental results for optimization |
| `initialize(batch_size, sampling_method)` | Generate initial batch (LHS/Sobol/K-Means/Random) |
| `optimize(batch_size, constraints)` | Run Bayesian optimization to recommend next batch |
| `save_results(filetype)` | Save recommendations to CSV/Excel/JSON |
| `calculate_current_hv()` | Calculate current Hypervolume (multi-objective progress) |
| `calculate_hv_by_batch()` | Track Hypervolume across optimization rounds |

---

## 📈 Understanding Optimization Results

### Predictions with Uncertainty

Output files include predicted values with uncertainties:

| batch | alkali | cobalt_catalyst | ... | pred yield | pred ee | yield | ee |
|-------|--------|-----------------|-----|------------|---------|-------|-----|
| 1 | DBU | [Co]-5 | ... | 62.35±3.12 | 85.20±2.87 | [exp_data] | [exp_data] |

- **`pred yield` / `pred ee`**: Model prediction ± uncertainty
- **`[exp_data]`**: Placeholder for your experimental results

### Explore vs Exploit

- **EXPLORE**: Testing new areas of the reaction space
- **EXPLOIT**: Refining near known good results

### Hypervolume Tracking

```python
hv = optimizer.calculate_current_hv()
print(f"Progress: {hv['hv_normalized']*100:.1f}%")
history = optimizer.calculate_hv_by_batch()
```

---

## ⚙️ Advanced Features

### Reaction Constraints

```python
constraints = {"alkali": ["DBU"], "solvent": ["DMSO"]}
optimizer.optimize(batch_size=5, constraints=constraints)
```

Or use `prohibited_reagent.json` for automatic loading.

### GPU Acceleration

SynBO auto-detects GPU. Force CPU: `optimizer.optimize(batch_size=5, device="cpu")`

### Excel Output with Molecular Structures

```python
optimizer.save_results(
    filetype="excel",
    figure_output=["cobalt_catalyst", "organo_catalyst"],
    figure_path="examples/figures",
)
```

---

## 🔧 CLI Quick Reference

```bash
synbo --version
synbo create-config -o my_config.json
synbo validate my_config.json
synbo init my_config.json -b 8 -m lhs -o results/
synbo optimize my_config.json results/batch-0.csv -b 5 -o results/
```

---

## 📦 Dependencies

Core: numpy, pandas, scikit-learn, torch, botorch | Chemistry: rdkit, epam.indigo | CLI: typer, rich | Viz: matplotlib, seaborn

See [`pyproject.toml`](pyproject.toml) for the complete list.

---

## 📚 Citation

```bibtex
@software{synbo2025,
  title={SynBO: Synthetic Bayesian Optimization for Chemical Reaction Optimization},
  author={Zhenzhi Tan},
  year={2025},
  url={https://github.com/yourusername/synbo}
}
```

---

## 📧 Contact

- **Author**: Zhenzhi Tan
- **Email**: zhenzhi-tan@outlook.com

---

**Happy Synthesizing! 🧪⚗️**
