Your first Scipy optimization with Blop#

In this tutorial, you will learn the three core concepts of Blop: DOFs (the parameters you can adjust), objectives (what you want to optimize), and the Agent (which coordinates the optimization). We’ll optimize a simple mathematical function using simulated devices—the same patterns apply to real hardware.

Setup#

First, let’s import what we need and start the data infrastructure:

import logging
import time
from typing import Any

from bluesky.protocols import HasHints, HasParent, Hints, NamedMovable, Readable, Status
from bluesky.run_engine import RunEngine
from bluesky_tiled_plugins import TiledWriter
from tiled.client import from_uri
from tiled.client.container import Container
from tiled.server import SimpleTiledServer

from blop.scipy import SCP, ScipyCFG, Objective, RangeDOF, Scipy

# Suppress noisy logs from httpx 
logging.getLogger("httpx").setLevel(logging.WARNING)
# Start a local Tiled server for data storage
tiled_server = SimpleTiledServer()

# Set up the Bluesky RunEngine and connect it to Tiled
RE = RunEngine({})
tiled_client = from_uri(tiled_server.uri)
tiled_writer = TiledWriter(tiled_client)
RE.subscribe(tiled_writer)
Tiled version 0.2.15
0

Creating simulated devices#

Bluesky controls devices through protocols. For this tutorial, we create simple simulated “movable” devices. In real experiments, you would use Ophyd devices or similar—the code below is just boilerplate to simulate hardware:

class AlwaysSuccessfulStatus(Status):
    def add_callback(self, callback) -> None:
        callback(self)

    def exception(self, timeout=0.0):
        return None

    @property
    def done(self) -> bool:
        return True

    @property
    def success(self) -> bool:
        return True


class ReadableSignal(Readable, HasHints, HasParent):
    def __init__(self, name: str) -> None:
        self._name = name
        self._value = 0.0

    @property
    def name(self) -> str:
        return self._name

    @property
    def hints(self) -> Hints:
        return {"fields": [self._name], "dimensions": [], "gridding": "rectilinear"}

    @property
    def parent(self) -> Any | None:
        return None

    def read(self):
        return {self._name: {"value": self._value, "timestamp": time.time()}}

    def describe(self):
        return {self._name: {"source": self._name, "dtype": "number", "shape": []}}


class MovableSignal(ReadableSignal, NamedMovable):
    def __init__(self, name: str, initial_value: float = 0.0) -> None:
        super().__init__(name)
        self._value: float = initial_value

    def set(self, value: float) -> Status:
        self._value = value
        return AlwaysSuccessfulStatus()

Defining DOFs and objectives#

DOFs (degrees of freedom) are the parameters the optimizer can adjust. Objectives are what you want to optimize. Here we define two DOFs (x1 and x2) that can range from -5 to 5, and one objective (the Himmelblau function) that we want to minimize:

x1 = MovableSignal("x1", initial_value=0.1)
x2 = MovableSignal("x2", initial_value=0.23)

dofs = [
    RangeDOF(actuator=x1, bounds=(-5, 5), parameter_type="float"),
    RangeDOF(actuator=x2, bounds=(-5, 5), parameter_type="float"),
]
objectives = [
    Objective(name="himmelblau_2d", minimize=True),
]
sensors = []

Writing the evaluation function#

The evaluation function computes objective values from experimental data. After each run, Blop calls this function with the run’s unique ID and the suggestions that were tried. It returns the computed objective values:

class Himmelblau2DEvaluation:
    def __init__(self, tiled_client: Container):
        self.tiled_client = tiled_client

    def __call__(self, uid: str, suggestions: list[dict]) -> list[dict]:
        run = self.tiled_client[uid]
        outcomes = []
        reordered_suggestions = run.start["blop_suggestions"]
        x1_data = run["primary/x1"].read()
        x2_data = run["primary/x2"].read()

        print(
            "[Himmelblau] evaluating suggestions: ",
            [s["_id"] for s in suggestions],
            " reordered to: ",
            [s["_id"] for s in reordered_suggestions],
        )
        for index, suggestion in enumerate(reordered_suggestions):
            # Special key to identify a suggestion
            suggestion_id = suggestion["_id"]
            x1 = x1_data[index]
            x2 = x2_data[index]
            # Himmelblau function: has four global minima where value = 0
            outcomes.append({"himmelblau_2d": (x1**2 + x2 - 11) ** 2 + (x1 + x2**2 - 7) ** 2, "_id": suggestion_id})

        return outcomes

Running the optimization#

The Agent brings everything together. Create one with your DOFs, objectives, and evaluation function, then run the optimization:

agent = Scipy.Agent(
    sensors=sensors,
    dofs=dofs,
    objectives=objectives,
    evaluation_function=Himmelblau2DEvaluation(tiled_client=tiled_client),
    name="simple-experiment",
    description="A simple experiment optimizing the Himmelblau function",
)

RE(agent.optimize(10))

╭───────────────────────────────────────────────── Optimization ──────────────────────────────────────────────────╮
 Optimizer  InteractiveOptimizer                                                                                 
 Actuators  x1, x2                                                                                               
 Sensors    N/A                                                                                                  
 Iterations 10                                                                                                   
 Run UID    afb6261d-8c36-476d-bdf1-64a7401f3b7a                                                                 
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
[Himmelblau] evaluating suggestions:
[0]
 reordered to:
[0]
[Himmelblau] evaluating suggestions:
[1]
 reordered to:
[1]
[Himmelblau] evaluating suggestions:
[2]
 reordered to:
[2]
[Himmelblau] evaluating suggestions:
[3]
 reordered to:
[3]
[Himmelblau] evaluating suggestions:
[4]
 reordered to:
[4]
[Himmelblau] evaluating suggestions:
[5]
 reordered to:
[5]
[Himmelblau] evaluating suggestions:
[6]
 reordered to:
[6]
[Himmelblau] evaluating suggestions:
[7]
 reordered to:
[7]
[Himmelblau] evaluating suggestions:
[8]
 reordered to:
[8]
[Himmelblau] evaluating suggestions:
[9]
 reordered to:
[9]
──────────────────────────────────────────────── Iteration 1 / 10 ─────────────────────────────────────────────────
  Acquire UID  fb5af019-9918-4c36-b903-61f227768dc8
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID  x1  x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             0 │  0   0            170 
└───────┴───────────────┴────┴────┴───────────────┘
  himmelblau_2d  min: 170  max: 170  mean: 170
  (1 pts sampled)
──────────────────────────────────────────────── Iteration 2 / 10 ─────────────────────────────────────────────────
  Acquire UID  a0eafc42-52eb-4294-9f77-509d37ccda1a
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID     x1  x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             1 │ 1e-08   0            170 
└───────┴───────────────┴───────┴────┴───────────────┘
  himmelblau_2d  min: 170  max: 170  mean: 170
  (2 pts sampled)
──────────────────────────────────────────────── Iteration 3 / 10 ─────────────────────────────────────────────────
  Acquire UID  e791f3bb-3d4c-4638-8869-0c586cd55e0d
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID  x1     x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             2 │  0  1e-08            170 
└───────┴───────────────┴────┴───────┴───────────────┘
  himmelblau_2d  min: 170  max: 170  mean: 170
  (3 pts sampled)
──────────────────────────────────────────────── Iteration 4 / 10 ─────────────────────────────────────────────────
  Acquire UID  0d67c43d-1380-4fa2-ab93-efa73762ea15
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID  x1  x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             3 │  5   5            890 
└───────┴───────────────┴────┴────┴───────────────┘
  himmelblau_2d  min: 170  max: 890  mean: 350
  (4 pts sampled)
──────────────────────────────────────────────── Iteration 5 / 10 ─────────────────────────────────────────────────
  Acquire UID  eff94fae-a1ac-48a4-9f12-524c2fce1872
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID  x1  x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             4 │  5   5            890 
└───────┴───────────────┴────┴────┴───────────────┘
  himmelblau_2d  min: 170  max: 890  mean: 458
  (5 pts sampled)
──────────────────────────────────────────────── Iteration 6 / 10 ─────────────────────────────────────────────────
  Acquire UID  038e33d0-f141-4a4f-bc5b-3248c9d88347
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID  x1  x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             5 │  5   5            890 
└───────┴───────────────┴────┴────┴───────────────┘
  himmelblau_2d  min: 170  max: 890  mean: 530
  (6 pts sampled)
──────────────────────────────────────────────── Iteration 7 / 10 ─────────────────────────────────────────────────
  Acquire UID  d92a02f0-8300-4491-b01e-123b24419665
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             6 │ 1.51545  1.51545        61.8302 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 61.8302  max: 890  mean: 463.119
  (7 pts sampled)
──────────────────────────────────────────────── Iteration 8 / 10 ─────────────────────────────────────────────────
  Acquire UID  285f5a14-5bd7-490e-8468-7913a5f0a5c0
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             7 │ 1.51545  1.51545        61.8302 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 61.8302  max: 890  mean: 412.958
  (8 pts sampled)
──────────────────────────────────────────────── Iteration 9 / 10 ─────────────────────────────────────────────────
  Acquire UID  d964e31f-d99c-4a24-981a-e035e1cecebd
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             8 │ 1.51545  1.51545        61.8302 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 61.8302  max: 890  mean: 373.943
  (9 pts sampled)
──────────────────────────────────────────────── Iteration 10 / 10 ────────────────────────────────────────────────
  Acquire UID  027d2d0f-60bb-4d9e-854b-db07d101562b
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             9 │ 3.25772  3.25772        55.4432 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 55.4432  max: 890  mean: 342.093
  (10 pts sampled)

                          Summary Statistics                           
┏━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━┓
 Name           Type         Min  Max     Mean      Std  Count 
┡━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━┩
 x1            │ param   │       0    5  2.28041  2.12132 │    10 │
 x2            │ param   │       0    5  2.28041  2.12132 │    10 │
 himmelblau_2d │ outcome │ 55.4432  890  342.093  381.119 │    10 │
└───────────────┴─────────┴─────────┴─────┴─────────┴─────────┴───────┘
────────────────────────────────────────────── Optimization Complete ──────────────────────────────────────────────

('afb6261d-8c36-476d-bdf1-64a7401f3b7a',
 'fb5af019-9918-4c36-b903-61f227768dc8',
 'a0eafc42-52eb-4294-9f77-509d37ccda1a',
 'e791f3bb-3d4c-4638-8869-0c586cd55e0d',
 '0d67c43d-1380-4fa2-ab93-efa73762ea15',
 'eff94fae-a1ac-48a4-9f12-524c2fce1872',
 '038e33d0-f141-4a4f-bc5b-3248c9d88347',
 'd92a02f0-8300-4491-b01e-123b24419665',
 '285f5a14-5bd7-490e-8468-7913a5f0a5c0',
 'd964e31f-d99c-4a24-981a-e035e1cecebd',
 '027d2d0f-60bb-4d9e-854b-db07d101562b')

Configuring the optimization#

Sometimes a default Agent optimization may not do all that you’d like. We expose a configuration object called ScipyCFG and a pure scipy interface so that the classic parameters of scipy minimize can be tweaked (and some multipoint sampling can be used).

config = ScipyCFG(dofs=dofs, objective=objectives[0], optimizer=SCP.DUAL_ANNEALING, threads=4, max_iter=2, eps=0.1)
agent = Scipy(
    sensors=sensors,
    config=config,
    evaluation_function=Himmelblau2DEvaluation(tiled_client=tiled_client),
    name="test_experiment",
)
res_uid = RE(agent.optimize(20, n_points=2))

╭───────────────────────────────────────────────── Optimization ──────────────────────────────────────────────────╮
 Optimizer  InteractiveOptimizer                                                                                 
 Actuators  x1, x2                                                                                               
 Sensors    N/A                                                                                                  
 Iterations 20  Points/iter 2                                                                                    
 Run UID    0ae83a41-10c8-4f8f-bb42-f25c1cf81107                                                                 
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
[Himmelblau] evaluating suggestions:
[0]
 reordered to:
[0]
[Himmelblau] evaluating suggestions:
[1]
 reordered to:
[1]
[Himmelblau] evaluating suggestions:
[2]
 reordered to:
[2]
[Himmelblau] evaluating suggestions:
[3]
 reordered to:
[3]
[Himmelblau] evaluating suggestions:
[4]
 reordered to:
[4]
[Himmelblau] evaluating suggestions:
[5]
 reordered to:
[5]
[Himmelblau] evaluating suggestions:
[6, 7]
 reordered to:
[6, 7]
[Himmelblau] evaluating suggestions:
[8]
 reordered to:
[8]
[Himmelblau] evaluating suggestions:
[9, 10]
 reordered to:
[9, 10]
[Himmelblau] evaluating suggestions:
[11]
 reordered to:
[11]
[Himmelblau] evaluating suggestions:
[12, 13]
 reordered to:
[13, 12]
[Himmelblau] evaluating suggestions:
[14]
 reordered to:
[14]
[Himmelblau] evaluating suggestions:
[15, 16]
 reordered to:
[15, 16]
[Himmelblau] evaluating suggestions:
[17]
 reordered to:
[17]
[Himmelblau] evaluating suggestions:
[18, 19]
 reordered to:
[18, 19]
[Himmelblau] evaluating suggestions:
[20]
 reordered to:
[20]
[Himmelblau] evaluating suggestions:
[21, 22]
 reordered to:
[21, 22]
[Himmelblau] evaluating suggestions:
[23]
 reordered to:
[23]
[Himmelblau] evaluating suggestions:
[24, 25]
 reordered to:
[25, 24]
[Himmelblau] evaluating suggestions:
[26]
 reordered to:
[26]
──────────────────────────────────────────────── Iteration 1 / 20 ─────────────────────────────────────────────────
  Acquire UID  90c16f39-1638-47c3-81a7-74d4a56382d8
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID  x1  x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             0 │  0   0            170 
└───────┴───────────────┴────┴────┴───────────────┘
  himmelblau_2d  min: 170  max: 170  mean: 170
  (1 pts sampled)
──────────────────────────────────────────────── Iteration 2 / 20 ─────────────────────────────────────────────────
  Acquire UID  df3976e0-284a-4fec-8406-a4d974aea060
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             1 │ 2.53419  2.53419        8.00382 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 8.00382  max: 170  mean: 89.0019
  (2 pts sampled)
──────────────────────────────────────────────── Iteration 3 / 20 ─────────────────────────────────────────────────
  Acquire UID  04f90002-c3a3-4bba-a3a6-ab483a5b2f6d
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID        x1        x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             2 │ -3.85909  -1.68838        68.9938 
└───────┴───────────────┴──────────┴──────────┴───────────────┘
  himmelblau_2d  min: 8.00382  max: 170  mean: 82.3325
  (3 pts sampled)
──────────────────────────────────────────────── Iteration 4 / 20 ─────────────────────────────────────────────────
  Acquire UID  f6f17f27-7518-40cd-8776-9103bb9fb100
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1        x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             3 │ 2.35455  -1.68838        54.2647 
└───────┴───────────────┴─────────┴──────────┴───────────────┘
  himmelblau_2d  min: 8.00382  max: 170  mean: 75.3156
  (4 pts sampled)
/home/runner/work/blop/blop/.pixi/envs/docs/lib/python3.13/site-packages/scipy/optimize/_dual_annealing.py:434: OptimizeWarning: Unknown solver options: max_iter
  mres = self.minimizer(self.func_wrapper.fun, x, **self.kwargs)
──────────────────────────────────────────────── Iteration 5 / 20 ─────────────────────────────────────────────────
  Acquire UID  b553f3c6-eb99-455e-9d95-d2582f5696ea
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1        x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             4 │ 2.35455  0.277822        47.6834 
└───────┴───────────────┴─────────┴──────────┴───────────────┘
  himmelblau_2d  min: 8.00382  max: 170  mean: 69.7891
  (5 pts sampled)
──────────────────────────────────────────────── Iteration 6 / 20 ─────────────────────────────────────────────────
  Acquire UID  57fac794-95e1-491f-a103-b81493b04b7b
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             5 │ 2.53419  2.53419        8.00382 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 8.00382  max: 170  mean: 59.4916
  (6 pts sampled)
────────────────────────────────────────── Iteration 7 / 20  (2 points) ───────────────────────────────────────────
  Acquire UID  0641c89d-740f-4fa0-87d4-d9f62981240c
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             6 │ 2.63419  2.53419        6.55967 
│     1 │             7 │ 2.53419  2.63419        9.89436 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 6.55967  max: 170  mean: 46.6754
  (8 pts sampled)
──────────────────────────────────────────────── Iteration 8 / 20 ─────────────────────────────────────────────────
  Acquire UID  afd72c0d-8ef2-478c-aa4b-8f4519f30934
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID  x1  x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━╇━━━━━━━━━━━━━━━┩
│     0 │             8 │  5  -5            610 
└───────┴───────────────┴────┴────┴───────────────┘
  himmelblau_2d  min: 6.55967  max: 610  mean: 109.267
  (9 pts sampled)
────────────────────────────────────────── Iteration 9 / 20  (2 points) ───────────────────────────────────────────
  Acquire UID  3db999c2-4b7f-4814-b728-2ff3ec6cf4eb
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID   x1    x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            10 │ 4.9    -5         588.57 
│     1 │             9 │   5  -4.9         567.25 
└───────┴───────────────┴─────┴──────┴───────────────┘
  himmelblau_2d  min: 6.55967  max: 610  mean: 194.475
  (11 pts sampled)
──────────────────────────────────────────────── Iteration 10 / 20 ────────────────────────────────────────────────
  Acquire UID  534fc451-f846-4bb3-97dc-5bc42b38d5d8
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1        x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            11 │ 3.28813  0.230532         13.388 
└───────┴───────────────┴─────────┴──────────┴───────────────┘
  himmelblau_2d  min: 6.55967  max: 610  mean: 179.384
  (12 pts sampled)
────────────────────────────────────────── Iteration 11 / 20  (2 points) ──────────────────────────────────────────
  Acquire UID  4d08a73d-32fa-4797-8b90-1ff5ffb3a463
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1        x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            12 │ 3.28813  0.330532        12.9991 
│     1 │            13 │ 3.38813  0.230532        13.1686 
└───────┴───────────────┴─────────┴──────────┴───────────────┘
  himmelblau_2d  min: 6.55967  max: 610  mean: 155.627
  (14 pts sampled)
──────────────────────────────────────────────── Iteration 12 / 20 ────────────────────────────────────────────────
  Acquire UID  ee73acd1-9c6b-43e4-8450-82c41c296b87
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            14 │ 2.75716  1.85291        3.04297 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 3.04297  max: 610  mean: 145.455
  (15 pts sampled)
────────────────────────────────────────── Iteration 13 / 20  (2 points) ──────────────────────────────────────────
  Acquire UID  ec1140a7-0146-42d1-b0bf-914addcf8af3
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            15 │ 2.75716  1.95291        2.27256 
│     1 │            16 │ 2.85716  1.85291        1.47124 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 1.47124  max: 610  mean: 128.563
  (17 pts sampled)
──────────────────────────────────────────────── Iteration 14 / 20 ────────────────────────────────────────────────
  Acquire UID  dc268829-bcdb-4cf4-ae79-11b65f91ebb4
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            17 │ 3.17632  1.89836        1.02331 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 1.02331  max: 610  mean: 121.477
  (18 pts sampled)
────────────────────────────────────────── Iteration 15 / 20  (2 points) ──────────────────────────────────────────
  Acquire UID  a630f2d2-d886-4642-983b-fc9699675752
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            18 │ 3.27632  1.89836        2.67996 
│     1 │            19 │ 3.17632  1.99836        1.21125 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 1.02331  max: 610  mean: 109.524
  (20 pts sampled)
──────────────────────────────────────────────── Iteration 16 / 20 ────────────────────────────────────────────────
  Acquire UID  bd5c6fcc-3b0b-498b-a9e6-501f656519cf
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            20 │ 2.96537  1.87549       0.377091 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 0.377091  max: 610  mean: 104.327
  (21 pts sampled)
────────────────────────────────────────── Iteration 17 / 20  (2 points) ──────────────────────────────────────────
  Acquire UID  89446ba6-f7aa-4e4e-87b0-8df503d62033
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            21 │ 2.96537  1.97549       0.070846 
│     1 │            22 │ 3.06537  1.87549       0.248015 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 0.070846  max: 610  mean: 95.2686
  (23 pts sampled)
──────────────────────────────────────────────── Iteration 18 / 20 ────────────────────────────────────────────────
  Acquire UID  aa919146-f17e-4579-be8d-c3f9dcecde38
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            23 │ 2.96552  1.94781       0.124337 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 0.070846  max: 610  mean: 91.3042
  (24 pts sampled)
────────────────────────────────────────── Iteration 19 / 20  (2 points) ──────────────────────────────────────────
  Acquire UID  f1aa60fb-f851-459d-a366-6af2c7421fcf
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            24 │ 2.96552  2.04781      0.0502168 
│     1 │            25 │ 3.06552  1.94781       0.138937 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 0.0502168  max: 610  mean: 84.2881
  (26 pts sampled)
──────────────────────────────────────────────── Iteration 20 / 20 ────────────────────────────────────────────────
  Acquire UID  0ebbea11-fba4-4308-87b3-60d6eb4a8800
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓
 Event  Suggestion ID       x1       x2  himmelblau_2d 
┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│     0 │            26 │ 2.95548  1.97711       0.101337 
└───────┴───────────────┴─────────┴─────────┴───────────────┘
  himmelblau_2d  min: 0.0502168  max: 610  mean: 81.1701
  (27 pts sampled)

                             Summary Statistics                              
┏━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━┓
 Name           Type           Min      Max     Mean      Std  Count 
┡━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━┩
 x1            │ param   │  -3.85909        5  2.77407  1.62322 │    27 │
 x2            │ param   │        -5  2.63419  0.67149  2.34147 │    27 │
 himmelblau_2d │ outcome │ 0.0502168      610  81.1701  186.268 │    27 │
└───────────────┴─────────┴───────────┴─────────┴─────────┴─────────┴───────┘
────────────────────────────────────────────── Optimization Complete ──────────────────────────────────────────────

Viewing the results#

Scipy is a local optimizer so it doesn’t have internal point tracking, but we can to grab it from our datastore.

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

res_client = tiled_client[res_uid[0]]
data = res_client["primary/internal"].read()
cols = ["suggestion_ids", "x1", "x2", "himmelblau_2d"]
vec = data[cols]
res = []
for _, row in vec.iterrows():
    vic = [row.suggestion_ids, row.x1, row.x2, row.himmelblau_2d]
    vic = [x.strip("[]").split() for x in vic]
    for id, x, y, obj in zip(*vic, strict=True):
        if id != "''":
            res.append([int(id.strip("'")), float(x), float(y), float(obj)])
res = np.array(res)

fig, ax = plt.subplots(figsize=(12, 8))

xb, yb = np.random.uniform(-5, 5, (2, 1000))
ax.tripcolor(xb, yb, (xb**2 + yb - 11) ** 2 + (xb + yb**2 - 7) ** 2, shading="gouraud")

i, x, y, z = res.T
ps = ax.scatter(x, y, c=range(len(x)), cmap="plasma", s=50)
plt.colorbar(ps).set_label("sample index")
plt.title("Visualizing Scipy's traversal of Himmelblau")
Text(0.5, 1.0, "Visualizing Scipy's traversal of Himmelblau")
../_images/2c0a7bf6660c2ea23aaaaef726c3b6e98cf04e291494dec67b7786fefe2128dc.png

Seeing the sample history

pd.DataFrame(data=res, columns=cols)
suggestion_ids x1 x2 himmelblau_2d
0 0.0 0.000000 0.000000 170.000000
1 1.0 2.534186 2.534186 8.003822
2 2.0 -3.859086 -1.688380 68.993793
3 3.0 2.354553 -1.688380 54.264720
4 4.0 2.354553 0.277822 47.683403
5 5.0 2.534186 2.534186 8.003822
6 6.0 2.634186 2.534186 6.559665
7 7.0 2.534186 2.634186 9.894364
8 8.0 5.000000 -5.000000 610.000000
9 10.0 4.900000 -5.000000 588.570100
10 9.0 5.000000 -4.900000 567.250100
11 11.0 3.288134 0.230532 13.388033
12 12.0 3.288134 0.330532 12.999098
13 13.0 3.388134 0.230532 13.168573
14 14.0 2.757156 1.852911 3.042967
15 15.0 2.757156 1.952911 2.272564
16 16.0 2.857156 1.852911 1.471237
17 17.0 3.176324 1.898359 1.023310
18 18.0 3.276324 1.898359 2.679958
19 19.0 3.176324 1.998359 1.211248
20 20.0 2.965371 1.875487 0.377091
21 21.0 2.965371 1.975487 0.070846
22 22.0 3.065371 1.875487 0.248015
23 23.0 2.965524 1.947810 0.124337
24 24.0 2.965524 2.047810 0.050217
25 25.0 3.065524 1.947810 0.138937
26 26.0 2.955479 1.977112 0.101337
print(agent.get_best_points())
[25, {'x1': np.float64(2.955478799181076), 'x2': np.float64(1.9771119825989758)}, {'himmelblau_2d': np.float64(0.12433745597668064)}]

The Himmelblau function has four global minima (all with value 0). The summarize output shows which one(s) the optimizer found.

What you learned#

You now understand the three core concepts of Blop:

  • DOFs: The parameters the optimizer adjusts (here, x1 and x2 with bounds)

  • Objectives: What you’re optimizing (here, minimizing the Himmelblau function)

  • Agent: Coordinates the optimization loop between Bluesky and the evaluation function

Next steps#

For a more comprehensive tutorial with multiple objectives and diagnostic tools, see Optimizing KB Mirrors.