def optimize(self): forces = self.geometry.forces energy = self.geometry.energy self.forces.append(forces) self.energies.append(energy) if self.cur_cycle > 0: # Gradient difference y = self.forces[-2] - forces # Coordinate difference / step s = self.steps[-1] # Curvature condition sy = s.dot(y) self.log(f"s·y={sy:.6f} (undamped)") # Hessian update self.update_func(s, y) # Results in simple SD step in the first cycle step = self.H.dot(forces) self.log(f"Calcualted {self.update} step") # Step restriction unscaled_norm = np.linalg.norm(step) step = scale_by_max_step(step, self.max_step) scaled_norm = np.linalg.norm(step) self.log(f"Unscaled norm(step)={unscaled_norm:.4f}") self.log(f" Scaled norm(step)={scaled_norm:.4f}") return step
def optimize(self): if self.is_cos and self.align: rot_vecs, rot_vec_lists, _ = fit_rigid( self.geometry, vector_lists=(self.steps, self.forces, self.coord_diffs, self.grad_diffs)) rot_steps, rot_forces, rot_coord_diffs, rot_grad_diffs = rot_vec_lists self.steps = rot_steps self.forces = rot_forces self.coord_diffs = rot_coord_diffs self.grad_diffs = rot_grad_diffs forces = self.geometry.forces self.forces.append(forces) energy = self.geometry.energy self.energies.append(energy) norm = np.linalg.norm(forces) self.log(f"norm(forces)={norm:.6f}") if self.cur_cycle > 0: y = self.forces[-2] - forces s = self.steps[-1] if self.double_damp: s, y = double_damp(s, y, s_list=self.coord_diffs, y_list=self.grad_diffs) self.grad_diffs.append(y) self.coord_diffs.append(s) # Drop superfluous oldest vectors self.coord_diffs = self.coord_diffs[-self.keep_last:] self.grad_diffs = self.grad_diffs[-self.keep_last:] step = bfgs_multiply(self.coord_diffs, self.grad_diffs, forces, beta=self.beta, gamma_mult=self.gamma_mult, logger=self.logger) step = scale_by_max_step(step, self.max_step) return step
def optimize(self): new_image_inds = self.geometry.new_image_inds string_size_changed = len(new_image_inds) > 0 if self.align and string_size_changed and self.is_cart_opt: procrustes(self.geometry) self.log("Aligned string.") forces = self.geometry.forces self.energies.append(self.geometry.energy) self.forces.append(forces) cur_size = self.geometry.string_size add_to_list = ( self.keep_last > 0 # Only add to s_list and y_list if we want to keep and self.cur_cycle > 0 # cycles and if we can actually calculate differences. and (not self. lbfgs_when_full # Add when LBFGS is allowed before fully grown. or self.lbfgs_when_full and self.geometry.fully_grown and not string_size_changed # Don't add when string has to be fully grown # but grew fully in this cycle. )) if add_to_list: inds = list(range(cur_size)) try: y = self.forces[-2] - forces s = self.coords[-1] - self.coords[-2] # Will be raised when the string grew in the previous cycle. except ValueError: cur_forces = np.delete(forces.reshape(cur_size, -1), new_image_inds, axis=0).flatten() y = self.forces[-2] - cur_forces cur_coords = np.delete(self.coords[-1].reshape(cur_size, -1), new_image_inds, axis=0).flatten() s = self.coords[-2] - cur_coords inds = np.delete(inds, new_image_inds) if self.double_damp: s, y = double_damp(s, y, s_list=self.s_list, y_list=self.y_list) self.s_list.append(s) self.y_list.append(y) self.inds.append(inds) # Drop oldest vectors self.s_list = self.s_list[-self.keep_last:] self.y_list = self.y_list[-self.keep_last:] self.inds = self.inds[-self.keep_last:] # Results in steepest descent step for empty s_list & y_list step = bfgs_multiply(self.s_list, self.y_list, forces, gamma_mult=self.gamma_mult, inds=self.inds, cur_size=cur_size, logger=self.logger) # When keep_last == 0 or LBFGS is not yet enabled then s_list and y_list will # be empty and step will be a simple SD step. We try to improve it via CG. if ((self.keep_last == 0 and self.cur_cycle > 0 and not string_size_changed) and (len(self.s_list) == 0 and len(self.y_list) == 0)): prev_forces = self.forces[-2] # Fletcher-Reeves beta = forces.dot(forces) / prev_forces.dot(prev_forces) # Polar-Ribiere # beta = forces.dot(forces - prev_forces) / prev_forces.dot(prev_forces) beta = min(beta, 1) step = forces + beta * self.steps[-1] self.log(f"Conjugate gradient correction, β={beta:.6f}") if self.scale_step == "global": step = scale_by_max_step(step, self.max_step) elif self.scale_step == "per_image": for image_step in step.reshape(len(self.geometry.images), -1): scale_by_max_step(image_step, self.max_step) else: raise Exception("Invalid scale_step={self.scale_step}!") return step
def optimize(self): gradient = self.geometry.gradient energy = self.geometry.energy self.forces.append(-gradient) self.energies.append(energy) self.log(f"norm(forces)={np.linalg.norm(gradient):.4e}") if self.bio: stretch_gradient, remainder_gradient = self.bio_mode(gradient) self.adjust_alpha_stretch() # Steepest descent against the stretch_gradient stretch_step = -self.alpha_stretch * stretch_gradient new_coords = self.geometry.coords + stretch_step self.geometry.coords = new_coords # Use only the remaining gradient for the rest of this method gradient = remainder_gradient self.gradients_for_precon.append(gradient) self.coords_for_precon.append(self.geometry.coords.copy()) if len(self.coords_for_precon) > 2: steps = np.diff(self.coords_for_precon, axis=0)[-self.hist_max:] grad_diffs = np.diff(self.gradients_for_precon, axis=0)[-self.hist_max:] self.log("Preconditioning gradient with information from " f"{len(steps)+1} previous cycles.") precon_grad = self.precondition_gradient(gradient, steps, grad_diffs, self.eps) step = -precon_grad else: self.log("Took pure steepest descent step.") step = self.alpha * -gradient scale_by_max_step(step, self.trust_radius) if self.linesearch: # Calculate energy at new geometry new_coords = self.geometry.coords + step _ = self.geometry.get_energy_and_forces_at(new_coords) new_energy = _["energy"] delta_energy = new_energy - energy self.log(f"Current energy is {energy:.6f} au. New energy is " f"{new_energy:.6f} au, ΔE={delta_energy:.6f} au.") energy_rise_too_big = new_energy > (energy + self.E_thresh) alpha_still_big_enough = self.alpha > (self.alpha_start / 10) alpha_stre_still_big_enough = self.alpha_stretch > ( self.alpha_stretch_start / 10) if energy_rise_too_big and alpha_still_big_enough and alpha_stre_still_big_enough: self.log(f"Energy increased by {delta_energy:.6f} au") self.gradients_for_precon = self.gradients_for_precon[-2:-1] self.coords_for_precon = self.coords_for_precon[-2:-1] self.log("Resetted history.") self.alpha /= 2 self.alpha_stretch /= 2 self.log(f"Decreased alpha to {self.alpha}") self.log("Reverting bad step") return None return step