Last active
August 15, 2018 13:03
-
-
Save nmayorov/05c04493f773cfb71b66468bdbbf800f to your computer and use it in GitHub Desktop.
"ELEC" nonlinear optimization problem definition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
from scipy.linalg import block_diag | |
class ProblemELEC: | |
def __init__(self, n_electrons, random_state=0): | |
self.n_electrons = n_electrons | |
self.rng = np.random.RandomState(random_state) | |
def x0(self): | |
phi = self.rng.uniform(0, 2 * np.pi, self.n_electrons) | |
theta = self.rng.uniform(-np.pi, np.pi, self.n_electrons) | |
x = np.cos(theta) * np.cos(phi) | |
y = np.cos(theta) * np.sin(phi) | |
z = np.sin(theta) | |
return np.hstack((x, y, z)) | |
def v0(self): | |
return np.zeros(self.n_electrons) | |
def _compute_coordinate_deltas(self, x): | |
x_coord = x[:self.n_electrons] | |
y_coord = x[self.n_electrons:2 * self.n_electrons] | |
z_coord = x[2 * self.n_electrons:] | |
dx = x_coord[:, None] - x_coord | |
dy = y_coord[:, None] - y_coord | |
dz = z_coord[:, None] - z_coord | |
return dx, dy, dz | |
def fun(self, x): | |
dx, dy, dz = self._compute_coordinate_deltas(x) | |
with np.errstate(divide='ignore'): | |
dm1 = (dx**2 + dy**2 + dz**2) ** -0.5 | |
dm1[np.diag_indices_from(dm1)] = 0 | |
return 0.5 * np.sum(dm1) | |
def grad(self, x): | |
dx, dy, dz = self._compute_coordinate_deltas(x) | |
with np.errstate(divide='ignore'): | |
dm3 = (dx**2 + dy**2 + dz**2) ** -1.5 | |
dm3[np.diag_indices_from(dm3)] = 0 | |
grad_x = -np.sum(dx * dm3, axis=1) | |
grad_y = -np.sum(dy * dm3, axis=1) | |
grad_z = -np.sum(dz * dm3, axis=1) | |
return np.hstack((grad_x, grad_y, grad_z)) | |
def hess(self, x): | |
dx, dy, dz = self._compute_coordinate_deltas(x) | |
d = (dx**2 + dy**2 + dz**2) ** 0.5 | |
with np.errstate(divide='ignore'): | |
dm3 = d ** -3 | |
dm5 = d ** -5 | |
i = np.arange(self.n_electrons) | |
dm3[i, i] = 0 | |
dm5[i, i] = 0 | |
Hxx = dm3 - 3 * dx**2 * dm5 | |
Hxx[i, i] = -np.sum(Hxx, axis=1) | |
Hxy = -3 * dx * dy * dm5 | |
Hxy[i, i] = -np.sum(Hxy, axis=1) | |
Hxz = -3 * dx * dz * dm5 | |
Hxz[i, i] = -np.sum(Hxz, axis=1) | |
Hyy = dm3 - 3 * dy**2 * dm5 | |
Hyy[i, i] = -np.sum(Hyy, axis=1) | |
Hyz = -3 * dy * dz * dm5 | |
Hyz[i, i] = -np.sum(Hyz, axis=1) | |
Hzz = dm3 - 3 * dz**2 * dm5 | |
Hzz[i, i] = -np.sum(Hzz, axis=1) | |
H = np.vstack(( | |
np.hstack((Hxx, Hxy, Hxz)), | |
np.hstack((Hxy, Hyy, Hyz)), | |
np.hstack((Hxz, Hyz, Hzz)) | |
)) | |
return H | |
def ceq(self, x): | |
x_coord = x[:self.n_electrons] | |
y_coord = x[self.n_electrons:2 * self.n_electrons] | |
z_coord = x[2 * self.n_electrons:] | |
return x_coord**2 + y_coord**2 + z_coord**2 - 1 | |
def ceq_jac(self, x): | |
x_coord = x[:self.n_electrons] | |
y_coord = x[self.n_electrons:2 * self.n_electrons] | |
z_coord = x[2 * self.n_electrons:] | |
Jx = 2 * np.diag(x_coord) | |
Jy = 2 * np.diag(y_coord) | |
Jz = 2 * np.diag(z_coord) | |
return np.hstack((Jx, Jy, Jz)) | |
def ceq_hess(self, x, v): | |
D = 2 * np.diag(v) | |
return block_diag(D, D, D) | |
def lagrangian_hess(self, x, v): | |
return self.hess(x) + self.ceq_hess(x, v) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is there a description of this problem someplace, please ?
What's a reasonable value for
n_electrons
?(I'm looking for problems with noisy gradients, not classification / not neuralnets, to try to plot gradient flows.)
Thanks