spagestic's picture
Refactor Operations Research Interfaces and Add Utility Functions
79bc79a
raw
history blame
3.96 kB
"""Gradio interfaces for Operations Research solvers."""
import gradio as gr
import numpy as np
def parse_vector(input_str: str, dtype=float) -> list:
"""Parses a comma-separated string into a list of numbers."""
if not input_str:
return []
try:
return [dtype(x.strip()) for x in input_str.split(',')]
except ValueError:
gr.Warning(f"Could not parse vector: '{input_str}'. Please use comma-separated numbers (e.g., '1,2,3.5').")
return []
def parse_matrix(input_str: str) -> np.ndarray:
"""Parses a string (rows separated by semicolons, elements by commas) into a NumPy array."""
if not input_str:
return np.array([])
try:
rows = input_str.split(';')
matrix = []
num_cols = -1
for i, row_str in enumerate(rows):
if not row_str.strip(): continue # Allow empty rows if they result from trailing semicolons
row = [float(x.strip()) for x in row_str.split(',')]
if num_cols == -1:
num_cols = len(row)
elif len(row) != num_cols:
raise ValueError(f"Row {i+1} has {len(row)} elements, expected {num_cols}.")
matrix.append(row)
if not matrix: # If all rows were empty or input_str was just ';'
return np.array([])
return np.array(matrix)
except ValueError as e:
gr.Warning(f"Could not parse matrix: '{input_str}'. Error: {e}. Expected format: '1,2;3,4'. Ensure all rows have the same number of columns.")
return np.array([])
def parse_relations(input_str: str) -> list[str]:
"""Parses a comma-separated string of relations into a list of strings."""
if not input_str:
return []
try:
relations = [r.strip() for r in input_str.split(',')]
valid_relations = {"<=", ">=", "="}
if not all(r in valid_relations for r in relations):
invalid_rels = [r for r in relations if r not in valid_relations]
gr.Warning(f"Invalid relation(s) found: {', '.join(invalid_rels)}. Allowed relations are: '<=', '>=', '='.")
return []
return relations
except Exception as e: # Catch any other unexpected errors during parsing
gr.Warning(f"Error parsing relations: '{input_str}'. Error: {e}")
return []
def parse_bounds(input_str: str) -> list[tuple]:
"""
Parses a string representing variable bounds into a list of tuples.
Format: "lower1,upper1; lower2,upper2; ..." (e.g., "0,None; 0,10; None,None")
'None' (case-insensitive) is used for no bound.
"""
if not input_str:
return []
bounds_list = []
try:
pairs = input_str.split(';')
for i, pair_str in enumerate(pairs):
if not pair_str.strip(): continue # Allow for trailing semicolons or empty entries
parts = pair_str.split(',')
if len(parts) != 2:
raise ValueError(f"Bound pair '{pair_str}' (entry {i+1}) does not have two elements. Expected format 'lower,upper'.")
lower_str, upper_str = parts[0].strip().lower(), parts[1].strip().lower()
lower = None if lower_str == 'none' else float(lower_str)
upper = None if upper_str == 'none' else float(upper_str)
if lower is not None and upper is not None and lower > upper:
raise ValueError(f"Lower bound {lower} cannot be greater than upper bound {upper} for pair '{pair_str}' (entry {i+1}).")
bounds_list.append((lower, upper))
return bounds_list
except ValueError as e:
gr.Warning(f"Could not parse bounds: '{input_str}'. Error: {e}. Expected format: 'lower1,upper1; lower2,upper2; ...' e.g., '0,None; 0,10'. Use 'None' for no bound.")
return []
except Exception as e: # Catch any other unexpected parsing errors
gr.Warning(f"Unexpected error parsing bounds: '{input_str}'. Error: {e}")
return []