ROSE Spec for Service#97
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a YAML Spec API layer to ROSE, allowing users to define Active Learning, Reinforcement Learning, and UQ workflows declaratively. It adds Pydantic-based schema validation, task adapters for Python and shell execution, a builder to construct learners, and comprehensive unit and integration tests. Additionally, the documentation has been updated with new guides, Mermaid diagrams, and a deprecation warning for RadicalExecutionBackend. Review feedback identifies a bug in build_learner_configs where parameters are discarded for parallel learners when cfg.learners is None, a typo in docs/index.md, and a recommendation to use standard dictionary equality instead of string-serialized sorted items for robust task description comparison in rose/spec/schema.py.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def build_learner_configs(self): | ||
| """LearnerConfig list for parallel learners, or None if no learners defined.""" | ||
| cfg = self.config | ||
| if cfg.learners is None: | ||
| return None | ||
| from rose.learner import LearnerConfig, TaskConfig | ||
|
|
||
| required = _REQUIRED_SLOTS[cfg.learner.type] | ||
| configs = [] | ||
| max_iter = cfg.learner.max_iter | ||
| params = dict(cfg.parameters) | ||
| params["pythonpath"] = list(cfg.remote.pythonpath) | ||
| lc_slots = required & _LEARNER_CONFIG_SLOTS | ||
| for i, learner_def in enumerate(cfg.learners): | ||
| # learner_id → dispatch routing key (popped by closure, never reaches user fn) | ||
| # iteration → per-entry so get_task_config(slot, n) returns the right value | ||
| # learner_label → human-readable learner name; only injected when non-empty | ||
| # parameters → user-defined values from the YAML parameters: block | ||
| base = {"learner_id": i, **params} | ||
| if learner_def.label: | ||
| base["learner_label"] = learner_def.label | ||
| schedule = {n: TaskConfig(kwargs={**base, "iteration": n}) for n in range(max_iter + 1)} | ||
| schedule[-1] = TaskConfig(kwargs={**base, "iteration": max_iter}) | ||
| configs.append( | ||
| LearnerConfig(**{slot: schedule for slot in lc_slots}, criterion=schedule) | ||
| ) | ||
| return configs |
There was a problem hiding this comment.
When cfg.learners is None (meaning shared tasks are used for parallel learners), build_learner_configs currently returns None. This means that any parameters defined in the parameters: block of the YAML spec are completely discarded and never passed to the parallel learners. We should generate the LearnerConfig list even when cfg.learners is None if parameters or remote pythonpath are defined.
def build_learner_configs(self):
"""LearnerConfig list for parallel learners, or None if no learners or parameters defined."""
cfg = self.config
if cfg.learners is None and not cfg.parameters and not cfg.remote.pythonpath:
return None
from rose.learner import LearnerConfig, TaskConfig
required = _REQUIRED_SLOTS[cfg.learner.type]
configs = []
max_iter = cfg.learner.max_iter
params = dict(cfg.parameters)
params["pythonpath"] = list(cfg.remote.pythonpath)
lc_slots = required & _LEARNER_CONFIG_SLOTS
num_learners = len(cfg.learners) if cfg.learners is not None else cfg.learner.parallel_learners
for i in range(num_learners):
# learner_id → dispatch routing key (popped by closure, never reaches user fn)
# iteration → per-entry so get_task_config(slot, n) returns the right value
# learner_label → human-readable learner name; only injected when non-empty
# parameters → user-defined values from the YAML parameters: block
base = {"learner_id": i, **params}
if cfg.learners is not None and cfg.learners[i].label:
base["learner_label"] = cfg.learners[i].label
schedule = {n: TaskConfig(kwargs={**base, "iteration": n}) for n in range(max_iter + 1)}
schedule[-1] = TaskConfig(kwargs={**base, "iteration": max_iter})
configs.append(
LearnerConfig(**{slot: schedule for slot in lc_slots}, criterion=schedule)
)
return configs| ## What is ROSE? 🌹 | ||
|
|
||
| ROSE: RADICAL Orchestrator for Surrogate Exploration (ROSE) toolkit is a framework designed to enable the concurrent and adaptive execution of simulation, surrogate training, and selection tasks on High-Performance Computing (HPC) resources. ROSE is a Python package that provides tools for developing active and reinforcement learning (AL and RL) methods for scientific applications. It enables users to define simulation and surrogate training tasks and automatically manage their execution on HPC resources via a predefined set of Learning Policies (Learners). | ||
| ROSE: RADICAL Orchestrator for Surrogate Exploration (ROSE) toolkit is a framework designed to enable the distribuited, concurrent and adaptive execution of simulation, surrogate training, and selection tasks on High-Performance Computing (HPC) resources at a scale. ROSE is a Python package that provides tools for developing active and reinforcement learning (AL and RL) methods for scientific applications. It enables users to define simulation and surrogate training tasks and automatically manage their execution on HPC resources via a predefined set of Learning Policies (Learners). |
There was a problem hiding this comment.
There is a typo in this line: 'distribuited' should be 'distributed'.
| ROSE: RADICAL Orchestrator for Surrogate Exploration (ROSE) toolkit is a framework designed to enable the distribuited, concurrent and adaptive execution of simulation, surrogate training, and selection tasks on High-Performance Computing (HPC) resources at a scale. ROSE is a Python package that provides tools for developing active and reinforcement learning (AL and RL) methods for scientific applications. It enables users to define simulation and surrogate training tasks and automatically manage their execution on HPC resources via a predefined set of Learning Policies (Learners). | |
| ROSE: RADICAL Orchestrator for Surrogate Exploration (ROSE) toolkit is a framework designed to enable the distributed, concurrent and adaptive execution of simulation, surrogate training, and selection tasks on High-Performance Computing (HPC) resources at a scale. ROSE is a Python package that provides tools for developing active and reinforcement learning (AL and RL) methods for scientific applications. It enables users to define simulation and surrogate training tasks and automatically manage their execution on HPC resources via a predefined set of Learning Policies (Learners). |
| "All learners must use the same type for a given slot." | ||
| ) | ||
| descs = [dict(l.tasks[slot].task_description or {}) for l in self.learners] | ||
| if len({str(sorted(d.items())) for d in descs}) > 1: |
There was a problem hiding this comment.
Using str(sorted(d.items())) to compare dictionaries can cause false validation failures if the dictionaries contain nested dictionaries with different key insertion orders (since dictionary insertion order is preserved in Python 3.7+). Using standard dictionary equality == is more robust and handles nested structures correctly regardless of key ordering.
| if len({str(sorted(d.items())) for d in descs}) > 1: | |
| if descs and any(d != descs[0] for d in descs): |
1-Improve docs 2-Remove RadicalExecutionBackend from docs 3-Address Gemini comments
Creating a YAML spec approach for better defining and re-using pre-defined learner recipes in ROSE. Mainly for service users.