def test01_alt(self): """Set directory""" if isdir(self.myoutdir): os.system('rm -rf {0}'.format(self.myoutdir)) myplot = PlotFenics() myplot.set_outdir(self.myoutdir) self.assertTrue(isdir(self.myoutdir))
def test05(self): """Indices list properly created""" myplot = PlotFenics(self.myoutdir) myplot.set_varname('testm') for ii in self.indexlist: self.m.vector()[:] = ii * np.ones(self.lenm, dtype='float') myplot.plot_vtk(self.m, ii) self.assertTrue(myplot.indices == self.indexlist)
def test04(self): """Plot parameter m""" myplot = PlotFenics(self.myoutdir) myplot.set_varname('testm') self.m.vector()[:] = 20 * np.ones(self.lenm, dtype='float') myplot.plot_vtk(self.m, 20) self.assertTrue(isfile(self.myoutdir+'testm_20.pvd') and \ isfile(self.myoutdir+'testm_20000000.vtu'))
def solvefwd(self, cost=False): """Solve fwd operators for given RHS""" self.nbfwdsolves += 1 if self.ObsOp.noise: self.noise = 0.0 if self.plot: self.plotu = PlotFenics(self.plotoutdir) self.plotu.set_varname('u{0}'.format(self.nbfwdsolves)) if cost: self.misfit = 0.0 for ii, rhs in enumerate(self.RHS): self.solve_A(self.u.vector(), rhs) if self.plot: self.plotu.plot_vtk(self.u, ii) u_obs, noiselevel = self.ObsOp.obs(self.u) self.U.append(u_obs) if self.ObsOp.noise: self.noise += noiselevel if cost: self.misfit += self.ObsOp.costfct(u_obs, self.UD[ii]) self.C.append(assemble(self.c)) if cost: self.misfit /= len(self.U) self.regul = self.Regul.cost(self.m) self.cost = self.misfit + self.regul if self.ObsOp.noise and self.myrank == 0: print 'Total noise in data misfit={:.5e}\n'.\ format(self.noise*.5/len(self.U)) self.ObsOp.noise = False # Safety if self.plot: self.plotu.gather_vtkplots()
def _set_plots(self, plot): self.plot = plot if self.plot: filename, ext = splitext(sys.argv[0]) self.plotoutdir = filename + '/Plots/' self.plotvarm = PlotFenics(self.plotoutdir) self.plotvarm.set_varname('m')
def test06(self): """Gather plots""" myplot = PlotFenics(self.myoutdir) myplot.set_varname('testm') myplot.indices = self.indexlist myplot.gather_vtkplots() self.assertTrue(isfile(self.myoutdir + 'testm.pvd'))
def test09(self): """Indices are reset when varname changed""" myplot = PlotFenics(self.myoutdir) myplot.set_varname('testm') indexlist = [1, 3] for ii in indexlist: self.m.vector()[:] = ii * np.ones(self.lenm, dtype='float') myplot.plot_vtk(self.m, ii) myplot.set_varname('testm2') self.assertTrue((myplot.varname == 'testm2') and \ (myplot.indices == []))
def test06(self): """Gather plots""" myplot = PlotFenics(self.myoutdir) myplot.set_varname('testm') myplot.indices = self.indexlist myplot.gather_vtkplots() self.assertTrue(isfile(self.myoutdir+'testm.pvd'))
def test09(self): """Indices are reset when varname changed""" myplot = PlotFenics(self.myoutdir) myplot.set_varname('testm') indexlist = [1,3] for ii in indexlist: self.m.vector()[:] = ii * np.ones(self.lenm, dtype='float') myplot.plot_vtk(self.m, ii) myplot.set_varname('testm2') self.assertTrue((myplot.varname == 'testm2') and \ (myplot.indices == []))
def __init__(self, mesh, trueImage, parameters=[]): """ Inputs: mesh = Fenics mesh trueImage = object from class Image parameters = dict """ # Mesh self.mesh = mesh self.V = dl.FunctionSpace(self.mesh, "Lagrange", 1) self.xx = self.V.dofmap().tabulate_all_coordinates(self.mesh) self.dimV = self.V.dim() self.test, self.trial = dl.TestFunction(self.V), dl.TrialFunction(self.V) self.f_true = dl.interpolate(trueImage, self.V) self.g, self.dg, self.gtmp = dl.Function(self.V), dl.Function(self.V), dl.Function(self.V) self.Grad = dl.Function(self.V) self.Gradnorm0 = None # mass matrix self.Mweak = dl.inner(self.test, self.trial) * dl.dx self.M = dl.assemble(self.Mweak) self.solverM = dl.LUSolver("petsc") self.solverM.parameters["symmetric"] = True self.solverM.parameters["reuse_factorization"] = True self.solverM.set_operator(self.M) # identity matrix self.I = dl.assemble(self.Mweak) self.I.zero() self.I.set_diagonal(dl.interpolate(dl.Constant(1), self.V).vector()) # self.targetnorm = np.sqrt((self.M*self.f_true.vector()).inner(self.f_true.vector())) self.targetnorm = np.sqrt((self.f_true.vector()).inner(self.f_true.vector())) # line search parameters self.parameters = {"alpha0": 1.0, "rho": 0.5, "c": 5e-5, "max_backtrack": 12} # regularization self.parameters.update({"eps": 1e-4, "k": 1.0, "regularization": "TV", "mode": "primaldual"}) self.parameters.update(parameters) self.define_regularization() self.regparam = 1.0 # plots: filename, ext = os.path.splitext(sys.argv[0]) if os.path.isdir(filename + "/"): shutil.rmtree(filename + "/") self.myplot = PlotFenics(filename)
def solveadj(self, grad=False): """Solve adj operators""" self.nbadjsolves += 1 if self.plot: self.plotp = PlotFenics(self.plotoutdir) self.plotp.set_varname('p{0}'.format(self.nbadjsolves)) self.Nbsrc = len(self.UD) if grad: self.MG.vector()[:] = np.zeros(self.lenm) for ii, C in enumerate(self.C): self.ObsOp.assemble_rhsadj(self.U[ii], self.UD[ii], \ self.rhs, self.bcadj) self.solve_A(self.p.vector(), self.rhs.vector()) if self.plot: self.plotp.plot_vtk(self.p, ii) self.E.append(assemble(self.e)) if grad: self.MG.vector().axpy(1.0/self.Nbsrc, \ C * self.p.vector()) if grad: self.MG.vector().axpy(1.0, self.Regul.grad(self.m)) self.solverM.solve(self.Grad.vector(), self.MG.vector()) self.Gradnorm = np.sqrt(self.Grad.vector().inner(self.MG.vector())) if self.plot: self.plotp.gather_vtkplots()
def test03(self): """Need index list defined to gather""" myplot = PlotFenics(self.myoutdir) myplot.set_varname('testm') self.assertRaises(NoIndicesError, myplot.gather_vtkplots)
def test02_alt(self): """Need varname defined to plot""" myplot = PlotFenics(self.myoutdir) self.m.vector()[:] = np.ones(self.lenm) self.assertRaises(NoVarnameError, myplot.plot_vtk, self.m)
def test02(self): """Instantiate class when dir exists""" myplot = PlotFenics(self.myoutdir) myplot.set_outdir(self.myoutdir + 'Test1/') self.assertTrue(myplot.outdir == self.myoutdir + 'Test1/')
def test01(self): """Instantiate class when dir does not exist""" if isdir(self.myoutdir): os.system('rm -rf {0}'.format(self.myoutdir)) myplot = PlotFenics(self.myoutdir) self.assertTrue(isdir(self.myoutdir))
def test00_defdir(self): """Check default directory""" if isdir(self.defoutdir): os.system('rm -rf {0}'.format(self.defoutdir)) myplot = PlotFenics() self.assertTrue(isdir(self.defoutdir))
def test00_inst(self): """Default instantiation""" myplot = PlotFenics()
def test08(self): """Fenics_vtu_correction variable""" myplot = PlotFenics(self.myoutdir) self.assertTrue(isfile(self.myoutdir+'testm_20'+\ myplot.Fenics_vtu_correction+'.vtu'))
def __init__(self, CGdeg, regularizationtype, h=1.0, parameters=[], image='image.dat'): class Image(dl.Expression): def __init__(self, Lx, Ly, data): self.data = data self.hx = Lx / float(self.data.shape[1] - 1) self.hy = Ly / float(self.data.shape[0] - 1) def eval(self, values, x): j = math.floor(x[0] / self.hx) i = math.floor(x[1] / self.hy) values[0] = self.data[i, j] data = np.loadtxt(image, delimiter=',') #Lx, Ly = float(data.shape[1])/float(data.shape[0]), 1. Lx, Ly = 2., 1. scaling = 100. * h # =1.0 => h~0.01 Lx, Ly = scaling * Lx, scaling * Ly np.random.seed(seed=1) noise_std_dev = 0.3 noise = noise_std_dev * np.random.randn(data.shape[0], data.shape[1]) print '||noise||={}'.format(np.linalg.norm(noise)) mesh = dl.RectangleMesh(dl.Point(0, 0), dl.Point(Lx, Ly), 200, 100) mcoord = mesh.coordinates() print 'hx={}, hy={}'.format((mcoord[-1][0] - mcoord[0][0]) / 200., (mcoord[-1][1] - mcoord[0][1]) / 100.) V = dl.FunctionSpace(mesh, 'Lagrange', CGdeg) trueImage = Image(Lx, Ly, data) noisyImage = Image(Lx, Ly, data + noise) print 'min(data)={}, max(data)={}'.format(np.amin(data), np.amax(data)) print 'min(data+noise)={}, max(data+noise)={}'.format( np.amin(data + noise), np.amax(data + noise)) self.u_true = dl.interpolate(trueImage, V) self.u_0 = dl.interpolate(noisyImage, V) self.u = dl.Function(V) self.ucopy = dl.Function(V) self.G = dl.Function(V) self.du = dl.Function(V) u_test = dl.TestFunction(V) u_trial = dl.TrialFunction(V) Mweak = dl.inner(u_test, u_trial) * dl.dx self.M = dl.assemble(Mweak) self.solverM = dl.LUSolver('petsc') self.solverM.parameters['symmetric'] = True self.solverM.parameters['reuse_factorization'] = True self.solverM.set_operator(self.M) self.regul = regularizationtype if self.regul == 'tikhonov': self.Regul = LaplacianPrior({'Vm': V, 'gamma': 1.0, 'beta': 0.0}) elif self.regul == 'TV': paramTV = {'Vm': V, 'k': 1.0, 'eps': 1e-4, 'GNhessian': True} paramTV.update(parameters) self.Regul = TV(paramTV) self.inexact = False elif self.regul == 'TVPD': paramTV = {'Vm': V, 'k': 1.0, 'eps': 1e-4, 'exact': False} paramTV.update(parameters) self.Regul = TVPD(paramTV) self.inexact = False self.alpha = 1.0 self.Hess = self.M self.parametersLS = {'alpha0':1.0, 'rho':0.5, 'c':5e-5, \ 'max_backtrack':12, 'cgtol':0.5, 'maxiter':50000} filename, ext = os.path.splitext(sys.argv[0]) #if os.path.isdir(filename + '/'): shutil.rmtree(filename + '/') self.myplot = PlotFenics(filename) try: solver = PETScKrylovSolver('cg', 'ml_amg') self.precond = 'ml_amg' except: print '*** WARNING: ML not installed -- using petsc_amg instead' self.precond = 'petsc_amg'
class ObjectiveFunctional(LinearOperator): """ Provides data misfit, gradient and Hessian information for the data misfit part of a time-independent symmetric inverse problem. """ __metaclass__ = abc.ABCMeta # Instantiation def __init__(self, V, Vm, bc, bcadj, \ RHSinput=[], ObsOp=[], UD=[], Regul=[], Data=[], plot=False, \ mycomm=None): # Define test, trial and all other functions self.trial = TrialFunction(V) self.test = TestFunction(V) self.mtrial = TrialFunction(Vm) self.mtest = TestFunction(Vm) self.rhs = Function(V) self.m = Function(Vm) self.mcopy = Function(Vm) self.srchdir = Function(Vm) self.delta_m = Function(Vm) self.MG = Function(Vm) self.Grad = Function(Vm) self.Gradnorm = 0.0 self.lenm = len(self.m.vector().array()) self.u = Function(V) self.ud = Function(V) self.diff = Function(V) self.p = Function(V) # Define weak forms to assemble A, C and E self._wkforma() self._wkformc() self._wkforme() # Store other info: self.ObsOp = ObsOp self.UD = UD self.reset() # Initialize U, C and E to [] self.Data = Data self.GN = 1.0 # GN = 0.0 => GN Hessian; = 1.0 => full Hessian # Operators and bc LinearOperator.__init__(self, self.delta_m.vector(), \ self.delta_m.vector()) self.bc = bc self.bcadj = bcadj self._assemble_solverM(Vm) self.assemble_A() self.assemble_RHS(RHSinput) self.Regul = Regul # Counters, tolerances and others self.nbPDEsolves = 0 # Updated when solve_A called self.nbfwdsolves = 0 # Counter for plots self.nbadjsolves = 0 # Counter for plots self._set_plots(plot) # MPI: self.mycomm = mycomm try: self.myrank = MPI.rank(self.mycomm) except: self.myrank = 0 def copy(self): """Define a copy method""" V = self.trial.function_space() Vm = self.mtrial.function_space() newobj = self.__class__(V, Vm, self.bc, self.bcadj, [], self.ObsOp, \ self.UD, self.Regul, self.Data, False) newobj.RHS = self.RHS newobj.update_m(self.m) return newobj def mult(self, mhat, y): """mult(self, mhat, y): do y = Hessian * mhat member self.GN sets full Hessian (=1.0) or GN Hessian (=0.0)""" N = self.Nbsrc # Number of sources y[:] = np.zeros(self.lenm) for C, E in zip(self.C, self.E): # Solve for uhat C.transpmult(mhat, self.rhs.vector()) self.bcadj.apply(self.rhs.vector()) self.solve_A(self.u.vector(), -self.rhs.vector()) # Solve for phat E.transpmult(mhat, self.rhs.vector()) Etmhat = self.rhs.vector().array() self.rhs.vector().axpy(1.0, self.ObsOp.incradj(self.u)) self.bcadj.apply(self.rhs.vector()) self.solve_A(self.p.vector(), -self.rhs.vector()) # Compute Hessian*x: y.axpy(1.0/N, C * self.p.vector()) y.axpy(self.GN/N, E * self.u.vector()) y.axpy(1.0, self.Regul.hessian(mhat)) # Getters def getm(self): return self.m def getmarray(self): return self.m.vector().array() def getmcopyarray(self): return self.mcopy.vector().array() def getVm(self): return self.mtrial.function_space() def getMGarray(self): return self.MG.vector().array() def getMGvec(self): return self.MG.vector() def getGradarray(self): return self.Grad.vector().array() def getGradnorm(self): return self.Gradnorm def getsrchdirarray(self): return self.srchdir.vector().array() def getsrchdirvec(self): return self.srchdir.vector() def getsrchdirnorm(self): return np.sqrt((self.MM*self.getsrchdirvec()).inner(self.getsrchdirvec())) def getgradxdir(self): return self.gradxdir def getcost(self): return self.cost, self.misfit, self.regul def getprecond(self): Prec = PETScKrylovSolver("richardson", "amg") Prec.parameters["maximum_iterations"] = 1 Prec.parameters["error_on_nonconvergence"] = False Prec.parameters["nonzero_initial_guess"] = False Prec.set_operator(self.Regul.get_precond()) return Prec def getMass(self): return self.MM # Setters def setsrchdir(self, arr): self.srchdir.vector()[:] = arr def setgradxdir(self, valueloc): """Sum all local results for Grad . Srch_dir""" try: valueglob = MPI.sum(self.mycomm, valueloc) except: valueglob = valueloc self.gradxdir = valueglob # Solve def solvefwd(self, cost=False): """Solve fwd operators for given RHS""" self.nbfwdsolves += 1 if self.ObsOp.noise: self.noise = 0.0 if self.plot: self.plotu = PlotFenics(self.plotoutdir) self.plotu.set_varname('u{0}'.format(self.nbfwdsolves)) if cost: self.misfit = 0.0 for ii, rhs in enumerate(self.RHS): self.solve_A(self.u.vector(), rhs) if self.plot: self.plotu.plot_vtk(self.u, ii) u_obs, noiselevel = self.ObsOp.obs(self.u) self.U.append(u_obs) if self.ObsOp.noise: self.noise += noiselevel if cost: self.misfit += self.ObsOp.costfct(u_obs, self.UD[ii]) self.C.append(assemble(self.c)) if cost: self.misfit /= len(self.U) self.regul = self.Regul.cost(self.m) self.cost = self.misfit + self.regul if self.ObsOp.noise and self.myrank == 0: print 'Total noise in data misfit={:.5e}\n'.\ format(self.noise*.5/len(self.U)) self.ObsOp.noise = False # Safety if self.plot: self.plotu.gather_vtkplots() def solvefwd_cost(self): """Solve fwd operators for given RHS and compute cost fct""" self.solvefwd(True) def solveadj(self, grad=False): """Solve adj operators""" self.nbadjsolves += 1 if self.plot: self.plotp = PlotFenics(self.plotoutdir) self.plotp.set_varname('p{0}'.format(self.nbadjsolves)) self.Nbsrc = len(self.UD) if grad: self.MG.vector()[:] = np.zeros(self.lenm) for ii, C in enumerate(self.C): self.ObsOp.assemble_rhsadj(self.U[ii], self.UD[ii], \ self.rhs, self.bcadj) self.solve_A(self.p.vector(), self.rhs.vector()) if self.plot: self.plotp.plot_vtk(self.p, ii) self.E.append(assemble(self.e)) if grad: self.MG.vector().axpy(1.0/self.Nbsrc, \ C * self.p.vector()) if grad: self.MG.vector().axpy(1.0, self.Regul.grad(self.m)) self.solverM.solve(self.Grad.vector(), self.MG.vector()) self.Gradnorm = np.sqrt(self.Grad.vector().inner(self.MG.vector())) if self.plot: self.plotp.gather_vtkplots() def solveadj_constructgrad(self): """Solve adj operators and assemble gradient""" self.solveadj(True) # Assembler def assemble_A(self): """Assemble operator A(m)""" self.A = assemble(self.a) self.bc.apply(self.A) self.set_solver() def solve_A(self, b, f): """Solve system of the form A.b = f, with b and f in form to be used in solver.""" self.solver.solve(b, f) self.nbPDEsolves += 1 def assemble_RHS(self, RHSin): """Assemble RHS for fwd solve""" if RHSin == []: self.RHS = None else: self.RHS = [] for rhs in RHSin: if isinstance(rhs, Expression): L = rhs*self.test*dx b = assemble(L) self.bc.apply(b) self.RHS.append(b) else: raise WrongInstanceError("rhs should be Expression") def _assemble_solverM(self, Vm): self.MM = assemble(inner(self.mtrial, self.mtest)*dx) self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(self.MM) def _set_plots(self, plot): self.plot = plot if self.plot: filename, ext = splitext(sys.argv[0]) self.plotoutdir = filename + '/Plots/' self.plotvarm = PlotFenics(self.plotoutdir) self.plotvarm.set_varname('m') def plotm(self, index): if self.plot: self.plotvarm.plot_vtk(self.m, index) def gatherm(self): if self.plot: self.plotvarm.gather_vtkplots() # Update param def update_Data(self, Data): """Update Data member""" self.Data = Data self.assemble_A() self.reset() def update_m(self, m): """Update values of parameter m""" if isinstance(m, np.ndarray): self.m.vector()[:] = m elif isinstance(m, Function): self.m.assign(m) elif isinstance(m, float): self.m.vector()[:] = m elif isinstance(m, int): self.m.vector()[:] = float(m) else: raise WrongInstanceError('Format for m not accepted') self.assemble_A() self.reset() def backup_m(self): self.mcopy.assign(self.m) def restore_m(self): self.update_m(self.mcopy) def reset(self): """Reset U, C and E""" self.U = [] self.C = [] self.E = [] def set_solver(self): """Reset solver for fwd operator""" self.solver = LUSolver() self.solver.parameters['reuse_factorization'] = True self.solver.set_operator(self.A) def addPDEcount(self, increment=1): """Increase 'nbPDEsolves' by 'increment'""" self.nbPDEsolves += increment def resetPDEsolves(self): self.nbPDEsolves = 0 # Additional methods for compatibility with CG solver: def init_vector(self, x, dim): """Initialize vector x to be compatible with parameter Does not work in dolfin 1.3.0""" self.MM.init_vector(x, 0) def init_vector130(self): """Initialize vector x to be compatible with parameter""" return Vector(Function(self.mcopy.function_space()).vector()) # Abstract methods @abc.abstractmethod def _wkforma(self): self.a = [] @abc.abstractmethod def _wkformc(self): self.c = [] @abc.abstractmethod def _wkforme(self): self.e = []
class ObjectiveImageDenoising(): def __init__(self, CGdeg, regularizationtype, h=1.0, parameters=[], image='image.dat'): class Image(dl.Expression): def __init__(self, Lx, Ly, data): self.data = data self.hx = Lx / float(self.data.shape[1] - 1) self.hy = Ly / float(self.data.shape[0] - 1) def eval(self, values, x): j = math.floor(x[0] / self.hx) i = math.floor(x[1] / self.hy) values[0] = self.data[i, j] data = np.loadtxt(image, delimiter=',') #Lx, Ly = float(data.shape[1])/float(data.shape[0]), 1. Lx, Ly = 2., 1. scaling = 100. * h # =1.0 => h~0.01 Lx, Ly = scaling * Lx, scaling * Ly np.random.seed(seed=1) noise_std_dev = 0.3 noise = noise_std_dev * np.random.randn(data.shape[0], data.shape[1]) print '||noise||={}'.format(np.linalg.norm(noise)) mesh = dl.RectangleMesh(dl.Point(0, 0), dl.Point(Lx, Ly), 200, 100) mcoord = mesh.coordinates() print 'hx={}, hy={}'.format((mcoord[-1][0] - mcoord[0][0]) / 200., (mcoord[-1][1] - mcoord[0][1]) / 100.) V = dl.FunctionSpace(mesh, 'Lagrange', CGdeg) trueImage = Image(Lx, Ly, data) noisyImage = Image(Lx, Ly, data + noise) print 'min(data)={}, max(data)={}'.format(np.amin(data), np.amax(data)) print 'min(data+noise)={}, max(data+noise)={}'.format( np.amin(data + noise), np.amax(data + noise)) self.u_true = dl.interpolate(trueImage, V) self.u_0 = dl.interpolate(noisyImage, V) self.u = dl.Function(V) self.ucopy = dl.Function(V) self.G = dl.Function(V) self.du = dl.Function(V) u_test = dl.TestFunction(V) u_trial = dl.TrialFunction(V) Mweak = dl.inner(u_test, u_trial) * dl.dx self.M = dl.assemble(Mweak) self.solverM = dl.LUSolver('petsc') self.solverM.parameters['symmetric'] = True self.solverM.parameters['reuse_factorization'] = True self.solverM.set_operator(self.M) self.regul = regularizationtype if self.regul == 'tikhonov': self.Regul = LaplacianPrior({'Vm': V, 'gamma': 1.0, 'beta': 0.0}) elif self.regul == 'TV': paramTV = {'Vm': V, 'k': 1.0, 'eps': 1e-4, 'GNhessian': True} paramTV.update(parameters) self.Regul = TV(paramTV) self.inexact = False elif self.regul == 'TVPD': paramTV = {'Vm': V, 'k': 1.0, 'eps': 1e-4, 'exact': False} paramTV.update(parameters) self.Regul = TVPD(paramTV) self.inexact = False self.alpha = 1.0 self.Hess = self.M self.parametersLS = {'alpha0':1.0, 'rho':0.5, 'c':5e-5, \ 'max_backtrack':12, 'cgtol':0.5, 'maxiter':50000} filename, ext = os.path.splitext(sys.argv[0]) #if os.path.isdir(filename + '/'): shutil.rmtree(filename + '/') self.myplot = PlotFenics(filename) try: solver = PETScKrylovSolver('cg', 'ml_amg') self.precond = 'ml_amg' except: print '*** WARNING: ML not installed -- using petsc_amg instead' self.precond = 'petsc_amg' def solve(self): if self.regul == 'tikhonov': self.solvetikhonov() elif self.regul == 'TV': self.solveTV() elif self.regul == 'TVPD': self.solveTV() def costmisfitreg(self): """ Compute objective function (cost = misfit + alpha*reg) """ misfit = 0.5*(self.Hess*(self.u.vector() - self.u_0.vector())).inner(\ self.u.vector() - self.u_0.vector()) reg = self.Regul.cost(self.u) cost = misfit + self.alpha * reg return cost, misfit, reg def gradient(self): """ Compute gradient at current value of self.u """ MG = self.Hess * (self.u.vector() - self.u_0.vector()) MG.axpy(self.alpha, self.Regul.grad(self.u)) self.solverM.solve(self.G.vector(), MG) return MG, np.sqrt(MG.inner(self.G.vector())) def mediummisfit(self): """ Compute medium misfit """ return np.sqrt((self.Hess*(self.u.vector() - self.u_true.vector())).inner(\ self.u.vector() - self.u_true.vector())) def solvetikhonov(self): print '\t{:12s} {:12s} {:12s} {:12s} {:12s} {:12s}'.format(\ 'iter', 'cost', 'misfit', 'reg', 'medmisfit', 'n_cg') self.u.vector().zero() MG, MGnorm = self.gradient() H = self.Hess + self.Regul.R * self.alpha solver = dl.PETScKrylovSolver("cg", "petsc_amg") solver.set_operator(H) cgiter = solver.solve(self.u.vector(), -MG) cost, misfit, reg = self.costmisfitreg() print '{:12s} {:12.4e} {:12.4e} {:12.4e} {:12.4e} {:12d}'.format(\ '', cost, misfit, reg, self.mediummisfit(), cgiter) def searchdirection(self, MG, cgtol): """ Compute search direction """ self.Regul.assemble_hessian(self.u) if self.Regul.isPD(): H = self.Hess + self.Regul.Hrs * self.alpha #H = self.Hess + self.Regul.H*self.alpha else: H = self.Hess + self.Regul.H * self.alpha if True: solver = dl.PETScKrylovSolver("cg", self.precond) solver.parameters['nonzero_initial_guess'] = False solver.parameters['relative_tolerance'] = cgtol solver.set_operator(H) cgiter = solver.solve(self.du.vector(), -1.0 * MG) else: solver = CGSolverSteihaug() solver.set_operator(H) solver.set_preconditioner(self.Regul.getprecond()) solver.parameters["rel_tolerance"] = cgtol solver.parameters["zero_initial_guess"] = True solver.parameters["print_level"] = -1 solver.solve(self.du.vector(), -1.0 * MG) cgiter = solver.iter MGdu = MG.inner(self.du.vector()) if MGdu > 0.0: print "*** WARNING: NOT a descent direction" return cgiter, MGdu def linesearch(self, MG): """ Perform inexact backtracking line search """ alphaLS = self.parametersLS['alpha0'] rhoLS = self.parametersLS['rho'] cLS = self.parametersLS['c'] cost, misfit, reg = self.costmisfitreg() costref = cost setfct(self.ucopy, self.u) MGdu = MG.inner(self.du.vector()) * cLS success = False for ii in xrange(self.parametersLS['max_backtrack']): setfct(self.u, self.ucopy) self.u.vector().axpy(alphaLS, self.du.vector()) cost, misfit, reg = self.costmisfitreg() if cost < costref + alphaLS * MGdu: success = True break else: alphaLS *= rhoLS if self.Regul.isPD(): self.Regul.update_w(self.du.vector(), alphaLS) return success, alphaLS, cost, misfit, reg def solveTV(self): """ Solve image denoising pb """ self.u.vector().zero() normu_true = self.mediummisfit() # initial state = noisy image setfct(self.u, self.u_0) print 'min(u)={}, max(u)={}'.format(\ np.amin(self.u.vector().array()), np.amax(self.u.vector().array())) cost, misfit, reg = self.costmisfitreg() costold = cost MG, normMG = self.gradient() normMG0 = normMG print '\t{:12s} {:12s} {:12s} {:12s} {:12s} {:12s} {:12s}\t{:12s} {:12s}'.format(\ 'iter', 'cost', 'misfit', 'reg', '||G||', 'a_LS', 'medmisfit', 'tol_cg', 'n_cg') print '{:12d} {:12.4e} {:12.4e} {:12.4e} {:12.4e} {:12s} {:12.2e} ({:7.2f} %)'.format(\ 0, cost, misfit, reg, normMG, '', self.mediummisfit(), 100.*self.mediummisfit()/normu_true) cgtol = self.parametersLS['cgtol'] maxiter = self.parametersLS['maxiter'] for ii in xrange(maxiter): if self.inexact: cgtol = min(cgtol, np.sqrt(normMG / normMG0)) #cgtol = min(0.5, np.sqrt(normMG/normMG0))) else: cgtol = 1e-12 cgiter, MGdu = self.searchdirection(MG, cgtol) success, alphaLS, cost, misfit, reg = self.linesearch(MG) MG, normMG = self.gradient() print '{:12d} {:12.4e} {:12.4e} {:12.4e} {:12.4e} {:12.4e} {:12.2e} ({:7.2f} %) {:12.2e} {:12d}'.format(\ ii+1, cost, misfit, reg, normMG, alphaLS, \ self.mediummisfit(), 100.*self.mediummisfit()/normu_true, cgtol, cgiter) if normMG < min(1e-12, 1e-10 * normMG0): print 'gradient sufficiently reduced -- optimization converged' break if not success: print 'Line search failed -- optimization aborted' break if np.abs(cost - costold) / costold < 1e-14: if cgtol < 1e-10: print 'cost functional stagnates -- optimization aborted' break else: cgtol *= 1e-3 costold = cost print 'min(u)={}, max(u)={}'.format(\ np.amin(self.u.vector().array()), np.amax(self.u.vector().array())) def plot(self, index=0, add=''): """ Plot target (w/ noise 0, or w/o noise 1) or current iterate (2) """ if index == 0: self.myplot.set_varname('data' + add) self.myplot.plot_vtk(self.u_0) elif index == 1: self.myplot.set_varname('target' + add) self.myplot.plot_vtk(self.u_true) elif index == 2: self.myplot.set_varname('solution' + add) self.myplot.plot_vtk(self.u)
class ObjectiveImageDenoising: """ Class to do image denoising """ def __init__(self, mesh, trueImage, parameters=[]): """ Inputs: mesh = Fenics mesh trueImage = object from class Image parameters = dict """ # Mesh self.mesh = mesh self.V = dl.FunctionSpace(self.mesh, "Lagrange", 1) self.xx = self.V.dofmap().tabulate_all_coordinates(self.mesh) self.dimV = self.V.dim() self.test, self.trial = dl.TestFunction(self.V), dl.TrialFunction(self.V) self.f_true = dl.interpolate(trueImage, self.V) self.g, self.dg, self.gtmp = dl.Function(self.V), dl.Function(self.V), dl.Function(self.V) self.Grad = dl.Function(self.V) self.Gradnorm0 = None # mass matrix self.Mweak = dl.inner(self.test, self.trial) * dl.dx self.M = dl.assemble(self.Mweak) self.solverM = dl.LUSolver("petsc") self.solverM.parameters["symmetric"] = True self.solverM.parameters["reuse_factorization"] = True self.solverM.set_operator(self.M) # identity matrix self.I = dl.assemble(self.Mweak) self.I.zero() self.I.set_diagonal(dl.interpolate(dl.Constant(1), self.V).vector()) # self.targetnorm = np.sqrt((self.M*self.f_true.vector()).inner(self.f_true.vector())) self.targetnorm = np.sqrt((self.f_true.vector()).inner(self.f_true.vector())) # line search parameters self.parameters = {"alpha0": 1.0, "rho": 0.5, "c": 5e-5, "max_backtrack": 12} # regularization self.parameters.update({"eps": 1e-4, "k": 1.0, "regularization": "TV", "mode": "primaldual"}) self.parameters.update(parameters) self.define_regularization() self.regparam = 1.0 # plots: filename, ext = os.path.splitext(sys.argv[0]) if os.path.isdir(filename + "/"): shutil.rmtree(filename + "/") self.myplot = PlotFenics(filename) def generatedata(self, noisepercent): """ compute data and add noisepercent (%) of noise """ sigma = noisepercent * np.linalg.norm(self.f_true.vector().array()) / np.sqrt(self.dimV) print "sigma_noise = ", sigma np.random.seed(11) # TODO: tmp eta = sigma * np.random.randn(self.dimV) self.dn = dl.Function(self.V) setfct(self.dn, eta) self.dn.vector().axpy(1.0, self.f_true.vector()) print "min(true)={}, max(true)={}".format( np.amin(self.f_true.vector().array()), np.amax(self.f_true.vector().array()) ) print "min(noisy)={}, max(noisy)={}".format( np.amin(self.dn.vector().array()), np.amax(self.dn.vector().array()) ) def define_regularization(self, parameters=None): if not parameters == None: self.parameters.update(parameters) regularization = self.parameters["regularization"] if regularization == "tikhonov": gamma = self.parameters["gamma"] beta = self.parameters["beta"] self.Reg = LaplacianPrior({"gamma": gamma, "beta": beta, "Vm": self.V}) self.inexact = False elif regularization == "TV": eps = self.parameters["eps"] k = self.parameters["k"] mode = self.parameters["mode"] if mode == "primaldual": self.Reg = self.Reg = TVPD({"eps": eps, "k": k, "Vm": self.V, "GNhessian": False}) elif mode == "full": self.Reg = TV({"eps": eps, "k": k, "Vm": self.V, "GNhessian": False}) else: self.Reg = TV({"eps": eps, "k": k, "Vm": self.V, "GNhessian": True}) self.inexact = False ### COST and DERIVATIVES def computecost(self, f=None): """ Compute cost functional at f """ if f == None: f = self.g df = f.vector() - self.dn.vector() # self.misfit = 0.5 * (self.M*df).inner(df) self.misfit = 0.5 * df.inner(df) self.reg = self.Reg.cost(f) self.cost = self.misfit + self.regparam * self.reg return self.cost def gradient(self, f=None): """ Compute M.g (discrete gradient) at a given point f """ if f == None: f = self.g df = f.vector() - self.dn.vector() # self.MGk = self.M*df self.MGk = df self.MGr = self.Reg.grad(f) self.MG = self.MGk + self.MGr * self.regparam self.solverM.solve(self.Grad.vector(), self.MG) self.Gradnorm = np.sqrt((self.MG).inner(self.Grad.vector())) if self.Gradnorm0 == None: self.Gradnorm0 = self.Gradnorm def Hessian(self, f=None): """ Assemble Hessian at f """ if f == None: f = self.g regularization = self.parameters["regularization"] if regularization == "TV": self.Reg.assemble_hessian(f) self.Hess = self.I + self.Reg.H * self.regparam # self.Hess = self.M + self.Reg.H*self.regparam elif regularization == "tikhonov": self.Hess = self.M + self.Reg.Minvprior * self.regparam ### SOLVER def searchdirection(self): """ Compute search direction """ self.gradient() self.Hessian() solver = dl.PETScKrylovSolver("cg", "petsc_amg") solver.parameters["nonzero_initial_guess"] = False # Inexact CG: if self.inexact: self.cgtol = min(0.5, np.sqrt(self.Gradnorm / self.Gradnorm0)) else: self.cgtol = 1e-8 solver.parameters["relative_tolerance"] = self.cgtol solver.set_operator(self.Hess) self.cgiter = solver.solve(self.dg.vector(), -1.0 * self.MG) if (self.MG).inner(self.dg.vector()) > 0.0: print "*** WARNING: NOT a descent direction" def linesearch(self): """ Perform inexact backtracking line search """ regularization = self.parameters["regularization"] # compute new direction for dual variables if regularization == "TV" and self.Reg.isPD(): self.Reg.compute_dw(self.dg) # line search for primal variable self.alpha = self.parameters["alpha0"] rho = self.parameters["rho"] c = self.parameters["c"] self.computecost() costref = self.cost cdJdf = ((self.MG).inner(self.dg.vector())) * c self.LS = False for ii in xrange(self.parameters["max_backtrack"]): setfct(self.gtmp, self.g.vector() + self.dg.vector() * self.alpha) if self.computecost(self.gtmp) < costref + self.alpha * cdJdf: self.g.vector().axpy(self.alpha, self.dg.vector()) self.LS = True break else: self.alpha *= rho # update dual variable if regularization == "TV" and self.Reg.isPD(): self.Reg.update_w(self.alpha) def solve(self, plot=False): """ Solve image denoising pb """ regularization = self.parameters["regularization"] print "\t{:12s} {:12s} {:12s} {:12s} {:12s} {:12s} {:12s}\t{:12s} {:12s}".format( "a_reg", "cost", "misfit", "reg", "||G||", "a_LS", "medmisfit", "tol_cg", "n_cg" ) # if regularization == "tikhonov": # pb is linear with tikhonov regularization self.searchdirection() self.g.vector().axpy(1.0, self.dg.vector()) self.computecost() self.alpha = 1.0 self.printout() else: self.computecost() cost = self.cost # initial printout df = self.f_true.vector() - self.g.vector() self.medmisfit = np.sqrt(df.inner(df)) # self.medmisfit = np.sqrt((self.M*df).inner(df)) self.relmedmisfit = self.medmisfit / self.targetnorm print ("{:12.1e} {:12.4e} {:12.4e} {:12.4e} {:12s} {:12s} {:12.2e}" + " ({:.3f})").format( self.regparam, self.cost, self.misfit, self.reg, "", "", self.medmisfit ** 2, self.relmedmisfit ) # iterate for ii in xrange(1000): self.searchdirection() self.linesearch() print ii + 1, self.printout() # Check termination conditions: if not self.LS: print "Line search failed" break if self.Gradnorm < min(1e-12, 1e-10 * self.Gradnorm0): print "gradient sufficiently reduced -- optimization converged" break elif np.abs(cost - self.cost) / cost < 1e-12: print "cost functional stagnates -- optimization converged" break cost = self.cost ### OUTPUT def printout(self): """ Print results """ df = self.f_true.vector() - self.g.vector() self.medmisfit = np.sqrt((self.M * df).inner(df)) self.relmedmisfit = self.medmisfit / self.targetnorm print ("{:12.1e} {:12.4e} {:12.4e} {:12.4e} {:12.4e} {:12.2e} {:12.2e}" + " ({:.3f}) {:12.2e} {:6d}").format( self.regparam, self.cost, self.misfit, self.reg, self.Gradnorm, self.alpha, self.medmisfit ** 2, self.relmedmisfit, self.cgtol, self.cgiter, ) def plot(self, index=0, add=""): """ Plot target (w/ noise 0, or w/o noise 1) or current iterate (2) """ if index == 0: self.myplot.set_varname("target" + add) self.myplot.plot_vtk(self.f_true) elif index == 1: self.myplot.set_varname("data" + add) self.myplot.plot_vtk(self.dn) elif index == 2: self.myplot.set_varname("solution" + add) self.myplot.plot_vtk(self.g) ### TESTS def test_gradient(self, f=None, n=5): """ test gradient with FD approx around point f """ if f == None: f = self.f_true pm = [1.0, -1.0] eps = 1e-5 self.gradient(f) for nn in xrange(1, n + 1): expr = dl.Expression("sin(n*pi*x[0]/200)*sin(n*pi*x[1]/100)", n=nn) df = dl.interpolate(expr, self.V) MGdf = self.MG.inner(df.vector()) cost = [] for sign in pm: setfct(self.g, f) self.g.vector().axpy(sign * eps, df.vector()) cost.append(self.computecost(self.g)) MGFD = (cost[0] - cost[1]) / (2 * eps) print "n={}:\tMGFD={:.5e}, MGdf={:.5e}, error={:.2e}".format( nn, MGFD, MGdf, np.abs(MGdf - MGFD) / np.abs(MGdf) ) def test_hessian(self, f=None, n=5): """ test Hessian with FD approx around point f """ if f == None: f = self.f_true pm = [1.0, -1.0] eps = 1e-5 self.Hessian(f) for nn in xrange(1, n + 1): expr = dl.Expression("sin(n*pi*x[0]/200)*sin(n*pi*x[1]/100)", n=nn) df = dl.interpolate(expr, self.V) Hdf = (self.Hess * df.vector()).array() MG = [] for sign in pm: setfct(self.g, f) self.g.vector().axpy(sign * eps, df.vector()) self.gradient(self.g) MG.append(self.MG.array()) HFD = (MG[0] - MG[1]) / (2 * eps) print "n={}:\tHFD={:.5e}, Hdf={:.5e}, error={:.2e}".format( nn, np.linalg.norm(HFD), np.linalg.norm(Hdf), np.linalg.norm(Hdf - HFD) / np.linalg.norm(Hdf) )