Skip to content

Commit

Permalink
Updated problem methods in solvers; fix the obj_terms to work with Cupy
Browse files Browse the repository at this point in the history
  • Loading branch information
fpicetti committed Apr 22, 2022
1 parent 4d93eec commit 9ce5e33
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 32 deletions.
14 changes: 8 additions & 6 deletions occamypy/solver/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def get_restart(self, log_file):
print("WARNING! No restart folder's path was found in %s" % log_file)
return

def save_results(self, iiter, problem, **kwargs):
def save_results(self, iiter, problem, force_save: bool = False, force_write: bool = False, **kwargs):
"""
Save results to disk
Expand All @@ -157,13 +157,13 @@ def save_results(self, iiter, problem, **kwargs):
"""
if not isinstance(problem, Problem):
raise TypeError("Input variable is not a Problem object")
force_save = kwargs.get("force_save", False)
force_write = kwargs.get("force_write", False)

# Getting a model from arguments if provided (necessary to remove preconditioning)
mod_save = kwargs.get("model", problem.get_model())
# Obtaining objective function value
objf_value = kwargs.get("obj", problem.get_obj(problem.get_model()))
obj_terms = kwargs.get("obj_terms", problem.obj_terms) if "obj_terms" in dir(problem) else None

# Save if it is forced to or if the solver hits a sampled iteration number
# The objective function is saved every iteration if requested
if self.save_obj:
Expand All @@ -172,10 +172,10 @@ def save_results(self, iiter, problem, **kwargs):
if obj_terms is not None:
if len(self.obj_terms) == 0:
# First time obj_terms are saved
self.obj_terms = np.expand_dims(np.append(self.obj_terms, deepcopy(obj_terms)), axis=0)
self.obj_terms = np.expand_dims(np.append(self.obj_terms, [float(_) for _ in obj_terms]), axis=0)
else:
self.obj_terms = np.append(self.obj_terms,
np.expand_dims(np.array(deepcopy(obj_terms)), axis=0),
np.expand_dims(np.array([float(_) for _ in obj_terms]), axis=0),
axis=0)
if iiter % self.iter_sampling == 0 or force_save:
if self.save_model:
Expand Down Expand Up @@ -242,12 +242,14 @@ def _write_steps(self, force_write=False):
res_file = self.prefix + "_residual.H" # File name in which the residual vector is saved
self.resSet.writeSet(res_file, mode=mode)

def run(self, prblm):
def run(self, problem, verbose: bool = False, restart: bool = False):
"""
Solve the given problem
Args:
problem: problem to be solved
verbose: verbosity flag
restart: restart previous inversion from restart folder
"""
raise NotImplementedError("Implement run Solver in the derived class.")

Expand Down
29 changes: 14 additions & 15 deletions occamypy/solver/linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
class CG(Solver):
"""Linear-Conjugate Gradient and Steepest-Descent Solver"""

# Default class methods/functions
def __init__(self, stopper, steepest=False, **kwargs):
"""
CG/SD constructor
Expand Down Expand Up @@ -141,7 +140,7 @@ def run(self, problem, verbose=False, restart=False):
beta = dot_grad_prec_grad / dot_grad_prec_grad_old
# Update search direction
cg_dmodl.scaleAdd(cg_prec_grad, beta, 1.0)
cg_dmodld = problem.get_dres(cg_mdl, cg_dmodl) # Project search direction into the data space
cg_dmodld = problem.get_pert_res(cg_mdl, cg_dmodl) # Project search direction into the data space
dot_cg_dmodld = cg_dmodld.dot(cg_dmodld)
if dot_cg_dmodld == 0.0:
success = False
Expand All @@ -157,7 +156,7 @@ def run(self, problem, verbose=False, restart=False):
if self.logger:
self.logger.addToLog(msg)
else:
prblm_gradd = problem.get_dres(cg_mdl, prblm_grad) # Project gradient into the data space
prblm_gradd = problem.get_pert_res(cg_mdl, prblm_grad) # Project gradient into the data space
# Computing alpha and beta coefficients
if iiter == 0 or self.steepest:
# Steepest descent
Expand Down Expand Up @@ -242,20 +241,20 @@ def run(self, problem, verbose=False, restart=False):
if precond:
cg_dmodl.scale(1.0 / alpha) # Unscaling the search direction
else:
# copying previous residuals dres = res_old
# copying previous residuals pert_res = res_old
cg_dres.copy(prblm_res)
# Computing actual change in the residual vector dres = res_new - res_old
# Computing actual change in the residual vector pert_res = res_new - res_old
prblm_res = problem.get_res(cg_mdl) # New residual vector
cg_dres.scaleAdd(prblm_res, -1.0, 1.0)
else:
# Setting residual vector to avoid its unnecessary computation (if model was not clipped)
if precond:
# res = res + alpha * dres
# res = res + alpha * pert_res
prblm_res.scaleAdd(cg_dmodld, 1.0, alpha) # Update residuals
else:
# dres = alpha * gradd + beta * dres
# pert_res = alpha * gradd + beta * pert_res
cg_dres.scaleAdd(prblm_gradd, beta, alpha) # Update residual step
# res = res + dres
# res = res + pert_res
prblm_res.scaleAdd(cg_dres) # Update residuals
problem.set_residual(prblm_res)

Expand Down Expand Up @@ -536,7 +535,7 @@ def run(self, problem, verbose=False, restart=False):
"""

# op.matvec(v) (i.e., projection of v onto the data space)
v_prblm = problem.get_dres(x, v)
v_prblm = problem.get_pert_res(x, v)
# u = op.matvec(v) - alpha * u
u.scaleAdd(v_prblm, -alpha, 1.0)
beta = u.norm()
Expand Down Expand Up @@ -665,7 +664,7 @@ def run(self, problem, verbose=False, restart=False):

class CGsym(Solver):
"""Linear-Conjugate Gradient and Steepest-Descent Solver for symmetric systems"""

def __init__(self, stopper, steepest=False, **kwargs):
"""
CG/SD for symmetric systems constructor
Expand Down Expand Up @@ -779,7 +778,7 @@ def run(self, problem, verbose=False, restart=False):
problem.prec.forward(False, prblm_res, cg_prec_res)
# dmodl = beta * dmodl - res
cg_dmodl.scaleAdd(cg_prec_res if precond else prblm_res, beta, -1.0) # Update search direction
prblm_ddmodl = problem.get_dres(cg_mdl, cg_dmodl) # Project search direction in the data space
prblm_ddmodl = problem.get_pert_res(cg_mdl, cg_dmodl) # Project search direction in the data space

dot_dmodl_ddmodl = cg_dmodl.dot(prblm_ddmodl)
if precond:
Expand Down Expand Up @@ -839,17 +838,17 @@ def run(self, problem, verbose=False, restart=False):
cg_dmodl.scaleAdd(prblm_mdl, 1.0, -1.0)
# dmodl is scaled by the inverse of the step length
cg_dmodl.scale(1.0 / alpha)
# copying previos residuals dres = res_old
# copying previos residuals pert_res = res_old
prblm_ddmodl.copy(prblm_res)
problem.set_model(cg_mdl)
# Computing actual change in the residual vector dres = res_new - res_old
# Computing actual change in the residual vector pert_res = res_new - res_old
prblm_res = problem.get_res(cg_mdl) # New residual vector
prblm_ddmodl.scaleAdd(prblm_res, -1.0, 1.0)
# dres is scaled by the inverse of the step length
# pert_res is scaled by the inverse of the step length
prblm_ddmodl.scale(1.0 / alpha)
else:
# Setting residual vector to avoid its unnecessary computation (if model was not clipped)
# res = res + alpha * dres = res + alpha * op * dmodl
# res = res + alpha * pert_res = res + alpha * op * dmodl
prblm_res.scaleAdd(prblm_ddmodl, sc2=alpha) # update residuals
problem.set_residual(prblm_res)
if iiter == 1:
Expand Down
22 changes: 11 additions & 11 deletions occamypy/solver/stepper.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def estimate_initial_guess(self, problem, modl, dmodl, logger):
"""Function to estimate initial step length value"""
try:
# Projecting search direction in the data space
dres = problem.get_dres(modl, dmodl)
pert_res = problem.get_pert_res(modl, dmodl)
except NotImplementedError:
if logger:
logger.addToLog(
Expand All @@ -29,8 +29,8 @@ def estimate_initial_guess(self, problem, modl, dmodl, logger):
alpha_guess = 1.0 / dmodl.norm()
return alpha_guess
res = problem.get_res(modl)
dres_res = res.dot(dres)
dres_dres = dres.dot(dres)
dres_res = res.dot(pert_res)
dres_dres = pert_res.dot(pert_res)
if dres_dres == 0.:
if logger:
logger.addToLog(
Expand Down Expand Up @@ -1012,7 +1012,7 @@ def __init__(self, c1=1.e-4, c2=0.9 , ntry=20, alpha=1., alpha_scale=0.8, alpha_
self.alpha_scale = alpha_scale
self.keepAlpha = keepAlpha

def alpha_zoom(self, problem, mdl0, mdl, obj0, dphi0, dmodl, alpha_lo, alpha_hi, logger=None):
def alpha_zoom(self, problem, mdl0, model, obj0, dphi0, dmodl, alpha_lo, alpha_hi, logger=None):
"""Algorithm 3.6, Page 61. "Numerical Optimization". Nocedal & Wright."""
itry = 0
alpha = 0.0
Expand All @@ -1022,22 +1022,22 @@ def alpha_zoom(self, problem, mdl0, mdl, obj0, dphi0, dmodl, alpha_lo, alpha_hi,
alpha_i = 0.5 * (alpha_lo + alpha_hi)
alpha = alpha_i
# x = x0 + alpha_i * p
mdl.copy(mdl0)
mdl.scaleAdd(dmodl, sc2=alpha_i)
model.copy(mdl0)
model.scaleAdd(dmodl, sc2=alpha_i)
# Evaluating objective and gradient function
obj_i = problem.get_obj(mdl)
obj_i = problem.get_obj(model)
if logger:
logger.addToLog("\t\tObjective function value of %.5e at m_i with alpha=%.5e [alpha_zoom]" %(obj_i, alpha_i))
if isnan(obj_i):
if logger:
logger.addToLog("\t\t!!!Problem with step length and objective function; Setting alpha = 0.0 [alpha_zoom]!!!")
alpha = 0.0
break
grad_i = problem.get_grad(mdl)
grad_i = problem.get_grad(model)
# x_lo = x0 + alpha_lo * p;
mdl.copy(mdl0)
mdl.scaleAdd(dmodl, sc2=alpha_lo) # x = x0 + alpha_i * p;
obj_lo = problem.get_obj(mdl)
model.copy(mdl0)
model.scaleAdd(dmodl, sc2=alpha_lo) # x = x0 + alpha_i * p;
obj_lo = problem.get_obj(model)
if logger:
logger.addToLog("\t\tObjective function value of %.5e at m_lo with alpha_lo=%.5e [alpha_zoom]" %(obj_lo, alpha_lo))
if isnan(obj_lo):
Expand Down

0 comments on commit 9ce5e33

Please sign in to comment.