Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/badger/gui/components/routine_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
LessThanConstraint,
MinimizeObjective,
MaximizeObjective,
ContinuousVariable,
)
from pydantic import ValidationError

Expand Down Expand Up @@ -1526,14 +1527,15 @@ 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"]

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])
Expand All @@ -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)
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand Down Expand Up @@ -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,
}

Expand Down
7 changes: 2 additions & 5 deletions src/badger/gui/mini/components/var_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,13 @@ 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)
upper_delta = abs(upper - value)
delta = max(lower_delta, upper_delta)

self.line_edit = QLineEdit(
f"±{delta:.3f}{'*' if is_clipped or not is_centered 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
Expand Down Expand Up @@ -264,7 +261,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):
Expand Down
54 changes: 44 additions & 10 deletions src/badger/gui/mini/pages/routine_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
LessThanConstraint,
MaximizeObjective,
MinimizeObjective,
ContinuousVariable,
)

from pydantic import ValidationError
Expand Down Expand Up @@ -1456,24 +1457,32 @@ 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"]

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 = [
Expand Down Expand Up @@ -1532,16 +1541,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", [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
Expand Down Expand Up @@ -1623,7 +1641,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]
Expand All @@ -1641,6 +1659,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]
Expand Down Expand Up @@ -1744,10 +1770,18 @@ 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:
# 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,
"lower_bound": bounds[0],
"upper_bound": bounds[1],
"current_bounds": current_bounds,
**option,
}

Expand Down
97 changes: 88 additions & 9 deletions src/badger/gui/windows/ind_lim_vrange_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)

Expand Down Expand Up @@ -103,6 +120,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))
Expand Down Expand Up @@ -155,9 +173,55 @@ 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)

# 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 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]),
)
)
self.configs["exact_bounds"] = 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(hard_lower)
sb_bounds_lower.setMaximum(hard_upper)
sb_bounds_lower.setDecimals(6)
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)

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(hard_lower)
sb_bounds_upper.setMaximum(hard_upper)
sb_bounds_upper.setDecimals(6)
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)

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)
Expand Down Expand Up @@ -214,6 +278,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:
Expand All @@ -229,19 +295,24 @@ def update_bounds_preview(self) -> None:
hard_upper = self.configs.get("upper_bound", 0)

option_idx = self.cb.currentIndex()
if option_idx == 1:
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),
]
elif option_idx == 1:
ratio = self.sb_ratio_full.value()
delta = 0.5 * ratio * (hard_upper - hard_lower)
bounds = [curr - delta, curr + delta]
elif option_idx == 2:
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
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 = [
Expand All @@ -262,10 +333,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()
Expand All @@ -275,6 +342,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())
# 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

Expand All @@ -290,6 +361,14 @@ def delta_changed(self, delta):
self.configs["delta"] = delta
self.update_bounds_preview()

def bounds_lower_changed(self, lower):
self.configs["exact_bounds"][0] = lower
self.update_bounds_preview()

def bounds_upper_changed(self, upper):
self.configs["exact_bounds"][1] = upper
self.update_bounds_preview()

def set(self):
self.update_config()
self.apply_config(self.name, self.configs)
Expand Down
Loading