Skip to content

Commit 8145905

Browse files
committed
Update & rework dependencies
1 parent 7af7cc1 commit 8145905

13 files changed

Lines changed: 245 additions & 253 deletions

File tree

.github/workflows/_testing.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
strategy:
1111
matrix:
1212
host-os: ["ubuntu-latest"]
13-
python-version: ["py310-cpu", "py311-cpu", "py312-cpu", "py313-cpu"]
13+
python-version: ["py311-cpu", "py312-cpu", "py313-cpu"]
1414
fail-fast: false
1515

1616
defaults:

pixi.toml

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ authors = [
66
channels = ["conda-forge"]
77
name = "blop"
88
platforms = ["linux-64", "osx-arm64"]
9-
version = "0.9.0"
9+
version = "1.0.0b1"
1010

1111
[dependencies]
12-
python = ">=3.10.0,<3.14"
12+
python = ">=3.11.0,<3.14"
1313

1414
[feature.dev.dependencies]
1515

@@ -32,7 +32,7 @@ tests = "pytest src/blop/tests"
3232
graphviz = ">=14.1.2,<15"
3333

3434
[feature.docs.pypi-dependencies]
35-
blop = { path = ".", editable = true, extras = ["cpu"] }
35+
blop = { path = ".", editable = true, extras = ["cpu", "ax", "qs"] }
3636
blop-sim = { path = "sim", editable = true }
3737
numpydoc = "*"
3838
sphinx-copybutton = "*"
@@ -46,9 +46,6 @@ bluesky-tiled-plugins = "*"
4646
ophyd-async = "*"
4747
opencv-python = "*"
4848

49-
[feature.py310.dependencies]
50-
python = "3.10.*"
51-
5249
[feature.py311.dependencies]
5350
python = "3.11.*"
5451

@@ -58,10 +55,6 @@ python = "3.12.*"
5855
[feature.py313.dependencies]
5956
python = "3.13.*"
6057

61-
[feature.dev.tasks]
62-
check = "pre-commit run --all-files"
63-
tests = "pytest src/blop/tests"
64-
6558
[feature.docs.tasks]
6659
start-queueserver = "docker compose -f docs/source/tutorials/queueserver/docker-compose.yml up -d --build --wait"
6760
stop-queueserver = "docker compose -f docs/source/tutorials/queueserver/docker-compose.yml down"
@@ -78,7 +71,6 @@ convert-tutorials-to-ipynb = "jupytext --to notebook docs/source/tutorials/*.md"
7871
dev = ["dev"]
7972
dev-cpu = ["dev-cpu"]
8073
docs = ["docs"]
81-
py310-cpu = ["dev-cpu", "py310"]
8274
py311-cpu = ["dev-cpu", "py311"]
8375
py312-cpu = ["dev-cpu", "py312"]
8476
py313-cpu = ["dev-cpu", "py313"]

pyproject.toml

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,8 @@ maintainers = [
2020
{ name = "Jennefer Maldonado", email = "jmaldonad@bnl.gov" },
2121
{ name = "Roman Chernikov", email = "rcherniko@bnl.gov" },
2222
]
23-
requires-python = ">=3.10"
24-
dependencies = [
25-
"ax-platform>=1.1.0,<1.3",
26-
"bluesky>=1.14.2",
27-
"bluesky-queueserver-api>=0.0.12",
28-
"torch",
29-
"botorch>=0.16.0",
30-
"gpytorch",
31-
"scipy",
32-
"networkx>=3",
33-
"numpy",
34-
"rich>=13",
35-
]
23+
requires-python = ">=3.11"
24+
dependencies = ["bluesky>=1.14.2", "networkx>=3", "numpy", "rich>=13"]
3625
classifiers = [
3726
"Development Status :: 4 - Beta",
3827
"License :: OSI Approved :: BSD License",
@@ -45,6 +34,7 @@ classifiers = [
4534
dynamic = ["version"]
4635

4736
[project.optional-dependencies]
37+
ax = ["ax-platform>=1.3.0,<1.4", "botorch>=0.18.0,<0.19.0", "gpytorch", "torch"]
4838
qs = ["bluesky-queueserver-api"]
4939
dev = [
5040
"pytest",
@@ -53,10 +43,9 @@ dev = [
5343
"ruff",
5444
"nbstripout",
5545
"pre-commit",
56-
"pandas-stubs",
5746
"coverage",
5847
"pyright",
59-
"blop[qs]",
48+
"blop[ax,qs]",
6049
]
6150
cpu = [
6251
# Empty extra - the source configuration below routes to CPU-only index

sim/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ version = "0.0.0"
88
description = "Example simulations and benchmarks for blop (not published to PyPI)"
99
readme = "README.md"
1010
authors = [{ name = "Thomas Hopkins", email = "thopkins1@bnl.gov" }]
11-
requires-python = ">=3.10"
11+
requires-python = ">=3.11"
1212
dependencies = [
1313
"blop",
1414
"ophyd-async",

src/blop/__init__.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from .ax import DOF, Agent, ChoiceDOF, DOFConstraint, Objective, OutcomeConstraint, RangeDOF, ScalarizedObjective
21
from .plans import acquire_baseline, default_acquire, optimize, optimize_step, sample_suggestions
32

43
try:
@@ -8,14 +7,6 @@
87

98
__all__ = [
109
"__version__",
11-
"Agent",
12-
"ChoiceDOF",
13-
"DOF",
14-
"DOFConstraint",
15-
"Objective",
16-
"OutcomeConstraint",
17-
"RangeDOF",
18-
"ScalarizedObjective",
1910
"acquire_baseline",
2011
"default_acquire",
2112
"optimize",

src/blop/ax/__init__.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
from ..queueserver import OptimizationResult
2-
from .agent import Agent as Agent
3-
from .agent import QueueserverAgent as QueueserverAgent
4-
from .dof import DOF, ChoiceDOF, DOFConstraint, RangeDOF
5-
from .objective import Objective, OutcomeConstraint, ScalarizedObjective, to_ax_objective_str
6-
from .optimizer import AxOptimizer
1+
try:
2+
from .agent import Agent as Agent
3+
from .dof import DOF, ChoiceDOF, DOFConstraint, RangeDOF
4+
from .objective import Objective, OutcomeConstraint, ScalarizedObjective, to_ax_objective_str
5+
from .optimizer import AxOptimizer
6+
except ImportError as e:
7+
raise ImportError("The ax integration requires additional dependencies. Install them with: pip install blop[ax]") from e
78

89
__all__ = [
910
"Agent",
10-
"QueueserverAgent",
1111
"DOF",
1212
"RangeDOF",
1313
"ChoiceDOF",
@@ -17,5 +17,4 @@
1717
"ScalarizedObjective",
1818
"to_ax_objective_str",
1919
"AxOptimizer",
20-
"OptimizationResult",
2120
]

src/blop/ax/agent.py

Lines changed: 2 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,14 @@
1-
import importlib.util
21
import logging
32
from collections.abc import Mapping, Sequence
4-
from concurrent.futures import Future
53
from typing import Any, cast
64

5+
import bluesky.preprocessors as bpp
76
from ax import Client, TOutcome, TParameterization
87
from ax.analysis.plotly.surface.contour import ContourPlot
8+
from ax.core.analysis_card import AnalysisCardBase
99
from ax.core.types import TParamValue
10-
11-
# ===============================
12-
# TODO: Remove when Python 3.10 is no longer supported
13-
if importlib.util.find_spec("ax.core.analysis_card") is not None:
14-
from ax.core.analysis_card import AnalysisCardBase
15-
else:
16-
from ax.analysis.analysis_card import AnalysisCardBase # type: ignore[import-untyped]
17-
# ===============================
18-
import bluesky.preprocessors as bpp
1910
from bluesky.callbacks import CallbackBase
20-
from bluesky.callbacks.zmq import RemoteDispatcher
2111
from bluesky.utils import MsgGenerator
22-
from bluesky_queueserver_api.zmq import REManagerAPI
2312

2413
from ..callbacks.logger import OptimizationLogger
2514
from ..callbacks.router import OptimizationCallbackRouter
@@ -30,10 +19,8 @@
3019
Actuator,
3120
EvaluationFunction,
3221
OptimizationProblem,
33-
QueueserverOptimizationProblem,
3422
Sensor,
3523
)
36-
from ..queueserver import OptimizationResult, QueueserverClient, QueueserverOptimizationRunner
3724
from ..utils import InferredReadable
3825
from .dof import DOF, DOFConstraint
3926
from .objective import Objective, OutcomeConstraint, ScalarizedObjective, to_ax_objective_str
@@ -561,182 +548,3 @@ def navigate_to_best(self, parameterization: Mapping | None = None) -> MsgGenera
561548
parameterization,
562549
)
563550
)
564-
565-
566-
class QueueserverAgent(_AxAgentMixin):
567-
"""
568-
An asynchronous interface that uses Ax as the backend for optimization and experiment tracking
569-
and the bluesky-queueserver-api for scheduling plan execution.
570-
571-
.. warning::
572-
573-
This class is **experimental**. The API is not yet stable and may change
574-
in future releases without a deprecation period. It is not recommended for
575-
production use.
576-
577-
Parameters
578-
----------
579-
re_manager_api : REManagerAPI
580-
The manager API for interaction with Bluesky queueserver.
581-
document_dispatcher : RemoteDispatcher
582-
Dispatcher for consuming Bluesky documents from the remote server.
583-
sensors : Sequence[str]
584-
The sensors to use for acquisition. These should be the minimal set
585-
of sensors that are needed to compute the objectives.
586-
dofs : Sequence[DOF]
587-
The degrees of freedom that the agent can control, which determine the search space.
588-
objectives : Sequence[Objective]
589-
The objectives which the agent will try to optimize.
590-
evaluation_function : EvaluationFunction
591-
The function to evaluate acquired data and produce outcomes.
592-
acquisition_plan : str | None, optional
593-
The acquisition plan to use for acquiring data from the beamline. If not provided,
594-
:func:`blop.plans.default_acquire` will be assumed.
595-
dof_constraints : Sequence[DOFConstraint] | None, optional
596-
Constraints on DOFs to refine the search space.
597-
outcome_constraints : Sequence[OutcomeConstraint] | None, optional
598-
Constraints on outcomes to be satisfied during optimization.
599-
checkpoint_path : str | None, optional
600-
The path to the checkpoint file to save the optimizer's state to.
601-
**kwargs : Any
602-
Additional keyword arguments to configure the Ax experiment.
603-
604-
See Also
605-
--------
606-
blop.protocols.Sensor : The protocol for sensors.
607-
blop.ax.dof.RangeDOF : For continuous parameters.
608-
blop.ax.dof.ChoiceDOF : For discrete parameters.
609-
blop.ax.objective.Objective : For defining objectives.
610-
blop.ax.optimizer.AxOptimizer : The optimizer used internally.
611-
blop.queueserver.QueueserverOptimizatonRunner : Runner that handles interaction with bluesky-queueserver.
612-
"""
613-
614-
def __init__(
615-
self,
616-
re_manager_api: REManagerAPI,
617-
document_dispatcher: RemoteDispatcher,
618-
sensors: Sequence[str],
619-
dofs: Sequence[DOF],
620-
objectives: Sequence[Objective],
621-
evaluation_function: EvaluationFunction,
622-
acquisition_plan: str | None = None,
623-
dof_constraints: Sequence[DOFConstraint] | None = None,
624-
outcome_constraints: Sequence[OutcomeConstraint] | None = None,
625-
checkpoint_path: str | None = None,
626-
acquisition_plan_kwargs: Mapping[str, Any] | None = None,
627-
**kwargs: Any,
628-
):
629-
self._sensors = sensors
630-
self._actuators: Sequence[str] = []
631-
for dof in dofs:
632-
if dof.actuator is not None:
633-
if isinstance(dof.actuator, Actuator):
634-
self._actuators.append(dof.actuator.name)
635-
else:
636-
self._actuators.append(dof.actuator)
637-
self._evaluation_function = evaluation_function
638-
self._acquisition_plan = acquisition_plan
639-
self._acquisition_plan_kwargs = acquisition_plan_kwargs or {}
640-
self._optimizer = AxOptimizer(
641-
parameters=[dof.to_ax_parameter_config() for dof in dofs],
642-
objective=to_ax_objective_str(objectives),
643-
parameter_constraints=[constraint.ax_constraint for constraint in dof_constraints] if dof_constraints else None,
644-
outcome_constraints=[constraint.ax_constraint for constraint in outcome_constraints]
645-
if outcome_constraints
646-
else None,
647-
checkpoint_path=checkpoint_path,
648-
**kwargs,
649-
)
650-
self._runner = QueueserverOptimizationRunner(
651-
self.to_optimization_problem(),
652-
QueueserverClient(re_manager_api, document_dispatcher),
653-
)
654-
655-
@property
656-
def evaluation_function(self) -> EvaluationFunction:
657-
return self._evaluation_function
658-
659-
@property
660-
def actuators(self) -> Sequence[str]:
661-
return self._actuators
662-
663-
@property
664-
def sensors(self) -> Sequence[str]:
665-
return self._sensors
666-
667-
@property
668-
def acquisition_plan(self) -> str | None:
669-
return self._acquisition_plan
670-
671-
def stop(self) -> None:
672-
self._runner.stop()
673-
674-
@property
675-
def current_iteration(self) -> int:
676-
return self._runner.current_iteration
677-
678-
def to_optimization_problem(self) -> QueueserverOptimizationProblem:
679-
return QueueserverOptimizationProblem(
680-
optimizer=self._optimizer,
681-
actuators=self._actuators,
682-
sensors=self._sensors,
683-
evaluation_function=self._evaluation_function,
684-
acquisition_plan=self._acquisition_plan,
685-
acquisition_plan_kwargs=self._acquisition_plan_kwargs,
686-
)
687-
688-
def run(self, iterations: int = 1, n_points: int = 1) -> Future[OptimizationResult]:
689-
"""
690-
Start the optimization loop.
691-
692-
Validates the queueserver state, then begins the suggest -> acquire -> ingest
693-
cycle. This method returns immediately; the optimization runs asynchronously
694-
via callbacks.
695-
696-
Parameters
697-
----------
698-
iterations : int
699-
Number of optimization iterations to run.
700-
n_points : int
701-
Number of points to suggest per iteration.
702-
703-
Returns
704-
-------
705-
concurrent.futures.Future[OptimizationResult]
706-
A future that resolves to an :class:`~blop.queueserver.OptimizationResult`
707-
when all iterations complete or when :meth:`stop` is called. If an
708-
unhandled exception occurs the future will hold it and re-raise on
709-
``.result()``.
710-
711-
Raises
712-
------
713-
RuntimeError
714-
If the queueserver environment is not ready.
715-
ValueError
716-
If required devices or plans are not available.
717-
"""
718-
return self._runner.run(iterations, n_points)
719-
720-
def submit_suggestions(self, suggestions: list[dict]) -> Future[OptimizationResult]:
721-
"""
722-
Evaluate specific parameter combinations.
723-
724-
Acquires data for given suggestions and ingests results. Supports both
725-
optimizer suggestions and manual points.
726-
727-
Parameters
728-
----------
729-
suggestions : list[dict]
730-
Either optimizer suggestions (with "_id") or manual points (without "_id").
731-
732-
Returns
733-
-------
734-
concurrent.futures.Future[OptimizationResult]
735-
A future that resolves to an :class:`~blop.queueserver.OptimizationResult`
736-
when the acquisition completes.
737-
738-
See Also
739-
--------
740-
run : Run the full optimization loop.
741-
"""
742-
return self._runner.submit_suggestions(suggestions)

0 commit comments

Comments
 (0)