source: trunk/GSASIImapvars.py @ 1676

Last change on this file since 1676 was 1676, checked in by toby, 9 years ago

Ignore equivalences that are not in use; start on svn switch implementation for help

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 50.5 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2015-02-28 03:46:37 +0000 (Sat, 28 Feb 2015) $
4# $Author: toby $
5# $Revision: 1676 $
6# $URL: trunk/GSASIImapvars.py $
7# $Id: GSASIImapvars.py 1676 2015-02-28 03:46:37Z toby $
8########### SVN repository information ###################
9"""
10*GSASIImapvars: Parameter constraints*
11======================================
12
13Module to implements algebraic contraints, parameter redefinition
14and parameter simplification contraints.
15
16Parameter redefinition (new vars) is done by creating one or more relationships
17between a set of parameters
18
19::
20
21   Mx1 * Px + My1 * Py +...
22   Mx2 * Px + Mz2 * Pz + ...
23
24where Pj is a parameter name and Mjk is a constant.
25
26Constant constraint Relations can also be supplied in the form of an equation:
27
28::
29
30  nx1 * Px + ny1 * Py +... = C1
31
32where Cn is a constant. These equations define an algebraic
33constrant.
34
35Parameters can also be "fixed" (held), which prevents them from being refined.
36
37All of the above three cases are input using routines
38GroupConstraints and GenerateConstraints. The input consists of a list of
39relationship dictionaries:
40
41.. code-block:: python
42
43    constrDict = [
44         {'0:12:Scale': 2.0, '0:14:Scale': 4.0, '0:13:Scale': 3.0, '0:0:Scale': 0.5},
45         {'2::C(10,6,1)': 1.0, '1::C(10,6,1)': 1.0},
46         {'0::A0': 0.0}]
47    fixedList = ['5.0', None, '0']
48
49Where the dictionary defines the first part of an expression and the corresponding fixedList
50item is either None (for parameter redefinition) or the constant value, for a constant
51constraint equation. A dictionary that contains a single term defines a variable that
52will be fixed (held). The multiplier and the fixedList value in this case are ignored.
53
54Parameters can also be equivalenced or "slaved" to another parameter, such that one
55(independent) parameter is equated to several (now dependent) parameters. In
56algebraic form this is:
57
58::
59
60   P0 = M1 * P1 = M2 * P2 = ...
61
62Thus parameters P0, P1 and P2,... are linearly equivalent. Routine StoreEquivalence is
63used to specify these equivalences.
64
65Parameter redefinition (new vars) describes a new, independent, parameter, which
66is defined in terms of dependent parameters that are defined in the
67Model, while fixed constrained relations effectively reduce the complexity
68of the Model by removing a degree of freedom. It is possible for a parameter to appear
69in both a parameter redefinition expression and a fixed constraint equation, but a
70parameter cannot be used a parameter equivalance cannot be used elsewhere (not fixed,
71constrained or redefined). Likewise a fixed parameter cannot be used elsewhere (not
72equivalanced, constrained or redefined).
73
74Relationships are grouped so that a set of dependent parameters appear
75in only one group (done in routine GroupConstraints.) Note that if a
76group contains relationships/equations that involve N dependent
77parameters, there must exist N-C new parameters, where C is the number
78of contraint equations in the group. Routine GenerateConstraints takes
79the output from GroupConstraints and generates the
80"missing" relationships and saves that information in the module's
81global variables. Each generated parameter is named sequentially using paramPrefix.
82
83A list of parameters that will be varied is specified as input to GenerateConstraints
84(varyList). A fixed parameter will simply be removed from this list preventing that
85parameter from being varied. Note that all parameters in a constraint relationship
86must specified as varied (appear in varyList) or none can be varied. This is
87checked in GenerateConstraints. Likewise, if all parameters in a constraint are
88not referenced in a refinement, the constraint is ignored, but if some parameters
89in a constraint group are not referenced in a refinement, but others are this
90constitutes and error.
91
92* When a new variable is created, the variable is assigned the name associated
93  in the constraint definition or it is assigned a default name of form
94  ``::constr<n>`` (see paramPrefix). The vary setting for variables used in the
95  constraint are ignored.
96  Note that any generated "missing" relations are not varied. Only
97  the input relations can be are varied.
98 
99* If all parameters in a fixed constraint equation are varied, the generated "missing"
100  relations in the group are all varied. This provides the N-C degrees of freedom.
101
102*External Routines*
103-------------------
104
105To define a set of constrained and unconstrained relations, one
106defines a list of dictionary defining constraint parameters and their
107values, a list of fixed values for each constraint and a list of
108parameters to be varied. In addition, one uses
109:func:`StoreEquivalence` to define parameters that are equivalent. One
110can then use :func:`CheckConstraints` to check that the input is
111internally consistent and finally :func:`GroupConstraints` and
112:func:`GenerateConstraints` to generate the internally used
113tables. Routines :func:`Map2Dict` is used to initialize the parameter
114dictionary and :func:`Dict2Map`, :func:`Dict2Deriv`, and
115:func:`ComputeDepESD` are used to apply constraints. Routine
116:func:`VarRemapShow` is used to print out the constraint information,
117as stored by :func:`GenerateConstraints`.
118
119:func:`InitVars`
120  This is optionally used to clear out all defined previously defined constraint information
121 
122:func:`StoreEquivalence`
123  To implement parameter redefinition, one calls StoreEquivalence. This should be called for every set of
124  equivalence relationships. There is no harm in using StoreEquivalence with the same independent variable:
125
126  .. code-block:: python
127
128       StoreEquivalence('x',('y',))
129       StoreEquivalence('x',('z',))
130
131  or equivalently
132
133  .. code-block:: python
134
135       StoreEquivalence('x',('y','z'))
136
137  The latter will run more efficiently. Note that mixing independent and dependent variables is
138  problematic. This is not allowed:
139
140  .. code-block:: python
141
142        StoreEquivalence('x',('y',))
143        StoreEquivalence('y',('z',))
144       
145  Use StoreEquivalence before calling GenerateConstraints or CheckConstraints
146
147:func:`CheckConstraints`
148   To check that input in internally consistent, use CheckConstraints
149
150:func:`Map2Dict`
151   To determine values for the parameters created in this module, one
152   calls Map2Dict. This will not apply contraints.
153
154:func:`Dict2Map`
155   To take values from the new independent parameters and constraints,
156   one calls Dict2Map. This will apply contraints.
157
158:func:`Dict2Deriv`
159   Use Dict2Deriv to determine derivatives on independent parameters
160   from those on dependent ones
161
162:func:`ComputeDepESD`     
163   Use ComputeDepESD to compute uncertainties on dependent variables
164
165:func:`VarRemapShow`
166   To show a summary of the parameter remapping, one calls VarRemapShow
167
168*Global Variables*
169------------------
170
171dependentParmList:
172   contains a list by group of lists of
173   parameters used in the group. Note that parameters listed in
174   dependentParmList should not be refined as they will not affect
175   the model
176
177indParmList:
178     a list of groups of Independent parameters defined in
179     each group. This contains both parameters used in parameter
180     redefinitions as well as names of generated new parameters.
181
182fixedVarList:
183     a list of variables that have been 'fixed'
184     by defining them as equal to a constant (::var: = 0). Note that
185     the constant value is ignored at present. These variables are
186     later removed from varyList which prevents them from being refined.
187     Unlikely to be used externally.
188
189arrayList:
190     a list by group of relationship matrices to relate
191     parameters in dependentParmList to those in indParmList. Unlikely
192     to be used externally.
193
194invarrayList:
195     a list by group of relationship matrices to relate
196     parameters in indParmList to those in dependentParmList. Unlikely
197     to be used externally.
198
199fixedDict:
200     a dictionary containing the fixed values corresponding
201     to parameter equations.  The dict key is an ascii string, but the
202     dict value is a float.  Unlikely to be used externally.
203
204*Routines*
205----------
206
207Note that parameter names in GSAS-II are strings of form ``<ph>:<hst>:<nam>``
208
209"""
210
211import numpy as np
212import GSASIIpath
213GSASIIpath.SetVersionNumber("$Revision: 1676 $")
214# data used for constraints;
215debug = False # turns on printing as constraint input is processed
216# note that constraints are stored listed by contraint groups, where each constraint
217# group contains those parameters that must be handled together
218dependentParmList = [] # contains a list of parameters in each group
219# note that parameters listed in dependentParmList should not be refined
220arrayList = [] # a list of of relationship matrices
221invarrayList = [] # a list of inverse relationship matrices
222indParmList = [] # a list of names for the new parameters
223fixedDict = {} # a dictionary containing the fixed values corresponding to defined parameter equations
224               # key is original ascii string, value is float
225fixedVarList = [] # List of variables that should not be refined
226
227# prefix for parameter names
228paramPrefix = "::constr"
229consNum = 0 # number of the next constraint to be created
230
231def InitVars():
232    '''Initializes all constraint information'''
233    global dependentParmList,arrayList,invarrayList,indParmList,fixedDict,consNum
234    dependentParmList = [] # contains a list of parameters in each group
235    arrayList = [] # a list of of relationship matrices
236    invarrayList = [] # a list of inverse relationship matrices
237    indParmList = [] # a list of names for the new parameters
238    fixedDict = {} # a dictionary containing the fixed values corresponding to defined parameter equations
239    consNum = 0 # number of the next constraint to be created
240    fixedVarList = []
241
242def VarKeys(constr):
243    """Finds the keys in a constraint that represent variables
244    e.g. eliminates any that start with '_'
245
246    :param dict constr: a single constraint entry of form::
247
248        {'var1': mult1, 'var2': mult2,... '_notVar': val,...}
249
250        (see :func:`GroupConstraints`)
251    :returns: a list of keys where any keys beginning with '_' are
252      removed.
253    """
254    return [i for i in constr.keys() if not i.startswith('_')]
255
256
257def GroupConstraints(constrDict):
258    """divide the constraints into groups that share no parameters.
259
260    :param dict constrDict: a list of dicts defining relationships/constraints
261
262    ::
263   
264       constrDict = [{<constr1>}, {<constr2>}, ...]
265
266    where {<constr1>} is {'var1': mult1, 'var2': mult2,... }
267
268    :returns: two lists of lists:
269   
270      * a list of grouped contraints where each constraint grouped containts a list
271        of indices for constraint constrDict entries
272      * a list containing lists of parameter names contained in each group
273     
274      """
275    assignedlist = [] # relationships that have been used
276    groups = [] # contains a list of grouplists
277    ParmList = []
278    for i,consi in enumerate(constrDict):
279        if i in assignedlist: continue # already in a group, skip
280        # starting a new group
281        grouplist = [i,]
282        assignedlist.append(i)
283        groupset = set(VarKeys(consi))
284        changes = True # always loop at least once
285        while(changes): # loop until we can't find anything to add to the current group
286            changes = False # but don't loop again unless we find something
287            for j,consj in enumerate(constrDict):
288                if j in assignedlist: continue # already in a group, skip
289                if len(set(VarKeys(consj)) & groupset) > 0: # true if this needs to be added
290                    changes = True
291                    grouplist.append(j)
292                    assignedlist.append(j)
293                    groupset = groupset | set(VarKeys(consj))
294        group = sorted(grouplist)
295        varlist = sorted(list(groupset))
296        groups.append(group)
297        ParmList.append(varlist)
298    return groups,ParmList
299
300def CheckConstraints(varyList,constrDict,fixedList):
301    '''Takes a list of relationship entries comprising a group of
302    constraints and checks for inconsistencies such as conflicts in
303    parameter/variable definitions and or inconsistently varied parameters.
304
305    :param list varyList: a list of parameters names that will be varied
306
307    :param dict constrDict: a list of dicts defining relationships/constraints
308      (as created in :func:`GSASIIstrIO.ProcessConstraints` and
309      documented in :func:`GroupConstraints`)
310
311    :param list fixedList: a list of values specifying a fixed value for each
312      dict in constrDict. Values are either strings that can be converted to
313      floats or ``None`` if the constraint defines a new parameter rather
314      than a constant.
315
316    :returns: two strings:
317
318      * the first lists conflicts internal to the specified constraints
319      * the second lists conflicts where the varyList specifies some
320        parameters in a constraint, but not all
321       
322      If there are no errors, both strings will be empty
323    '''
324    import re
325    global dependentParmList,arrayList,invarrayList,indParmList,consNum
326    errmsg = ''
327    warnmsg = ''
328    fixVlist = []
329    # process fixed variables (holds)
330    for cdict in constrDict:
331        # N.B. No "_" names in holds
332        if len(cdict) == 1:
333            fixVlist.append(cdict.keys()[0])
334   
335    # process equivalences: make a list of dependent and independent vars
336    #    and check for repeated uses (repetition of a parameter as an
337    #    independent var is OK)
338    indepVarList = []
339    depVarList = []
340    multdepVarList = []
341    for varlist,mapvars,multarr,invmultarr in zip(
342        dependentParmList,indParmList,arrayList,invarrayList):
343        if multarr is None: # an equivalence
344            zeromult = False
345            for mv in mapvars:
346                varied = 0
347                notvaried = ''
348                if mv in varyList:
349                    varied += 1
350                else:
351                    if notvaried: notvaried += ', '
352                    notvaried += mv
353                if mv not in indepVarList: indepVarList.append(mv)
354                for v,m in zip(varlist,invmultarr):
355                    if m == 0: zeromult = True
356                    if v in varyList:
357                        varied += 1
358                    else:
359                        if notvaried: notvaried += ', '
360                        notvaried += v
361                    if v in depVarList:
362                        multdepVarList.append(v)
363                    else:
364                        depVarList.append(v)
365            if varied > 0 and varied != len(varlist)+1:
366                warnmsg += "\nNot all variables refined in equivalence:\n\t"
367                s = ""
368                for v in varlist:
369                    if s != "": s+= " & "
370                    s += str(v)           
371                warnmsg += str(mv) + " => " + s
372                warnmsg += '\nNot refined: ' + notvaried + '\n'
373            if zeromult:
374                errmsg += "\nZero multiplier is invalid in equivalence:\n\t"
375                s = ""
376                for v in varlist:
377                    if s != "": s+= " & "
378                    s += str(v)           
379                errmsg += str(mv) + " => " + s + '\n'
380
381    # check for errors:
382    inboth = set(indepVarList).intersection(set(depVarList))
383    if len(inboth) > 0:
384        errmsg += "\nThe following parameters(s) are used as both dependent and independent variables in Equivalence relations:\n"
385        s = ''
386        for var in sorted(inboth):
387            if s != "": s+= ", "
388            s += str(var)
389        errmsg += '\t'+ s + '\n'
390    if len(multdepVarList) > 0:
391        errmsg += "\nThe following parameters(s) are used in multiple Equivalence relations as dependent variables:\n"
392        s = ''
393        for var in sorted(set(multdepVarList)):
394            if s != "": s+= ", "
395            s += str(var)           
396        errmsg += '\t'+ s + '\n'
397    equivVarList = list(set(indepVarList).union(set(depVarList)))
398    if debug: print 'equivVarList',equivVarList
399    inboth = set(fixVlist).intersection(set(equivVarList))
400    if len(inboth) > 0:
401        errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n"
402        s = ''
403        for var in sorted(inboth):
404            if s != "": s+= ", "
405            s += str(var)
406        errmsg += '\t'+ s + '\n'
407
408    groups,parmlist = GroupConstraints(constrDict)
409    # scan through parameters in each relationship. Are all varied? If only some are
410    # varied, create a warning message.
411    for group,varlist in zip(groups,parmlist):
412        if len(varlist) == 1: continue
413        for rel in group:
414            varied = 0
415            notvaried = ''
416            for var in constrDict[rel]:
417                if var.startswith('_'): continue
418                if not re.match('[0-9]*:[0-9\*]*:',var):
419                    warnmsg += "\nVariable "+str(var)+" does not begin with a ':'"
420                if var in varyList:
421                    varied += 1
422                else:
423                    if notvaried: notvaried += ', '
424                    notvaried += var
425                if var in fixVlist:
426                    errmsg += '\nParameter '+var+" is Fixed and used in a constraint:\n\t"
427                    errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])+"\n"
428            if varied > 0 and varied != len(VarKeys(constrDict[rel])):
429                warnmsg += "\nNot all variables refined in constraint:\n\t"
430                warnmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
431                warnmsg += '\nNot refined: ' + notvaried + '\n'
432    if errmsg or warnmsg:
433        return errmsg,warnmsg
434
435    # now look for process each group and create the relations that are needed to form
436    # non-singular square matrix
437    for group,varlist in zip(groups,parmlist):
438        if len(varlist) == 1: continue # a constraint group with a single variable can be ignored
439        if len(varlist) < len(group): # too many relationships -- no can do
440            errmsg += "\nOver-constrained input. "
441            errmsg += "There are more constraints " + str(len(group))
442            errmsg += "\n\tthan variables " + str(len(varlist)) + "\n"
443            for rel in group:
444                errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
445                errmsg += "\n"
446                continue
447        try:
448            multarr = _FillArray(group,constrDict,varlist)
449            _RowEchelon(len(group),multarr,varlist)
450        except:
451            errmsg += "\nSingular input. "
452            errmsg += "There are internal inconsistencies in these constraints\n"
453            for rel in group:
454                errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
455                errmsg += "\n"
456            continue
457        try:
458            multarr = _FillArray(group,constrDict,varlist,FillDiagonals=True)
459            GramSchmidtOrtho(multarr,len(group))
460        except:
461            errmsg += "\nUnexpected singularity with constraints (in Gram-Schmidt)\n"
462            for rel in group:
463                errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
464                errmsg += "\n"
465            continue
466        mapvar = []
467        group = group[:]
468        # scan through all generated and input variables
469        # Check again for inconsistent variable use
470        # for new variables -- where varied and unvaried parameters get grouped
471        # together. I don't think this can happen when not flagged before, but
472        # it does not hurt to check again.
473        for i in range(len(varlist)):
474            varied = 0
475            notvaried = ''
476            if len(group) > 0:
477                rel = group.pop(0)
478                fixedval = fixedList[rel]
479                for var in VarKeys(constrDict[rel]):
480                    if var in varyList:
481                        varied += 1
482                    else:
483                        if notvaried: notvaried += ', '
484                        notvaried += var
485            else:
486                fixedval = None
487            if fixedval is None:
488                varname = paramPrefix + str(consNum) # assign a name to a variable
489                mapvar.append(varname)
490                consNum += 1
491            else:
492                mapvar.append(fixedval)
493            if varied > 0 and notvaried != '':
494                warnmsg += "\nNot all variables refined in generated constraint"
495                warnmsg += '\nPlease report this unexpected error\n'
496                for rel in group:
497                    warnmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
498                    warnmsg += "\n"
499                warnmsg += '\n\tNot refined: ' + notvaried + '\n'
500        try:
501            np.linalg.inv(multarr)           
502        except:
503            errmsg += "\nSingular input. "
504            errmsg += "The following constraints are not "
505            errmsg += "linearly independent\n\tor do not "
506            errmsg += "allow for generation of a non-singular set\n"
507            errmsg += 'Please report this unexpected error\n'
508            for rel in group:
509                errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
510                errmsg += "\n"
511    return errmsg,warnmsg
512
513def GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict=None,SeqHist=None):
514    '''Takes a list of relationship entries comprising a group of
515    constraints and builds the relationship lists and their inverse
516    and stores them in global variables Also checks for internal
517    conflicts or inconsistencies in parameter/variable definitions.
518
519    :param list groups: a list of grouped contraints where each constraint
520      grouped containts a list of indices for constraint constrDict entries,
521      created in :func:`GroupConstraints` (returned as 1st value)
522
523    :param list parmlist: a list containing lists of parameter names
524      contained in each group, created in :func:`GroupConstraints`
525      (returned as 2nd value)
526
527    :param list varyList: a list of parameters names (strings of form
528      ``<ph>:<hst>:<nam>``) that will be varied. Note that this is changed here.
529   
530    :param dict constrDict: a list of dicts defining relationships/constraints
531      (as defined in :func:`GroupConstraints`)
532
533    :param list fixedList: a list of values specifying a fixed value for each
534      dict in constrDict. Values are either strings that can be converted to
535      floats, float values or None if the constraint defines a new parameter.
536     
537    :param dict parmDict: a dict containing all parameters defined in current
538      refinement.
539
540    :param int SeqHist: number of current histogram, when used in a sequential
541      refinement. None (default) otherwise. Wildcard variable names are
542      set to the current histogram, when found if not None.
543    '''
544    global dependentParmList,arrayList,invarrayList,indParmList,consNum
545    msg = ''
546
547    # process fixed (held) variables
548    for cdict in constrDict:
549        if len(cdict) == 1:
550            fixedVarList.append(cdict.keys()[0])
551   
552    # process equivalences: make a list of dependent and independent vars
553    #    and check for repeated uses (repetition of a parameter as an
554    #    independent var is OK [A=B; A=C], but chaining: [A=B; B=C] is not good)
555    indepVarList = []
556    depVarList = []
557    multdepVarList = []
558    translateTable = {} # lookup table for wildcard referenced variables
559    for varlist,mapvars,multarr,invmultarr in zip(       # process equivalences
560        dependentParmList,indParmList,arrayList,invarrayList):
561        if multarr is None: # true only if an equivalence
562            zeromult = False
563            for mv in mapvars:
564                #s = ''
565                varied = 0
566                notvaried = ''
567                if mv in varyList:
568                    varied += 1
569                else:
570                    if notvaried: notvaried += ', '
571                    notvaried += mv
572                if parmDict is not None and mv not in parmDict:
573                    print "Dropping equivalence for variable "+str(mv)+". Not defined in this refinement"
574                    #msg += "\nCannot equivalence to variable "+str(mv)+". Not defined in this refinement"
575                    #continue
576                else: 
577                    if mv not in indepVarList: indepVarList.append(mv)
578                for v,m in zip(varlist,invmultarr):
579                    if parmDict is not None and v not in parmDict:
580                        print "Dropping equivalence for variable "+str(v)+". Not defined in this refinement"
581                        continue
582                    if m == 0: zeromult = True
583                    if v in varyList:
584                        varied += 1
585                    else:
586                        if notvaried: notvaried += ', '
587                        notvaried += v
588                    if v in depVarList:
589                        multdepVarList.append(v)
590                    else:
591                        depVarList.append(v)
592            if varied > 0 and varied != len(varlist)+1:
593                msg += "\nNot all variables refined in equivalence:\n\t"
594                s = ""
595                for v in varlist:
596                    if s != "": s+= " & "
597                    s += str(v)           
598                msg += str(mv) + " => " + s
599                msg += '\nNot refined: ' + notvaried + '\n'
600            if zeromult:
601                msg += "\nZero multiplier is invalid in equivalence:\n\t"
602                s = ""
603                for v in varlist:
604                    if s != "": s+= " & "
605                    s += str(v)           
606                msg += str(mv) + " => " + s + '\n'
607
608    if debug: print 'indepVarList',indepVarList
609    if debug: print 'depVarList',depVarList
610    # check for errors:
611    inboth = set(indepVarList).intersection(set(depVarList))
612    if len(inboth) > 0:
613        msg += "\nThe following parameters(s) are used as both dependent and independent variables in Equivalence relations:\n"
614        s = ''
615        for var in sorted(inboth):
616            if s != "": s+= ", "
617            s += str(var)
618        msg += '\t'+ s + '\n'
619    if len(multdepVarList) > 0:
620        msg += "\nThe following parameters(s) are used in multiple Equivalence relations as dependent variables:\n"
621        s = ''
622        for var in sorted(set(multdepVarList)):
623            if s != "": s+= ", "
624            s += str(var)           
625        msg += '\t'+ s + '\n'
626    equivVarList = list(set(indepVarList).union(set(depVarList)))
627
628    # scan through parameters in each relationship. Are all varied? If only some are
629    # varied, create an error message.
630    for group,varlist in zip(groups,parmlist):
631        if len(varlist) == 1: continue
632        for rel in group:
633            varied = 0
634            notvaried = ''
635            unused = 0
636            notused = ''
637            for var in constrDict[rel]:
638                if var.startswith('_'): continue
639                if var.split(':')[1] == '*' and SeqHist is not None:
640                    # convert wildcard var to reference current histogram; save translation in table
641                    sv = var.split(':')
642                    sv[1] = str(SeqHist)
643                    translateTable[var] = ':'.join(sv)
644                    var = translateTable[var]
645                if parmDict is not None and var not in parmDict:
646                    unused += 1
647                    if notvaried: notused += ', '
648                    notused += var
649                if var in varyList:
650                    varied += 1
651                else:
652                    if notvaried: notvaried += ', '
653                    notvaried += var
654                if var in fixedVarList:
655                    msg += '\nError: parameter '+var+" is Fixed and used in a constraint:\n\t"
656                    msg += _FormatConstraint(constrDict[rel],fixedList[rel])+"\n"
657            #if unused > 0:# and unused != len(VarKeys(constrDict[rel])):
658            if unused > 0 and unused != len(VarKeys(constrDict[rel])):
659                msg += "\nSome (but not all) variables in constraint are not defined:\n\t"
660                msg += _FormatConstraint(constrDict[rel],fixedList[rel])
661                msg += '\nNot used: ' + notused + '\n'
662            if varied > 0 and varied != len(VarKeys(constrDict[rel])):
663                msg += "\nNot all variables refined in constraint:\n\t"
664                msg += _FormatConstraint(constrDict[rel],fixedList[rel])
665                msg += '\nNot refined: ' + notvaried + '\n'
666    # if there were errors found, go no farther
667    if msg:
668        print ' *** ERROR in constraint definitions! ***'
669        print msg
670        raise Exception
671               
672    # now process each group and create the relations that are needed to form
673    # a non-singular square matrix
674    # If all are varied and this is a constraint equation, then set VaryFree flag
675    # so that the newly created relationships will be varied
676    for group,varlist in zip(groups,parmlist):
677        if len(varlist) == 1: continue
678        # for constraints, if all included variables are refined,
679        # set the VaryFree flag, and remaining degrees of freedom will be
680        # varied (since consistency was checked, if any one variable is
681        # refined, then assume that all are)
682        varsList = [] # make a list of all the referenced variables as well
683        VaryFree = False
684        for rel in group:
685            varied = 0
686            unused = 0
687            for var in VarKeys(constrDict[rel]):
688                var = translateTable.get(var,var) # replace wildcards
689                if parmDict is not None and var not in parmDict:
690                    unused += 1                   
691                if var not in varsList: varsList.append(var)
692                if var in varyList: varied += 1
693            if fixedList[rel] is not None and varied > 0:
694                VaryFree = True
695        if len(varlist) < len(group): # too many relationships -- no can do
696            msg = 'too many relationships'
697            break
698        # Since we checked before, if any variables are unused, then all must be.
699        # If so, this set of relationships can be ignored
700        if unused:
701            if debug: print('Constraint ignored (all variables undefined)')
702            if debug: print ('    '+_FormatConstraint(constrDict[rel],fixedList[rel]))
703            continue
704        # fill in additional degrees of freedom
705        try:
706            arr = _FillArray(group,constrDict,varlist)
707            _RowEchelon(len(group),arr,varlist)
708            constrArr = _FillArray(group,constrDict,varlist,FillDiagonals=True)
709            GramSchmidtOrtho(constrArr,len(group))
710        except:
711            msg = 'Singular relationships'
712            break
713        mapvar = []
714        group = group[:]
715        # scan through all generated and input relationships, we need to add to the varied list
716        # all the new parameters where VaryFree has been set or where a New Var is varied.
717        #
718        # If a group does not contain any fixed values (constraint equations)
719        # and nothing in the group is varied, drop this group, so that the
720        # dependent parameters can be refined individually.
721        unused = True
722        for i in range(len(varlist)):
723            if len(group) > 0: # get the original equation reference
724                rel = group.pop(0)
725                fixedval = fixedList[rel]
726                varyflag = constrDict[rel].get('_vary',False)
727                varname = constrDict[rel].get('_name','')
728            else: # this relationship has been generated
729                varyflag = False
730                varname = ''
731                fixedval = None
732            if fixedval is None: # this is a new variable, not a constraint
733                if not varname:
734                    varname = paramPrefix + str(consNum) # no assigned name, create one
735                    consNum += 1
736                mapvar.append(varname)
737                # vary the new relationship if it is a degree of freedom in
738                # a set of contraint equations or if a New Var is flagged to be varied.
739                if VaryFree or varyflag: 
740                    unused = False
741                    varyList.append(varname)
742                    # fix (prevent varying) of all the variables inside the constraint group
743                    # (dependent vars)
744                    for var in varsList:
745                        if var in varyList: varyList.remove(var)
746            else:
747                unused = False
748                mapvar.append(fixedval)
749        if unused: # skip over constraints that don't matter (w/o fixed value or any refined variables)
750            if debug: print('Constraint ignored (all variables unrefined)')
751            if debug: print ('   '+_FormatConstraint(constrDict[rel],fixedList[rel]))
752            continue 
753        dependentParmList.append([translateTable.get(var,var) for var in varlist])
754        arrayList.append(constrArr)
755        invarrayList.append(np.linalg.inv(constrArr))
756        indParmList.append(mapvar)
757    if msg:
758        print ' *** ERROR in constraint definitions! ***'
759        print msg
760        print VarRemapShow(varyList)
761        raise Exception
762    # setup dictionary containing the fixed values
763    global fixedDict
764    # key is original ascii string, value is float
765    for fixedval in fixedList:
766        if fixedval:
767            fixedDict[fixedval] = float(fixedval)
768
769    if debug: # on debug, show what is parsed & generated, semi-readable
770        print 50*'-'
771        print VarRemapShow(varyList)
772        print 'Varied: ',varyList
773        print 'Not Varied: ',fixedVarList
774
775def StoreEquivalence(independentVar,dependentList):
776    '''Takes a list of dependent parameter(s) and stores their
777    relationship to a single independent parameter (independentVar)
778
779    :param str independentVar: name of master parameter that will be used to determine the value
780      to set the dependent variables
781
782    :param list dependentList: a list of parameters that will set from
783         independentVar. Each item in the list can be a string with the parameter
784         name or a tuple containing a name and multiplier:
785         ``['parm1',('parm2',.5),]``
786
787    '''
788   
789    global dependentParmList,arrayList,invarrayList,indParmList
790    mapList = []
791    multlist = []
792    for var in dependentList:
793        if isinstance(var, basestring):
794            mult = 1.0
795        elif len(var) == 2:
796            var,mult = var
797        else:
798            raise Exception("Cannot parse "+repr(var) + " as var or (var,multiplier)")
799        mapList.append(var)
800        multlist.append(tuple((mult,)))
801    # added relationships to stored values
802    arrayList.append(None)
803    invarrayList.append(np.array(multlist))
804    indParmList.append(tuple((independentVar,)))
805    dependentParmList.append(mapList)
806    return
807
808def GetDependentVars():
809    '''Return a list of dependent variables: e.g. variables that are
810    constrained in terms of other variables
811
812    :returns: a list of variable names
813
814    '''
815    dependentVars = []
816    global dependentParmList
817    for lst in dependentParmList:
818        for itm in lst: dependentVars.append(itm)
819    return dependentVars
820
821def GetIndependentVars():
822    '''Return a list of independent variables: e.g. variables that are
823    created by constraints of other variables
824
825    :returns: a list of variable names
826
827    '''
828    independentVars = []
829    global indParmList,fixedDict
830    for lst in indParmList:
831        for name in lst:
832            if name in fixedDict: continue
833            independentVars.append(name)
834    return independentVars
835
836def PrintIndependentVars(parmDict,varyList,sigDict,PrintAll=False,pFile=None):
837    '''Print the values and uncertainties on the independent variables'''
838    global dependentParmList,arrayList,invarrayList,indParmList,fixedDict
839    printlist = []
840    mapvars = GetIndependentVars()
841    for i,name in enumerate(mapvars):
842        if name in fixedDict: continue
843        if PrintAll or name in varyList:
844            sig = sigDict.get(name)
845            printlist.append([name,parmDict[name],sig])
846    if len(printlist) == 0: return
847    s1 = ''
848    print >>pFile,130*'-'
849    print >>pFile,"Variables generated by constraints"
850    printlist.append(3*[None])
851    for name,val,esd in printlist:
852        if len(s1) > 120 or name is None:
853            print >>pFile,''
854            print >>pFile,s1
855            print >>pFile,s2
856            print >>pFile,s3
857            s1 = ''
858            if name is None: break
859        if s1 == "":
860            s1 = ' name  :'
861            s2 = ' value :'
862            s3 = ' sig   :'
863        s1 += '%15s' % (name)
864        s2 += '%15.4f' % (val)
865        if esd is None:
866            s3 += '%15s' % ('n/a')
867        else:   
868            s3 += '%15.4f' % (esd)
869
870def ComputeDepESD(covMatrix,varyList,parmDict):
871    '''Compute uncertainties for dependent parameters from independent ones
872    returns a dictionary containing the esd values for dependent parameters
873    '''
874    sigmaDict = {}
875    for varlist,mapvars,invmultarr in zip(dependentParmList,indParmList,invarrayList):
876        #if invmultarr is None: continue # probably not needed
877        try: 
878            valuelist = [parmDict[var] for var in mapvars]
879        except KeyError:
880            continue
881        # get the v-covar matrix for independent parameters
882        vcov = np.zeros((len(mapvars),len(mapvars)))
883        for i1,name1 in enumerate(mapvars):
884            if name1 not in varyList: continue
885            iv1 = varyList.index(name1)
886            for i2,name2 in enumerate(mapvars):
887                if name2 not in varyList: continue
888                iv2 = varyList.index(name2)
889                vcov[i1][i2] = covMatrix[iv1][iv2]
890        # vec is the vector that multiplies each of the independent values
891        for v,vec in zip(varlist,invmultarr):
892            sigmaDict[v] = np.sqrt(np.inner(vec.T,np.inner(vcov,vec)))
893    return sigmaDict
894
895def _FormatConstraint(RelDict,RelVal):
896    '''Formats a Constraint or Function for use in a convenient way'''
897    linelen = 45
898    s = [""]
899    for var,val in RelDict.items():
900        if var.startswith('_'): continue
901        if len(s[-1]) > linelen: s.append(' ')
902        m = val
903        if s[-1] != "" and m >= 0:
904            s[-1] += ' + '
905        elif s[-1] != "":
906            s[-1] += ' - '
907            m = abs(m)
908        s[-1] += '%.3f*%s '%(m,var)
909    if len(s[-1]) > linelen: s.append(' ')
910    if RelVal is None:
911        s[-1] += ' = New variable'
912    else:
913        s[-1] += ' = ' + RelVal
914    s1 = ''
915    for s2 in s:
916        if s1 != '': s1 += '\n\t'
917        s1 += s2
918    return s1
919
920def VarRemapShow(varyList):
921    '''List out the saved relationships. This should be done after the constraints have been
922    defined using :func:`StoreEquivalence`, :func:`GroupConstraints` and :func:`GenerateConstraints`.
923
924    :returns: a string containing the details of the contraint relationships
925    '''
926    s = ''
927    if len(fixedVarList) > 0:
928        s += 'Fixed Variables:\n'
929        for v in fixedVarList:
930            s += '    ' + v + '\n'
931    s += 'Variable mapping relations:\n'
932    global dependentParmList,arrayList,invarrayList,indParmList,fixedDict
933    for varlist,mapvars,multarr,invmultarr in zip(
934        dependentParmList,indParmList,arrayList,invarrayList):
935        for i,mv in enumerate(mapvars):
936            if multarr is None:
937                s += '  ' + str(mv) + ' is equivalent to parameter(s): '
938                j = 0
939                for v,m in zip(varlist,invmultarr):
940                    if debug: print 'v,m[0]: ',v,m[0]
941                    if j > 0: s += '  & '
942                    j += 1
943                    s += str(v)
944                    if m != 1:
945                        s += " / " + str(m[0])                       
946                s += '\n'
947                continue
948            s += %s = ' % mv
949            j = 0
950            for m,v in zip(multarr[i,:],varlist):
951                if m == 0: continue
952                if j > 0: s += ' + '
953                j += 1
954                s += '(%s * %s)' % (m,v)
955            if mv in varyList: s += ' VARY'
956            s += '\n'
957    s += 'Inverse variable mapping relations:\n'
958    for varlist,mapvars,invmultarr in zip(dependentParmList,indParmList,invarrayList):
959        for i,mv in enumerate(varlist):
960            s += %s = ' % mv
961            j = 0
962            for m,v in zip(invmultarr[i,:],mapvars):
963                if m == 0: continue
964                if j > 0: s += ' + '
965                j += 1
966                s += '(%s * %s)' % (m,v)
967            s += '\n'
968    return s
969
970def Dict2Deriv(varyList,derivDict,dMdv):
971    '''Compute derivatives for Independent Parameters from the
972    derivatives for the original parameters
973
974    :param list varyList: a list of parameters names that will be varied
975
976    :param dict derivDict: a dict containing derivatives for parameter values keyed by the
977      parameter names.
978
979    :param list dMdv: a Jacobian, as a list of np.array containing derivatives for dependent
980      parameter computed from derivDict
981
982    '''
983    global dependentParmList,arrayList,invarrayList,indParmList,invarrayList
984    for varlist,mapvars,multarr,invmultarr in zip(dependentParmList,indParmList,arrayList,invarrayList):
985        for i,name in enumerate(mapvars):
986            # grouped variables: need to add in the derv. w/r
987            # dependent variables to the independent ones
988            if name not in varyList: continue # skip if independent var not varied
989            if multarr is None:
990                for v,m in zip(varlist,invmultarr):
991                    if debug: print 'start dMdv',dMdv[varyList.index(name)]
992                    if debug: print 'add derv',v,'/',m[0],'to derv',name,'add=',derivDict[v] / m[0]
993                    if m == 0: continue
994                    dMdv[varyList.index(name)] += derivDict[v] / m[0]
995            else:
996                for v,m in zip(varlist,multarr[i,:]):
997                    if debug: print 'start dMdv',dMdv[varyList.index(name)]
998                    if debug: print 'add derv',v,'*',m,'to derv',name,'add=',m * derivDict[v]
999                    if m == 0: continue
1000                    dMdv[varyList.index(name)] += m * derivDict[v]
1001
1002def Map2Dict(parmDict,varyList):
1003    '''Create (or update) the Independent Parameters from the original
1004    set of Parameters
1005
1006    Removes dependent variables from the varyList
1007
1008    This should be done once, after the constraints have been
1009    defined using :func:`StoreEquivalence`,
1010    :func:`GroupConstraints` and :func:`GenerateConstraints` and
1011    before any variable refinement is done
1012    to complete the parameter dictionary by defining independent
1013    parameters and satisfying the constraint equations.
1014
1015    :param dict parmDict: a dict containing parameter values keyed by the
1016      parameter names.
1017      This will contain updated values for both dependent and independent
1018      parameters after Dict2Map is called. It will also contain some
1019      unexpected entries of every constant value {'0':0.0} & {'1.0':1.0},
1020      which do not cause any problems.
1021
1022    :param list varyList: a list of parameters names that will be varied
1023   
1024
1025    '''
1026    # process the Independent vars: remove dependent ones from varylist
1027    # and then compute values for the Independent ones from their dependents
1028    global dependentParmList,arrayList,invarrayList,indParmList,fixedDict
1029    for varlist,mapvars,multarr in zip(dependentParmList,indParmList,arrayList):
1030        for item in varlist:
1031            try:
1032                varyList.remove(item)
1033            except ValueError:
1034                pass
1035        if multarr is None: continue
1036        valuelist = [parmDict[var] for var in varlist]
1037        parmDict.update(zip(mapvars,
1038                            np.dot(multarr,np.array(valuelist)))
1039                        )
1040    # now remove fixed variables from the varyList
1041    global fixedVarList
1042    for item in fixedVarList:
1043        try:
1044            varyList.remove(item)
1045        except ValueError:
1046            pass
1047    # Set constrained parameters to their fixed values
1048    parmDict.update(fixedDict)
1049
1050def Dict2Map(parmDict,varyList):
1051    '''Applies the constraints defined using :func:`StoreEquivalence`,
1052    :func:`GroupConstraints` and :func:`GenerateConstraints` by changing
1053    values in a dict containing the parameters. This should be
1054    done before the parameters are used for any computations
1055
1056    :param dict parmDict: a dict containing parameter values keyed by the
1057      parameter names.
1058      This will contain updated values for both dependent and independent
1059      parameters after Dict2Map is called. It will also contain some
1060      unexpected entries of every constant value {'0':0.0} & {'1.0':1.0},
1061      which do not cause any problems.
1062
1063    :param list varyList: a list of parameters names that will be varied
1064   
1065    '''
1066    global dependentParmList,arrayList,invarrayList,indParmList,fixedDict
1067    # reset fixed values (should not be needed, but very quick)
1068    # - this seems to update parmDict with {'0':0.0} & {'1.0':1.0} - probably not what was intended
1069    # not needed, but also not a problem - BHT
1070    parmDict.update(fixedDict)
1071    for varlist,mapvars,invmultarr in zip(dependentParmList,indParmList,invarrayList):
1072        #if invmultarr is None: continue
1073        try: 
1074            valuelist = [parmDict[var] for var in mapvars]
1075        except KeyError:
1076            continue
1077        parmDict.update(zip(varlist,
1078                            np.dot(invmultarr,np.array(valuelist)))
1079                        )
1080
1081#======================================================================
1082# internal routines follow (these routines are unlikely to be called
1083# from outside the module)
1084
1085def GramSchmidtOrtho(a,nkeep=0):
1086    '''Use the Gram-Schmidt process (http://en.wikipedia.org/wiki/Gram-Schmidt) to
1087    find orthonormal unit vectors relative to first row.
1088
1089    If nkeep is non-zero, the first nkeep rows in the array are not changed
1090   
1091    input:
1092       arrayin: a 2-D non-singular square array
1093    returns:
1094       a orthonormal set of unit vectors as a square array
1095    '''
1096    def proj(a,b):
1097        'Projection operator'
1098        return a*(np.dot(a,b)/np.dot(a,a))
1099    for j in range(nkeep,len(a)):
1100        for i in range(j):
1101            a[j] -= proj(a[i],a[j])
1102        if np.allclose(np.linalg.norm(a[j]),0.0):
1103            raise Exception,"Singular input to GramSchmidtOrtho"
1104        a[j] /= np.linalg.norm(a[j])
1105    return a
1106
1107def _FillArray(sel,dict,collist,FillDiagonals=False):
1108    '''Construct a n by n matrix (n = len(collist)
1109    filling in the rows using the relationships defined
1110    in the dictionaries selected by sel
1111
1112    If FillDiagonals is True, diagonal elements in the
1113    array are set to 1.0
1114    '''
1115    n = len(collist)
1116    if FillDiagonals:
1117        arr = np.eye(n)
1118    else:
1119        arr = np.zeros(2*[n,])
1120    # fill the top rows
1121    for i,cnum in enumerate(sel):
1122        for j,var in enumerate(collist):
1123            arr[i,j] = dict[cnum].get(var,0)
1124    return arr
1125
1126def _SwapColumns(i,m,v):
1127    '''Swap columns in matrix m as well as the labels in v
1128    so that element (i,i) is replaced by the first non-zero element in row i after that element
1129
1130    Throws an exception if there are no non-zero elements in that row
1131    '''
1132    for j in range(i+1,len(v)):
1133        if not np.allclose(m[i,j],0):
1134            m[:,(i,j)] = m[:,(j,i)]
1135            v[i],v[j] = v[j],v[i]
1136            return
1137    else:
1138        raise Exception,'Singular input'
1139
1140def _RowEchelon(m,arr,collist):
1141    '''Convert the first m rows in Matrix arr to row-echelon form
1142    exchanging columns in the matrix and collist as needed.
1143
1144    throws an exception if the matrix is singular because
1145    the first m rows are not linearly independent
1146    '''
1147    n = len(collist)
1148    for i in range(m):
1149        if np.allclose(arr[i,i],0):
1150            _SwapColumns(i,arr,collist)
1151        arr[i,:] /= arr[i,i] # normalize row
1152        # subtract current row from subsequent rows to set values to left of diagonal to 0
1153        for j in range(i+1,m):
1154            arr[j,:] -= arr[i,:] * arr[j,i]
1155
1156if __name__ == "__main__":
1157    parmdict = {}
1158    constrDict = [
1159        {'0:12:Scale': 2.0, '0:11:Scale': 1.0, '0:14:Scale': 4.0, '0:13:Scale': 3.0, '0:0:Scale': 0.5},
1160        {'0:0:eA': 0.0},
1161        {'2::C(10,6,1)': 1.0, '1::C(10,6,1)': 1.0},
1162        {'1::C(10,0,1)': 1.0, '2::C(10,0,1)': 1.0},
1163        {'1::AUiso:0': 1.0, '0::AUiso:0': 1.0},
1164        {'0::A0': 0.0}
1165        ]
1166    fixedList = ['5.0', '0', None, None, '1.0', '0']
1167    StoreEquivalence('2::atomx:3',('2::atomy:3', ('2::atomz:3',2,), ))
1168    #StoreEquivalence('1::atomx:3',('2::atomx:3', ('2::atomz:3',2,), )) # error: dependent & independent vars mixed
1169    #StoreEquivalence('1::atomx:3',('2::atomy:3', ('2::atomz:3',2,), )) # error: dependent vars repeated
1170    #StoreEquivalence('0:1:eA',('0:0:eA',)) # error: equiv & fixed
1171    #StoreEquivalence('0:99:Scale',('0:12:Scale',)) # error: equiv & constrained
1172    #StoreEquivalence('0:12:Scale',('0:99:Scale',)) # error: equiv & constrained
1173    varylist = ['2::atomx:3',
1174                '2::C(10,6,1)', '1::C(10,6,1)',
1175                '2::atomy:3', '2::atomz:3',
1176                '0:12:Scale', '0:11:Scale', '0:14:Scale', '0:13:Scale', '0:0:Scale']
1177#    e,w = CheckConstraints([,
1178#                     [{'2:0:Scale': 1.0, '5:0:Scale': 1.0, '10:0:Scale': 1.0, '6:0:Scale': 1.0, '9:0:Scale': 1.0, '8:0:Scale': 1.0,# '3:0:Scale': 1.0, '4:0:Scale': 1.0, '7:0:Scale': 1.0, '1:0:Scale': 1.0, '0:0:Scale': 1.0}],
1179#                     ['1.0'])
1180#    if e: print 'error=',e
1181#    if w: print 'error=',w
1182#    varyList = ['0::A0', '0::AUiso:0', '0::Afrac:1', '0::Afrac:2', '0::Afrac:3', '0::Afrac:4', '0::dAx:5', '0::dAy:5', '0::dAz:5', '0::AUiso:5', ':0:Back;0', ':0:Back;1', ':0:Back;2', ':0:Back;3', ':0:Back;4', ':0:Back;5', ':0:Back;6', ':0:Back;7', ':0:Back;8', ':0:Back;9', ':0:Back;10', ':0:Back;11', ':0:U', ':0:V', ':0:W', ':0:X', ':0:Y', ':0:Scale', ':0:DisplaceX', ':0:DisplaceY']
1183#    constrDict = [
1184#        {'0::Afrac:4': 24.0, '0::Afrac:1': 16.0, '0::Afrac:3': 24.0, '0::Afrac:2': 16.0},
1185#        {'0::Afrac:1': 1.0, '0::Afrac:2': 1.0},
1186#        {'0::Afrac:4': 1.0, '0::Afrac:3': 1.0}]
1187#    fixedList = ['40.0', '1.0', '1.0']
1188
1189    errmsg, warnmsg = CheckConstraints(varylist,constrDict,fixedList)
1190    if errmsg:
1191        print "*** Error ********************"
1192        print errmsg
1193    if warnmsg:
1194        print "*** Warning ********************"
1195        print warnmsg
1196    if errmsg or warnmsg:
1197        sys.exit()
1198    groups,parmlist = GroupConstraints(constrDict)
1199    GenerateConstraints(groups,parmlist,varylist,constrDict,fixedList)
1200    print VarRemapShow(varylist)
1201    parmdict.update( {
1202        '0:12:Scale': 1.0, '0:11:Scale': 1.0, '0:14:Scale': 1.0, '0:13:Scale': 1.0, '0:0:Scale': 2.0,
1203        '0:0:eA': 0.0,
1204        '2::C(10,6,1)': 0.2, '1::C(10,6,1)': 0.3,
1205        '1::C(10,0,1)': 0.2, '2::C(10,0,1)': 0.3,
1206        '1::AUiso:0': 0.02, '0::AUiso:0': 0.03,
1207        '0::A0': 0.0,
1208        '2::atomx:3':0.23,'2::atomy:3':-.23, '2::atomz:3':-0.11,
1209        })
1210    print 'parmdict start',parmdict
1211    print 'varylist start',varylist
1212    before = parmdict.copy()
1213    Map2Dict(parmdict,varylist)
1214    print 'parmdict before and after Map2Dict'
1215    print '  key / before / after'
1216    for key in sorted(parmdict.keys()):
1217        print '  '+key,'\t',before.get(key),'\t',parmdict[key]
1218    print 'varylist after',varylist
1219    before = parmdict.copy()
1220    Dict2Map(parmdict,varylist)
1221    print 'after Dict2Map'
1222    print '  key / before / after'
1223    for key in sorted(parmdict.keys()):
1224        print '  '+key,'\t',before.get(key),'\t',parmdict[key]
1225#    dMdv = len(varylist)*[0]
1226#    deriv = {}
1227#    for i,v in enumerate(parmdict.keys()): deriv[v]=i
1228#    Dict2Deriv(varylist,deriv,dMdv)
Note: See TracBrowser for help on using the repository browser.