File size: 6,010 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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# EVOLVE-BLOCK-START
import numpy as np
from scipy.optimize import minimize
from scipy.interpolate import interp1d
import time


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


def compute_c1_fft(f_values, dx):
    f = np.maximum(f_values, 0.0)
    N = len(f)
    M = 2 * N
    fft_f = np.fft.rfft(f, n=M)
    conv = np.fft.irfft(fft_f * fft_f, n=M) * dx
    integral_sq = (np.sum(f) * dx) ** 2
    if integral_sq < 1e-20:
        return 1e10
    return float(np.max(conv) / integral_sq)


def compute_c1_smooth_and_grad(f, N, dx, alpha=200.0):
    M = 2 * N
    fft_f = np.fft.rfft(f, n=M)
    conv = np.fft.irfft(fft_f * fft_f, n=M) * dx
    integral = np.sum(f) * dx
    if integral < 1e-15:
        return 1e10, np.zeros(N)
    integral_sq = integral ** 2
    max_val = np.max(conv)
    shifted = conv - max_val
    mask = shifted > -50.0 / alpha
    weights = np.zeros_like(conv)
    weights[mask] = np.exp(alpha * shifted[mask])
    sum_w = np.sum(weights)
    if sum_w < 1e-30:
        weights[np.argmax(conv)] = 1.0
        sum_w = 1.0
    smooth_max = max_val + np.log(sum_w) / alpha
    softmax_w = weights / sum_w
    c1 = smooth_max / integral_sq
    fft_sw = np.fft.rfft(softmax_w, n=M)
    fft_fp = np.fft.rfft(f, n=M)
    corr = np.fft.irfft(fft_sw * np.conj(fft_fp), n=M)
    grad_f = 2.0 * corr[:N] * dx / integral_sq - 2.0 * smooth_max * dx / (integral**3)
    return c1, grad_f


def opt(params, N, dx, alpha, maxiter, method='L-BFGS-B'):
    def obj(p, a=alpha):
        f = p ** 2
        c1, g = compute_c1_smooth_and_grad(f, N, dx, a)
        return c1, g * 2 * p
    if method == 'CG':
        result = minimize(obj, params, jac=True, method='CG',
                         options={'maxiter': maxiter, 'gtol': 1e-12})
    else:
        result = minimize(obj, params, jac=True, method='L-BFGS-B',
                         options={'maxiter': maxiter, 'ftol': 1e-16, 'gtol': 1e-15})
    return result.x


def upscale(f, N_new):
    N_old = len(f)
    x_old = np.linspace(0, 1, N_old)
    x_new = np.linspace(0, 1, N_new)
    interp = interp1d(x_old, f, kind='linear', fill_value=0.0, bounds_error=False)
    return np.maximum(interp(x_new), 0.0)


def run():
    t0 = time.time()

    N = 10000
    dx = 0.5 / N

    # Try to load best saved function
    try:
        f_loaded = np.load('/workspace/best_f_10000.npy')
        if len(f_loaded) == N:
            best_c1 = compute_c1_fft(f_loaded, dx)
            best_f = f_loaded.copy()
            print(f"Loaded N={N}: C1 = {best_c1:.10f}")
        else:
            raise FileNotFoundError
    except:
        try:
            # Try multiple 5k sources
            best_5k_c1 = np.inf
            f_5k = None
            for fname in ['/workspace/best_f_5000_hard.npy', '/workspace/best_f_5000.npy']:
                try:
                    f_tmp = np.load(fname)
                    c1_tmp = compute_c1_fft(f_tmp, 0.5/len(f_tmp))
                    if c1_tmp < best_5k_c1:
                        best_5k_c1 = c1_tmp
                        f_5k = f_tmp
                except:
                    pass
            if f_5k is None:
                raise FileNotFoundError
            f_init = upscale(f_5k, N)
        except:
            # Fallback: start fresh
            np.random.seed(684)
            f_150 = np.ones(150) + 0.3 * np.random.randn(150)
            f_150 = np.maximum(f_150, 0.01)
            params = np.sqrt(f_150 + 1e-12)
            for alpha in [50, 500, 5000]:
                params = opt(params, 150, 0.5/150, alpha, 500)
            f_init = upscale(params ** 2, N)

        params = np.sqrt(np.maximum(f_init, 0.0) + 1e-12)
        for alpha in [200.0, 2000.0, 20000.0]:
            params = opt(params, N, dx, alpha, 5000)
        best_f = params ** 2
        best_c1 = compute_c1_fft(best_f, dx)
        print(f"From upscale: C1 = {best_c1:.10f}")

    params = np.sqrt(np.maximum(best_f, 0.0) + 1e-12)

    # Alpha cycling - maximize iterations
    print(f"\nAlpha cycling at N={N}:")
    cycle = 0
    while time.time() - t0 < 1500:
        # Low alpha: explore landscape
        for alpha in [0.5, 2.0, 10.0]:
            params = opt(params, N, dx, alpha, 500)
        # Medium alpha: refine
        for alpha in [100.0, 1000.0]:
            params = opt(params, N, dx, alpha, 1000)
        # High alpha: converge to true max
        for alpha in [10000.0, 100000.0]:
            params = opt(params, N, dx, alpha, 2000)
        # Occasional CG for different search direction
        if cycle % 3 == 2:
            params = opt(params, N, dx, 10000.0, 2000, method='CG')
            params = opt(params, N, dx, 100000.0, 2000)

        f_out = params ** 2
        c1 = compute_c1_fft(f_out, dx)
        if c1 < best_c1:
            best_c1 = c1
            best_f = f_out.copy()
            if cycle % 5 == 0:
                print(f"  Cycle {cycle}: C1 = {c1:.10f}, t={time.time()-t0:.0f}s")
        cycle += 1

    print(f"  Total cycles: {cycle}, C1: {best_c1:.10f}")

    # Final refinement with very high alpha
    params = np.sqrt(np.maximum(best_f, 0.0) + 1e-12)
    params = opt(params, N, dx, 1000000.0, 15000)
    f_out = params ** 2
    c1 = compute_c1_fft(f_out, dx)
    if c1 < best_c1:
        best_c1 = c1
        best_f = f_out.copy()

    print(f"\nFinal C1 (FFT): {best_c1:.10f}")
    print(f"Score: {1.5052939684401607 / best_c1:.10f}")

    # Save for future runs
    f_out = np.maximum(best_f, 0.0)
    np.save('/workspace/best_f_10000.npy', f_out)

    # Compute exact C1 using np.convolve for the evaluator
    autoconv = np.convolve(f_out, f_out, mode='full') * dx
    integral_sq = (np.sum(f_out) * dx) ** 2
    c1_final = float(np.max(autoconv / integral_sq))

    print(f"C1 (exact): {c1_final:.10f}")

    return f_out, c1_final, c1_final, N


# EVOLVE-BLOCK-END