diff --git a/SimPEG/Solver.py b/SimPEG/Solver.py deleted file mode 100644 index 2087ef5f..00000000 --- a/SimPEG/Solver.py +++ /dev/null @@ -1,75 +0,0 @@ -import numpy as np -import scipy.sparse.linalg as linalg - - -class Solver(object): - """docstring for Solver""" - def __init__(self, A, doDirect=True, flag=None, options={}): - - assert type(doDirect) is bool, 'doDirect must be a boolean' - assert flag in [None, 'L', 'U', 'D'], "flag must be set to None, 'L', 'U', or 'D'" - - self.A = A - - self.dsolve = None - self.doDirect = doDirect - self.flag = flag - self.options = options - - def solve(self, b): - if self.flag is None and self.doDirect: - return self.solveDirect(b, **self.options) - elif self.flag is None and not self.doDirect: - return self.solveIter(b, **self.options) - elif self.flag == 'U': - return self.solveBackward(b) - elif self.flag == 'L': - return self.solveForward(b) - elif self.flag == 'D': - return self.solveDiagonal(b) - else: - raise Exception('Unknown flag.') - pass - - def clean(self): - """Cleans up the memory""" - del self.dsolve - self.dsolve = None - - def solveDirect(self, b, backend='scipy'): - assert np.shape(self.A)[1] == np.shape(b)[0], 'Dimension mismatch' - - if self.dsolve is None: - self.A = self.A.tocsc() # for efficiency - self.dsolve = linalg.factorized(self.A) - - if len(b.shape) == 1 or b.shape[1] == 1: - # Just one RHS - return self.dsolve(b) - - # Multiple RHSs - X = np.empty_like(b) - for i in range(b.shape[1]): - X[:,i] = self.dsolve(b[:,i]) - - return X - - def solveIter(self, b, M=None, iterSolver='CG'): - pass - - def solveBackward(self, b): - pass - - def solveForward(self, b): - pass - - def solveDiagonal(self, b): - diagA = self.A.diagonal() - if len(b.shape) == 1 or b.shape[1] == 1: - # Just one RHS - return b/diagA - # Multiple RHSs - X = np.empty_like(b) - for i in range(b.shape[1]): - X[:,i] = b[:,i]/diagA - return X diff --git a/SimPEG/__init__.py b/SimPEG/__init__.py index 8107e847..f50e1cef 100644 --- a/SimPEG/__init__.py +++ b/SimPEG/__init__.py @@ -1,5 +1,11 @@ -import mesh import utils +from utils import Solver +import mesh import inverse +<<<<<<< HEAD from Solver import Solver import visulize +======= +import forward +import regularization +>>>>>>> refs/remotes/origin/master diff --git a/SimPEG/forward/DCProblem.py b/SimPEG/forward/DCProblem.py new file mode 100644 index 00000000..132966a8 --- /dev/null +++ b/SimPEG/forward/DCProblem.py @@ -0,0 +1,249 @@ +from SimPEG.mesh import TensorMesh +from SimPEG.forward import Problem, SyntheticProblem, ModelTransforms +from SimPEG.tests import checkDerivative +from SimPEG.utils import ModelBuilder, sdiag, mkvc +from SimPEG import Solver +import numpy as np +import scipy.sparse as sp +import scipy.sparse.linalg as linalg + + +class DCProblem(ModelTransforms.LogModel, Problem): + """ + **DCProblem** + + Geophysical DC resistivity problem. + + """ + def __init__(self, mesh): + super(DCProblem, self).__init__(mesh) + self.mesh.setCellGradBC('neumann') + + def reshapeFields(self, u): + if len(u.shape) == 1: + u = u.reshape([-1, self.RHS.shape[1]], order='F') + return u + + def createMatrix(self, m): + """ + Makes the matrix A(m) for the DC resistivity problem. + + :param numpy.array m: model + :rtype: scipy.csc_matrix + :return: A(m) + + .. math:: + c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 + + Where M() is the mass matrix and mT is the model transform. + """ + D = self.mesh.faceDiv + G = self.mesh.cellGrad + sigma = self.modelTransform(m) + Msig = self.mesh.getFaceMass(sigma) + A = D*Msig*G + return A.tocsc() + + def dpred(self, m, u=None): + """ + Predicted data. + + .. math:: + d_\\text{pred} = Pu(m) + """ + if u is None: + u = self.field(m) + + u = self.reshapeFields(u) + + return mkvc(self.P*u) + + def field(self, m): + A = self.createMatrix(m) + solve = Solver(A) + phi = solve.solve(self.RHS) + return mkvc(phi) + + def J(self, m, v, u=None): + """ + :param numpy.array m: model + :param numpy.array v: vector to multiply + :param numpy.array u: fields + :rtype: numpy.array + :return: Jv + + .. math:: + c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 + + \\nabla_u (A(m)u - q) = A(m) + + \\nabla_m (A(m)u - q) = G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) + + Where M() is the mass matrix and mT is the model transform. + + .. math:: + J = - P \left( \\nabla_u c(m, u) \\right)^{-1} \\nabla_m c(m, u) + + J(v) = - P ( A(m)^{-1} ( G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) v ) ) + """ + if u is None: + u = self.field(m) + + u = self.reshapeFields(u) + + P = self.P + D = self.mesh.faceDiv + G = self.mesh.cellGrad + A = self.createMatrix(m) + Av_dm = self.mesh.getFaceMassDeriv() + mT_dm = self.modelTransformDeriv(m) + + dCdu = A + + dCdm = np.empty_like(u) + for i, ui in enumerate(u.T): # loop over each column + dCdm[:, i] = D * ( sdiag( G * ui ) * ( Av_dm * ( mT_dm * v ) ) ) + + solve = Solver(dCdu) + Jv = - P * solve.solve(dCdm) + return mkvc(Jv) + + def Jt(self, m, v, u=None): + """Takes data, turns it into a model..ish""" + + if u is None: + u = self.field(m) + + u = self.reshapeFields(u) + v = self.reshapeFields(v) + + P = self.P + D = self.mesh.faceDiv + G = self.mesh.cellGrad + A = self.createMatrix(m) + Av_dm = self.mesh.getFaceMassDeriv() + mT_dm = self.modelTransformDeriv(m) + + dCdu = A.T + solve = Solver(dCdu) + + w = solve.solve(P.T*v) + + Jtv = 0 + for i, ui in enumerate(u.T): # loop over each column + Jtv += sdiag( G * ui ) * ( D.T * w[:,i] ) + + Jtv = - mT_dm.T * ( Av_dm.T * Jtv ) + return Jtv + + + +def genTxRxmat(nelec, spacelec, surfloc, elecini, mesh): + """ Generate projection matrix (Q) and """ + elecend = 0.5+spacelec*(nelec-1) + elecLocR = np.linspace(elecini, elecend, nelec) + elecLocT = elecLocR+1 + nrx = nelec-1 + ntx = nelec-1 + q = np.zeros((mesh.nC, ntx)) + Q = np.zeros((mesh.nC, nrx)) + + for i in range(nrx): + + rxind1 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocR[i])) + rxind2 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocR[i+1])) + + txind1 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocT[i])) + txind2 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocT[i+1])) + + q[txind1,i] = 1 + q[txind2,i] = -1 + Q[rxind1,i] = 1 + Q[rxind2,i] = -1 + + Q = sp.csr_matrix(Q) + rxmidLoc = (elecLocR[0:nelec-1]+elecLocR[1:nelec])*0.5 + return q, Q, rxmidLoc + + + +if __name__ == '__main__': + + from SimPEG.regularization import Regularization + from SimPEG import inverse + import matplotlib.pyplot as plt + + # Create the mesh + h1 = np.ones(20) + h2 = np.ones(100) + mesh = TensorMesh([h1,h2]) + + # Create some parameters for the model + sig1 = np.log(1) + sig2 = np.log(0.01) + + # Create a synthetic model from a block in a half-space + p0 = [5, 10] + p1 = [15, 50] + condVals = [sig1, sig2] + mSynth = ModelBuilder.defineBlockConductivity(p0,p1,mesh.gridCC,condVals) + plt.colorbar(mesh.plotImage(mSynth)) + plt.show() + + # Set up the projection + nelec = 50 + spacelec = 2 + surfloc = 0.5 + elecini = 0.5 + elecend = 0.5+spacelec*(nelec-1) + elecLocR = np.linspace(elecini, elecend, nelec) + rxmidLoc = (elecLocR[0:nelec-1]+elecLocR[1:nelec])*0.5 + q, Q, rxmidloc = genTxRxmat(nelec, spacelec, surfloc, elecini, mesh) + P = Q.T + + # Create some data + class syntheticDCProblem(DCProblem, SyntheticProblem): + pass + + synthetic = syntheticDCProblem(mesh); + synthetic.P = P + synthetic.RHS = q + dobs, Wd = synthetic.createData(mSynth, std=0.05) + + u = synthetic.field(mSynth) + u = synthetic.reshapeFields(u) + mesh.plotImage(u[:,10]) + # plt.show() + + # Now set up the problem to do some minimization + problem = DCProblem(mesh) + problem.P = P + problem.RHS = q + problem.dobs = dobs + problem.std = dobs*0 + 0.05 + m0 = mesh.gridCC[:,0]*0+sig2 + + opt = inverse.InexactGaussNewton(maxIterLS=20, maxIter=10, tolF=1e-6, tolX=1e-6, tolG=1e-6, maxIterCG=6) + reg = Regularization(mesh) + inv = inverse.Inversion(problem, reg, opt, beta0=1e4) + + # Check Derivative + derChk = lambda m: [inv.dataObj(m), inv.dataObjDeriv(m)] + checkDerivative(derChk, mSynth) + + + + print inv.dataObj(m0) + print inv.dataObj(mSynth) + + m = inv.run(m0) + + plt.colorbar(mesh.plotImage(m)) + print m + plt.show() + + + + + + diff --git a/SimPEG/forward/DCProblem/DCProblem.py b/SimPEG/forward/DCProblem/DCProblem.py deleted file mode 100644 index 1df8897b..00000000 --- a/SimPEG/forward/DCProblem/DCProblem.py +++ /dev/null @@ -1,168 +0,0 @@ -from SimPEG.mesh import TensorMesh -from SimPEG.forward import Problem, SyntheticProblem -from SimPEG.tests import checkDerivative -from SimPEG.utils import ModelBuilder, sdiag -import numpy as np -import scipy.sparse.linalg as linalg -import DCutils - -class DCProblem(Problem): - """ - **DCProblem** - - Geophysical DC resistivity problem. - - """ - def __init__(self, mesh): - super(DCProblem, self).__init__(mesh) - self.mesh.setCellGradBC('neumann') - - def createMatrix(self, m): - """ - Makes the matrix A(m) for the DC resistivity problem. - - :param numpy.array m: model - :rtype: scipy.csc_matrix - :return: A(m) - - .. math:: - c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 - - Where M() is the mass matrix and mT is the model transform. - """ - D = self.mesh.faceDiv - G = self.mesh.cellGrad - sigma = self.modelTransform(m) - Msig = self.mesh.getFaceMass(sigma) - A = D*Msig*G - return A.tocsc() - - def field(self, m): - A = self.createMatrix(m) - solve = linalg.factorized(A) - - nRHSs = self.RHS.shape[1] # Number of RHSs - phi = np.zeros((self.mesh.nC, nRHSs)) + np.nan - for ii in range(nRHSs): - phi[:,ii] = solve(self.RHS[:,ii]) - - return phi - - def J(self, m, v, u=None, solve=None): - """ - :param numpy.array m: model - :param numpy.array v: vector to multiply - :param numpy.array u: fields - :rtype: numpy.array - :return: Jv - - .. math:: - c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0 - - \\nabla_u (A(m)u - q) = A(m) - - \\nabla_m (A(m)u - q) = G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) - - Where M() is the mass matrix and mT is the model transform. - - .. math:: - J = - P \left( \\nabla_u c(m, u) \\right)^{-1} \\nabla_m c(m, u) - - J(v) = - P ( A(m)^{-1} ( G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) v ) ) - """ - P = self.P - D = self.mesh.faceDiv - G = self.mesh.cellGrad - A = self.createMatrix(m) - Av_dm = self.mesh.getFaceMassDeriv() - mT_dm = self.modelTransformDeriv(m) - - dCdu = A - dCdm = D * ( sdiag( G * u ) * ( Av_dm * ( mT_dm * v ) ) ) - - if solve is None: - solve = linalg.factorized(dCdu) - - Jv = - P * solve(dCdm) - return Jv - - def Jt(self, m, v, u=None, solve=None): - P = self.P - D = self.mesh.faceDiv - G = self.mesh.cellGrad - A = self.createMatrix(m) - Av_dm = self.mesh.getFaceMassDeriv() - mT_dm = self.modelTransformDeriv(m) - - dCdu = A.T - - if solve is None: - solve = linalg.factorized(dCdu.tocsc()) - w = solve(P.T*v) - - Jtv = - mT_dm.T * ( Av_dm.T * ( sdiag( G * u ) * ( D.T * w ) ) ) - return Jtv - - -if __name__ == '__main__': - # Create the mesh - h1 = np.ones(100) - h2 = np.ones(100) - mesh = TensorMesh([h1,h2]) - - # Create some parameters for the model - sig1 = 1 - sig2 = 0.01 - - # Create a synthetic model from a block in a half-space - p0 = [20, 20] - p1 = [50, 50] - condVals = [sig1, sig2] - mSynth = ModelBuilder.defineBlockConductivity(p0,p1,mesh.gridCC,condVals) - mesh.plotImage(mSynth, showIt=False) - - - # Set up the projection - nelec = 50 - spacelec = 2 - surfloc = 0.5 - elecini = 0.5 - elecend = 0.5+spacelec*(nelec-1) - elecLocR = np.linspace(elecini, elecend, nelec) - rxmidLoc = (elecLocR[0:nelec-1]+elecLocR[1:nelec])*0.5 - q, Q, rxmidloc = DCutils.genTxRxmat(nelec, spacelec, surfloc, elecini, mesh) - P = Q.T - - # Create some data - class syntheticDCProblem(DCProblem, SyntheticProblem): - pass - - synthetic = syntheticDCProblem(mesh); - synthetic.P = P - synthetic.RHS = q - dobs, Wd = synthetic.createData(mSynth, std=0.05) - - u = synthetic.field(mSynth) - mesh.plotImage(u[:,10], showIt=True) - - # Now set up the problem to do some minimization - problem = DCProblem(mesh) - problem.P = P - problem.RHS = q - problem.W = Wd - problem.dobs = dobs - m0 = mesh.gridCC[:,0]*0+sig1 - - print problem.misfit(m0) - print problem.misfit(mSynth) - - # Check Derivative - derChk = lambda m: [problem.misfit(m), problem.misfitDeriv(m)] - checkDerivative(derChk, mSynth) - - # Adjoint Test - u = np.random.rand(mesh.nC) - v = np.random.rand(mesh.nC) - w = np.random.rand(dobs.shape[0]) - print w.dot(problem.J(mSynth, v, u=u)) - print v.dot(problem.Jt(mSynth, w, u=u)) diff --git a/SimPEG/forward/DCProblem/DCutils.py b/SimPEG/forward/DCProblem/DCutils.py deleted file mode 100644 index f3445096..00000000 --- a/SimPEG/forward/DCProblem/DCutils.py +++ /dev/null @@ -1,29 +0,0 @@ -import numpy as np -import scipy.sparse as sp - -def genTxRxmat(nelec, spacelec, surfloc, elecini, mesh): - """ Generate projection matrix (Q) and """ - elecend = 0.5+spacelec*(nelec-1) - elecLocR = np.linspace(elecini, elecend, nelec) - elecLocT = elecLocR+1 - nrx = nelec-1 - ntx = nelec-1 - q = np.zeros((mesh.nC, ntx)) - Q = np.zeros((mesh.nC, nrx)) - - for i in range(nrx): - - rxind1 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocR[i])) - rxind2 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocR[i+1])) - - txind1 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocT[i])) - txind2 = np.argwhere((mesh.gridCC[:,0]==surfloc) & (mesh.gridCC[:,1]==elecLocT[i+1])) - - q[txind1,i] = 1 - q[txind2,i] = -1 - Q[rxind1,i] = 1 - Q[rxind2,i] = -1 - - Q = sp.csr_matrix(Q) - rxmidLoc = (elecLocR[0:nelec-1]+elecLocR[1:nelec])*0.5 - return q, Q, rxmidLoc diff --git a/SimPEG/forward/DCProblem/__init__.py b/SimPEG/forward/DCProblem/__init__.py deleted file mode 100644 index a868cf80..00000000 --- a/SimPEG/forward/DCProblem/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from DCProblem import * -from DCutils import * diff --git a/SimPEG/forward/LinearProblem.py b/SimPEG/forward/LinearProblem.py new file mode 100644 index 00000000..d30a5b4d --- /dev/null +++ b/SimPEG/forward/LinearProblem.py @@ -0,0 +1,89 @@ +import numpy as np +from SimPEG.mesh import TensorMesh +from SimPEG.forward import Problem +from SimPEG.regularization import Regularization +from SimPEG.inverse import * +import matplotlib.pyplot as plt + + +class LinearProblem(Problem): + """docstring for LinearProblem""" + + def dpred(self, m, u=None): + return self.G.dot(m) + + def J(self, m, v, u=None): + return G.dot(v) + + def Jt(self, m, v, u=None): + return G.T.dot(v) + +if __name__ == '__main__': + N = 100 + h = np.ones(N)/N + M = TensorMesh([h]) + + nk = 20 + jk = np.linspace(1.,20.,nk) + p = -0.25 + q = 0.25 + + + + g = lambda k: np.exp(p*jk[k]*M.vectorCCx)*np.cos(2*np.pi*q*jk[k]*M.vectorCCx) + + G = np.empty((nk, M.nC)) + + for i in range(nk): + G[i,:] = g(i) + + + + plt.figure(1) + for i in range(nk): + plt.plot(G[i,:]) + + + m_true = np.zeros(M.nC) + m_true[M.vectorCCx > 0.3] = 1. + m_true[M.vectorCCx > 0.45] = -0.5 + m_true[M.vectorCCx > 0.6] = 0 + + + d_true = G.dot(m_true) + noise = 0.1 * np.random.rand(d_true.size) + + d_obs = d_true + noise + + # plt.figure(3) + # plt.plot(d_true,'-o') + # plt.plot(d_obs,'r-o') + + + + + + prob = LinearProblem(M) + prob.G = G + prob.dobs = d_obs + prob.std = np.ones_like(d_obs)*0.1 + + reg = Regularization(M) + + opt = InexactGaussNewton(maxIter=20) + + inv = Inversion(prob,reg,opt,beta0=1e-4) + + m0 = np.zeros_like(m_true) + + mrec = inv.run(m0) + + + plt.figure(2) + + plt.plot(M.vectorCCx, m_true, 'b-') + plt.plot(M.vectorCCx, mrec, 'r-') + + + + plt.show() diff --git a/SimPEG/forward/ModelTransforms.py b/SimPEG/forward/ModelTransforms.py new file mode 100644 index 00000000..ea89b974 --- /dev/null +++ b/SimPEG/forward/ModelTransforms.py @@ -0,0 +1,49 @@ +import numpy as np +from SimPEG.utils import mkvc, sdiag + +class LogModel(object): + """docstring for LogModel""" + def modelTransform(self, m): + """ + :param numpy.array m: model + :rtype: numpy.array + :return: transformed model + + The modelTransform changes the model into the physical property. + + A common example of this is to invert for electrical conductivity + in log space. In this case, your model will be log(sigma) and to + get back to sigma, you can take the exponential: + + .. math:: + + m = \log{\sigma} + + \exp{m} = \exp{\log{\sigma}} = \sigma + """ + return np.exp(mkvc(m)) + + def modelTransformDeriv(self, m): + """ + :param numpy.array m: model + :rtype: scipy.csr_matrix + :return: derivative of transformed model + + The modelTransform changes the model into the physical property. + The modelTransformDeriv provides the derivative of the modelTransform. + + If the model transform is: + + .. math:: + + m = \log{\sigma} + + \exp{m} = \exp{\log{\sigma}} = \sigma + + Then the derivative is: + + .. math:: + + \\frac{\partial \exp{m}}{\partial m} = \\text{sdiag}(\exp{m}) + """ + return sdiag(np.exp(mkvc(m))) diff --git a/SimPEG/forward/Problem.py b/SimPEG/forward/Problem.py index 5b716f1f..2e6831f7 100644 --- a/SimPEG/forward/Problem.py +++ b/SimPEG/forward/Problem.py @@ -1,5 +1,6 @@ import numpy as np from SimPEG.utils import mkvc, sdiag +import scipy.sparse as sp norm = np.linalg.norm @@ -49,16 +50,6 @@ class Problem(object): def RHS(self, value): self._RHS = value - @property - def W(self): - """ - Standard deviation weighting matrix. - """ - return self._W - @W.setter - def W(self, value): - self._W = value - @property def P(self): """ @@ -72,6 +63,15 @@ class Problem(object): def P(self, value): self._P = value + @property + def std(self): + """ + Estimated Standard Deviations. + """ + return self._std + @std.setter + def std(self, value): + self._std = value @property def dobs(self): @@ -83,16 +83,35 @@ class Problem(object): def dobs(self, value): self._dobs = value - def evalFunction(self, m, doDerivative=True): + def dpred(self, m, u=None): """ - :param numpy.array m: model - :param bool doDerivative: do you want to compute the derivative? - :rtype: numpy.array - :return: Jv - """ - f = self.misfit(m) + Predicted data. - return f, g, H + .. math:: + d_\\text{pred} = Pu(m) + """ + if u is None: + u = self.field(m) + return self.P*u + + def dataResidual(self, m, u=None): + """ + :param numpy.array m: geophysical model + :param numpy.array u: fields + :rtype: float + :return: data misfit + + The data misfit: + + .. math:: + + \mu_\\text{data} = \mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs} + + Where P is a projection matrix that brings the field on the full domain to the data measurement locations; + u is the field of interest; d_obs is the observed data. + """ + + return self.dpred(m, u=u) - self.dobs def J(self, m, v, u=None): """ @@ -131,10 +150,38 @@ class Problem(object): :rtype: numpy.array :return: JTv - Transpose of J + Effect of transpose of J on a vector v. """ pass + + def J_approx(self, m, v, u=None): + """ + + :param numpy.array m: model + :param numpy.array v: vector to multiply + :param numpy.array u: fields + :rtype: numpy.array + :return: Jv + + Approximate effect of J on a vector v + + """ + return self.J(m, v, u) + + def Jt_approx(self, m, v, u=None): + """ + :param numpy.array m: model + :param numpy.array v: vector to multiply + :param numpy.array u: fields + :rtype: numpy.array + :return: JTv + + Approximate transpose of J*v + + """ + return self.Jt(m, v, u) + def field(self, m): """ The field given the model. @@ -145,17 +192,6 @@ class Problem(object): """ pass - def dpred(self, m, u=None): - """ - Predicted data. - - .. math:: - d_\\text{pred} = Pu(m) - """ - if u is None: - u = self.field(m) - return self.P*u - def modelTransform(self, m): """ :param numpy.array m: model @@ -168,13 +204,8 @@ class Problem(object): in log space. In this case, your model will be log(sigma) and to get back to sigma, you can take the exponential: - .. math:: - - m = \log{\sigma} - - \exp{m} = \exp{\log{\sigma}} = \sigma """ - return np.exp(mkvc(m)) + return m def modelTransformDeriv(self, m): """ @@ -184,129 +215,10 @@ class Problem(object): The modelTransform changes the model into the physical property. The modelTransformDeriv provides the derivative of the modelTransform. - - If the model transform is: - - .. math:: - - m = \log{\sigma} - - \exp{m} = \exp{\log{\sigma}} = \sigma - - Then the derivative is: - - .. math:: - - \\frac{\partial \exp{m}}{\partial m} = \\text{sdiag}(\exp{m}) """ - return sdiag(np.exp(mkvc(m))) + return sp.eye(m.size) - def misfit(self, m, u=None): - """ - :param numpy.array m: geophysical model - :param numpy.array u: fields - :rtype: float - :return: data misfit - The data misfit using an l_2 norm is: - - .. math:: - - \mu_\\text{data} = {1\over 2}\left| \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) \\right|_2^2 - - Where P is a projection matrix that brings the field on the full domain to the data measurement locations; - u is the field of interest; d_obs is the observed data; and W is the weighting matrix. - """ - - R = self.W*(self.dpred(m, u=u) - self.dobs) - R = mkvc(R) - return 0.5*R.dot(R) - - def misfitDeriv(self, m, u=None): - """ - :param numpy.array m: geophysical model - :param numpy.array u: fields - :rtype: numpy.array - :return: data misfit derivative - - The data misfit using an l_2 norm is: - - .. math:: - - \mu_\\text{data} = {1\over 2}\left| \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) \\right|_2^2 - - If the field, u, is provided, the calculation of the data is fast: - - .. math:: - - \mathbf{d}_\\text{pred} = \mathbf{Pu(m)} - - \mathbf{R} = \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) - - Where P is a projection matrix that brings the field on the full domain to the data measurement locations; - u is the field of interest; d_obs is the observed data; and W is the weighting matrix. - - The derivative of this, with respect to the model, is: - - .. math:: - - \\frac{\partial \mu_\\text{data}}{\partial \mathbf{m}} = \mathbf{J}^\\top \mathbf{W \circ R} - - """ - if u is None: - u = self.field(m) - - R = self.W*(self.dpred(m, u=u) - self.dobs) - - dmisfit = 0 - for i in range(self.RHS.shape[1]): # Loop over each right hand side - dmisfit += self.Jt(m, self.W[:,i]*R[:,i], u=u[:,i]) - - return dmisfit - - def misfitDerivDeriv(self, m, u=None): - """ - :param numpy.array m: geophysical model - :param numpy.array u: fields - :rtype: numpy.array - :return: data misfit derivative - - The data misfit using an l_2 norm is: - - .. math:: - - \mu_\\text{data} = {1\over 2}\left| \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) \\right|_2^2 - - If the field, u, is provided, the calculation of the data is fast: - - .. math:: - - \mathbf{d}_\\text{pred} = \mathbf{Pu(m)} - - \mathbf{R} = \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) - - Where P is a projection matrix that brings the field on the full domain to the data measurement locations; - u is the field of interest; d_obs is the observed data; and W is the weighting matrix. - - The derivative of this, with respect to the model, is: - - .. math:: - - \\frac{\partial \mu_\\text{data}}{\partial \mathbf{m}} = \mathbf{J}^\\top \mathbf{W \circ R} - - \\frac{\partial^2 \mu_\\text{data}}{\partial^2 \mathbf{m}} = \mathbf{J}^\\top \mathbf{W \circ W J} - - """ - if u is None: - u = self.field(m) - - R = self.W*(self.dpred(m, u=u) - self.dobs) - - dmisfit = 0 - for i in range(self.RHS.shape[1]): # Loop over each right hand side - dmisfit += self.Jt(m, self.W[:,i]*R[:,i], u=u[:,i]) - - return dmisfit class SyntheticProblem(object): @@ -337,3 +249,6 @@ class SyntheticProblem(object): eps = np.linalg.norm(mkvc(dobs),2)*1e-5 Wd = 1/(abs(dobs)*std+eps) return dobs, Wd + + + diff --git a/SimPEG/forward/__init__.py b/SimPEG/forward/__init__.py index fe849d41..33c9a6b1 100644 --- a/SimPEG/forward/__init__.py +++ b/SimPEG/forward/__init__.py @@ -1,2 +1,4 @@ from Problem import * import DCProblem +from LinearProblem import LinearProblem +import ModelTransforms diff --git a/SimPEG/inverse/BetaSchedule.py b/SimPEG/inverse/BetaSchedule.py new file mode 100644 index 00000000..fe197340 --- /dev/null +++ b/SimPEG/inverse/BetaSchedule.py @@ -0,0 +1,12 @@ + + +class Cooling(object): + """Simple Beta Schedule""" + + beta0 = 1.e6 + beta_coolingFactor = 5. + + def getBeta(self): + if self._beta is None: + return beta0 + return self._beta / beta_coolingFactor diff --git a/SimPEG/inverse/Inversion.py b/SimPEG/inverse/Inversion.py new file mode 100644 index 00000000..3aa8ce58 --- /dev/null +++ b/SimPEG/inverse/Inversion.py @@ -0,0 +1,221 @@ +import numpy as np +import scipy.sparse as sp +from SimPEG.utils import sdiag, mkvc + +class Inversion(object): + """docstring for Inversion""" + + maxIter = 10 + name = 'SimPEG Inversion' + + def __init__(self, prob, reg, opt, **kwargs): + self.prob = prob + self.reg = reg + self.opt = opt + self.opt.parent = self + self.setKwargs(**kwargs) + + def setKwargs(self, **kwargs): + """Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist.""" + for attr in kwargs: + if hasattr(self, attr): + setattr(self, attr, kwargs[attr]) + else: + raise Exception('%s attr is not recognized' % attr) + + def printInit(self): + print "%s %s %s" % ('='*22, self.name, '='*22) + print " # beta phi_d phi_m f norm(dJ) #LS" + print "%s" % '-'*62 + + def printIter(self): + print "%3d %1.2e %1.2e %1.2e %1.2e %1.2e %3d" % (self.opt._iter, self._beta, self._phi_d_last, self._phi_m_last, self.opt.f, np.linalg.norm(self.opt.g), self.opt._iterLS) + + @property + def Wd(self): + """ + Standard deviation weighting matrix. + """ + if getattr(self,'_Wd',None) is None: + eps = np.linalg.norm(mkvc(self.prob.dobs),2)*1e-5 + self._Wd = 1/(abs(self.prob.dobs)*self.prob.std+eps) + return self._Wd + + @property + def phi_d_target(self): + """ + target for phi_d + + By default this is the number of data. + + Note that we do not set the target if it is None, but we return the default value. + """ + if getattr(self, '_phi_d_target', None) is None: + return self.prob.dobs.size # + return self._phi_d_target + @phi_d_target.setter + def phi_d_target(self, value): + self._phi_d_target = value + + def run(self, m0): + m = m0 + self._iter = 0 + self._beta = None + while True: + self._beta = self.getBeta() + m = self.opt.minimize(self.evalFunction,m) + if self.stoppingCriteria(): break + self._iter += 1 + return m + + beta0 = 1.e2 + beta_coolingFactor = 5. + + def getBeta(self): + if self._beta is None: + return self.beta0 + return self._beta / self.beta_coolingFactor + + def stoppingCriteria(self): + self._STOP = np.zeros(2,dtype=bool) + self._STOP[0] = self._iter >= self.maxIter + self._STOP[1] = self._phi_d_last <= self.phi_d_target + return np.any(self._STOP) + + + def evalFunction(self, m, return_g=True, return_H=True): + + u = self.prob.field(m) + phi_d = self.dataObj(m, u) + phi_m = self.reg.modelObj(m) + + self._phi_d_last = phi_d + self._phi_m_last = phi_m + + f = phi_d + self._beta * phi_m + + out = (f,) + if return_g: + phi_dDeriv = self.dataObjDeriv(m, u=u) + phi_mDeriv = self.reg.modelObjDeriv(m) + + g = phi_dDeriv + self._beta * phi_mDeriv + out += (g,) + + if return_H: + def H_fun(v): + phi_d2Deriv = self.dataObj2Deriv(m, v, u=u) + phi_m2Deriv = self.reg.modelObj2Deriv(m)*v + + return phi_d2Deriv + self._beta * phi_m2Deriv + + operator = sp.linalg.LinearOperator( (m.size, m.size), H_fun, dtype=float ) + out += (operator,) + return out + + + def dataObj(self, m, u=None): + """ + :param numpy.array m: geophysical model + :param numpy.array u: fields + :rtype: float + :return: data misfit + + The data misfit using an l_2 norm is: + + .. math:: + + \mu_\\text{data} = {1\over 2}\left| \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) \\right|_2^2 + + Where P is a projection matrix that brings the field on the full domain to the data measurement locations; + u is the field of interest; d_obs is the observed data; and W is the weighting matrix. + """ + # TODO: ensure that this is a data is vector and Wd is a matrix. + R = self.Wd*self.prob.dataResidual(m, u=u) + R = mkvc(R) + return 0.5*np.vdot(R, R) + + def dataObjDeriv(self, m, u=None): + """ + :param numpy.array m: geophysical model + :param numpy.array u: fields + :rtype: numpy.array + :return: data misfit derivative + + The data misfit using an l_2 norm is: + + .. math:: + + \mu_\\text{data} = {1\over 2}\left| \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) \\right|_2^2 + + If the field, u, is provided, the calculation of the data is fast: + + .. math:: + + \mathbf{d}_\\text{pred} = \mathbf{Pu(m)} + + \mathbf{R} = \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) + + Where P is a projection matrix that brings the field on the full domain to the data measurement locations; + u is the field of interest; d_obs is the observed data; and W is the weighting matrix. + + The derivative of this, with respect to the model, is: + + .. math:: + + \\frac{\partial \mu_\\text{data}}{\partial \mathbf{m}} = \mathbf{J}^\\top \mathbf{W \circ R} + + """ + if u is None: + u = self.prob.field(m) + + R = self.Wd*self.prob.dataResidual(m, u=u) + + dmisfit = self.prob.Jt(m, self.Wd * R, u=u) + + return dmisfit + + def dataObj2Deriv(self, m, v, u=None): + """ + :param numpy.array m: geophysical model + :param numpy.array u: fields + :rtype: numpy.array + :return: data misfit derivative + + The data misfit using an l_2 norm is: + + .. math:: + + \mu_\\text{data} = {1\over 2}\left| \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) \\right|_2^2 + + If the field, u, is provided, the calculation of the data is fast: + + .. math:: + + \mathbf{d}_\\text{pred} = \mathbf{Pu(m)} + + \mathbf{R} = \mathbf{W} \circ (\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) + + Where P is a projection matrix that brings the field on the full domain to the data measurement locations; + u is the field of interest; d_obs is the observed data; and W is the weighting matrix. + + The derivative of this, with respect to the model, is: + + .. math:: + + \\frac{\partial \mu_\\text{data}}{\partial \mathbf{m}} = \mathbf{J}^\\top \mathbf{W \circ R} + + \\frac{\partial^2 \mu_\\text{data}}{\partial^2 \mathbf{m}} = \mathbf{J}^\\top \mathbf{W \circ W J} + + """ + if u is None: + u = self.prob.field(m) + + R = self.Wd*self.prob.dataResidual(m, u=u) + + # TODO: abstract to different norms a little cleaner. + # \/ it goes here. in l2 it is the identity. + dmisfit = self.prob.Jt_approx(m, self.Wd * self.Wd * self.prob.J_approx(m, v, u=u), u=u) + + return dmisfit + diff --git a/SimPEG/inverse/Optimize.py b/SimPEG/inverse/Optimize.py index eee18b16..6221246f 100644 --- a/SimPEG/inverse/Optimize.py +++ b/SimPEG/inverse/Optimize.py @@ -2,54 +2,154 @@ import numpy as np import matplotlib.pyplot as plt from SimPEG.utils import mkvc, sdiag norm = np.linalg.norm +import scipy.sparse as sp +from SimPEG import Solver + +try: + from pubsub import pub + doPub = True +except Exception, e: + print 'Warning: you may not have the required pubsub installed, use pypubsub. You will not be able to listen to events.' + doPub = False + class Minimize(object): - """docstring for Minimize""" + """ + + Minimize is a general class for derivative based optimization. + + + """ name = "GeneralOptimizationAlgorithm" maxIter = 20 maxIterLS = 10 + maxStep = np.inf LSreduction = 1e-4 LSshorten = 0.5 - tolF = 1e-4 - tolX = 1e-4 - tolG = 1e-4 - eps = 1e-16 + tolF = 1e-1 + tolX = 1e-1 + tolG = 1e-1 + eps = 1e-5 - def __init__(self, problem, **kwargs): - self.problem = problem + def __init__(self, **kwargs): + self._id = int(np.random.rand()*1e6) # create a unique identifier to this program to be used in pubsub self.setKwargs(**kwargs) def setKwargs(self, **kwargs): - # Set the variables, throw an error if they don't exist. + """Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist.""" for attr in kwargs: if hasattr(self, attr): setattr(self, attr, kwargs[attr]) else: raise Exception('%s attr is not recognized' % attr) - def minimize(self, x0): + def minimize(self, evalFunction, x0): + """ + Minimizes the function (evalFunction) starting at the location x0. + :param def evalFunction: function handle that evaluates: f, g, H = F(x) + :param numpy.ndarray x0: starting location + :rtype: numpy.ndarray + :return: x, the last iterate of the optimization algorithm + + evalFunction is a function handle:: + + (f[, g][, H]) = evalFunction(x, return_g=False, return_H=False ) + + + Events are fired with the following inputs via pypubsub:: + + Minimize.printInit (minimize) + Minimize.evalFunction (minimize, f, g, H) + Minimize.printIter (minimize) + Minimize.searchDirection (minimize, p) + Minimize.scaleSearchDirection (minimize, p) + Minimize.modifySearchDirection (minimize, xt, passLS) + Minimize.endIteration (minimize, xt) + Minimize.printDone (minimize) + + To hook into one of these events (must have pypubsub installed):: + + from pubsub import pub + def listener(minimize,p): + print 'The search direction is: ', p + pub.subscribe(listener, 'Minimize.searchDirection') + + You can use pubsub communication to debug your code, it is not used internally. + + + The algorithm for general minimization is as follows:: + + startup(x0) + printInit() + + while True: + f, g, H = evalFunction(xc) + printIter() + if stoppingCriteria(): break + p = findSearchDirection() + p = scaleSearchDirection(p) + xt, passLS = modifySearchDirection(p) + if not passLS: + xt, caught = modifySearchDirectionBreak(p) + if not caught: return xc + doEndIteration(xt) + + printDone() + return xc + """ + self.evalFunction = evalFunction self.startup(x0) self.printInit() while True: - self.f, self.g, self.H = self.evalFunction(self.xc) + self.f, self.g, self.H = evalFunction(self.xc, return_g=True, return_H=True) + if doPub: pub.sendMessage('Minimize.evalFunction', minimize=self, f=self.f, g=self.g, H=self.H) self.printIter() if self.stoppingCriteria(): break p = self.findSearchDirection() - xt, passLS = self.linesearch(p) + if doPub: pub.sendMessage('Minimize.searchDirection', minimize=self, p=p) + p = self.scaleSearchDirection(p) + if doPub: pub.sendMessage('Minimize.scaleSearchDirection', minimize=self, p=p) + xt, passLS = self.modifySearchDirection(p) + if doPub: pub.sendMessage('Minimize.modifySearchDirection', minimize=self, xt=xt, passLS=passLS) if not passLS: - xt = self.linesearchBreak(p) + xt, caught = self.modifySearchDirectionBreak(p) + if not caught: return self.xc self.doEndIteration(xt) + if doPub: pub.sendMessage('Minimize.endIteration', minimize=self, xt=xt) self.printDone() return self.xc + @property + def parent(self): + """ + This is the parent of the optimization routine. + """ + return getattr(self, '_parent', None) + @parent.setter + def parent(self, value): + self._parent = value + def startup(self, x0): + """ + **startup** is called at the start of any new minimize call. + + This will set:: + + x0 = x0 + xc = x0 + _iter = _iterLS = 0 + + :param numpy.ndarray x0: initial x + :rtype: None + :return: None + """ self._iter = 0 self._iterLS = 0 self._STOP = np.zeros((5,1),dtype=bool) @@ -59,29 +159,57 @@ class Minimize(object): self.xOld = x0 def printInit(self): + """ + **printInit** is called at the beginning of the optimization routine. + + If there is a parent object, printInit will check for a + parent.printInit function and call that. + + """ + if doPub: pub.sendMessage('Minimize.printInit', minimize=self) + if self.parent is not None and hasattr(self.parent, 'printInit'): + self.parent.printInit() + return print "%s %s %s" % ('='*22, self.name, '='*22) print "iter\tJc\t\tnorm(dJ)\tLS" print "%s" % '-'*57 def printIter(self): + """ + **printIter** is called directly after function evaluations. + + If there is a parent object, printIter will check for a + parent.printIter function and call that. + + """ + if doPub: pub.sendMessage('Minimize.printIter', minimize=self) + if self.parent is not None and hasattr(self.parent, 'printIter'): + self.parent.printIter() + return print "%3d\t%1.2e\t%1.2e\t%d" % (self._iter, self.f, norm(self.g), self._iterLS) def printDone(self): + """ + **printDone** is called at the end of the optimization routine. + + If there is a parent object, printDone will check for a + parent.printDone function and call that. + + """ + if doPub: pub.sendMessage('Minimize.printDone', minimize=self) + if self.parent is not None and hasattr(self.parent, 'printDone'): + self.parent.printDone() + return print "%s STOP! %s" % ('-'*25,'-'*25) - print "%d : |fc-fOld| = %1.4e <= tolF*(1+|fStop|) = %1.4e" % (self._STOP[0], abs(self.f-self.fOld), self.tolF*(1+abs(self.fStop))) - print "%d : |xc-xOld| = %1.4e <= tolX*(1+|x0|) = %1.4e" % (self._STOP[1], norm(self.xc-self.xOld), self.tolX*(1+norm(self.x0))) + # TODO: put controls on gradient value, min model update, and function value + if self._iter > 0: + print "%d : |fc-fOld| = %1.4e <= tolF*(1+|fStop|) = %1.4e" % (self._STOP[0], abs(self.f-self.fOld), self.tolF*(1+abs(self.fStop))) + print "%d : |xc-xOld| = %1.4e <= tolX*(1+|x0|) = %1.4e" % (self._STOP[1], norm(self.xc-self.xOld), self.tolX*(1+norm(self.x0))) print "%d : |g| = %1.4e <= tolG*(1+|fStop|) = %1.4e" % (self._STOP[2], norm(self.g), self.tolG*(1+abs(self.fStop))) print "%d : |g| = %1.4e <= 1e3*eps = %1.4e" % (self._STOP[3], norm(self.g), 1e3*self.eps) print "%d : iter = %3d\t <= maxIter\t = %3d" % (self._STOP[4], self._iter, self.maxIter) print "%s DONE! %s\n" % ('='*25,'='*25) - def evalFunction(self, x, doDerivative=True): - f, g, H = self.problem(x) - return f, g, H - - def findSearchDirection(self): - return -self.g - def stoppingCriteria(self): if self._iter == 0: self.fStop = self.f # Save this for stopping criteria @@ -94,14 +222,87 @@ class Minimize(object): self._STOP[4] = self._iter >= self.maxIter return all(self._STOP[0:3]) | any(self._STOP[3:]) - def linesearch(self, p): + def projection(self, p): + """ + projects the search direction. + + by default, no projection is applied. + + :param numpy.ndarray p: searchDirection + :rtype: numpy.ndarray + :return: p, projected search direction + """ + return p + + def findSearchDirection(self): + """ + **findSearchDirection** should return an approximation of: + + .. math:: + + H p = - g + + Where you are solving for the search direction, p + + The default is: + + .. math:: + + H = I + + p = - g + + And corresponds to SteepestDescent. + + The latest function evaluations are present in:: + + self.f, self.g, self.H + + :rtype: numpy.ndarray + :return: p, Search Direction + """ + return -self.g + + def scaleSearchDirection(self, p): + """ + **scaleSearchDirection** should scale the search direction if appropriate. + + Set the parameter **maxStep** in the minimize object, to scale back the gradient to a maximum size. + + :param numpy.ndarray p: searchDirection + :rtype: numpy.ndarray + :return: p, Scaled Search Direction + """ + + if self.maxStep < np.abs(p.max()): + p = self.maxStep*p/np.abs(p.max()) + return p + + def modifySearchDirection(self, p): + """ + **modifySearchDirection** changes the search direction based on some sort of linesearch or trust-region criteria. + + By default, an Armijo backtracking linesearch is preformed with the following parameters: + + * maxIterLS, the maximum number of linesearch iterations + * LSreduction, the expected reduction expected, default: 1e-4 + * LSshorten, how much the step is reduced, default: 0.5 + + If the linesearch is completed, and a descent direction is found, passLS is returned as True. + + Else, a modifySearchDirectionBreak call is preformed. + + :param numpy.ndarray p: searchDirection + :rtype: numpy.ndarray,bool + :return: (xt, passLS) + """ # Armijo linesearch descent = np.inner(self.g, p) t = 1 iterLS = 0 while iterLS < self.maxIterLS: - xt = self.xc + t*p - ft, temp, temp = self.evalFunction(xt, doDerivative=False) + xt = self.projection(self.xc + t*p) + ft = self.evalFunction(xt, return_g=False, return_H=False) if ft < self.f + t*self.LSreduction*descent: break iterLS += 1 @@ -110,10 +311,37 @@ class Minimize(object): self._iterLS = iterLS return xt, iterLS < self.maxIterLS - def linesearchBreak(self, p): - raise Exception('The linesearch got broken. Boo.') + def modifySearchDirectionBreak(self, p): + """ + Code is called if modifySearchDirection fails + to find a descent direction. + + The search direction is passed as input and + this function must pass back both a new searchDirection, + and if the searchDirection break has been caught. + + By default, no additional work is done, and the + evalFunction returns a False indicating the break was not caught. + + :param numpy.ndarray p: searchDirection + :rtype: numpy.ndarray,bool + :return: (xt, breakCaught) + """ + print 'The linesearch got broken. Boo.' + return p, False def doEndIteration(self, xt): + """ + **doEndIteration** is called at the end of each minimize iteration. + + By default, function values and x locations are shuffled to store 1 past iteration in memory. + + self.xc must be updated in this code. + + :param numpy.ndarray xt: tested new iterate that ensures a descent direction. + :rtype: None + :return: None + """ # store old values self.fOld = self.f self.xOld, self.xc = self.xc, xt @@ -123,7 +351,19 @@ class Minimize(object): class GaussNewton(Minimize): name = 'GaussNewton' def findSearchDirection(self): - return np.linalg.solve(self.H,-self.g) + return Solver(self.H).solve(-self.g) + + +class InexactGaussNewton(Minimize): + name = 'InexactGaussNewton' + + maxIterCG = 10 + tolCG = 1e-5 + + def findSearchDirection(self): + # TODO: use BFGS as a preconditioner or gauss sidel of the WtW or solve WtW directly + p, info = sp.linalg.cg(self.H, -self.g, tol=self.tolCG, maxiter=self.maxIterCG) + return p class SteepestDescent(Minimize): @@ -133,18 +373,15 @@ class SteepestDescent(Minimize): if __name__ == '__main__': from SimPEG.tests import Rosenbrock, checkDerivative + import matplotlib.pyplot as plt x0 = np.array([2.6, 3.7]) checkDerivative(Rosenbrock, x0, plotIt=False) - xOpt = GaussNewton(Rosenbrock, maxIter=20).minimize(x0) + + def listener1(minimize,p): + print 'hi: ', p + if doPub: pub.subscribe(listener1, 'Minimize.searchDirection') + + xOpt = GaussNewton(maxIter=20,tolF=1e-10,tolX=1e-10,tolG=1e-10).minimize(Rosenbrock,x0) print "xOpt=[%f, %f]" % (xOpt[0], xOpt[1]) - xOpt = SteepestDescent(Rosenbrock, maxIter=20, maxIterLS=15).minimize(x0) + xOpt = SteepestDescent(maxIter=30, maxIterLS=15,tolF=1e-10,tolX=1e-10,tolG=1e-10).minimize(Rosenbrock, x0) print "xOpt=[%f, %f]" % (xOpt[0], xOpt[1]) - - def simplePass(x): - return np.sin(x), sdiag(np.cos(x)) - - def simpleFail(x): - return np.sin(x), -sdiag(np.cos(x)) - - checkDerivative(simplePass, np.random.randn(5), plotIt=False) - checkDerivative(simpleFail, np.random.randn(5), plotIt=False) diff --git a/SimPEG/inverse/__init__.py b/SimPEG/inverse/__init__.py index b2a5e506..14bddce7 100644 --- a/SimPEG/inverse/__init__.py +++ b/SimPEG/inverse/__init__.py @@ -1 +1,3 @@ from Optimize import * +from Inversion import * +import BetaSchedule diff --git a/SimPEG/mesh/BaseMesh.py b/SimPEG/mesh/BaseMesh.py index 29c43dd1..6a9a8032 100644 --- a/SimPEG/mesh/BaseMesh.py +++ b/SimPEG/mesh/BaseMesh.py @@ -2,6 +2,7 @@ import numpy as np from SimPEG.utils import mkvc + class BaseMesh(object): """ BaseMesh does all the counting you don't want to do. @@ -216,6 +217,12 @@ class BaseMesh(object): :rtype: int :return: nC + + .. plot:: + + from SimPEG.mesh import TensorMesh + import numpy as np + TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(centers=True,showIt=True) """ fget = lambda self: np.prod(self.n) return locals() @@ -270,6 +277,12 @@ class BaseMesh(object): :rtype: int :return: nN + + .. plot:: + + from SimPEG.mesh import TensorMesh + import numpy as np + TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(nodes=True,showIt=True) """ fget = lambda self: np.prod(self.n + 1) return locals() @@ -324,6 +337,12 @@ class BaseMesh(object): :rtype: numpy.array (dim, ) :return: [prod(nEx), prod(nEy), prod(nEz)] + + .. plot:: + + from SimPEG.mesh import TensorMesh + import numpy as np + TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(edges=True,showIt=True) """ fget = lambda self: np.array([np.prod(x) for x in [self.nEx, self.nEy, self.nEz] if not x is None]) return locals() @@ -378,6 +397,12 @@ class BaseMesh(object): :rtype: numpy.array (dim, ) :return: [prod(nFx), prod(nFy), prod(nFz)] + + .. plot:: + + from SimPEG.mesh import TensorMesh + import numpy as np + TensorMesh([np.ones(n) for n in [2,3]]).plotGrid(faces=True,showIt=True) """ fget = lambda self: np.array([np.prod(x) for x in [self.nFx, self.nFy, self.nFz] if not x is None]) return locals() diff --git a/SimPEG/mesh/Cyl1DMesh.py b/SimPEG/mesh/Cyl1DMesh.py index 915bf0ef..93b82b25 100644 --- a/SimPEG/mesh/Cyl1DMesh.py +++ b/SimPEG/mesh/Cyl1DMesh.py @@ -5,8 +5,8 @@ from SimPEG.utils import mkvc, ndgrid, sdiag class Cyl1DMesh(object): """ - Cyl1DMesh is a mesh class for cylindrically symmetric 1D problems - """ + Cyl1DMesh is a mesh class for cylindrically symmetric 1D problems + """ _meshType = 'CYL1D' @@ -20,7 +20,7 @@ class Cyl1DMesh(object): assert len(h_i.shape) == 1, ("h[%i] must be a 1D numpy array." % i) # Ensure h contains 1D vectors - self._h = [mkvc(x) for x in h] + self._h = [mkvc(x.astype(float)) for x in h] if z0 is None: z0 = 0 @@ -146,7 +146,7 @@ class Cyl1DMesh(object): def vectorCCz(): doc = "Cell centered grid vector (1D) in the z direction" - fget = lambda self: self.hz.cumsum() - self.hz/2 + self._z0 + fget = lambda self: self.hz.cumsum() - self.hz/2 + self._z0 return locals() vectorCCz = property(**vectorCCz()) @@ -177,7 +177,7 @@ class Cyl1DMesh(object): self._gridFr = ndgrid([self.vectorNr, self.vectorCCz]) return self._gridFr return locals() - _gridFr = None + _gridFr = None gridFr = property(**gridFr()) def gridFz(): @@ -187,7 +187,7 @@ class Cyl1DMesh(object): self._gridFz = ndgrid([self.vectorCCr, self.vectorNz]) return self._gridFz return locals() - _gridFz = None + _gridFz = None gridFz = property(**gridFz()) #################################################### @@ -350,23 +350,23 @@ class Cyl1DMesh(object): np.all(loc[:,1]<=self.vectorNz.max()), \ "Points outside of mesh" - + if locType=='fz': Q = sp.lil_matrix((loc.shape[0], self.nF), dtype=float) for i, iloc in enumerate(loc): # Point is on a z-interface - if np.any(np.abs(self.vectorNz-iloc[1])<0.001): + if np.any(np.abs(self.vectorNz-iloc[1])<0.001): dFz = self.gridFz-iloc #Distance to z faces dFz[dFz[:,0]>0,:] = np.inf #Looking for next face to the left... indL = np.argmin(np.sum(dFz**2, axis=1)) #Closest one if self.gridFz[indL,0] == self.vectorCCr.max(): #Point in outer half cell (linear extrapolation) - zFL = self.gridFz[indL,:] - zFLL = self.gridFz[indL-1,:] + zFL = self.gridFz[indL,:] + zFLL = self.gridFz[indL-1,:] Q[i, indL+self.nFr] = (iloc[0] - zFLL[0])/(zFL[0] - zFLL[0]) Q[i, indL+self.nFr-1] = -(iloc[0] - zFL[0])/(zFL[0] - zFLL[0]) else: - zFL = self.gridFz[indL,:] + zFL = self.gridFz[indL,:] zFR = self.gridFz[indL+1,:] Q[i,indL+self.nFr] = (zFR[0] - iloc[0])/(zFR[0] - zFL[0]) Q[i,indL+self.nFr+1] = (iloc[0] - zFL[0])/(zFR[0] - zFL[0]) @@ -400,7 +400,7 @@ class Cyl1DMesh(object): Q[i, indAL+self.nFr-1] = -(dzB/DZ)*(drL/DR) Q[i, indAL+self.nFr] = (dzB/DZ)*(drLL/DR) else: - indBR = indBL+1 # Face below and to the right + indBR = indBL+1 # Face below and to the right indAR = indAL + 1 # Face above and to the right zF_BR = self.gridFz[indBR,:] diff --git a/SimPEG/mesh/DiffOperators.py b/SimPEG/mesh/DiffOperators.py index 110fe9bd..598b392a 100644 --- a/SimPEG/mesh/DiffOperators.py +++ b/SimPEG/mesh/DiffOperators.py @@ -161,6 +161,68 @@ class DiffOperators(object): _cellGrad = None cellGrad = property(**cellGrad()) + def cellGradx(): + doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions." + + def fget(self): + if getattr(self, '_cellGradx', None) is None: + BC = ['neumann', 'neumann'] + n = self.n + if(self.dim == 1): + G1 = ddxCellGrad(n[0], BC) + elif(self.dim == 2): + G1 = sp.kron(speye(n[1]), ddxCellGrad(n[0], BC)) + elif(self.dim == 3): + G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC)) + # Compute areas of cell faces & volumes + S = self.r(self.area, 'F','Fx', 'V') + V = self.vol + self._cellGradx = sdiag(S)*G1*sdiag(1/V) + return self._cellGradx + return locals() + cellGradx = property(**cellGradx()) + + + def cellGrady(): + doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions." + def fget(self): + if self.dim < 2: + return None + if getattr(self, '_cellGrady', None) is None: + BC = ['neumann', 'neumann'] + n = self.n + if(self.dim == 2): + G2 = sp.kron(ddxCellGrad(n[1], BC), speye(n[0])) + elif(self.dim == 3): + G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC), speye(n[0])) + # Compute areas of cell faces & volumes + S = self.r(self.area, 'F','Fy', 'V') + V = self.vol + self._cellGrady = sdiag(S)*G2*sdiag(1/V) + return self._cellGrady + return locals() + cellGrady = property(**cellGrady()) + + + + def cellGradz(): + doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions." + def fget(self): + if self.dim < 3: + return None + if getattr(self, '_cellGradz', None) is None: + BC = ['neumann', 'neumann'] + n = self.n + G3 = kron3(ddxCellGrad(n[2], BC), speye(n[1]), speye(n[0])) + # Compute areas of cell faces & volumes + S = self.r(self.area, 'F','Fz', 'V') + V = self.vol + self._cellGradz = sdiag(S)*G3*sdiag(1/V) + return self._cellGradz + return locals() + cellGradz = property(**cellGradz()) + + def edgeCurl(): doc = "Construct the 3D curl operator." diff --git a/SimPEG/mesh/InnerProducts.py b/SimPEG/mesh/InnerProducts.py index 9fe84ac3..f0ac4ab0 100644 --- a/SimPEG/mesh/InnerProducts.py +++ b/SimPEG/mesh/InnerProducts.py @@ -81,9 +81,9 @@ class InnerProducts(object): def getFaceInnerProduct(self, mu=None, returnP=False): """Wrapper function, - :py:func:`SimPEG.InnerProducts.getEdgeInnerProduct` + :py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getEdgeInnerProduct` - :py:func:`SimPEG.InnerProducts.getEdgeInnerProduct2D` + :py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getEdgeInnerProduct2D` """ if self.dim == 2: return getFaceInnerProduct2D(self, mu, returnP) @@ -93,9 +93,9 @@ class InnerProducts(object): def getEdgeInnerProduct(self, sigma=None, returnP=False): """Wrapper function, - :py:func:`SimPEG.InnerProducts.getFaceInnerProduct` + :py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getFaceInnerProduct` - :py:func:`SimPEG.InnerProducts.getFaceInnerProduct2D` + :py:func:`SimPEG.mesh.InnerProducts.InnerProducts.getFaceInnerProduct2D` """ if self.dim == 2: return getEdgeInnerProduct2D(self, sigma, returnP) diff --git a/SimPEG/mesh/LogicallyOrthogonalMesh.py b/SimPEG/mesh/LogicallyOrthogonalMesh.py index 7b1e1bca..b510a754 100644 --- a/SimPEG/mesh/LogicallyOrthogonalMesh.py +++ b/SimPEG/mesh/LogicallyOrthogonalMesh.py @@ -38,7 +38,7 @@ class LogicallyOrthogonalMesh(BaseMesh, DiffOperators, InnerProducts, LomView): # Save nodes to private variable _gridN as vectors self._gridN = np.ones((nodes[0].size, self.dim)) for i, node_i in enumerate(nodes): - self._gridN[:, i] = mkvc(node_i) + self._gridN[:, i] = mkvc(node_i.astype(float)) def gridCC(): doc = "Cell-centered grid." diff --git a/SimPEG/mesh/TensorMesh.py b/SimPEG/mesh/TensorMesh.py index a3329c14..6cc5d8a6 100644 --- a/SimPEG/mesh/TensorMesh.py +++ b/SimPEG/mesh/TensorMesh.py @@ -1,9 +1,10 @@ import numpy as np +import scipy.sparse as sp from BaseMesh import BaseMesh from TensorView import TensorView from DiffOperators import DiffOperators from InnerProducts import InnerProducts -from SimPEG.utils import ndgrid, mkvc +from SimPEG.utils import ndgrid, mkvc, spzeros, interpmat class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): @@ -38,7 +39,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): assert len(h_i.shape) == 1, ("h[%i] must be a 1D numpy array." % i) # Ensure h contains 1D vectors - self._h = [mkvc(x) for x in h] + self._h = [mkvc(x.astype(float)) for x in h] def __str__(self): outStr = ' ---- {0:d}-D TensorMesh ---- '.format(self.dim) @@ -156,7 +157,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridCC is None: - self._gridCC = ndgrid([x for x in [self.vectorCCx, self.vectorCCy, self.vectorCCz] if not x is None]) + self._gridCC = ndgrid(self.getTensor('CC')) return self._gridCC return locals() _gridCC = None # Store grid by default @@ -167,7 +168,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridN is None: - self._gridN = ndgrid([x for x in [self.vectorNx, self.vectorNy, self.vectorNz] if not x is None]) + self._gridN = ndgrid(self.getTensor('N')) return self._gridN return locals() _gridN = None # Store grid by default @@ -178,7 +179,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridFx is None: - self._gridFx = ndgrid([x for x in [self.vectorNx, self.vectorCCy, self.vectorCCz] if not x is None]) + self._gridFx = ndgrid(self.getTensor('Fx')) return self._gridFx return locals() _gridFx = None # Store grid by default @@ -189,7 +190,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridFy is None and self.dim > 1: - self._gridFy = ndgrid([x for x in [self.vectorCCx, self.vectorNy, self.vectorCCz] if not x is None]) + self._gridFy = ndgrid(self.getTensor('Fy')) return self._gridFy return locals() _gridFy = None # Store grid by default @@ -200,7 +201,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridFz is None and self.dim > 2: - self._gridFz = ndgrid([x for x in [self.vectorCCx, self.vectorCCy, self.vectorNz] if not x is None]) + self._gridFz = ndgrid(self.getTensor('Fz')) return self._gridFz return locals() _gridFz = None # Store grid by default @@ -211,7 +212,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridEx is None: - self._gridEx = ndgrid([x for x in [self.vectorCCx, self.vectorNy, self.vectorNz] if not x is None]) + self._gridEx = ndgrid(self.getTensor('Ex')) return self._gridEx return locals() _gridEx = None # Store grid by default @@ -222,7 +223,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridEy is None and self.dim > 1: - self._gridEy = ndgrid([x for x in [self.vectorNx, self.vectorCCy, self.vectorNz] if not x is None]) + self._gridEy = ndgrid(self.getTensor('Ey')) return self._gridEy return locals() _gridEy = None # Store grid by default @@ -233,7 +234,7 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): def fget(self): if self._gridEz is None and self.dim > 2: - self._gridEz = ndgrid([x for x in [self.vectorNx, self.vectorNy, self.vectorCCz] if not x is None]) + self._gridEz = ndgrid(self.getTensor('Ez')) return self._gridEz return locals() _gridEz = None # Store grid by default @@ -312,6 +313,98 @@ class TensorMesh(BaseMesh, TensorView, DiffOperators, InnerProducts): _edge = None edge = property(**edge()) + # --------------- Methods --------------------- + + def getTensor(self, locType): + """ Returns a tensor list. + + :param str locType: What tensor (see below) + :rtype: list + :return: list of the tensors that make up the mesh. + + locType can be:: + + 'Ex' -> x-component of field defined on edges + 'Ey' -> y-component of field defined on edges + 'Ez' -> z-component of field defined on edges + 'Fx' -> x-component of field defined on faces + 'Fy' -> y-component of field defined on faces + 'Fz' -> z-component of field defined on faces + 'N' -> scalar field defined on nodes + 'CC' -> scalar field defined on cell centers + """ + + if locType is 'Fx': + ten = [self.vectorNx , self.vectorCCy, self.vectorCCz] + elif locType is 'Fy': + ten = [self.vectorCCx, self.vectorNy , self.vectorCCz] + elif locType is 'Fz': + ten = [self.vectorCCx, self.vectorCCy, self.vectorNz ] + elif locType is 'Ex': + ten = [self.vectorCCx, self.vectorNy , self.vectorNz ] + elif locType is 'Ey': + ten = [self.vectorNx , self.vectorCCy, self.vectorNz ] + elif locType is 'Ez': + ten = [self.vectorNx , self.vectorNy , self.vectorCCz] + elif locType is 'CC': + ten = [self.vectorCCx, self.vectorCCy, self.vectorCCz] + elif locType is 'N': + ten = [self.vectorNx , self.vectorNy , self.vectorNz ] + + return [t for t in ten if t is not None] + + + def isInside(self, pts): + """ + Determines if a set of points are inside a mesh. + + :param numpy.ndarray pts: Location of points to test + :rtype numpy.ndarray + :return inside, numpy array of booleans + """ + + pts = np.atleast_2d(pts) + inside = (pts[:,0] >= self.vectorNx.min()) & (pts[:,0] <= self.vectorNx.max()) + if self.dim > 1: + inside = inside & ((pts[:,1] >= self.vectorNy.min()) & (pts[:,1] <= self.vectorNy.max())) + if self.dim > 2: + inside = inside & ((pts[:,2] >= self.vectorNz.min()) & (pts[:,2] <= self.vectorNz.max())) + return inside + + def getInterpolationMat(self, loc, locType): + """ Produces interpolation matrix + + :param numpy.ndarray loc: Location of points to interpolate to + :param str locType: What to interpolate (see below) + :rtype: scipy.sparse.csr.csr_matrix + :return: M, the interpolation matrix + + locType can be:: + + 'Ex' -> x-component of field defined on edges + 'Ey' -> y-component of field defined on edges + 'Ez' -> z-component of field defined on edges + 'Fx' -> x-component of field defined on faces + 'Fy' -> y-component of field defined on faces + 'Fz' -> z-component of field defined on faces + 'N' -> scalar field defined on nodes + 'CC' -> scalar field defined on cell centers + """ + + loc = np.atleast_2d(loc) + assert np.all(self.isInside(loc)), "Points outside of mesh" + + ind = 0 if 'x' in locType else 1 if 'y' in locType else 2 if 'z' in locType else -1 + if locType in ['Fx','Fy','Fz','Ex','Ey','Ez'] and self.dim >= ind: + nF_nE = self.nF if 'F' in locType else self.nE + components = [spzeros(loc.shape[0], n) for n in nF_nE] + components[ind] = interpmat(loc, *self.getTensor(locType)) + Q = sp.hstack(components) + elif locType in ['CC', 'N']: + Q = interpmat(loc, *self.getTensor(locType)) + else: + raise NotImplementedError('getInterpolationMat: locType=='+locType+' and mesh.dim=='+str(self.dim)) + return Q if __name__ == '__main__': print('Welcome to tensor mesh!') @@ -327,5 +420,6 @@ if __name__ == '__main__': h = h[:testDim] M = TensorMesh(h) + print M xn = M.plotGrid() diff --git a/SimPEG/mesh/TensorView.py b/SimPEG/mesh/TensorView.py index 687a520d..0b9ff7b3 100644 --- a/SimPEG/mesh/TensorView.py +++ b/SimPEG/mesh/TensorView.py @@ -267,6 +267,9 @@ class TensorView(object): if faces: ax.plot(xs1[:, 0], xs1[:, 1], 'g>') ax.plot(xs2[:, 0], xs2[:, 1], 'g^') + if edges: + ax.plot(self.gridEx[:, 0], self.gridEx[:, 1], 'c>') + ax.plot(self.gridEy[:, 0], self.gridEy[:, 1], 'c^') # Plot the grid lines if lines: diff --git a/SimPEG/regularization/Regularization.py b/SimPEG/regularization/Regularization.py new file mode 100644 index 00000000..6f5970e6 --- /dev/null +++ b/SimPEG/regularization/Regularization.py @@ -0,0 +1,120 @@ +from SimPEG.utils import sdiag +import numpy as np + +class Regularization(object): + """docstring for Regularization""" + + @property + def mref(self): + if getattr(self, '_mref', None) is None: + self._mref = np.zeros(self.mesh.nC); + return self._mref + @mref.setter + def mref(self, value): + self._mref = value + + @property + def Ws(self): + if getattr(self,'_Ws', None) is None: + self._Ws = sdiag(self.mesh.vol) + return self._Ws + + @property + def Wx(self): + if getattr(self, '_Wx', None) is None: + a = self.mesh.r(self.mesh.area,'F','Fx','V') + self._Wx = sdiag(a)*self.mesh.cellGradx + return self._Wx + + @property + def Wy(self): + if getattr(self, '_Wy', None) is None: + a = self.mesh.r(self.mesh.area,'F','Fy','V') + self._Wy = sdiag(a)*self.mesh.cellGrady + return self._Wy + + @property + def Wz(self): + if getattr(self, '_Wz', None) is None: + a = self.mesh.r(self.mesh.area,'F','Fz','V') + self._Wz = sdiag(a)*self.mesh.cellGradz + return self._Wz + + + + def __init__(self, mesh): + self.mesh = mesh + self._Wx = None + self._Wy = None + self._Wz = None + self.alpha_s = 1e-6 + self.alpha_x = 1 + self.alpha_y = 1 + self.alpha_z = 1 + + def pnorm(self, r): + return 0.5*r.dot(r) + + def modelObj(self, m): + mresid = m - self.mref + + mobj = self.alpha_s * self.pnorm( self.Ws * mresid ) + + mobj += self.alpha_x * self.pnorm( self.Wx * mresid ) + + if self.mesh.dim > 1: + mobj += self.alpha_y * self.pnorm( self.Wy * mresid ) + if self.mesh.dim > 2: + mobj += self.alpha_z * self.pnorm( self.Wz * mresid ) + + return mobj + + def modelObjDeriv(self, m): + """ + + In 1D: + + .. math:: + + m_{\\text{obj}} = {1 \over 2}\\alpha_s \left\| W_s (m- m_{\\text{ref}})\\right\|^2_2 + + {1 \over 2}\\alpha_x \left\| W_x (m- m_{\\text{ref}})\\right\|^2_2 + + \\frac{ \partial m_{\\text{obj}} }{\partial m} = + \\alpha_s W_s^{\\top} W_s (m - m_{\\text{ref}}) + + \\alpha_x W_x^{\\top} W_x (m - m_{\\text{ref}}) + + + \\frac{ \partial^2 m_{\\text{obj}} }{\partial m^2} = + \\alpha_s W_s^{\\top} W_s + + \\alpha_x W_x^{\\top} W_x + + """ + + mresid = m - self.mref + + mobjDeriv = self.alpha_s * self.Ws.T * ( self.Ws * mresid) + + mobjDeriv = mobjDeriv + self.alpha_x * self.Wx.T * ( self.Wx * mresid) + + if self.mesh.dim > 1: + mobjDeriv = mobjDeriv + self.alpha_y * self.Wy.T * ( self.Wy * mresid) + if self.mesh.dim > 2: + mobjDeriv = mobjDeriv + self.alpha_z * self.Wz.T * ( self.Wz * mresid) + + return mobjDeriv + + + def modelObj2Deriv(self, m): + mresid = m - self.mref + + mobj2Deriv = self.alpha_s * self.Ws.T * self.Ws + + mobj2Deriv = mobj2Deriv + self.alpha_x * self.Wx.T * self.Wx + + if self.mesh.dim > 1: + mobj2Deriv = mobj2Deriv + self.alpha_y * self.Wy.T * self.Wy + if self.mesh.dim > 2: + mobj2Deriv = mobj2Deriv + self.alpha_z * self.Wz.T * self.Wz + + return mobj2Deriv + diff --git a/SimPEG/regularization/__init__.py b/SimPEG/regularization/__init__.py new file mode 100644 index 00000000..0230f8c3 --- /dev/null +++ b/SimPEG/regularization/__init__.py @@ -0,0 +1 @@ +from Regularization import Regularization diff --git a/SimPEG/tests/HTMLTestRunner.py b/SimPEG/tests/HTMLTestRunner.py new file mode 100644 index 00000000..af384971 --- /dev/null +++ b/SimPEG/tests/HTMLTestRunner.py @@ -0,0 +1,824 @@ +""" +A TestRunner for use with the Python unit testing framework. It +generates a HTML report to show the result at a glance. + +The simplest way to use this is to invoke its main method. E.g. + + import unittest + import HTMLTestRunner + + ... define your tests ... + + if __name__ == '__main__': + HTMLTestRunner.main() + + +For more customization options, instantiates a HTMLTestRunner object. +HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. + + # output to a file + fp = file('my_report.html', 'wb') + runner = HTMLTestRunner.HTMLTestRunner( + stream=fp, + title='My unit test', + description='This demonstrates the report output by HTMLTestRunner.' + ) + + # Use an external stylesheet. + # See the Template_mixin class for more customizable options + runner.STYLESHEET_TMPL = '' + + # run the test + runner.run(my_test_suite) + + +------------------------------------------------------------------------ +Copyright (c) 2004-2007, Wai Yip Tung +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name Wai Yip Tung nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +# URL: http://tungwaiyip.info/software/HTMLTestRunner.html + +__author__ = "Wai Yip Tung" +__version__ = "0.8.2" + + +""" +Change History + +Version 0.8.2 +* Show output inline instead of popup window (Viorel Lupu). + +Version in 0.8.1 +* Validated XHTML (Wolfgang Borgert). +* Added description of test classes and test cases. + +Version in 0.8.0 +* Define Template_mixin class for customization. +* Workaround a IE 6 bug that it does not treat + +%(heading)s +%(report)s +%(ending)s + + + +""" + # variables: (title, generator, stylesheet, heading, report, ending) + + + # ------------------------------------------------------------------------ + # Stylesheet + # + # alternatively use a for external style sheet, e.g. + # + + STYLESHEET_TMPL = """ + +""" + + + + # ------------------------------------------------------------------------ + # Heading + # + + HEADING_TMPL = """
+

%(title)s

+%(parameters)s +

%(description)s

+
+ +""" # variables: (title, parameters, description) + + HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

+""" # variables: (name, value) + + + + # ------------------------------------------------------------------------ + # Report + # + + REPORT_TMPL = """ +

Show +Summary +Failed +All +

+ ++++++++ + + + + + + + + +%(test_list)s + + + + + + + + +
Test Group/Test caseCountPassFailErrorView
Total%(count)s%(Pass)s%(fail)s%(error)s 
+""" # variables: (test_list, count, Pass, fail, error) + + REPORT_CLASS_TMPL = r""" + + %(desc)s + %(count)s + %(Pass)s + %(fail)s + %(error)s + Detail + +""" # variables: (style, desc, count, Pass, fail, error, cid) + + + REPORT_TEST_WITH_OUTPUT_TMPL = r""" + +
%(desc)s
+ + + + + %(status)s + + + + + + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_NO_OUTPUT_TMPL = r""" + +
%(desc)s
+ %(status)s + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_OUTPUT_TMPL = r""" +%(id)s: %(output)s +""" # variables: (id, output) + + + + # ------------------------------------------------------------------------ + # ENDING + # + + ENDING_TMPL = """
 
""" + +# -------------------- The end of the Template class ------------------- + + +TestResult = unittest.TestResult + +class _TestResult(TestResult): + # note: _TestResult is a pure representation of results. + # It lacks the output and reporting ability compares to unittest._TextTestResult. + + def __init__(self, verbosity=1): + TestResult.__init__(self) + self.stdout0 = None + self.stderr0 = None + self.success_count = 0 + self.failure_count = 0 + self.error_count = 0 + self.verbosity = verbosity + + # result is a list of result in 4 tuple + # ( + # result code (0: success; 1: fail; 2: error), + # TestCase object, + # Test output (byte string), + # stack trace, + # ) + self.result = [] + + + def startTest(self, test): + TestResult.startTest(self, test) + # just one buffer for both stdout and stderr + self.outputBuffer = StringIO.StringIO() + stdout_redirector.fp = self.outputBuffer + stderr_redirector.fp = self.outputBuffer + self.stdout0 = sys.stdout + self.stderr0 = sys.stderr + sys.stdout = stdout_redirector + sys.stderr = stderr_redirector + + + def complete_output(self): + """ + Disconnect output redirection and return buffer. + Safe to call multiple times. + """ + if self.stdout0: + sys.stdout = self.stdout0 + sys.stderr = self.stderr0 + self.stdout0 = None + self.stderr0 = None + return self.outputBuffer.getvalue() + + + def stopTest(self, test): + # Usually one of addSuccess, addError or addFailure would have been called. + # But there are some path in unittest that would bypass this. + # We must disconnect stdout in stopTest(), which is guaranteed to be called. + self.complete_output() + + + def addSuccess(self, test): + self.success_count += 1 + TestResult.addSuccess(self, test) + output = self.complete_output() + self.result.append((0, test, output, '')) + if self.verbosity > 1: + sys.stderr.write('ok ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('.') + + def addError(self, test, err): + self.error_count += 1 + TestResult.addError(self, test, err) + _, _exc_str = self.errors[-1] + output = self.complete_output() + self.result.append((2, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('E ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('E') + + def addFailure(self, test, err): + self.failure_count += 1 + TestResult.addFailure(self, test, err) + _, _exc_str = self.failures[-1] + output = self.complete_output() + self.result.append((1, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('F ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('F') + + +class HTMLTestRunner(Template_mixin): + """ + """ + def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): + self.stream = stream + self.verbosity = verbosity + if title is None: + self.title = self.DEFAULT_TITLE + else: + self.title = title + if description is None: + self.description = self.DEFAULT_DESCRIPTION + else: + self.description = description + + self.startTime = datetime.datetime.now() + + + def run(self, test): + "Run the given test case or test suite." + result = _TestResult(self.verbosity) + test(result) + self.stopTime = datetime.datetime.now() + self.generateReport(test, result) + print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) + return result + + + def sortResult(self, result_list): + # unittest does not seems to run in any particular order. + # Here at least we want to group them together by class. + rmap = {} + classes = [] + for n,t,o,e in result_list: + cls = t.__class__ + if not rmap.has_key(cls): + rmap[cls] = [] + classes.append(cls) + rmap[cls].append((n,t,o,e)) + r = [(cls, rmap[cls]) for cls in classes] + return r + + + def getReportAttributes(self, result): + """ + Return report attributes as a list of (name, value). + Override this to add custom attributes. + """ + startTime = str(self.startTime)[:19] + duration = str(self.stopTime - self.startTime) + status = [] + if result.success_count: status.append('Pass %s' % result.success_count) + if result.failure_count: status.append('Failure %s' % result.failure_count) + if result.error_count: status.append('Error %s' % result.error_count ) + if status: + status = ' '.join(status) + else: + status = 'none' + return [ + ('Start Time', startTime), + ('Duration', duration), + ('Status', status), + ] + + + def generateReport(self, test, result): + report_attrs = self.getReportAttributes(result) + generator = 'HTMLTestRunner %s' % __version__ + stylesheet = self._generate_stylesheet() + heading = self._generate_heading(report_attrs) + report = self._generate_report(result) + ending = self._generate_ending() + output = self.HTML_TMPL % dict( + title = saxutils.escape(self.title), + generator = generator, + stylesheet = stylesheet, + heading = heading, + report = report, + ending = ending, + ) + self.stream.write(output.encode('utf8')) + + + def _generate_stylesheet(self): + return self.STYLESHEET_TMPL + + + def _generate_heading(self, report_attrs): + a_lines = [] + for name, value in report_attrs: + line = self.HEADING_ATTRIBUTE_TMPL % dict( + name = saxutils.escape(name), + value = saxutils.escape(value), + ) + a_lines.append(line) + heading = self.HEADING_TMPL % dict( + title = saxutils.escape(self.title), + parameters = ''.join(a_lines), + description = saxutils.escape(self.description), + ) + return heading + + + def _generate_report(self, result): + rows = [] + sortedResult = self.sortResult(result.result) + for cid, (cls, cls_results) in enumerate(sortedResult): + # subtotal for a class + np = nf = ne = 0 + for n,t,o,e in cls_results: + if n == 0: np += 1 + elif n == 1: nf += 1 + else: ne += 1 + + # format class description + if cls.__module__ == "__main__": + name = cls.__name__ + else: + name = "%s.%s" % (cls.__module__, cls.__name__) + doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" + desc = doc and '%s: %s' % (name, doc) or name + + row = self.REPORT_CLASS_TMPL % dict( + style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', + desc = desc, + count = np+nf+ne, + Pass = np, + fail = nf, + error = ne, + cid = 'c%s' % (cid+1), + ) + rows.append(row) + + for tid, (n,t,o,e) in enumerate(cls_results): + self._generate_report_test(rows, cid, tid, n, t, o, e) + + report = self.REPORT_TMPL % dict( + test_list = ''.join(rows), + count = str(result.success_count+result.failure_count+result.error_count), + Pass = str(result.success_count), + fail = str(result.failure_count), + error = str(result.error_count), + ) + return report + + + def _generate_report_test(self, rows, cid, tid, n, t, o, e): + # e.g. 'pt1.1', 'ft1.1', etc + has_output = bool(o or e) + tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) + name = t.id().split('.')[-1] + doc = t.shortDescription() or "" + desc = doc and ('%s: %s' % (name, doc)) or name + tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL + + # o and e should be byte string because they are collected from stdout and stderr? + if isinstance(o,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # uo = unicode(o.encode('string_escape')) + uo = o.decode('latin-1') + else: + uo = o + if isinstance(e,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # ue = unicode(e.encode('string_escape')) + ue = e.decode('latin-1') + else: + ue = e + + script = self.REPORT_TEST_OUTPUT_TMPL % dict( + id = tid, + output = saxutils.escape(uo+ue), + ) + + row = tmpl % dict( + tid = tid, + Class = (n == 0 and 'hiddenRow' or 'none'), + style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), + desc = desc, + script = script, + status = self.STATUS[n], + ) + rows.append(row) + if not has_output: + return + + def _generate_ending(self): + return self.ENDING_TMPL + + +############################################################################## +# Facilities for running tests from the command line +############################################################################## + +# Note: Reuse unittest.TestProgram to launch test. In the future we may +# build our own launcher to support more specific command line +# parameters like test title, CSS, etc. +class TestProgram(unittest.TestProgram): + """ + A variation of the unittest.TestProgram. Please refer to the base + class for command line parameters. + """ + def runTests(self): + # Pick HTMLTestRunner as the default test runner. + # base class's testRunner parameter is not useful because it means + # we have to instantiate HTMLTestRunner before we know self.verbosity. + if self.testRunner is None: + self.testRunner = HTMLTestRunner(verbosity=self.verbosity) + unittest.TestProgram.runTests(self) + +main = TestProgram + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) diff --git a/SimPEG/tests/TestUtils.py b/SimPEG/tests/TestUtils.py index 9b2158c4..140b56fa 100644 --- a/SimPEG/tests/TestUtils.py +++ b/SimPEG/tests/TestUtils.py @@ -1,12 +1,15 @@ import numpy as np import matplotlib.pyplot as plt from pylab import norm -from SimPEG.utils import mkvc +from SimPEG.utils import mkvc, sdiag from SimPEG import utils from SimPEG.mesh import TensorMesh, LogicallyOrthogonalMesh import numpy as np import unittest +import inspect +happiness = ['The test be workin!', 'You get a gold star!', 'Yay passed!', 'Happy little convergence test!', 'That was easy!', 'Testing is important.', 'You are awesome.', 'Go Test Go!', 'Once upon a time, a happy little test passed.', 'And then everyone was happy.'] +sadness = ['No gold star for you.','Try again soon.','Thankfully, persistence is a great substitute for talent.','It might be easier to call this a feature...','Coffee break?', 'Boooooooo :(', 'Testing is important. Do it again.'] class OrderTest(unittest.TestCase): """ @@ -67,8 +70,7 @@ class OrderTest(unittest.TestCase): name = "Order Test" expectedOrders = 2. # This can be a list of orders, must be the same length as meshTypes - _expectedOrder = 2. - tolerance = 0.85 + tolerance = 0.85 # This can also be a list, must be the same length as meshTypes meshSizes = [4, 8, 16, 32] meshTypes = ['uniformTensorMesh'] _meshType = meshTypes[0] @@ -124,6 +126,8 @@ class OrderTest(unittest.TestCase): """ assert type(self.meshTypes) == list, 'meshTypes must be a list' + if type(self.tolerance) is not list: + self.tolerance = np.ones(len(self.meshTypes))*self.tolerance # if we just provide one expected order, repeat it for each mesh type if type(self.expectedOrders) == float or type(self.expectedOrders) == int: @@ -134,6 +138,7 @@ class OrderTest(unittest.TestCase): for ii_meshType, meshType in enumerate(self.meshTypes): self._meshType = meshType + self._tolerance = self.tolerance[ii_meshType] self._expectedOrder = self.expectedOrders[ii_meshType] order = [] @@ -144,7 +149,7 @@ class OrderTest(unittest.TestCase): err = self.getError() if ii == 0: print '' - print 'Testing convergence on ' + self.M._meshType + ' of: ' + self.name + print self._meshType + ': ' + self.name print '_____________________________________________' print ' h | error | e(i-1)/e(i) | order' print '~~~~~~|~~~~~~~~~~~~~|~~~~~~~~~~~~~|~~~~~~~~~~' @@ -155,21 +160,28 @@ class OrderTest(unittest.TestCase): err_old = err max_h_old = max_h print '---------------------------------------------' - passTest = np.mean(np.array(order)) > self.tolerance*self._expectedOrder + passTest = np.mean(np.array(order)) > self._tolerance*self._expectedOrder if passTest: - print ['The test be workin!', 'You get a gold star!', 'Yay passed!', 'Happy little convergence test!', 'That was easy!'][np.random.randint(5)] + print happiness[np.random.randint(len(happiness))] else: print 'Failed to pass test on ' + self._meshType + '.' + print sadness[np.random.randint(len(sadness))] print '' self.assertTrue(passTest) -def Rosenbrock(x): +def Rosenbrock(x, return_g=True, return_H=True): """Rosenbrock function for testing GaussNewton scheme""" f = 100*(x[1]-x[0]**2)**2+(1-x[0])**2 g = np.array([2*(200*x[0]**3-200*x[0]*x[1]+x[0]-1), 200*(x[1]-x[0]**2)]) H = np.array([[-400*x[1]+1200*x[0]**2+2, -400*x[0]], [-400*x[0], 200]]) - return f, g, H + + out = (f,) + if return_g: + out += (g,) + if return_H: + out += (H,) + return out def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None): """ @@ -186,6 +198,16 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None): :rtype: bool :return: did you pass the test?! + + .. plot:: + :include-source: + + from SimPEG.tests import checkDerivative + from SimPEG.utils import sdiag + import numpy as np + def simplePass(x): + return np.sin(x), sdiag(np.cos(x)) + checkDerivative(simplePass, np.random.randn(5)) """ print "%s checkDerivative %s" % ('='*20, '='*20) @@ -206,7 +228,11 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None): for i in range(num): Jt = fctn(x0+t[i]*dx) E0[i] = l2norm(Jt[0]-Jc[0]) # 0th order Taylor - E1[i] = l2norm(Jt[0]-Jc[0]-t[i]*Jc[1].dot(dx)) # 1st order Taylor + if inspect.isfunction(Jc[1]): + E1[i] = l2norm(Jt[0]-Jc[0]-t[i]*Jc[1](dx)) # 1st order Taylor + else: + # We assume it is a numpy.ndarray + E1[i] = l2norm(Jt[0]-Jc[0]-t[i]*Jc[1].dot(dx)) # 1st order Taylor order0 = np.log10(E0[:-1]/E0[1:]) order1 = np.log10(E1[:-1]/E1[1:]) print "%d\t%1.2e\t%1.3e\t\t%1.3e\t\t%1.3f" % (i, t[i], E0[i], E1[i], np.nan if i == 0 else order1[i-1]) @@ -222,9 +248,12 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None): passTest = belowTol or correctOrder if passTest: - print "%s PASS! %s\n" % ('='*25, '='*25) + print "%s PASS! %s" % ('='*25, '='*25) + print happiness[np.random.randint(len(happiness))]+'\n' else: print "%s\n%s FAIL! %s\n%s" % ('*'*57, '<'*25, '>'*25, '*'*57) + print sadness[np.random.randint(len(sadness))]+'\n' + if plotIt: plt.figure() @@ -238,3 +267,19 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None): plt.show() return passTest + + +if __name__ == '__main__': + + def simplePass(x): + return np.sin(x), sdiag(np.cos(x)) + + def simpleFunction(x): + return np.sin(x), lambda xi: sdiag(np.cos(x))*xi + + def simpleFail(x): + return np.sin(x), -sdiag(np.cos(x)) + + checkDerivative(simplePass, np.random.randn(5), plotIt=False) + checkDerivative(simpleFunction, np.random.randn(5), plotIt=False) + checkDerivative(simpleFail, np.random.randn(5), plotIt=False) diff --git a/SimPEG/tests/api_TestResults.rst b/SimPEG/tests/api_TestResults.rst new file mode 100644 index 00000000..0d63a328 --- /dev/null +++ b/SimPEG/tests/api_TestResults.rst @@ -0,0 +1,355 @@ +.. _api_TestResults: + +.. raw:: html + + + + + +
+

Test Report

+

Start Time: 2013-11-05 15:24:44

+

Duration: 0:00:00.007500

+

Status: Pass 22

+ +

This demonstrates the report output by Prasanna.Yelsangikar.

+
+ + + +

Show + Summary + Failed + All +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Test Group/Test caseCountPassFailErrorView
test_basemesh.TestBaseMesh111100Detail
test_meshDimensions
pass
test_mesh_nc
pass
test_mesh_nc_xyz
pass
test_mesh_ne
pass
test_mesh_nf
pass
test_mesh_numbers
pass
test_mesh_r_CC_M
pass
test_mesh_r_E_M
pass
test_mesh_r_E_V
pass
test_mesh_r_F_M
pass
test_mesh_r_F_V
pass
test_basemesh.TestMeshNumbers2D111100Detail
test_meshDimensions
pass
test_mesh_nc
pass
test_mesh_nc_xyz
pass
test_mesh_ne
pass
test_mesh_nf
pass
test_mesh_numbers
pass
test_mesh_r_CC_M
pass
test_mesh_r_E_M
pass
test_mesh_r_E_V
pass
test_mesh_r_F_M
pass
test_mesh_r_F_V
pass
Total222200 
+ diff --git a/SimPEG/tests/runTests.py b/SimPEG/tests/runTests.py index b6662f00..c44f9a1e 100644 --- a/SimPEG/tests/runTests.py +++ b/SimPEG/tests/runTests.py @@ -1,11 +1,50 @@ +import os import glob import unittest +import HTMLTestRunner # This code will run all tests in directory named test_*.py +TITLE = 'Test Results' test_file_strings = glob.glob('test_*.py') module_strings = [str[0:len(str)-3] for str in test_file_strings] suites = [unittest.defaultTestLoader.loadTestsFromName(str) for str in module_strings] testSuite = unittest.TestSuite(suites) -text_runner = unittest.TextTestRunner().run(testSuite) +unittest.TextTestRunner(verbosity=2).run(testSuite) + + +outfile = open("report.html", "w") +runner = HTMLTestRunner.HTMLTestRunner( + stream=outfile, + title=TITLE, + description='SimPEG Test Report was automatically generated.' + ) + +runner.run(testSuite) +outfile.close() + +reader = open("report.html", "r") +writer = open("../../docs/api_TestResults.rst", "w") + +writer.write('.. _api_TestResults:\n\nTest Results\n============\n\n.. raw:: html\n\n') + +go = False +for line in reader: + skip = False + if line == ' + + + +
+

Start Time: 2013-11-05 16:10:07

+

Duration: 0:00:31.280082

+

Status: Pass 99

+ +

SimPEG Test Report was automatically generated.

+
+ + + +

Show + Summary + Failed + All +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Test Group/Test caseCountPassFailErrorView
test_basemesh.TestBaseMesh111100Detail
test_meshDimensions
pass
test_mesh_nc
pass
test_mesh_nc_xyz
pass
test_mesh_ne
pass
test_mesh_nf
pass
test_mesh_numbers
pass
test_mesh_r_CC_M
pass
test_mesh_r_E_M
pass
test_mesh_r_E_V
pass
test_mesh_r_F_M
pass
test_mesh_r_F_V
pass
test_basemesh.TestMeshNumbers2D111100Detail
test_meshDimensions
pass
test_mesh_nc
pass
test_mesh_nc_xyz
pass
test_mesh_ne
pass
test_mesh_nf
pass
test_mesh_numbers
pass
test_mesh_r_CC_M
pass
test_mesh_r_E_M
pass
test_mesh_r_E_V
pass
test_mesh_r_F_M
pass
test_mesh_r_F_V
pass
test_forward_DCproblem.DCProblemTests4400Detail
test_adjoint
pass
test_dataObj
+ + + + pass + + + + +
test_misfit
+ + + + pass + + + + +
test_modelObj
+ + + + pass + + + + +
test_forward_problem.ProblemTests2200Detail
test_modelTransform
+ + + + pass + + + + +
test_regularization
+ + + + pass + + + + +
test_interpolation.TestInterpolation1D2200Detail
test_orderCC
+ + + + pass + + + + +
test_orderN
+ + + + pass + + + + +
test_interpolation.TestInterpolation2d6600Detail
test_orderCC
+ + + + pass + + + + +
test_orderEx
+ + + + pass + + + + +
test_orderEy
+ + + + pass + + + + +
test_orderFx
+ + + + pass + + + + +
test_orderFy
+ + + + pass + + + + +
test_orderN
+ + + + pass + + + + +
test_interpolation.TestInterpolation3D8800Detail
test_orderCC
+ + + + pass + + + + +
test_orderEx
+ + + + pass + + + + +
test_orderEy
+ + + + pass + + + + +
test_orderEz
+ + + + pass + + + + +
test_orderFx
+ + + + pass + + + + +
test_orderFy
+ + + + pass + + + + +
test_orderFz
+ + + + pass + + + + +
test_orderN
+ + + + pass + + + + +
test_LogicallyOrthogonalMesh.BasicLOMTests8800Detail
test_area_3D
pass
test_edge_2D
pass
test_edge_3D
pass
test_grid
pass
test_normals
pass
test_tangents
pass
test_vol_2D
pass
test_vol_3D
pass
test_massMatrices.TestInnerProducts: Integrate an function over a unit cube domain using edgeInnerProducts and faceInnerProducts.6600Detail
test_order1_edges
+ + + + pass + + + + +
test_order1_faces
+ + + + pass + + + + +
test_order3_edges
+ + + + pass + + + + +
test_order3_faces
+ + + + pass + + + + +
test_order6_edges
+ + + + pass + + + + +
test_order6_faces
+ + + + pass + + + + +
test_massMatrices.TestInnerProducts2D: Integrate an function over a unit cube domain using edgeInnerProducts and faceInnerProducts.6600Detail
test_order1_edges
+ + + + pass + + + + +
test_order1_faces
+ + + + pass + + + + +
test_order2_faces
+ + + + pass + + + + +
test_order3_edges
+ + + + pass + + + + +
test_order3_faces
+ + + + pass + + + + +
test_order6_edges
+ + + + pass + + + + +
test_operators.TestCurl1100Detail
test_order
+ + + + pass + + + + +
test_operators.TestFaceDiv1100Detail
test_order
+ + + + pass + + + + +
test_operators.TestFaceDiv2D1100Detail
test_order
+ + + + pass + + + + +
test_operators.TestNodalGrad1100Detail
test_order
+ + + + pass + + + + +
test_operators.TestNodalGrad2D1100Detail
test_order
+ + + + pass + + + + +
test_Solver.TestSolver101000Detail
test_directDiagonal_1
pass
test_directDiagonal_M
pass
test_directFactored_1
pass
test_directFactored_M
pass
test_directLower_1
pass
test_directLower_M
pass
test_directSpsolve_1
pass
test_directSpsolve_M
pass
test_directUpper_1
pass
test_directUpper_M
pass
test_tensorMesh.BasicTensorMeshTests7700Detail
test_area_3D
pass
test_edge_2D
pass
test_edge_3D
pass
test_vectorCC_2D
pass
test_vectorN_2D
pass
test_vol_2D
pass
test_vol_3D
pass
test_tensorMesh.TestPoissonEqn2200Detail
test_orderBackward
+ + + + pass + + + + +
test_orderForward
+ + + + pass + + + + +
test_utils.TestCheckDerivative3300Detail
test_simpleFail
+ + + + pass + + + + +
test_simpleFunction
+ + + + pass + + + + +
test_simplePass
+ + + + pass + + + + +
test_utils.TestSequenceFunctions8800Detail
test_indexCube_2D
pass
test_indexCube_3D
pass
test_invXXXBlockDiagonal
pass
test_mkvc1
pass
test_mkvc2
pass
test_mkvc3
pass
test_ndgrid_2D
pass
test_ndgrid_3D
pass
Total999900 
+ diff --git a/docs/api_Utils.rst b/docs/api_Utils.rst index e952010b..91d87d29 100644 --- a/docs/api_Utils.rst +++ b/docs/api_Utils.rst @@ -19,3 +19,7 @@ Utilities :members: :undoc-members: +.. automodule:: SimPEG.utils.interputils + :members: + :undoc-members: + diff --git a/docs/examples/mesh/plot_LogicallyOrthogonalMesh.py b/docs/examples/mesh/plot_LogicallyOrthogonalMesh.py index 55350946..bb49ae9a 100644 --- a/docs/examples/mesh/plot_LogicallyOrthogonalMesh.py +++ b/docs/examples/mesh/plot_LogicallyOrthogonalMesh.py @@ -1,4 +1,5 @@ -from SimPEG import LogicallyOrthogonalMesh, utils +from SimPEG.mesh import LogicallyOrthogonalMesh +from SimPEG import utils import matplotlib.pyplot as plt X, Y = utils.exampleLomGird([3,3],'rotate') M = LogicallyOrthogonalMesh([X, Y]) diff --git a/docs/index.rst b/docs/index.rst index 692363a2..12db6f97 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,8 +1,3 @@ -.. SimPEG documentation master file, created by - sphinx-quickstart on Fri Aug 30 18:42:44 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - SimPEG ====== @@ -24,10 +19,8 @@ Meshing & Operators api_BaseMesh api_TensorMesh - api_TensorView api_LogicallyOrthogonalMesh api_Cyl1DMesh - api_LOMView api_DiffOperators api_InnerProducts @@ -54,6 +47,7 @@ Testing SimPEG :maxdepth: 2 api_Tests + api_TestResults Utility Codes @@ -62,6 +56,7 @@ Utility Codes .. toctree:: :maxdepth: 2 + api_Solver api_Utils diff --git a/docs/requirements.txt b/docs/requirements.txt index 24ce15ab..dcecbe99 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,2 @@ numpy +pypubsub