File size: 2,513 Bytes
2facf1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import numpy as np
from scipy.optimize import minimize, differential_evolution


def compute_c1(f_values, n_points):
    dx = 0.5 / n_points
    f_nn = np.maximum(f_values, 0.0)
    autoconv = np.convolve(f_nn, f_nn, mode='full') * dx
    integral_sq = (np.sum(f_nn) * dx) ** 2
    if integral_sq < 1e-12:
        return 1e10
    return np.max(autoconv) / integral_sq


def heights_to_function(heights, n_eval):
    """Convert step heights to fine-grained function values"""
    n_steps = len(heights)
    f = np.zeros(n_eval)
    step_width = n_eval / n_steps
    for i in range(n_steps):
        start = int(i * step_width)
        end = int((i + 1) * step_width)
        end = min(end, n_eval)
        f[start:end] = max(heights[i], 0.0)
    return f


def optimize_step_function(n_steps, n_eval, seed=42):
    dx = 0.5 / n_eval

    def objective(heights):
        f = heights_to_function(np.maximum(heights, 0.0), n_eval)
        return compute_c1(f, n_eval)

    # Differential evolution
    bounds = [(0.01, 5.0)] * n_steps
    result = differential_evolution(
        objective, bounds, seed=seed,
        maxiter=2000, tol=1e-12, polish=True,
        popsize=40, mutation=(0.5, 1.5), recombination=0.9,
        workers=1,
    )

    heights = np.maximum(result.x, 0.0)
    f = heights_to_function(heights, n_eval)
    c1 = compute_c1(f, n_eval)

    # Polish with Nelder-Mead
    result2 = minimize(objective, heights, method='Nelder-Mead',
                       options={'maxiter': 50000, 'xatol': 1e-12, 'fatol': 1e-12})
    heights2 = np.maximum(result2.x, 0.0)
    f2 = heights_to_function(heights2, n_eval)
    c1_2 = compute_c1(f2, n_eval)

    if c1_2 < c1:
        return f2, c1_2, heights2
    return f, c1, heights


def run():
    best_c1 = float('inf')
    best_f = None
    best_n = None
    n_eval = 4000

    configs = [
        (10, 42), (15, 42), (20, 42), (25, 42), (30, 42),
        (40, 42), (50, 42), (60, 42),
        (20, 0), (30, 0), (40, 0), (50, 0),
        (20, 7), (30, 7), (40, 7),
    ]

    for n_steps, seed in configs:
        print(f"n_steps={n_steps}, seed={seed}...", end=" ", flush=True)
        f, c1, heights = optimize_step_function(n_steps, n_eval, seed)
        print(f"C1={c1:.10f}")

        if c1 < best_c1:
            best_c1 = c1
            best_f = f
            best_n = n_eval
            print(f"  *** NEW BEST: C1={c1:.10f}, heights={np.round(heights, 4)}")

    print(f"\nFinal best C1: {best_c1:.10f}")
    return best_f, best_c1, best_c1, best_n