# import sys, math # from copy import deepcopy # import numpy as np # from numpy import linalg # from diceplayer.DPpack.SetGlobals import * # epsilon = 1e-8 # ####################################### functions ###################################### # def best_previous_point(): # min_energy = 0 # idx = 0 # for energy in internal["energy"][:-1]: # if energy < min_energy or abs(energy - min_energy) < 1e-10: # min_energy = energy # min_idx = idx # idx += 1 # return min_idx # def best_point(): # min_energy = 0 # idx = 0 # for energy in internal["energy"]: # if energy < min_energy or abs(energy - min_energy) < 1e-10: # min_energy = energy # min_idx = idx # idx += 1 # return min_idx # def line_search(fh): # X1 = internal["position"][-1] # numpy array # e1 = internal["energy"][-1] # G1 = internal["gradient"][-1] # numpy array # idx = best_previous_point() # X0 = internal["position"][idx] # numpy array # e0 = internal["energy"][idx] # G0 = internal["gradient"][idx] # numpy array # # First try a quartic fit # fh.write("Attempting a quartic fit.\n") # success, y0 = quartic_fit(X0, X1, e0, e1, G0, G1, fh) # if success and y0 > 0: # if y0 < 1: # new_point = X0 + y0 * (X1 - X0) # new_gradient = interpolate_gradient(G0, G1, y0) # new_gradient = perpendicular_projection(new_gradient, X1 - X0) # fh.write("Line search succeded.\n") # return True, new_point, new_gradient # else: # idx = best_point() # if idx == len(internal["energy"]) - 1: # new_point = X0 + y0 * (X1 - X0) # new_gradient = interpolate_gradient(G0, G1, y0) # new_gradient = perpendicular_projection(new_gradient, X1 - X0) # fh.write("Line search succeded.\n") # return True, new_point, new_gradient # else: # fh.write("Quartic step is not acceptable. ") # elif success: # fh.write("Quartic step is not acceptable. ") # # If no condition is met, then y0 is unacceptable. Try the cubic fit next # fh.write("Attempting a cubic fit.\n") # success, y0 = cubic_fit(X0, X1, e0, e1, G0, G1, fh) # if success and y0 > 0: # if y0 < 1: # new_point = X0 + y0 * (X1 - X0) # new_gradient = interpolate_gradient(G0, G1, y0) # new_gradient = perpendicular_projection(new_gradient, X1 - X0) # fh.write("Line search succeded.\n") # return True, new_point, new_gradient # else: # previous_step = X1 - internal["position"][-2] # previous_step_size = linalg.norm(previous_step) # new_point = X0 + y0 * (X1 - X0) # step = new_point - X1 # step_size = linalg.norm(step) # if step_size < previous_step_size: # new_gradient = interpolate_gradient(G0, G1, y0) # new_gradient = perpendicular_projection(new_gradient, X1 - X0) # fh.write("Line search succeded.\n") # return True, new_point, new_gradient # else: # fh.write("Cubic step is not acceptable. ") # elif success: # fh.write("Cubic step is not acceptable. ") # # If no condition is met again, then all fits fail. # fh.write("All fits fail. ") # # Then, if the latest point is not the best, use y0 = 0.5 (step to the midpoint) # idx = best_point() # if idx < len(internal["energy"]) - 1: # y0 = 0.5 # new_point = X0 + y0 * (X1 - X0) # new_gradient = interpolate_gradient(G0, G1, y0) # new_gradient = perpendicular_projection(new_gradient, X1 - X0) # fh.write("Moving to the midpoint.\n") # return True, new_point, new_gradient # # If the latest point is the best point, no linear search is done # fh.write("No linear search will be used in this step.\n") # return False, None, None # ## For cubic and quartic fits, G0 and G1 are the gradient vectors # def cubic_fit(X0, X1, e0, e1, G0, G1, fh): # line = X1 - X0 # line /= linalg.norm(line) # g0 = np.dot(G0, line) # g1 = np.dot(G1, line) # De = e1 - e0 # fh.write( # "De = {:<18.15e} g0 = {:<12.8f} g1 = {:<12.8f}\n".format(De, g0, g1) # ) # alpha = g1 + g0 - 2 * De # if abs(alpha) < epsilon: # fh.write("Cubic fit failed: alpha too small\n") # return False, None # beta = 3 * De - 2 * g0 - g1 # discriminant = 4 * (beta**2 - 3 * alpha * g0) # if discriminant < 0: # fh.write("Cubic fit failed: no minimum found (negative Delta)\n") # return False, None # if abs(discriminant) < epsilon: # fh.write("Cubic fit failed: no minimum found (null Delta)\n") # return False, None # y0 = (-beta + math.sqrt(discriminant / 4)) / (3 * alpha) # fh.write("Minimum found with y0 = {:<8.4f}\n".format(y0)) # return True, y0 # def quartic_fit(X0, X1, e0, e1, G0, G1, fh): # line = X1 - X0 # line /= linalg.norm(line) # g0 = np.dot(G0, line) # g1 = np.dot(G1, line) # De = e1 - e0 # Dg = g1 - g0 # fh.write( # "De = {:<18.15e} g0 = {:<12.8f} g1 = {:<12.8f}\n".format(De, g0, g1) # ) # if Dg < 0 or De - g0 < 0: # fh.write("Quartic fit failed: negative alpha\n") # return False, None # if abs(Dg) < epsilon or abs(De - g0) < epsilon: # fh.write("Quartic fit failed: alpha too small\n") # return False, None # discriminant = 16 * (Dg**2 - 3 * (g1 + g0 - 2 * De) ** 2) # if discriminant < 0: # fh.write("Quartic fit failed: no minimum found (negative Delta)\n") # return False, None # alpha1 = (Dg + math.sqrt(discriminant / 16)) / 2 # alpha2 = (Dg - math.sqrt(discriminant / 16)) / 2 # fh.write("alpha1 = {:<7.4e} alpha2 = {:<7.4e}\n".format(alpha1, alpha2)) # alpha = alpha1 # beta = g1 + g0 - 2 * De - 2 * alpha # gamma = De - g0 - alpha - beta # y0 = (-1 / (2 * alpha)) * ( # (beta**3 - 4 * alpha * beta * gamma + 8 * g0 * alpha**2) / 4 # ) ** (1 / 3) # fh.write("Minimum found with y0 = {:<8.4f}\n".format(y0)) # return True, y0 # def rfo_step(gradient, hessian, type): # dim = len(gradient) # aug_hessian = [] # for i in range(dim): # aug_hessian.extend(hessian[i, :].tolist()) # aug_hessian.append(gradient[i]) # aug_hessian.extend(gradient.tolist()) # aug_hessian.append(0) # aug_hessian = np.array(aug_hessian).reshape(dim + 1, dim + 1) # evals, evecs = linalg.eigh(aug_hessian) # if type == "min": # step = np.array(evecs[:-1, 0]) # elif type == "ts": # step = np.array(evecs[:-1, 1]) # return step # def update_trust_radius(): # if internal["trust_radius"] == None: # internal["trust_radius"] = player["maxstep"] # elif len(internal["energy"]) > 1: # X1 = internal["position"][-1] # X0 = internal["position"][-2] # Dx = X1 - X0 # displace = linalg.norm(Dx) # e1 = internal["energy"][-1] # e0 = internal["energy"][-2] # De = e1 - e0 # g0 = internal["gradient"][-2] # h0 = internal["hessian"][-2] # rho = De / (np.dot(g0, Dx) + 0.5 * np.dot(Dx, np.matmul(h0, Dx.T).T)) # if rho > 0.75 and displace > 0.8 * internal["trust_radius"]: # internal["trust_radius"] = 2 * internal["trust_radius"] # elif rho < 0.25: # internal["trust_radius"] = 0.25 * displace # return # def interpolate_gradient(G0, G1, y0): # DG = G1 - G0 # gradient = G0 + y0 * DG # return gradient # def perpendicular_projection(vector, line): # direction = line / linalg.norm(line) # projection = np.dot(vector, direction) * direction # return vector - projection