Beispiel #1
0
 def __init__(self, atoms, constraints):
     self.atoms = atoms
     natoms = len(self.atoms)
     nconst = sum([len(c) for c in constraints])
     b = N.zeros((nconst, natoms), N.Float)
     c = N.zeros((nconst,), N.Float)
     i = 0
     for cons in constraints:
         cons.setCoefficients(self.atoms, b, c, i)
         i = i + len(cons)
     u, s, vt = LA.singular_value_decomposition(b)
     self.rank = 0
     for i in range(min(natoms, nconst)):
         if s[i] > 0.:
             self.rank = self.rank + 1
     self.b = b
     self.bi = LA.generalized_inverse(b)
     self.p = N.identity(natoms)-N.dot(self.bi, self.b)
     self.c = c
     self.bi_c = N.dot(self.bi, c)
     c_test = N.dot(self.b, self.bi_c)
     if N.add.reduce((c_test-c)**2)/nconst > 1.e-12:
         Utility.warning("The charge constraints are inconsistent."
                         " They will be applied as a least-squares"
                         " condition.")
Beispiel #2
0
 def __init__(self, atoms, constraints):
     self.atoms = atoms
     natoms = len(self.atoms)
     nconst = sum([len(c) for c in constraints])
     b = N.zeros((nconst, natoms), N.Float)
     c = N.zeros((nconst, ), N.Float)
     i = 0
     for cons in constraints:
         cons.setCoefficients(self.atoms, b, c, i)
         i = i + len(cons)
     u, s, vt = LA.singular_value_decomposition(b)
     self.rank = 0
     for i in range(min(natoms, nconst)):
         if s[i] > 0.:
             self.rank = self.rank + 1
     self.b = b
     self.bi = LA.generalized_inverse(b)
     self.p = N.identity(natoms) - N.dot(self.bi, self.b)
     self.c = c
     self.bi_c = N.dot(self.bi, c)
     c_test = N.dot(self.b, self.bi_c)
     if N.add.reduce((c_test - c)**2) / nconst > 1.e-12:
         Utility.warning("The charge constraints are inconsistent."
                         " They will be applied as a least-squares"
                         " condition.")
Beispiel #3
0
def symmetricTensorBasis(cell, space_group):
    from CDTK.Crystal import UnitCell
    subspace = 1. * N.equal.outer(N.arange(6), N.arange(6))
    for tr in cartesianCoordinateSymmetryTransformations(cell, space_group):
        rot = symmetricTensorRotationMatrix(tr.tensor.array)
        ev, axes = LA.eigenvectors(rot)
        new_subspace = []
        for i in range(6):
            if abs(ev[i] - 1.) < 1.e-12:
                p = N.dot(N.transpose(subspace), N.dot(subspace, axes[i].real))
                new_subspace.append(p)
        m, s, subspace = LA.singular_value_decomposition(N.array(new_subspace))
        nb = N.sum(s / s[0] > 1.e-12)
        subspace = subspace[:nb]
    return [SymmetricTensor(a) for a in subspace]
Beispiel #4
0
def superpositionFit(confs):
    """
    :param confs: the weight, reference position, and alternate
                  position for each atom
    :type confs: sequence of (float, Vector, Vector)
    :returns: the quaternion representing the rotation,
              the center of mass in the reference configuration,
              the center of mass in the alternate configuraton,
              and the RMS distance after the optimal superposition
    """
    w_sum = 0.
    wr_sum = N.zeros((3,), N.Float)
    for w, r_ref, r in confs:
        w_sum += w
        wr_sum += w*r_ref.array
    ref_cms = wr_sum/w_sum
    pos = N.zeros((3,), N.Float)
    possq = 0.
    cross = N.zeros((3, 3), N.Float)
    for w, r_ref, r in confs:
        w = w/w_sum
        r_ref = r_ref.array-ref_cms
        r = r.array
        pos = pos + w*r
        possq = possq + w*N.add.reduce(r*r) \
                      + w*N.add.reduce(r_ref*r_ref)
        cross = cross + w*r[:, N.NewAxis]*r_ref[N.NewAxis, :]
    k = N.zeros((4, 4), N.Float)
    k[0, 0] = -cross[0, 0]-cross[1, 1]-cross[2, 2]
    k[0, 1] = cross[1, 2]-cross[2, 1]
    k[0, 2] = cross[2, 0]-cross[0, 2]
    k[0, 3] = cross[0, 1]-cross[1, 0]
    k[1, 1] = -cross[0, 0]+cross[1, 1]+cross[2, 2]
    k[1, 2] = -cross[0, 1]-cross[1, 0]
    k[1, 3] = -cross[0, 2]-cross[2, 0]
    k[2, 2] = cross[0, 0]-cross[1, 1]+cross[2, 2]
    k[2, 3] = -cross[1, 2]-cross[2, 1]
    k[3, 3] = cross[0, 0]+cross[1, 1]-cross[2, 2]
    for i in range(1, 4):
        for j in range(i):
            k[i, j] = k[j, i]
    k = 2.*k
    for i in range(4):
        k[i, i] = k[i, i] + possq - N.add.reduce(pos*pos)
    from Scientific import LA
    e, v = LA.eigenvectors(k)
    i = N.argmin(e)
    v = v[i]
    if v[0] < 0: v = -v
    if e[i] <= 0.:
        rms = 0.
    else:
        rms = N.sqrt(e[i])
    from Scientific.Geometry import Quaternion
    return Quaternion.Quaternion(v), Vector(ref_cms), \
           Vector(pos), rms
Beispiel #5
0
def superpositionFit(confs):
    """
    :param confs: the weight, reference position, and alternate
                  position for each atom
    :type confs: sequence of (float, Vector, Vector)
    :returns: the quaternion representing the rotation,
              the center of mass in the reference configuration,
              the center of mass in the alternate configuraton,
              and the RMS distance after the optimal superposition
    """
    w_sum = 0.
    wr_sum = N.zeros((3, ), N.Float)
    for w, r_ref, r in confs:
        w_sum += w
        wr_sum += w * r_ref.array
    ref_cms = wr_sum / w_sum
    pos = N.zeros((3, ), N.Float)
    possq = 0.
    cross = N.zeros((3, 3), N.Float)
    for w, r_ref, r in confs:
        w = w / w_sum
        r_ref = r_ref.array - ref_cms
        r = r.array
        pos = pos + w * r
        possq = possq + w*N.add.reduce(r*r) \
                      + w*N.add.reduce(r_ref*r_ref)
        cross = cross + w * r[:, N.NewAxis] * r_ref[N.NewAxis, :]
    k = N.zeros((4, 4), N.Float)
    k[0, 0] = -cross[0, 0] - cross[1, 1] - cross[2, 2]
    k[0, 1] = cross[1, 2] - cross[2, 1]
    k[0, 2] = cross[2, 0] - cross[0, 2]
    k[0, 3] = cross[0, 1] - cross[1, 0]
    k[1, 1] = -cross[0, 0] + cross[1, 1] + cross[2, 2]
    k[1, 2] = -cross[0, 1] - cross[1, 0]
    k[1, 3] = -cross[0, 2] - cross[2, 0]
    k[2, 2] = cross[0, 0] - cross[1, 1] + cross[2, 2]
    k[2, 3] = -cross[1, 2] - cross[2, 1]
    k[3, 3] = cross[0, 0] + cross[1, 1] - cross[2, 2]
    for i in range(1, 4):
        for j in range(i):
            k[i, j] = k[j, i]
    k = 2. * k
    for i in range(4):
        k[i, i] = k[i, i] + possq - N.add.reduce(pos * pos)
    from Scientific import LA
    e, v = LA.eigenvectors(k)
    i = N.argmin(e)
    v = v[i]
    if v[0] < 0: v = -v
    if e[i] <= 0.:
        rms = 0.
    else:
        rms = N.sqrt(e[i])
    from Scientific.Geometry import Quaternion
    return Quaternion.Quaternion(v), Vector(ref_cms), \
           Vector(pos), rms
Beispiel #6
0
 def findTransformationAsQuaternion(self, conf1, conf2=None):
     universe = self.universe()
     if conf1.universe != universe:
         raise ValueError("conformation is for a different universe")
     if conf2 is None:
         conf1, conf2 = conf2, conf1
     else:
         if conf2.universe != universe:
             raise ValueError("conformation is for a different universe")
     ref = conf1
     conf = conf2
     weights = universe.masses()
     weights = weights / self.mass()
     ref_cms = self.centerOfMass(ref).array
     pos = Numeric.zeros((3, ), Numeric.Float)
     possq = 0.
     cross = Numeric.zeros((3, 3), Numeric.Float)
     for a in self.atomList():
         r = a.position(conf).array
         r_ref = a.position(ref).array - ref_cms
         w = weights[a]
         pos = pos + w * r
         possq = possq + w*Numeric.add.reduce(r*r) \
                       + w*Numeric.add.reduce(r_ref*r_ref)
         cross = cross + w * r[:,
                               Numeric.NewAxis] * r_ref[Numeric.NewAxis, :]
     k = Numeric.zeros((4, 4), Numeric.Float)
     k[0, 0] = -cross[0, 0] - cross[1, 1] - cross[2, 2]
     k[0, 1] = cross[1, 2] - cross[2, 1]
     k[0, 2] = cross[2, 0] - cross[0, 2]
     k[0, 3] = cross[0, 1] - cross[1, 0]
     k[1, 1] = -cross[0, 0] + cross[1, 1] + cross[2, 2]
     k[1, 2] = -cross[0, 1] - cross[1, 0]
     k[1, 3] = -cross[0, 2] - cross[2, 0]
     k[2, 2] = cross[0, 0] - cross[1, 1] + cross[2, 2]
     k[2, 3] = -cross[1, 2] - cross[2, 1]
     k[3, 3] = cross[0, 0] + cross[1, 1] - cross[2, 2]
     for i in range(1, 4):
         for j in range(i):
             k[i, j] = k[j, i]
     k = 2. * k
     for i in range(4):
         k[i, i] = k[i, i] + possq - Numeric.add.reduce(pos * pos)
     from Scientific import LA
     e, v = LA.eigenvectors(k)
     i = Numeric.argmin(e)
     v = v[i]
     if v[0] < 0: v = -v
     if e[i] <= 0.:
         rms = 0.
     else:
         rms = Numeric.sqrt(e[i])
     return Quaternion.Quaternion(v), Vector(ref_cms), \
            Vector(pos), rms
Beispiel #7
0
def rigidMovement(atoms, vector):
    a = N.zeros((len(atoms), 3, 2, 3), N.Float)
    b = N.zeros((len(atoms), 3), N.Float)
    for i in range(len(atoms)):
        a[i, :, 0, :] = delta.array
        a[i, :, 1, :] = (epsilon * atoms[i].position()).array
        b[i] = vector[atoms[i]].array
    a.shape = (3 * len(atoms), 6)
    b.shape = (3 * len(atoms), )
    vo = N.dot(LA.generalized_inverse(a), b)
    return Vector(vo[:3]), Vector(vo[3:])
 def findTransformationAsQuaternion(self, conf1, conf2 = None):
     universe = self.universe()
     if conf1.universe != universe:
         raise ValueError("conformation is for a different universe")
     if conf2 is None:
         conf1, conf2 = conf2, conf1
     else:
         if conf2.universe != universe:
             raise ValueError("conformation is for a different universe")
     ref = conf1
     conf = conf2
     weights = universe.masses()
     weights = weights/self.mass()
     ref_cms = self.centerOfMass(ref).array
     pos = Numeric.zeros((3,), Numeric.Float)
     possq = 0.
     cross = Numeric.zeros((3, 3), Numeric.Float)
     for a in self.atomList():
         r = a.position(conf).array
         r_ref = a.position(ref).array-ref_cms
         w = weights[a]
         pos = pos + w*r
         possq = possq + w*Numeric.add.reduce(r*r) \
                       + w*Numeric.add.reduce(r_ref*r_ref)
         cross = cross + w*r[:, Numeric.NewAxis]*r_ref[Numeric.NewAxis, :]
     k = Numeric.zeros((4, 4), Numeric.Float)
     k[0, 0] = -cross[0, 0]-cross[1, 1]-cross[2, 2]
     k[0, 1] = cross[1, 2]-cross[2, 1]
     k[0, 2] = cross[2, 0]-cross[0, 2]
     k[0, 3] = cross[0, 1]-cross[1, 0]
     k[1, 1] = -cross[0, 0]+cross[1, 1]+cross[2, 2]
     k[1, 2] = -cross[0, 1]-cross[1, 0]
     k[1, 3] = -cross[0, 2]-cross[2, 0]
     k[2, 2] = cross[0, 0]-cross[1, 1]+cross[2, 2]
     k[2, 3] = -cross[1, 2]-cross[2, 1]
     k[3, 3] = cross[0, 0]+cross[1, 1]-cross[2, 2]
     for i in range(1, 4):
         for j in range(i):
             k[i, j] = k[j, i]
     k = 2.*k
     for i in range(4):
         k[i, i] = k[i, i] + possq - Numeric.add.reduce(pos*pos)
     from Scientific import LA
     e, v = LA.eigenvectors(k)
     i = Numeric.argmin(e)
     v = v[i]
     if v[0] < 0: v = -v
     if e[i] <= 0.:
         rms = 0.
     else:
         rms = Numeric.sqrt(e[i])
     return Quaternion.Quaternion(v), Vector(ref_cms), \
            Vector(pos), rms
Beispiel #9
0
    def __init__(self, *parameters):
        """
        :param parameters: one of

            1) three lattice vectors or

            2) six numbers: the lengths of the three lattice
               vectors (a, b, c) followed by the three
               angles (alpha, beta, gamma).

        """
        if len(parameters) == 6:
            self.a, self.b, self.c, self.alpha, self.beta, self.gamma = \
                    parameters
            e1 = Vector(self.a, 0, 0)
            e2 = self.b * Vector(N.cos(self.gamma), N.sin(self.gamma), 0.)
            e3_x = N.cos(self.beta)
            e3_y = (N.cos(self.alpha)-N.cos(self.beta)*N.cos(self.gamma)) \
                   / N.sin(self.gamma)
            e3_z = N.sqrt(1. - e3_x**2 - e3_y**2)
            e3 = self.c * Vector(e3_x, e3_y, e3_z)
            self.basis = (e1, e2, e3)
        elif len(parameters) == 3:
            assert isVector(parameters[0])
            assert isVector(parameters[1])
            assert isVector(parameters[2])
            self.basis = list(parameters)
            e1, e2, e3 = self.basis
            self.a = e1.length()
            self.b = e2.length()
            self.c = e3.length()
            self.alpha = N.arccos(e2 * e3 / (self.b * self.c))
            self.beta = N.arccos(e1 * e3 / (self.a * self.c))
            self.gamma = N.arccos(e1 * e2 / (self.a * self.b))
        else:
            raise ValueError("Parameter list incorrect")

        r = LA.inverse(N.transpose([e1, e2, e3]))
        self.reciprocal_basis = [Vector(r[0]), Vector(r[1]), Vector(r[2])]
Beispiel #10
0
    def __init__(self, system, points, constraints = None):
        """
        :param system: any chemical object (usually a molecule)
        :param points: a list of point/potential pairs (a vector for the
                       evaluation point, a number for the potential),
                       or a dictionary whose keys are Configuration objects
                       and whose values are lists of point/potential pairs.
                       The latter case permits combined fits for several
                       conformations of the system.
        :param constraints: an optional list of constraint objects
                            (:class:`~MMTK.ChargeFit.TotalChargeConstraint`
                            and/or
                            :class:`~MMTK.ChargeFit.EqualityConstraint` objects). 
                            If the constraints are inconsistent, a warning is
                            printed and the result will satisfy the
                            constraints only in a least-squares sense.
        """
        self.atoms = system.atomList()
        if type(points) != type({}):
            points = {None: points}
        if constraints is not None:
            constraints = ChargeConstraintSet(self.atoms, constraints)
        npoints = sum([len(v) for v in points.values()])
        natoms = len(self.atoms)
        if npoints < natoms:
            raise ValueError("Not enough data points for fit")

        m = N.zeros((npoints, natoms), N.Float)
        phi = N.zeros((npoints,), N.Float)
        i = 0
        for conf, pointlist in points.items():
            for r, p in pointlist:
                for j in range(natoms):
                    m[i, j] = 1./(r-self.atoms[j].position(conf)).length()
                phi[i] = p
                i = i + 1
        m = m*Units.electrostatic_energy

        m_test = m
        phi_test = phi

        if constraints is not None:
            phi -= N.dot(m, constraints.bi_c)
            m = N.dot(m, constraints.p)
            c_rank = constraints.rank
        else:
            c_rank = 0

        u, s, vt = LA.singular_value_decomposition(m)
        s_test = s[:len(s)-c_rank]
        cutoff = 1.e-10*N.maximum.reduce(s_test)
        nonzero = N.repeat(s_test, N.not_equal(s_test, 0.))
        self.rank = len(nonzero)
        self.condition = N.maximum.reduce(nonzero) / \
                         N.minimum.reduce(nonzero)
        self.effective_rank = N.add.reduce(N.greater(s, cutoff))
        if self.effective_rank < self.rank:
            self.effective_condition = N.maximum.reduce(nonzero) / cutoff
        else:
            self.effective_condition = self.condition
        if self.effective_rank < natoms-c_rank:
            Utility.warning('Not all charges are uniquely determined' +
                            ' by the available data')

        for i in range(natoms):
            if s[i] > cutoff:
                s[i] = 1./s[i]
            else:
                s[i] = 0.
        q = N.dot(N.transpose(vt),
                  s*N.dot(N.transpose(u)[:natoms, :], phi))
        if constraints is not None:
            q = constraints.bi_c + N.dot(constraints.p, q)

        deviation = N.dot(m_test, q)-phi_test
        self.rms_error = N.sqrt(N.dot(deviation, deviation))
        deviation = N.fabs(deviation/phi_test)
        self.relative_rms_error = N.sqrt(N.dot(deviation, deviation))

        self.charges = {}
        for i in range(natoms):
            self.charges[self.atoms[i]] = q[i]
Beispiel #11
0
    def __init__(self, system, points, constraints=None):
        """
        :param system: any chemical object (usually a molecule)
        :param points: a list of point/potential pairs (a vector for the
                       evaluation point, a number for the potential),
                       or a dictionary whose keys are Configuration objects
                       and whose values are lists of point/potential pairs.
                       The latter case permits combined fits for several
                       conformations of the system.
        :param constraints: an optional list of constraint objects
                            (:class:`~MMTK.ChargeFit.TotalChargeConstraint`
                            and/or
                            :class:`~MMTK.ChargeFit.EqualityConstraint` objects). 
                            If the constraints are inconsistent, a warning is
                            printed and the result will satisfy the
                            constraints only in a least-squares sense.
        """
        self.atoms = system.atomList()
        if type(points) != type({}):
            points = {None: points}
        if constraints is not None:
            constraints = ChargeConstraintSet(self.atoms, constraints)
        npoints = sum([len(v) for v in points.values()])
        natoms = len(self.atoms)
        if npoints < natoms:
            raise ValueError("Not enough data points for fit")

        m = N.zeros((npoints, natoms), N.Float)
        phi = N.zeros((npoints, ), N.Float)
        i = 0
        for conf, pointlist in points.items():
            for r, p in pointlist:
                for j in range(natoms):
                    m[i, j] = 1. / (r - self.atoms[j].position(conf)).length()
                phi[i] = p
                i = i + 1
        m = m * Units.electrostatic_energy

        m_test = m
        phi_test = phi

        if constraints is not None:
            phi -= N.dot(m, constraints.bi_c)
            m = N.dot(m, constraints.p)
            c_rank = constraints.rank
        else:
            c_rank = 0

        u, s, vt = LA.singular_value_decomposition(m)
        s_test = s[:len(s) - c_rank]
        cutoff = 1.e-10 * N.maximum.reduce(s_test)
        nonzero = N.repeat(s_test, N.not_equal(s_test, 0.))
        self.rank = len(nonzero)
        self.condition = N.maximum.reduce(nonzero) / \
                         N.minimum.reduce(nonzero)
        self.effective_rank = N.add.reduce(N.greater(s, cutoff))
        if self.effective_rank < self.rank:
            self.effective_condition = N.maximum.reduce(nonzero) / cutoff
        else:
            self.effective_condition = self.condition
        if self.effective_rank < natoms - c_rank:
            Utility.warning('Not all charges are uniquely determined' +
                            ' by the available data')

        for i in range(natoms):
            if s[i] > cutoff:
                s[i] = 1. / s[i]
            else:
                s[i] = 0.
        q = N.dot(N.transpose(vt), s * N.dot(N.transpose(u)[:natoms, :], phi))
        if constraints is not None:
            q = constraints.bi_c + N.dot(constraints.p, q)

        deviation = N.dot(m_test, q) - phi_test
        self.rms_error = N.sqrt(N.dot(deviation, deviation))
        deviation = N.fabs(deviation / phi_test)
        self.relative_rms_error = N.sqrt(N.dot(deviation, deviation))

        self.charges = {}
        for i in range(natoms):
            self.charges[self.atoms[i]] = q[i]
def leastSquaresFit(model,
                    parameters,
                    data,
                    max_iterations=None,
                    stopping_limit=0.005,
                    validator=None):
    """General non-linear least-squares fit using the
    X{Levenberg-Marquardt} algorithm and X{automatic differentiation}.

    @param model: the function to be fitted. It will be called
        with two parameters: the first is a tuple containing all fit
        parameters, and the second is the first element of a data point (see
        below). The return value must be a number.  Since automatic
        differentiation is used to obtain the derivatives with respect to the
        parameters, the function may only use the mathematical functions known
        to the module FirstDerivatives.
    @type model: callable

    @param parameters: a tuple of initial values for the
        fit parameters
    @type parameters: C{tuple} of numbers

    @param data: a list of data points to which the model
        is to be fitted. Each data point is a tuple of length two or
        three. Its first element specifies the independent variables
        of the model. It is passed to the model function as its first
        parameter, but not used in any other way. The second element
        of each data point tuple is the number that the return value
        of the model function is supposed to match as well as possible.
        The third element (which defaults to 1.) is the statistical
        variance of the data point, i.e. the inverse of its statistical
        weight in the fitting procedure.
    @type data: C{list}

    @returns: a list containing the optimal parameter values
        and the chi-squared value describing the quality of the fit
    @rtype: C{(list, float)}
    """
    n_param = len(parameters)
    p = ()
    i = 0
    for param in parameters:
        p = p + (DerivVar(param, i), )
        i = i + 1
    id = N.identity(n_param)
    l = 0.001
    chi_sq, alpha = _chiSquare(model, p, data)
    niter = 0
    while 1:
        delta = LA.solve_linear_equations(alpha + l * N.diagonal(alpha) * id,
                                          -0.5 * N.array(chi_sq[1]))
        next_p = map(lambda a, b: a + b, p, delta)
        if validator is not None:
            while not validator(*next_p):
                delta *= 0.8
                next_p = map(lambda a, b: a + b, p, delta)
        next_chi_sq, next_alpha = _chiSquare(model, next_p, data)
        if next_chi_sq > chi_sq:
            l = 10. * l
        else:
            l = 0.1 * l
            if chi_sq[0] - next_chi_sq[0] < stopping_limit: break
            p = next_p
            chi_sq = next_chi_sq
            alpha = next_alpha
        niter = niter + 1
        if max_iterations is not None and niter == max_iterations:
            raise IterationCountExceededError
    return [p[0] for p in next_p], next_chi_sq[0]
from MMTK import *
from MMTK.Proteins import Protein
from Scientific import N, LA

# Import the graphics module. Substitute any other graphics
# module name to make the example use that module.
from Scientific.Visualization import VRML2
module = VRML2

# Create the protein and find its center of mass and tensor of inertia.
protein = Protein('insulin')
center, inertia = protein.centerAndMomentOfInertia()

# Diagonalize the inertia tensor and scale the axes to a suitable length.
mass = protein.mass()
diagonal, directions = LA.eigenvectors(inertia.array)
diagonal = N.sqrt(diagonal / mass)

# Generate the backbone graphics objects.
graphics = protein.graphicsObjects(graphics_module=module,
                                   model='backbone',
                                   color='red')

# Add an atomic wireframe representation of all valine sidechains
valines = protein.residuesOfType('val')
sidechains = valines.map(lambda r: r.sidechain)
graphics = graphics + sidechains.graphicsObjects(
    graphics_module=module, model='wireframe', color='blue')

# Add three arrows corresponding to the principal axes of inertia.
for length, axis in map(None, diagonal, directions):
def leastSquaresFit(model, parameters, data, max_iterations=None,
                    stopping_limit = 0.005):
    """General non-linear least-squares fit using the
    X{Levenberg-Marquardt} algorithm and X{automatic differentiation}.

    @param model: the function to be fitted. It will be called
        with two parameters: the first is a tuple containing all fit
        parameters, and the second is the first element of a data point (see
        below). The return value must be a number.  Since automatic
        differentiation is used to obtain the derivatives with respect to the
        parameters, the function may only use the mathematical functions known
        to the module FirstDerivatives.
    @type param: callable

    @param parameters: a tuple of initial values for the
        fit parameters
    @type parameters: C{tuple} of numbers

    @param data: a list of data points to which the model
        is to be fitted. Each data point is a tuple of length two or
        three. Its first element specifies the independent variables
        of the model. It is passed to the model function as its first
        parameter, but not used in any other way. The second element
        of each data point tuple is the number that the return value
        of the model function is supposed to match as well as possible.
        The third element (which defaults to 1.) is the statistical
        variance of the data point, i.e. the inverse of its statistical
        weight in the fitting procedure.
    @type data: C{list}

    @returns: a list containing the optimal parameter values
        and the chi-squared value describing the quality of the fit
    @rtype: C{(list, float)}
    """
    n_param = len(parameters)
    p = ()
    i = 0
    for param in parameters:
        p = p + (DerivVar(param, i),)
        i = i + 1
    id = N.identity(n_param)
    l = 0.001
    chi_sq, alpha = _chiSquare(model, p, data)
    niter = 0
    while 1:
        delta = LA.solve_linear_equations(alpha+l*N.diagonal(alpha)*id,
                                          -0.5*N.array(chi_sq[1]))
        next_p = map(lambda a,b: a+b, p, delta)
        next_chi_sq, next_alpha = _chiSquare(model, next_p, data)
        if next_chi_sq > chi_sq:
            l = 10.*l
        else:
            l = 0.1*l
            if chi_sq[0] - next_chi_sq[0] < stopping_limit: break
            p = next_p
            chi_sq = next_chi_sq
            alpha = next_alpha
        niter = niter + 1
        if max_iterations is not None and niter == max_iterations:
            raise IterationCountExceededError
    return [p[0] for p in next_p], next_chi_sq[0]
Beispiel #15
0
    def __init__(self, object, points, constraints = None):
        self.atoms = object.atomList()
        if type(points) != type({}):
            points = {None: points}
        if constraints is not None:
            constraints = ChargeConstraintSet(self.atoms, constraints)
        npoints = reduce(operator.add, map(len, points.values()))
        natoms = len(self.atoms)
        if npoints < natoms:
            raise ValueError("Not enough data points for fit")

        m = Numeric.zeros((npoints, natoms), Numeric.Float)
        phi = Numeric.zeros((npoints,), Numeric.Float)
        i = 0
        for conf, pointlist in points.items():
            for r, p in pointlist:
                for j in range(natoms):
                    m[i, j] = 1./(r-self.atoms[j].position(conf)).length()
                phi[i] = p
                i = i + 1
        m = m*Units.electrostatic_energy

        m_test = m
        phi_test = phi

        if constraints is not None:
            phi = phi-Numeric.dot(m, constraints.bi_c)
            m = Numeric.dot(m, constraints.p)
            c_rank = constraints.rank
        else:
            c_rank = 0

        u, s, vt = LinearAlgebra.singular_value_decomposition(m)
        s_test = s[:len(s)-c_rank]
        cutoff = 1.e-10*Numeric.maximum.reduce(s_test)
        nonzero = Numeric.repeat(s_test, Numeric.not_equal(s_test, 0.))
        self.rank = len(nonzero)
        self.condition = Numeric.maximum.reduce(nonzero) / \
                         Numeric.minimum.reduce(nonzero)
        self.effective_rank = Numeric.add.reduce(Numeric.greater(s, cutoff))
        if self.effective_rank < self.rank:
            self.effective_condition = Numeric.maximum.reduce(nonzero) / cutoff
        else:
            self.effective_condition = self.condition
        if self.effective_rank < natoms-c_rank:
            Utility.warning('Not all charges are uniquely determined' +
                            ' by the available data')

        for i in range(natoms):
            if s[i] > cutoff:
                s[i] = 1./s[i]
            else:
                s[i] = 0.
        q = Numeric.dot(Numeric.transpose(vt),
                        s*Numeric.dot(Numeric.transpose(u)[:natoms, :], phi))
        if constraints is not None:
            q = constraints.bi_c + Numeric.dot(constraints.p, q)

        deviation = Numeric.dot(m_test, q)-phi_test
        self.rms_error = Numeric.sqrt(Numeric.dot(deviation, deviation))
        deviation = Numeric.fabs(deviation/phi_test)
        self.relative_rms_error = Numeric.sqrt(Numeric.dot(deviation,
                                                           deviation))

        self.charges = {}
        for i in range(natoms):
            self.charges[self.atoms[i]] = q[i]
Beispiel #16
0
    def __init__(self, object, points, constraints=None):
        self.atoms = object.atomList()
        if type(points) != type({}):
            points = {None: points}
        if constraints is not None:
            constraints = ChargeConstraintSet(self.atoms, constraints)
        npoints = reduce(operator.add, map(len, points.values()))
        natoms = len(self.atoms)
        if npoints < natoms:
            raise ValueError("Not enough data points for fit")

        m = Numeric.zeros((npoints, natoms), Numeric.Float)
        phi = Numeric.zeros((npoints, ), Numeric.Float)
        i = 0
        for conf, pointlist in points.items():
            for r, p in pointlist:
                for j in range(natoms):
                    m[i, j] = 1. / (r - self.atoms[j].position(conf)).length()
                phi[i] = p
                i = i + 1
        m = m * Units.electrostatic_energy

        m_test = m
        phi_test = phi

        if constraints is not None:
            phi = phi - Numeric.dot(m, constraints.bi_c)
            m = Numeric.dot(m, constraints.p)
            c_rank = constraints.rank
        else:
            c_rank = 0

        u, s, vt = LinearAlgebra.singular_value_decomposition(m)
        s_test = s[:len(s) - c_rank]
        cutoff = 1.e-10 * Numeric.maximum.reduce(s_test)
        nonzero = Numeric.repeat(s_test, Numeric.not_equal(s_test, 0.))
        self.rank = len(nonzero)
        self.condition = Numeric.maximum.reduce(nonzero) / \
                         Numeric.minimum.reduce(nonzero)
        self.effective_rank = Numeric.add.reduce(Numeric.greater(s, cutoff))
        if self.effective_rank < self.rank:
            self.effective_condition = Numeric.maximum.reduce(nonzero) / cutoff
        else:
            self.effective_condition = self.condition
        if self.effective_rank < natoms - c_rank:
            Utility.warning('Not all charges are uniquely determined' +
                            ' by the available data')

        for i in range(natoms):
            if s[i] > cutoff:
                s[i] = 1. / s[i]
            else:
                s[i] = 0.
        q = Numeric.dot(Numeric.transpose(vt),
                        s * Numeric.dot(Numeric.transpose(u)[:natoms, :], phi))
        if constraints is not None:
            q = constraints.bi_c + Numeric.dot(constraints.p, q)

        deviation = Numeric.dot(m_test, q) - phi_test
        self.rms_error = Numeric.sqrt(Numeric.dot(deviation, deviation))
        deviation = Numeric.fabs(deviation / phi_test)
        self.relative_rms_error = Numeric.sqrt(
            Numeric.dot(deviation, deviation))

        self.charges = {}
        for i in range(natoms):
            self.charges[self.atoms[i]] = q[i]