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")
Text(0.5, 1.0, "Visualizing Scipy's traversal of Himmelblau")
../_images/3ea9c8f25e51aa8ad6faba1cad5595d90645181f40d612e25646d09cec3f9d86.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 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, 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.