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)
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. Blop passes it the hashable identifier returned by the acquisition plan and the suggestions that were tried. This tutorial uses the default acquisition plan, so the identifier is a Bluesky run UID and blop_acquisition_order associates measurements with outcomes.
from collections.abc import Hashable, Mapping, Sequence
class Himmelblau2DEvaluation:
def __init__(self, tiled_client: Container):
self.tiled_client = tiled_client
def __call__(self, uid: Hashable, suggestions: Sequence[Mapping]) -> Sequence[Mapping]:
if not isinstance(uid, str):
raise TypeError(f"Himmelblau2DEvaluation requires a Bluesky run UID string, got {uid!r}")
run = self.tiled_client[uid]
outcomes = []
acquisition_order = run.start["blop_acquisition_order"]
x1_data = run["primary/x1"].read()
x2_data = run["primary/x2"].read()
print("[Himmelblau] evaluating acquired order: ", acquisition_order)
for index, suggestion_id in enumerate(acquisition_order):
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 f4f606fa-94b2-4939-8c4b-42a2fdb285f1 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
[Himmelblau] evaluating acquired order:
[0]
[Himmelblau] evaluating acquired order:
[1]
[Himmelblau] evaluating acquired order:
[2]
[Himmelblau] evaluating acquired order:
[3]
[Himmelblau] evaluating acquired order:
[4]
[Himmelblau] evaluating acquired order:
[5]
[Himmelblau] evaluating acquired order:
[6]
[Himmelblau] evaluating acquired order:
[7]
[Himmelblau] evaluating acquired order:
[8]
[Himmelblau] evaluating acquired order:
[9]
──────────────────────────────────────────────── Iteration 1 / 10 ─────────────────────────────────────────────────
Acquire UID 7b0e2031-2b74-411f-b1b6-3c6361529261
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 a754b492-ec6a-4386-80da-95a23ad5295a
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 8ca3c4cb-b704-40e0-987b-75d8bcf948c0
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 b3e6d932-058f-45ff-a149-ed66aae403b1
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 5f6c5d6a-e02b-421c-b8c0-bafcdeef83b6
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 8ca9fa98-73d7-4a1a-9a66-cf62c8e1ad90
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 37830f33-80e9-499d-88bf-87d1d4d7e594
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 88e8f5c3-462b-492b-8465-5bd2bd64551d
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 f4057703-076e-4a0b-bc44-6f3073e9c05a
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 30450d9b-eb4c-41c9-94d8-a6f2013462e3
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 ──────────────────────────────────────────────
('f4f606fa-94b2-4939-8c4b-42a2fdb285f1',
'7b0e2031-2b74-411f-b1b6-3c6361529261',
'a754b492-ec6a-4386-80da-95a23ad5295a',
'8ca3c4cb-b704-40e0-987b-75d8bcf948c0',
'b3e6d932-058f-45ff-a149-ed66aae403b1',
'5f6c5d6a-e02b-421c-b8c0-bafcdeef83b6',
'8ca9fa98-73d7-4a1a-9a66-cf62c8e1ad90',
'37830f33-80e9-499d-88bf-87d1d4d7e594',
'88e8f5c3-462b-492b-8465-5bd2bd64551d',
'f4057703-076e-4a0b-bc44-6f3073e9c05a',
'30450d9b-eb4c-41c9-94d8-a6f2013462e3')
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 37f633ac-0a1c-4da8-8692-c6883bb6f1a6 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
[Himmelblau] evaluating acquired order:
[0]
[Himmelblau] evaluating acquired order:
[1]
[Himmelblau] evaluating acquired order:
[2]
[Himmelblau] evaluating acquired order:
[3]
[Himmelblau] evaluating acquired order:
[4]
[Himmelblau] evaluating acquired order:
[5]
[Himmelblau] evaluating acquired order:
[6, 7]
[Himmelblau] evaluating acquired order:
[8]
[Himmelblau] evaluating acquired order:
[9, 10]
[Himmelblau] evaluating acquired order:
[11]
[Himmelblau] evaluating acquired order:
[12, 13]
[Himmelblau] evaluating acquired order:
[14]
[Himmelblau] evaluating acquired order:
[15, 16]
[Himmelblau] evaluating acquired order:
[17]
[Himmelblau] evaluating acquired order:
[18, 19]
[Himmelblau] evaluating acquired order:
[20]
[Himmelblau] evaluating acquired order:
[21, 22]
[Himmelblau] evaluating acquired order:
[23]
[Himmelblau] evaluating acquired order:
[24, 25]
[Himmelblau] evaluating acquired order:
[26]
──────────────────────────────────────────────── Iteration 1 / 20 ─────────────────────────────────────────────────
Acquire UID cbd8bd13-3706-4b0e-8de8-6fe5121b7c9e
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 5c334e0e-8a23-4a27-95de-024db289f95d
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 1 │ 1.74791 │ 3.59792 │ 78.0762 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 78.0762 max: 170 mean: 124.038 (2 pts sampled)
──────────────────────────────────────────────── Iteration 3 / 20 ─────────────────────────────────────────────────
Acquire UID 719ae46f-46b3-4a4d-922c-4628542efbee
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 2 │ 2.36391 │ 1.11978 │ 29.8617 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 29.8617 max: 170 mean: 92.646 (3 pts sampled)
──────────────────────────────────────────────── Iteration 4 / 20 ─────────────────────────────────────────────────
Acquire UID 71fdc2a9-c2a4-406d-8f77-dc57960c3cd6
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 3 │ -4.41474 │ 1.11978 │ 195.589 │ └───────┴───────────────┴──────────┴─────────┴───────────────┘
himmelblau_2d min: 29.8617 max: 195.589 mean: 118.382 (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 6a0b8c66-1181-4504-be5d-14b2b8408464
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 4 │ -4.41474 │ -1.93127 │ 102.075 │ └───────┴───────────────┴──────────┴──────────┴───────────────┘
himmelblau_2d min: 29.8617 max: 195.589 mean: 115.12 (5 pts sampled)
──────────────────────────────────────────────── Iteration 6 / 20 ─────────────────────────────────────────────────
Acquire UID 3e669abe-005b-42cc-996d-7a1979ff18a2
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 5 │ 2.36391 │ 1.11978 │ 29.8617 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 29.8617 max: 195.589 mean: 100.911 (6 pts sampled)
────────────────────────────────────────── Iteration 7 / 20 (2 points) ───────────────────────────────────────────
Acquire UID c7c40135-9488-4e30-9ccd-1813b92c66a5
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 6 │ 2.46391 │ 1.11978 │ 25.284 │ │ 1 │ 7 │ 2.36391 │ 1.21978 │ 27.4855 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 25.284 max: 195.589 mean: 82.2791 (8 pts sampled)
──────────────────────────────────────────────── Iteration 8 / 20 ─────────────────────────────────────────────────
Acquire UID 824b210a-0e57-4c95-9979-c7d6d0e2f834
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━╇━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 8 │ 5 │ 5 │ 890 │ └───────┴───────────────┴────┴────┴───────────────┘
himmelblau_2d min: 25.284 max: 890 mean: 172.026 (9 pts sampled)
────────────────────────────────────────── Iteration 9 / 20 (2 points) ───────────────────────────────────────────
Acquire UID e74f3275-44bc-4dfb-8a2e-cd1d71924d5f
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 10 │ 5 │ 4.9 │ 841.65 │ │ 1 │ 9 │ 4.9 │ 5 │ 848.77 │ └───────┴───────────────┴─────┴─────┴───────────────┘
himmelblau_2d min: 25.284 max: 890 mean: 294.423 (11 pts sampled)
──────────────────────────────────────────────── Iteration 10 / 20 ────────────────────────────────────────────────
Acquire UID bd36dcd4-ae6a-4392-a090-022693d88bd8
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 11 │ 2.82269 │ 1.79509 │ 2.443 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 2.443 max: 890 mean: 270.091 (12 pts sampled)
────────────────────────────────────────── Iteration 11 / 20 (2 points) ──────────────────────────────────────────
Acquire UID 5158f48a-3093-4976-9d27-66d7d9cf2858
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 12 │ 2.82269 │ 1.89509 │ 1.6369 │ │ 1 │ 13 │ 2.92269 │ 1.79509 │ 1.17029 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 1.17029 max: 890 mean: 231.707 (14 pts sampled)
──────────────────────────────────────────────── Iteration 12 / 20 ────────────────────────────────────────────────
Acquire UID 6d13be83-a605-44b4-871b-b031284b390f
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 14 │ 3.00338 │ 2.13331 │ 0.330935 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 0.330935 max: 890 mean: 216.282 (15 pts sampled)
────────────────────────────────────────── Iteration 13 / 20 (2 points) ──────────────────────────────────────────
Acquire UID b5111adc-4eb4-45e3-a1e9-797f12fe683e
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 15 │ 3.10338 │ 2.13331 │ 1.01234 │ │ 1 │ 16 │ 3.00338 │ 2.23331 │ 1.04649 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 0.330935 max: 890 mean: 190.958 (17 pts sampled)
──────────────────────────────────────────────── Iteration 14 / 20 ────────────────────────────────────────────────
Acquire UID 6ccc4325-a968-4e0c-8fba-d2f0e20c56a7
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 17 │ 2.94086 │ 1.97363 │ 0.169523 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 0.169523 max: 890 mean: 180.359 (18 pts sampled)
────────────────────────────────────────── Iteration 15 / 20 (2 points) ──────────────────────────────────────────
Acquire UID 56aba4dc-5307-498f-baac-07d1e71424fb
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 18 │ 2.94086 │ 2.07363 │ 0.135107 │ │ 1 │ 19 │ 3.04086 │ 1.97363 │ 0.0526939 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 0.0526939 max: 890 mean: 162.333 (20 pts sampled)
──────────────────────────────────────────────── Iteration 16 / 20 ────────────────────────────────────────────────
Acquire UID c8b4b3ff-f56a-4131-a21a-a44b63d31039
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 20 │ 2.95082 │ 1.9801 │ 0.114178 │ └───────┴───────────────┴─────────┴────────┴───────────────┘
himmelblau_2d min: 0.0526939 max: 890 mean: 154.608 (21 pts sampled)
────────────────────────────────────────── Iteration 17 / 20 (2 points) ──────────────────────────────────────────
Acquire UID 3d74646e-3c75-414d-8f51-82b5502501d7
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 21 │ 3.05082 │ 1.9801 │ 0.0835195 │ │ 1 │ 22 │ 2.95082 │ 2.0801 │ 0.122266 │ └───────┴───────────────┴─────────┴────────┴───────────────┘
himmelblau_2d min: 0.0526939 max: 890 mean: 141.173 (23 pts sampled)
──────────────────────────────────────────────── Iteration 18 / 20 ────────────────────────────────────────────────
Acquire UID 37822853-4a60-4345-abff-f609b9c0f4ad
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 23 │ 2.95469 │ 1.9782 │ 0.102458 │ └───────┴───────────────┴─────────┴────────┴───────────────┘
himmelblau_2d min: 0.0526939 max: 890 mean: 135.295 (24 pts sampled)
────────────────────────────────────────── Iteration 19 / 20 (2 points) ──────────────────────────────────────────
Acquire UID a030b246-d514-4df7-9c48-4890c32da3c2
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 24 │ 2.95469 │ 2.0782 │ 0.111569 │ │ 1 │ 25 │ 3.05469 │ 1.9782 │ 0.0967211 │ └───────┴───────────────┴─────────┴────────┴───────────────┘
himmelblau_2d min: 0.0526939 max: 890 mean: 124.895 (26 pts sampled)
──────────────────────────────────────────────── Iteration 20 / 20 ────────────────────────────────────────────────
Acquire UID 725a9f30-cb49-4af2-891e-a21876ef5c80
┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Event ┃ Suggestion ID ┃ x1 ┃ x2 ┃ himmelblau_2d ┃ ┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ 0 │ 26 │ 2.95639 │ 1.97537 │ 0.100887 │ └───────┴───────────────┴─────────┴─────────┴───────────────┘
himmelblau_2d min: 0.0526939 max: 890 mean: 120.273 (27 pts sampled)
Summary Statistics ┏━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┳━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━┓ ┃ Name ┃ Type ┃ Min ┃ Max ┃ Mean ┃ Std ┃ Count ┃ ┡━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━┩ │ x1 │ param │ -4.41474 │ 5 │ 2.40177 │ 2.18454 │ 27 │ │ x2 │ param │ -1.93127 │ 5 │ 2.01192 │ 1.41868 │ 27 │ │ himmelblau_2d │ outcome │ 0.0526939 │ 890 │ 120.273 │ 271.546 │ 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")
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 | 1.747914 | 3.597915 | 78.076207 |
| 2 | 2.0 | 2.363908 | 1.119784 | 29.861719 |
| 3 | 3.0 | -4.414742 | 1.119784 | 195.589253 |
| 4 | 4.0 | -4.414742 | -1.931267 | 102.074716 |
| 5 | 5.0 | 2.363908 | 1.119784 | 29.861719 |
| 6 | 6.0 | 2.463908 | 1.119784 | 25.284014 |
| 7 | 7.0 | 2.363908 | 1.219784 | 27.485457 |
| 8 | 8.0 | 5.000000 | 5.000000 | 890.000000 |
| 9 | 10.0 | 5.000000 | 4.900000 | 841.650100 |
| 10 | 9.0 | 4.900000 | 5.000000 | 848.770100 |
| 11 | 11.0 | 2.822687 | 1.795089 | 2.442996 |
| 12 | 12.0 | 2.822687 | 1.895089 | 1.636899 |
| 13 | 13.0 | 2.922687 | 1.795089 | 1.170290 |
| 14 | 14.0 | 3.003381 | 2.133308 | 0.330935 |
| 15 | 15.0 | 3.103381 | 2.133308 | 1.012343 |
| 16 | 16.0 | 3.003381 | 2.233308 | 1.046486 |
| 17 | 17.0 | 2.940862 | 1.973632 | 0.169523 |
| 18 | 18.0 | 2.940862 | 2.073632 | 0.135107 |
| 19 | 19.0 | 3.040862 | 1.973632 | 0.052694 |
| 20 | 20.0 | 2.950820 | 1.980101 | 0.114178 |
| 21 | 21.0 | 3.050819 | 1.980101 | 0.083519 |
| 22 | 22.0 | 2.950820 | 2.080101 | 0.122266 |
| 23 | 23.0 | 2.954692 | 1.978200 | 0.102458 |
| 24 | 24.0 | 2.954692 | 2.078200 | 0.111569 |
| 25 | 25.0 | 3.054692 | 1.978200 | 0.096721 |
| 26 | 26.0 | 2.956395 | 1.975370 | 0.100887 |
print(agent.get_best_points())
[25, {'x1': np.float64(2.956394972606685), 'x2': np.float64(1.9753701699329131)}, {'himmelblau_2d': np.float64(0.10245832147786835)}]
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,
x1andx2with 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.