From 7f32a1317e0bd31e3380ecb0fbce4c7ea45f5e01 Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 2 Jul 2026 12:31:34 -0700 Subject: [PATCH 1/7] initial ui and logic for exact bounds in dialog --- .../gui/windows/ind_lim_vrange_dialog.py | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/src/badger/gui/windows/ind_lim_vrange_dialog.py b/src/badger/gui/windows/ind_lim_vrange_dialog.py index 6f0c0db3..0bc0e39e 100644 --- a/src/badger/gui/windows/ind_lim_vrange_dialog.py +++ b/src/badger/gui/windows/ind_lim_vrange_dialog.py @@ -103,6 +103,7 @@ def init_ui(self): "ratio wrt current value", "ratio wrt full range", "delta around current value", + "exact bounds", ] ) cb.setCurrentIndex(self.configs.get("limit_option_idx", 0)) @@ -155,9 +156,40 @@ def init_ui(self): hbox_delta.addWidget(lbl) hbox_delta.addWidget(sb_delta, 1) + # Absolute bounds config (used in non-relative mode) + bounds_config = QWidget() + vbox_bounds = QVBoxLayout(bounds_config) + vbox_bounds.setContentsMargins(0, 0, 0, 0) + + hbox_bounds_lower = QHBoxLayout() + hbox_bounds_lower.setContentsMargins(0, 0, 0, 0) + lbl_lower = QLabel("Lower") + self.sb_bounds_lower = sb_bounds_lower = QDoubleSpinBox() + sb_bounds_lower.setMinimum(float(self.configs.get("hard_lower_bound", -1e14))) + sb_bounds_lower.setMaximum(float(self.configs.get("hard_upper_bound", 1e14))) + sb_bounds_lower.setDecimals(6) + sb_bounds_lower.setValue(float(self.configs.get("lower_bound", 0.0))) + hbox_bounds_lower.addWidget(lbl_lower) + hbox_bounds_lower.addWidget(sb_bounds_lower, 1) + + hbox_bounds_upper = QHBoxLayout() + hbox_bounds_upper.setContentsMargins(0, 0, 0, 0) + lbl_upper = QLabel("Upper") + self.sb_bounds_upper = sb_bounds_upper = QDoubleSpinBox() + sb_bounds_upper.setMinimum(float(self.configs.get("hard_lower_bound", -1e14))) + sb_bounds_upper.setMaximum(float(self.configs.get("hard_upper_bound", 1e14))) + sb_bounds_upper.setDecimals(6) + sb_bounds_upper.setValue(float(self.configs.get("upper_bound", 0.0))) + hbox_bounds_upper.addWidget(lbl_upper) + hbox_bounds_upper.addWidget(sb_bounds_upper, 1) + + vbox_bounds.addLayout(hbox_bounds_lower) + vbox_bounds.addLayout(hbox_bounds_upper) + stacks.addWidget(ratio_curr_config) stacks.addWidget(ratio_full_config) stacks.addWidget(delta_config) + stacks.addWidget(bounds_config) stacks.setCurrentIndex(self.configs.get("limit_option_idx", 0)) vbox_config.addWidget(stacks) @@ -214,6 +246,8 @@ def config_logic(self): self.sb_ratio_curr.valueChanged.connect(self.ratio_curr_changed) self.sb_ratio_full.valueChanged.connect(self.ratio_full_changed) self.sb_delta.valueChanged.connect(self.delta_changed) + self.sb_bounds_lower.valueChanged.connect(self.bounds_lower_changed) + self.sb_bounds_upper.valueChanged.connect(self.bounds_upper_changed) self.update_bounds_preview() def _clip(self, value: float, lower: float, upper: float) -> float: @@ -229,6 +263,13 @@ def update_bounds_preview(self) -> None: hard_upper = self.configs.get("upper_bound", 0) option_idx = self.cb.currentIndex() + if option_idx == 0: + ratio = self.sb_ratio_curr.value() + sign = math.copysign(1.0, curr) if curr != 0 else 0.0 + bounds = [ + curr * (1 - 0.5 * sign * ratio), + curr * (1 + 0.5 * sign * ratio), + ] if option_idx == 1: ratio = self.sb_ratio_full.value() delta = 0.5 * ratio * (hard_upper - hard_lower) @@ -237,11 +278,15 @@ def update_bounds_preview(self) -> None: delta = self.sb_delta.value() bounds = [curr - delta, curr + delta] else: - ratio = self.sb_ratio_curr.value() - sign = math.copysign(1.0, curr) if curr != 0 else 0.0 + hard_lower = float( + self.configs.get("hard_lower_bound", self.sb_bounds_lower.value()) + ) + hard_upper = float( + self.configs.get("hard_upper_bound", self.sb_bounds_upper.value()) + ) bounds = [ - curr * (1 - 0.5 * sign * ratio), - curr * (1 + 0.5 * sign * ratio), + float(self.sb_bounds_lower.value()), + float(self.sb_bounds_upper.value()), ] bounds = [ @@ -275,6 +320,11 @@ def update_config(self): self.configs["ratio_full"] = self.sb_ratio_full.value() if "delta" not in self.configs: self.configs["delta"] = self.sb_delta.value() + + lower = float(self.sb_bounds_lower.value()) + upper = float(self.sb_bounds_upper.value()) + self.configs["lower_bound"] = min(lower, upper) + self.configs["upper_bound"] = max(lower, upper) except ValueError: pass # Optionally handle invalid input @@ -290,6 +340,14 @@ def delta_changed(self, delta): self.configs["delta"] = delta self.update_bounds_preview() + def bounds_lower_changed(self, lower): + self.configs["lower_bound"] = lower + self.update_bounds_preview() + + def bounds_upper_changed(self, upper): + self.configs["upper_bound"] = upper + self.update_bounds_preview() + def set(self): self.update_config() self.apply_config(self.name, self.configs) From d65dca0c736c7af5867fa5a88c0f04f510241759 Mon Sep 17 00:00:00 2001 From: michaellans Date: Fri, 31 Jul 2026 15:33:25 -0700 Subject: [PATCH 2/7] add exact bounds option to bounds config and dialog window --- src/badger/gui/mini/components/var_table.py | 5 +- src/badger/gui/mini/pages/routine_page.py | 50 +++++++++++++++---- .../gui/windows/ind_lim_vrange_dialog.py | 46 +++++++++-------- 3 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/badger/gui/mini/components/var_table.py b/src/badger/gui/mini/components/var_table.py index ac353204..c1360a80 100644 --- a/src/badger/gui/mini/components/var_table.py +++ b/src/badger/gui/mini/components/var_table.py @@ -227,7 +227,6 @@ def __init__( lower, upper = bounds delta = 0.5 * abs(upper - lower) - is_centered = self._is_centered(value, lower, upper) if is_clipped: lower_delta = abs(value - lower) @@ -235,7 +234,7 @@ def __init__( delta = max(lower_delta, upper_delta) self.line_edit = QLineEdit( - f"±{delta:.3f}{'*' if is_clipped or not is_centered else ''}" + f"±{delta:.3f}{'*' if is_clipped else ''}" ) self.line_edit.setReadOnly(True) self.line_edit.setAlignment( @@ -264,7 +263,7 @@ def __init__( layout.addWidget(self.line_edit) layout.addWidget(self._button_stack) - if is_clipped or not is_centered: + if is_clipped: self.setToolTip("Requested bounds are clipped by hardware limits") def set_selected(self, is_selected: bool): diff --git a/src/badger/gui/mini/pages/routine_page.py b/src/badger/gui/mini/pages/routine_page.py index 4bed3b91..a8ddf985 100644 --- a/src/badger/gui/mini/pages/routine_page.py +++ b/src/badger/gui/mini/pages/routine_page.py @@ -1456,6 +1456,7 @@ def set_ind_vrange(self, vname, config): "ratio_full": config["ratio_full"], "ratio_curr": config["ratio_curr"], "delta": config["delta"], + "exact_bounds": config["exact_bounds"], } option_idx = option["limit_option_idx"] @@ -1463,17 +1464,24 @@ def set_ind_vrange(self, vname, config): env = self.create_env() curr = env.get_variables([vname])[vname] - # 0: ratio with current value, 1: ratio with full range, 2: delta around current value + # set bounds based on selected option if option_idx == 1: + # ratio with full range ratio = option["ratio_full"] delta = 0.5 * ratio * (hard_bounds[1] - hard_bounds[0]) bounds = [curr - delta, curr + delta] new_bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() elif option_idx == 2: + # delta around current value delta = option["delta"] bounds = [curr - delta, curr + delta] new_bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() + elif option_idx == 3: + # set exact bounds + bounds = option.get("exact_bounds", hard_bounds) + new_bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() else: + # ratio around current value ratio = option["ratio_curr"] sign = np.sign(curr) bounds = [ @@ -1532,16 +1540,25 @@ def adjust_variable_range_options(self, ratio: float, var_name: str = None): # get copy of selected vrange option option = copy.copy(self.ratio_var_ranges.get(vname, self.limit_option)) option_idx = option["limit_option_idx"] - - if option_idx == 1: - key = "ratio_full" - elif option_idx == 2: - key = "delta" + if option_idx == 3: + # exact bounds: scale span by ratio around the current center. + exact_bounds = list(option.get("exact_bounds") or [0.0, 0.0]) + lo, hi = sorted(exact_bounds) + center = 0.5 * (lo + hi) + half_span = 0.5 * (hi - lo) * ratio + option["exact_bounds"] = [center - half_span, center + half_span] else: - key = "ratio_curr" + # relative bounds options + if option_idx == 1: + key = "ratio_full" + elif option_idx == 2: + key = "delta" + else: + key = "ratio_curr" + + # update selected option with multiplication by ratio + option[key] = option[key] * ratio - # update selected option with multiplication by ratio - option[key] = option[key] * ratio self.ratio_var_ranges[vname] = option # recalculate bounds @@ -1623,7 +1640,7 @@ def calc_auto_bounds(self): limit_option = self.limit_option option_idx = limit_option["limit_option_idx"] - # 0: ratio with current value, 1: ratio with full range, 2: delta around current value + # 0: ratio with current value, 1: ratio with full range, 2: delta around current value, 3: exact bounds if option_idx == 1: ratio = limit_option["ratio_full"] hard_bounds = vrange[name] @@ -1641,6 +1658,14 @@ def calc_auto_bounds(self): clipped[name] = bounds != new_bounds vrange[name] = new_bounds logger.info(f"Auto bounds for {name} (delta): {new_bounds}") + elif option_idx == 3: + exact_bounds = limit_option.get("exact_bounds") + hard_bounds = vrange[name] + bounds = sorted(exact_bounds) if exact_bounds else list(hard_bounds) + new_bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() + clipped[name] = bounds != new_bounds + vrange[name] = new_bounds + logger.info(f"Auto bounds for {name} (exact): {new_bounds}") else: ratio = limit_option["ratio_curr"] hard_bounds = vrange[name] @@ -1744,10 +1769,15 @@ def handle_var_config(self, vname): except KeyError: option = self.limit_option + current_bounds = self.env_box.var_table.bounds.get(vname, bounds) + if current_bounds is None or len(current_bounds) != 2: + current_bounds = bounds + configs = { "current_value": curr, "lower_bound": bounds[0], "upper_bound": bounds[1], + "current_bounds": current_bounds, **option, } diff --git a/src/badger/gui/windows/ind_lim_vrange_dialog.py b/src/badger/gui/windows/ind_lim_vrange_dialog.py index 0bc0e39e..db475f75 100644 --- a/src/badger/gui/windows/ind_lim_vrange_dialog.py +++ b/src/badger/gui/windows/ind_lim_vrange_dialog.py @@ -161,14 +161,26 @@ def init_ui(self): vbox_bounds = QVBoxLayout(bounds_config) vbox_bounds.setContentsMargins(0, 0, 0, 0) + # Get hard limits (full variable range) + hard_lower = float(self.configs.get("lower_bound", -1e10)) + hard_upper = float(self.configs.get("upper_bound", 1e10)) + + # Get exact bounds if previously set + if "exact_bounds" not in self.configs: + self.configs["exact_bounds"] = self.configs.get( + "current_bounds", [hard_lower, hard_upper] + ) + exact_bounds = self.configs["exact_bounds"] + hbox_bounds_lower = QHBoxLayout() hbox_bounds_lower.setContentsMargins(0, 0, 0, 0) lbl_lower = QLabel("Lower") self.sb_bounds_lower = sb_bounds_lower = QDoubleSpinBox() - sb_bounds_lower.setMinimum(float(self.configs.get("hard_lower_bound", -1e14))) - sb_bounds_lower.setMaximum(float(self.configs.get("hard_upper_bound", 1e14))) + sb_bounds_lower.setMinimum(hard_lower) + sb_bounds_lower.setMaximum(hard_upper) sb_bounds_lower.setDecimals(6) - sb_bounds_lower.setValue(float(self.configs.get("lower_bound", 0.0))) + sb_bounds_lower.setSingleStep((hard_upper - hard_lower) / 20) + sb_bounds_lower.setValue(exact_bounds[0]) hbox_bounds_lower.addWidget(lbl_lower) hbox_bounds_lower.addWidget(sb_bounds_lower, 1) @@ -176,10 +188,11 @@ def init_ui(self): hbox_bounds_upper.setContentsMargins(0, 0, 0, 0) lbl_upper = QLabel("Upper") self.sb_bounds_upper = sb_bounds_upper = QDoubleSpinBox() - sb_bounds_upper.setMinimum(float(self.configs.get("hard_lower_bound", -1e14))) - sb_bounds_upper.setMaximum(float(self.configs.get("hard_upper_bound", 1e14))) + sb_bounds_upper.setMinimum(hard_lower) + sb_bounds_upper.setMaximum(hard_upper) sb_bounds_upper.setDecimals(6) - sb_bounds_upper.setValue(float(self.configs.get("upper_bound", 0.0))) + sb_bounds_upper.setSingleStep((hard_upper - hard_lower) / 20) + sb_bounds_upper.setValue(exact_bounds[1]) hbox_bounds_upper.addWidget(lbl_upper) hbox_bounds_upper.addWidget(sb_bounds_upper, 1) @@ -270,7 +283,7 @@ def update_bounds_preview(self) -> None: curr * (1 - 0.5 * sign * ratio), curr * (1 + 0.5 * sign * ratio), ] - if option_idx == 1: + elif option_idx == 1: ratio = self.sb_ratio_full.value() delta = 0.5 * ratio * (hard_upper - hard_lower) bounds = [curr - delta, curr + delta] @@ -278,12 +291,6 @@ def update_bounds_preview(self) -> None: delta = self.sb_delta.value() bounds = [curr - delta, curr + delta] else: - hard_lower = float( - self.configs.get("hard_lower_bound", self.sb_bounds_lower.value()) - ) - hard_upper = float( - self.configs.get("hard_upper_bound", self.sb_bounds_upper.value()) - ) bounds = [ float(self.sb_bounds_lower.value()), float(self.sb_bounds_upper.value()), @@ -307,10 +314,6 @@ def update_bounds_preview(self) -> None: def update_config(self): try: - # lower = float(self.lbl_hard_lower.text()) - # upper = float(self.lbl_hard_upper.text()) - # self.configs["lower_bound"] = lower - # self.configs["upper_bound"] = upper # Fill in default values if not exist if "limit_option_idx" not in self.configs: self.configs["limit_option_idx"] = self.cb.currentIndex() @@ -320,11 +323,10 @@ def update_config(self): self.configs["ratio_full"] = self.sb_ratio_full.value() if "delta" not in self.configs: self.configs["delta"] = self.sb_delta.value() - lower = float(self.sb_bounds_lower.value()) upper = float(self.sb_bounds_upper.value()) - self.configs["lower_bound"] = min(lower, upper) - self.configs["upper_bound"] = max(lower, upper) + # Keep exact bounds ordered to avoid invalid [lower, upper] ranges. + self.configs["exact_bounds"] = [min(lower, upper), max(lower, upper)] except ValueError: pass # Optionally handle invalid input @@ -341,11 +343,11 @@ def delta_changed(self, delta): self.update_bounds_preview() def bounds_lower_changed(self, lower): - self.configs["lower_bound"] = lower + self.configs["exact_bounds"][0] = lower self.update_bounds_preview() def bounds_upper_changed(self, upper): - self.configs["upper_bound"] = upper + self.configs["exact_bounds"][1] = upper self.update_bounds_preview() def set(self): From afec5db0b00f943293d03089691808ff46940cce Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 6 Aug 2026 14:23:55 -0700 Subject: [PATCH 3/7] display current bounds in dialog --- src/badger/gui/windows/ind_lim_vrange_dialog.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/badger/gui/windows/ind_lim_vrange_dialog.py b/src/badger/gui/windows/ind_lim_vrange_dialog.py index db475f75..a3240716 100644 --- a/src/badger/gui/windows/ind_lim_vrange_dialog.py +++ b/src/badger/gui/windows/ind_lim_vrange_dialog.py @@ -48,6 +48,7 @@ def init_ui(self): info_group = QFrame() vbox_info = QVBoxLayout(info_group) vbox_info.setContentsMargins(2, 2, 2, 2) + vbox_info.setSpacing(2) # Current value row hbox_current = QHBoxLayout() @@ -69,6 +70,22 @@ def init_ui(self): # Add the rows to the info group vbox_info.addLayout(hbox_current) + if "current_bounds" in self.configs: + # Display current bounds + hbox_bounds = QHBoxLayout() + hbox_bounds.setContentsMargins(0, 0, 0, 0) + lbl_bounds = QLabel("Current bounds:") + lbl_bounds.setFixedWidth(128) + current_bounds = self.configs["current_bounds"] + bounds_text = f"[{current_bounds[0]:.5f}, {current_bounds[1]:.5f}]" + lbl_current_bounds = QLabel(bounds_text) + lbl_current_bounds.setStyleSheet("color: #788D9C;") + hbox_bounds.addWidget(lbl_bounds) + hbox_bounds.addStretch() + hbox_bounds.addWidget(lbl_current_bounds) + hbox_bounds.addSpacing(8) + vbox_info.addLayout(hbox_bounds) + # Add the info group to the main layout vbox.addWidget(info_group) From 7afd4f71c91b7954decf5d16bb1dfeb4a0e5b41e Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 6 Aug 2026 16:00:40 -0700 Subject: [PATCH 4/7] clarify fallback logic for missing keys when trying to get config bounds --- src/badger/gui/mini/pages/routine_page.py | 1 + src/badger/gui/windows/ind_lim_vrange_dialog.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/badger/gui/mini/pages/routine_page.py b/src/badger/gui/mini/pages/routine_page.py index a8ddf985..29d73cc9 100644 --- a/src/badger/gui/mini/pages/routine_page.py +++ b/src/badger/gui/mini/pages/routine_page.py @@ -1771,6 +1771,7 @@ def handle_var_config(self, vname): current_bounds = self.env_box.var_table.bounds.get(vname, bounds) if current_bounds is None or len(current_bounds) != 2: + # default to env bounds if bounds not set current_bounds = bounds configs = { diff --git a/src/badger/gui/windows/ind_lim_vrange_dialog.py b/src/badger/gui/windows/ind_lim_vrange_dialog.py index a3240716..d0b591b1 100644 --- a/src/badger/gui/windows/ind_lim_vrange_dialog.py +++ b/src/badger/gui/windows/ind_lim_vrange_dialog.py @@ -182,12 +182,14 @@ def init_ui(self): hard_lower = float(self.configs.get("lower_bound", -1e10)) hard_upper = float(self.configs.get("upper_bound", 1e10)) - # Get exact bounds if previously set - if "exact_bounds" not in self.configs: - self.configs["exact_bounds"] = self.configs.get( - "current_bounds", [hard_lower, hard_upper] + # Get exact bounds from configs, or use current or hard bounds if not specified. + exact_bounds = list( + self.configs.get( + "exact_bounds", + self.configs.get("current_bounds", [hard_lower, hard_upper]), ) - exact_bounds = self.configs["exact_bounds"] + ) + self.configs["exact_bounds"] = exact_bounds hbox_bounds_lower = QHBoxLayout() hbox_bounds_lower.setContentsMargins(0, 0, 0, 0) @@ -196,7 +198,7 @@ def init_ui(self): sb_bounds_lower.setMinimum(hard_lower) sb_bounds_lower.setMaximum(hard_upper) sb_bounds_lower.setDecimals(6) - sb_bounds_lower.setSingleStep((hard_upper - hard_lower) / 20) + sb_bounds_lower.setSingleStep((hard_upper - hard_lower) / 50) sb_bounds_lower.setValue(exact_bounds[0]) hbox_bounds_lower.addWidget(lbl_lower) hbox_bounds_lower.addWidget(sb_bounds_lower, 1) @@ -208,7 +210,7 @@ def init_ui(self): sb_bounds_upper.setMinimum(hard_lower) sb_bounds_upper.setMaximum(hard_upper) sb_bounds_upper.setDecimals(6) - sb_bounds_upper.setSingleStep((hard_upper - hard_lower) / 20) + sb_bounds_upper.setSingleStep((hard_upper - hard_lower) / 50) sb_bounds_upper.setValue(exact_bounds[1]) hbox_bounds_upper.addWidget(lbl_upper) hbox_bounds_upper.addWidget(sb_bounds_upper, 1) From 94827d509096c6636346afb2252b0b06e28a4a0f Mon Sep 17 00:00:00 2001 From: michaellans Date: Fri, 7 Aug 2026 15:41:56 -0700 Subject: [PATCH 5/7] update main gui routine_page for exact bounds dialog option --- src/badger/gui/components/routine_page.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/badger/gui/components/routine_page.py b/src/badger/gui/components/routine_page.py index c7215d0c..f697ca5b 100644 --- a/src/badger/gui/components/routine_page.py +++ b/src/badger/gui/components/routine_page.py @@ -56,6 +56,7 @@ LessThanConstraint, MinimizeObjective, MaximizeObjective, + ContinuousVariable, ) from pydantic import ValidationError @@ -1526,6 +1527,7 @@ def set_ind_vrange(self, vname, config): "ratio_full": config["ratio_full"], "ratio_curr": config["ratio_curr"], "delta": config["delta"], + "exact_bounds": config.get("exact_bounds", hard_bounds), } option_idx = option["limit_option_idx"] @@ -1533,7 +1535,7 @@ def set_ind_vrange(self, vname, config): env = self.create_env() curr = env.get_variables([vname])[vname] - # 0: ratio with current value, 1: ratio with full range, 2: delta around current value + # 0: ratio with current value, 1: ratio with full range, 2: delta around current value, 3: exact bounds if option_idx == 1: ratio = option["ratio_full"] delta = 0.5 * ratio * (hard_bounds[1] - hard_bounds[0]) @@ -1543,6 +1545,9 @@ def set_ind_vrange(self, vname, config): delta = option["delta"] bounds = [curr - delta, curr + delta] bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() + elif option_idx == 3: + bounds = sorted(option.get("exact_bounds", hard_bounds)) + bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() else: ratio = option["ratio_curr"] sign = np.sign(curr) @@ -1629,7 +1634,7 @@ def calc_auto_bounds(self): limit_option = self.limit_option option_idx = limit_option["limit_option_idx"] - # 0: ratio with current value, 1: ratio with full range, 2: delta around current value + # 0: ratio with current value, 1: ratio with full range, 2: delta around current value, 3: exact bounds if option_idx == 1: ratio = limit_option["ratio_full"] hard_bounds = vrange[name] @@ -1645,6 +1650,13 @@ def calc_auto_bounds(self): bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() vrange[name] = bounds logger.info(f"Auto bounds for {name} (delta): {bounds}") + elif option_idx == 3: + hard_bounds = vrange[name] + exact_bounds = limit_option.get("exact_bounds") + bounds = sorted(exact_bounds) if exact_bounds else list(hard_bounds) + bounds = np.clip(bounds, hard_bounds[0], hard_bounds[1]).tolist() + vrange[name] = bounds + logger.info(f"Auto bounds for {name} (exact): {bounds}") else: ratio = limit_option["ratio_curr"] hard_bounds = vrange[name] @@ -1744,10 +1756,17 @@ def handle_var_config(self, vname): except KeyError: option = self.limit_option + current_bounds = self.env_box.var_table.bounds.get(vname, bounds) + if current_bounds is None: + current_bounds = bounds + if isinstance(current_bounds, ContinuousVariable): + current_bounds = current_bounds.domain + configs = { "current_value": curr, "lower_bound": bounds[0], "upper_bound": bounds[1], + "current_bounds": current_bounds, **option, } From 573512f933d87be132cf3da8d3602fee81445219 Mon Sep 17 00:00:00 2001 From: michaellans Date: Fri, 7 Aug 2026 17:59:26 -0700 Subject: [PATCH 6/7] linting --- src/badger/gui/mini/components/var_table.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/badger/gui/mini/components/var_table.py b/src/badger/gui/mini/components/var_table.py index c1360a80..f4cc9a23 100644 --- a/src/badger/gui/mini/components/var_table.py +++ b/src/badger/gui/mini/components/var_table.py @@ -233,9 +233,7 @@ def __init__( upper_delta = abs(upper - value) delta = max(lower_delta, upper_delta) - self.line_edit = QLineEdit( - f"±{delta:.3f}{'*' if is_clipped else ''}" - ) + self.line_edit = QLineEdit(f"±{delta:.3f}{'*' if is_clipped else ''}") self.line_edit.setReadOnly(True) self.line_edit.setAlignment( Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter From 588c3eee8cfd441a44995314aa7e8b3b408ba0a5 Mon Sep 17 00:00:00 2001 From: michaellans Date: Mon, 10 Aug 2026 18:22:20 -0700 Subject: [PATCH 7/7] minor bug fix from testing --- src/badger/gui/mini/pages/routine_page.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/badger/gui/mini/pages/routine_page.py b/src/badger/gui/mini/pages/routine_page.py index 29d73cc9..b2cdf8da 100644 --- a/src/badger/gui/mini/pages/routine_page.py +++ b/src/badger/gui/mini/pages/routine_page.py @@ -39,6 +39,7 @@ LessThanConstraint, MaximizeObjective, MinimizeObjective, + ContinuousVariable, ) from pydantic import ValidationError @@ -1542,7 +1543,7 @@ def adjust_variable_range_options(self, ratio: float, var_name: str = None): option_idx = option["limit_option_idx"] if option_idx == 3: # exact bounds: scale span by ratio around the current center. - exact_bounds = list(option.get("exact_bounds") or [0.0, 0.0]) + exact_bounds = list(option.get("exact_bounds", [0.0, 0.0])) lo, hi = sorted(exact_bounds) center = 0.5 * (lo + hi) half_span = 0.5 * (hi - lo) * ratio @@ -1770,9 +1771,11 @@ def handle_var_config(self, vname): option = self.limit_option current_bounds = self.env_box.var_table.bounds.get(vname, bounds) - if current_bounds is None or len(current_bounds) != 2: + if current_bounds is None: # default to env bounds if bounds not set current_bounds = bounds + if isinstance(current_bounds, ContinuousVariable): + current_bounds = current_bounds.domain configs = { "current_value": curr,