spagestic's picture
Refactor Operations Research Interfaces and Add Utility Functions
79bc79a
"""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 []