# EVOLVE-BLOCK-START """Optimized solver for the AC inequality task using Lp gradient descent.""" import time import numpy as np from scipy.signal import fftconvolve def evaluate_sequence(sequence: list[float]) -> float: if not isinstance(sequence, list): return float(np.inf) if not sequence: return float(np.inf) clean: list[float] = [] for x in sequence: if isinstance(x, bool) or not isinstance(x, (int, float)): return float(np.inf) if np.isnan(x) or np.isinf(x): return float(np.inf) clean.append(float(x)) clean = [max(0.0, min(1000.0, x)) for x in clean] n = len(clean) conv = np.convolve(clean, clean) max_b = float(np.max(conv)) sum_a = float(np.sum(clean)) if sum_a < 0.01: return float(np.inf) return float(2.0 * n * max_b / (sum_a**2)) def run(seed: int = 42, budget_s: float = 10.0, **kwargs) -> list[float]: del kwargs rng = np.random.default_rng(seed) start = time.time() deadline = start + budget_s * 0.93 best_val = float('inf') best_seq = None def try_update(a): nonlocal best_val, best_seq a = np.clip(a, 0.0, 1000.0) val = evaluate_sequence(a.tolist()) if val < best_val: best_val = val best_seq = a.copy() return val def fast_obj(a): conv = fftconvolve(a, a) s = np.sum(a) if s < 1e-10: return float('inf') return 2.0 * len(a) * np.max(conv) / s**2 def lp_gradient_descent(a, time_limit, p_start=4, p_end=32): """Projected Lp gradient descent on the convolution peak.""" n = len(a) a = np.maximum(a, 1e-12).astype(np.float64) target_sum = np.sum(a) best_a = a.copy() best_obj = fast_obj(a) lr = 0.01 no_improve = 0 t_start = time.time() t_end = t_start + time_limit step = 0 while time.time() < t_end: step += 1 # Adaptive p progress = min(1.0, step / 2000.0) p = p_start + (p_end - p_start) * progress # Compute convolution conv = fftconvolve(a, a) conv = np.maximum(conv, 0) # numerical safety # Lp weights conv_max = np.max(conv) if conv_max < 1e-20: break # Normalized weights for numerical stability w = (conv / conv_max) ** (p - 1) # Gradient: correlate(w, a) gives grad[i] = sum_k w[k]*a[k-i] a_rev = a[::-1] grad = fftconvolve(w, a_rev, mode='valid') if len(grad) != n: # Adjust if sizes don't match perfectly grad = grad[:n] if len(grad) > n else np.pad(grad, (0, n - len(grad))) # Project: remove mean to preserve sum grad -= np.mean(grad) # Normalize gradient gnorm = np.linalg.norm(grad) if gnorm < 1e-20: break grad = grad / gnorm # Line search with backtracking step_size = lr improved = False for _ in range(5): a_new = a - step_size * grad * np.sqrt(n) a_new = np.maximum(a_new, 1e-12) a_new = a_new / np.sum(a_new) * target_sum new_obj = fast_obj(a_new) if new_obj < best_obj: a = a_new best_a = a.copy() best_obj = new_obj lr = step_size * 1.05 improved = True no_improve = 0 break step_size *= 0.5 if not improved: no_improve += 1 lr *= 0.8 if no_improve > 50: # Restart with perturbation a = best_a + rng.normal(0, 0.01 * np.mean(best_a), n) a = np.maximum(a, 1e-12) a = a / np.sum(a) * target_sum lr = 0.01 no_improve = 0 return best_a, best_obj # Phase 1: Quick evaluation of many starting points candidates = [] for n in [500, 1000, 2000, 4000]: j = np.arange(n, dtype=np.float64) # Best analytical: c + exp(beta * j/n) for beta in [3.0, 4.0, 5.0, 6.0, 8.0]: exp_part = np.exp(beta * j / n) for c_ratio in [0.1, 0.3, 0.5, 0.8, 1.0, 2.0]: c = c_ratio * np.max(exp_part) / beta a = c + exp_part candidates.append(a.copy()) # Exponential only for beta in [0.8, 1.0, 1.2, 1.5, 2.0]: a = np.exp(beta * j / n) candidates.append(a.copy()) # Evaluate all candidates quickly scored = [] for a in candidates: val = fast_obj(a) scored.append((val, a)) scored.sort(key=lambda x: x[0]) # Phase 2: Optimize top candidates num_to_optimize = min(8, len(scored)) time_per = max(0.3, (deadline - time.time() - 2.0) / num_to_optimize) for i in range(num_to_optimize): if time.time() > deadline - 2: break val_init, a_init = scored[i] a_opt, val_opt = lp_gradient_descent(a_init.copy(), time_per) try_update(a_opt) # Phase 3: Final refinement of best solution remaining = deadline - time.time() if remaining > 1.0 and best_seq is not None: # Try also changing length (upsample/downsample) a = best_seq.copy() for factor in [0.5, 0.75, 1.5, 2.0]: new_n = max(100, int(len(a) * factor)) a_resized = np.interp(np.linspace(0, 1, new_n), np.linspace(0, 1, len(a)), a) a_opt, val = lp_gradient_descent(a_resized, min(0.5, remaining / 6)) try_update(a_opt) if time.time() > deadline - 1: break remaining = deadline - time.time() if remaining > 0.5: a_opt, val = lp_gradient_descent(best_seq.copy(), remaining - 0.3, p_start=16, p_end=64) try_update(a_opt) return [float(x) for x in best_seq.tolist()] # EVOLVE-BLOCK-END