diffpy.srfit 3.1.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. diffpy/__init__.py +15 -0
  2. diffpy/srfit/__init__.py +41 -0
  3. diffpy/srfit/equation/__init__.py +31 -0
  4. diffpy/srfit/equation/builder.py +702 -0
  5. diffpy/srfit/equation/equationmod.py +213 -0
  6. diffpy/srfit/equation/literals/__init__.py +71 -0
  7. diffpy/srfit/equation/literals/abcs.py +81 -0
  8. diffpy/srfit/equation/literals/argument.py +76 -0
  9. diffpy/srfit/equation/literals/literal.py +69 -0
  10. diffpy/srfit/equation/literals/operators.py +380 -0
  11. diffpy/srfit/equation/visitors/__init__.py +94 -0
  12. diffpy/srfit/equation/visitors/argfinder.py +62 -0
  13. diffpy/srfit/equation/visitors/printer.py +120 -0
  14. diffpy/srfit/equation/visitors/swapper.py +142 -0
  15. diffpy/srfit/equation/visitors/validator.py +113 -0
  16. diffpy/srfit/equation/visitors/visitor.py +62 -0
  17. diffpy/srfit/exceptions.py +29 -0
  18. diffpy/srfit/fitbase/__init__.py +53 -0
  19. diffpy/srfit/fitbase/calculator.py +110 -0
  20. diffpy/srfit/fitbase/configurable.py +57 -0
  21. diffpy/srfit/fitbase/constraint.py +117 -0
  22. diffpy/srfit/fitbase/fitcontribution.py +351 -0
  23. diffpy/srfit/fitbase/fithook.py +227 -0
  24. diffpy/srfit/fitbase/fitrecipe.py +826 -0
  25. diffpy/srfit/fitbase/fitresults.py +674 -0
  26. diffpy/srfit/fitbase/parameter.py +314 -0
  27. diffpy/srfit/fitbase/parameterset.py +110 -0
  28. diffpy/srfit/fitbase/profile.py +405 -0
  29. diffpy/srfit/fitbase/profilegenerator.py +168 -0
  30. diffpy/srfit/fitbase/profileparser.py +179 -0
  31. diffpy/srfit/fitbase/recipeorganizer.py +1074 -0
  32. diffpy/srfit/fitbase/restraint.py +117 -0
  33. diffpy/srfit/fitbase/simplerecipe.py +230 -0
  34. diffpy/srfit/fitbase/validatable.py +59 -0
  35. diffpy/srfit/interface/__init__.py +33 -0
  36. diffpy/srfit/interface/interface.py +180 -0
  37. diffpy/srfit/pdf/__init__.py +24 -0
  38. diffpy/srfit/pdf/basepdfgenerator.py +296 -0
  39. diffpy/srfit/pdf/characteristicfunctions.py +432 -0
  40. diffpy/srfit/pdf/debyepdfgenerator.py +115 -0
  41. diffpy/srfit/pdf/pdfcontribution.py +311 -0
  42. diffpy/srfit/pdf/pdfgenerator.py +77 -0
  43. diffpy/srfit/pdf/pdfparser.py +226 -0
  44. diffpy/srfit/sas/__init__.py +30 -0
  45. diffpy/srfit/sas/prcalculator.py +139 -0
  46. diffpy/srfit/sas/sasgenerator.py +70 -0
  47. diffpy/srfit/sas/sasimport.py +66 -0
  48. diffpy/srfit/sas/sasparameter.py +70 -0
  49. diffpy/srfit/sas/sasparser.py +133 -0
  50. diffpy/srfit/sas/sasprofile.py +94 -0
  51. diffpy/srfit/structure/__init__.py +60 -0
  52. diffpy/srfit/structure/basestructureparset.py +62 -0
  53. diffpy/srfit/structure/bvsrestraint.py +101 -0
  54. diffpy/srfit/structure/cctbxparset.py +292 -0
  55. diffpy/srfit/structure/diffpyparset.py +303 -0
  56. diffpy/srfit/structure/objcrystparset.py +1552 -0
  57. diffpy/srfit/structure/sgconstraints.py +761 -0
  58. diffpy/srfit/structure/srrealparset.py +92 -0
  59. diffpy/srfit/util/__init__.py +50 -0
  60. diffpy/srfit/util/argbinders.py +29 -0
  61. diffpy/srfit/util/inpututils.py +50 -0
  62. diffpy/srfit/util/nameutils.py +45 -0
  63. diffpy/srfit/util/observable.py +93 -0
  64. diffpy/srfit/util/tagmanager.py +150 -0
  65. diffpy/srfit/util/weakrefcallable.py +170 -0
  66. diffpy/srfit/version.py +26 -0
  67. diffpy_srfit-3.1.0rc0.dist-info/METADATA +216 -0
  68. diffpy_srfit-3.1.0rc0.dist-info/RECORD +73 -0
  69. diffpy_srfit-3.1.0rc0.dist-info/WHEEL +5 -0
  70. diffpy_srfit-3.1.0rc0.dist-info/licenses/AUTHORS.rst +10 -0
  71. diffpy_srfit-3.1.0rc0.dist-info/licenses/LICENSE.rst +138 -0
  72. diffpy_srfit-3.1.0rc0.dist-info/licenses/LICENSE_DANSE.rst +34 -0
  73. diffpy_srfit-3.1.0rc0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,702 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # diffpy.srfit by DANSE Diffraction group
5
+ # Simon J. L. Billinge
6
+ # (c) 2008 The Trustees of Columbia University
7
+ # in the City of New York. All rights reserved.
8
+ #
9
+ # File coded by: Chris Farrow
10
+ #
11
+ # See AUTHORS.txt for a list of people who contributed.
12
+ # See LICENSE_DANSE.txt for license information.
13
+ #
14
+ ##############################################################################
15
+ """Classes and utilities for creating equations.
16
+
17
+ The EquationFactory class is used to create an equation (an Equation instance)
18
+ from a string equation. User-defined Literals can be registered with the
19
+ factory so that they are used in the equation. Registered Literals are
20
+ referenced by name, and when a new Literal of the same name is registered, the
21
+ factory will swap out the old Literal for the new one in all equations built by
22
+ the factory.
23
+
24
+ An example using the EquationFactory:
25
+ The makeEquation method turns the string-representation of an equation into
26
+ a callable object.
27
+ > factory = EquationFactory()
28
+ > eq = factory.makeEquation("A*sin(a*x)")
29
+ will create an equation that evaluates as "A*sin(a*x)". The equation takes no
30
+ arguments.
31
+
32
+ Custom Arguments and constants can be included in the equation:
33
+ > factory.registerConstant("offset", 3)
34
+ > A = Argument(name = "A", value = 1.0)
35
+ > factory.registerArgument("A", A)
36
+ > eq = factory.makeEquation("A*sin(a*x) + offset")
37
+ This includes a constant offset in the equation and makes sure that the
38
+ user-defined Argument is in the equation. This can be used to assure that
39
+ the same instance of an Argument appears in multiple equations. Other literals
40
+ can be registered in a similar fashion.
41
+
42
+ The BaseBuilder class does the hard work of making an equation from a string in
43
+ EquationFactory.makeEquation. BaseBuilder can be used directly to create
44
+ equations. BaseBuilder is specified in the ArgumentBuilder and OperatorBuilder
45
+ classes. You can create builders from Literals or equations by using the
46
+ "wrap" methods within this module or by using the builder classes directly.
47
+
48
+ With a collection of BaseBuilder objects, one can simply write the equation
49
+ using normal python syntax:
50
+ > A = ArgumentBuilder(name = "A")
51
+ > a = ArgumentBuilder(name = "a")
52
+ > x = ArgumentBuilder(name = "x")
53
+ > # sin is defined in this module as an OperatorBuilder
54
+ > sin = getBuilder("sin")
55
+ > beq = A*sin(a*x)
56
+ > eq = beq.getEquation()
57
+
58
+ The equation builder can also handle scalar constants. Staring with the above
59
+ setup:
60
+ > beq2 = A*sin(a*x) + 3
61
+ > eq2 = beq2.getEquation()
62
+ Here, we didn't have to wrap '3' in an ArgumentBuilder. Non scalars, constant
63
+ or otherwise, must be wrapped as ArgumentBuilders in order to be used in this
64
+ way.
65
+
66
+ BaseBuilder can make use of user-defined functions. Any callable python
67
+ object can be wrapped as an OperatorBuilder with the wrapFunction method. For
68
+ example.
69
+ > _f = lambda a, b : (a-b)/(a+b)
70
+ > f = wrapFunction("f", _f)
71
+ > # Using BaseBuilder
72
+ > a = ArgumentBuilder(name = "a")
73
+ > b = ArgumentBuilder(name = "b")
74
+ > c = ArgumentBuilder(name = "c")
75
+ > beq = c*f(a,b)
76
+ > eq = beq.makeEquation()
77
+ """
78
+ import inspect
79
+ import numbers
80
+ import token
81
+ import tokenize
82
+
83
+ import numpy
84
+ import six
85
+
86
+ import diffpy.srfit.equation.literals as literals
87
+ from diffpy.srfit.equation.equationmod import Equation
88
+ from diffpy.srfit.equation.literals.literal import Literal
89
+
90
+ __all__ = [
91
+ "EquationFactory",
92
+ "BaseBuilder",
93
+ "ArgumentBuilder",
94
+ "OperatorBuilder",
95
+ "wrapArgument",
96
+ "wrapOperator",
97
+ "wrapFunction",
98
+ "getBuilder",
99
+ ]
100
+
101
+ # NOTE - the builder cannot handle numpy arrays on the left of a binary
102
+ # operation because the array will automatically loop the operator of the
103
+ # right-side over its arguments. This results in an array of BaseBuilder
104
+ # instances, not an BaseBuilder that contains an array.
105
+
106
+ _builders = {}
107
+
108
+
109
+ class EquationFactory(object):
110
+ """A Factory for equations.
111
+
112
+ builders -- A dictionary of BaseBuilders registered with the
113
+ factory, indexed by name.
114
+ newargs -- A set of new arguments created by makeEquation. This is
115
+ redefined whenever makeEquation is called.
116
+ equations -- Set of equations that have been built by the
117
+ EquationFactory.
118
+ """
119
+
120
+ symbols = ("+", "-", "*", "/", "**", "%", "|")
121
+ ignore = ("(", ",", ")")
122
+
123
+ def __init__(self):
124
+ """Initialize.
125
+
126
+ This registers "pi" and "e" as constants within the factory.
127
+ """
128
+ self.builders = dict(_builders)
129
+ self.newargs = set()
130
+ self.equations = set()
131
+ self.registerConstant("pi", numpy.pi)
132
+ self.registerConstant("e", numpy.e)
133
+ return
134
+
135
+ def makeEquation(
136
+ self, eqstr, buildargs=True, argclass=literals.Argument, argkw={}
137
+ ):
138
+ """Make an equation from an equation string.
139
+
140
+ Arguments
141
+ eqstr -- An equation in string form using standard python
142
+ syntax. The equation string can use any function
143
+ registered literal or function, including numpy ufuncs
144
+ that are automatically registered.
145
+ buildargs -- A flag indicating whether missing arguments can be
146
+ created by the Factory (default True). If False, then
147
+ the a ValueError will be raised if there are undefined
148
+ arguments in the eqstr. Built arguments will be of type
149
+ argclass.
150
+ argclass -- Class to use when creating new Arguments (default
151
+ diffpy.srfit.equation.literals.Argument). The class
152
+ constructor must accept the 'name' key word.
153
+ argkw -- Key word dictionary to pass to the argclass constructor
154
+ (default {}).
155
+
156
+ Returns a callable Literal representing the equation string.
157
+ """
158
+ self._prepareBuilders(eqstr, buildargs, argclass, argkw)
159
+ beq = eval(eqstr, {}, self.builders)
160
+ # handle scalar numbers or numpy arrays
161
+ if isinstance(beq, (numbers.Number, numpy.ndarray)):
162
+ lit = literals.Argument(value=beq, const=True)
163
+ eq = Equation(name="", root=lit)
164
+ else:
165
+ eq = beq.getEquation()
166
+ self.equations.add(eq)
167
+ return eq
168
+
169
+ def registerConstant(self, name, value):
170
+ """Register a named constant with the factory.
171
+
172
+ Returns the registered builder.
173
+ """
174
+ arg = literals.Argument(name=name, value=value, const=True)
175
+ return self.registerArgument(name, arg)
176
+
177
+ def registerArgument(self, name, arg):
178
+ """Register a named Argument with the factory.
179
+
180
+ Returns the registered builder.
181
+ """
182
+ argbuilder = wrapArgument(name, arg)
183
+ return self.registerBuilder(name, argbuilder)
184
+
185
+ def registerOperator(self, name, op):
186
+ """Register an Operator literal with the factory.
187
+
188
+ Operators can be used with or without arguments (or parentheses)
189
+ in an equation string. If used with arguments, then the
190
+ Operator will use the passed arguments as arguments for the
191
+ operation. If used without arguments, it is assumed that the
192
+ operator is already populated with arguments, and those will be
193
+ used.
194
+
195
+ Returns the registered builder.
196
+ """
197
+ opbuilder = wrapOperator(name, op)
198
+ return self.registerBuilder(name, opbuilder)
199
+
200
+ def registerFunction(self, name, func, argnames):
201
+ """Register a named function with the factory.
202
+
203
+ This will register a builder for the function.
204
+
205
+ name -- The name of the function
206
+ func -- A callable python object
207
+ argnames-- The argument names for func. If these names do not
208
+ correspond to builders, then new constants with value 0
209
+ will be created for each name.
210
+
211
+ Returns the registered builder.
212
+ """
213
+ for n in argnames:
214
+ if n not in self.builders:
215
+ self.registerConstant(n, 0)
216
+ opbuilder = wrapFunction(name, func, len(argnames))
217
+ for argname in argnames:
218
+ builder = self.builders[argname]
219
+ argliteral = builder.literal
220
+ opbuilder.literal.addLiteral(argliteral)
221
+
222
+ return self.registerBuilder(name, opbuilder)
223
+
224
+ def registerBuilder(self, name, builder):
225
+ """Register builder in this module so it can be used in makeEquation.
226
+
227
+ If an extant builder with the given name is already registered,
228
+ this will replace all instances of the old builder's literal in
229
+ the factory's equation set with the new builder's literal. Note
230
+ that this may lead to errors if one of the replacements causes a
231
+ self-reference.
232
+
233
+ Raises ValueError if the new builder's literal causes a self-
234
+ reference in an existing equation.
235
+ """
236
+ if not isinstance(name, six.string_types):
237
+ raise TypeError("Name must be a string")
238
+ if not isinstance(builder, BaseBuilder):
239
+ raise TypeError("builder must be a BaseBuilder instance")
240
+ # Swap out the old builder's literal, if necessary
241
+ newlit = builder.literal
242
+ swapbyname = isinstance(builder, ArgumentBuilder)
243
+ bloldlits = set()
244
+ if name in self.builders:
245
+ bloldlits.add(self.builders[name].literal)
246
+ for eq in self.equations:
247
+ eqoldlits = bloldlits
248
+ if swapbyname and name in eq.argdict:
249
+ eqoldlits = bloldlits.union((eq.argdict[name],))
250
+ for oldlit in eqoldlits:
251
+ if oldlit is newlit:
252
+ continue
253
+ eq.swap(oldlit, newlit)
254
+ # Now store the new builder
255
+ self.builders[name] = builder
256
+ return builder
257
+
258
+ def deRegisterBuilder(self, name):
259
+ """De-register a builder by name.
260
+
261
+ This does not change the equations that use the Literal wrapped
262
+ by the builder.
263
+ """
264
+ if name in self.builders:
265
+ del self.builders[name]
266
+ return
267
+
268
+ def wipeout(self, eq):
269
+ """Invalidate the specified equation and remove it from the factory.
270
+
271
+ This will remove the equation from the purview of the factory
272
+ and also change its formula to return NaN. This ensures that eq
273
+ does not observe any object in the factory and thus prevents its
274
+ indirect pickling with the factory because of observer callback
275
+ function.
276
+
277
+ No return value.
278
+ """
279
+ if eq is None:
280
+ assert eq not in self.equations
281
+ return
282
+ self.equations.discard(eq)
283
+ # invalidate this equation to clean up any observer relations of
284
+ # objects in the factory towards its literals tree.
285
+ nan = literals.Argument("nan", value=numpy.nan, const=True)
286
+ eq.setRoot(nan)
287
+ return
288
+
289
+ def _prepareBuilders(self, eqstr, buildargs, argclass, argkw):
290
+ """Prepare builders so that equation string can be evaluated.
291
+
292
+ This method checks the equation string for errors and missing
293
+ arguments, and creates new arguments if allowed. In the process it
294
+ rebuilds the newargs attribute.
295
+
296
+ Arguments
297
+ eqstr -- An equation in string as specified in the makeEquation
298
+ method.
299
+ buildargs -- A flag indicating whether missing arguments can be
300
+ created by the factory. If False, then the a ValueError
301
+ will be raised if there are undefined arguments in the
302
+ eqstr.
303
+ argclass -- Class to use when creating new Arguments. The class
304
+ constructor must accept the 'name' key word.
305
+ argkw -- Key word dictionary to pass to the argclass
306
+ constructor.
307
+
308
+ Raises ValueError if new arguments must be created, but this is
309
+ disallowed due to the buildargs flag.
310
+ Raises SyntaxError if the equation string uses invalid syntax.
311
+
312
+ Returns a dictionary of the name, BaseBuilder pairs.
313
+ """
314
+
315
+ eqargs = self._getUndefinedArgs(eqstr)
316
+
317
+ # Raise an error if there are arguments that need to be created, but
318
+ # this is disallowed.
319
+ if not buildargs and eqargs:
320
+ eqargsstr = ", ".join(eqargs)
321
+ msg = "The equation contains undefined arguments: %s" % eqargsstr
322
+ raise ValueError(msg)
323
+
324
+ # Make the arguments
325
+ newargs = set()
326
+ for argname in eqargs:
327
+ arg = argclass(name=argname, **argkw)
328
+ argbuilder = ArgumentBuilder(name=argname, arg=arg)
329
+ newargs.add(arg)
330
+ self.registerBuilder(argname, argbuilder)
331
+
332
+ self.newargs = newargs
333
+
334
+ return
335
+
336
+ def _getUndefinedArgs(self, eqstr):
337
+ """Get the undefined arguments from eqstr.
338
+
339
+ This tokenizes eqstr and extracts undefined arguments. An
340
+ undefined argument is defined as any token that is not a special
341
+ character that does not correspond to a builder.
342
+
343
+ Raises SyntaxError if the equation string uses invalid syntax.
344
+ """
345
+ interface = six.StringIO(eqstr).readline
346
+ # output is an iterator. Each entry (token) is a 5-tuple
347
+ # token[0] = token type
348
+ # token[1] = token string
349
+ # token[2] = (srow, scol) - row and col where the token begins
350
+ # token[3] = (erow, ecol) - row and col where the token ends
351
+ # token[4] = line where the token was found
352
+ tokens = tokenize.generate_tokens(interface)
353
+
354
+ # Scan for tokens. Throw a SyntaxError if the tokenizer chokes.
355
+ args = set()
356
+
357
+ try:
358
+ for tok in tokens:
359
+ if tok[0] in (token.NAME, token.OP):
360
+ args.add(tok[1])
361
+ except tokenize.TokenError:
362
+ m = "invalid syntax: '%s'" % eqstr
363
+ raise SyntaxError(m)
364
+
365
+ # Scan the tokens for names that do not correspond to registered
366
+ # builders. These will be treated as arguments that need to be
367
+ # generated.
368
+ for tok in set(args):
369
+ # Move genuine variables to the eqargs dictionary
370
+ if (
371
+ # Check registered builders
372
+ tok in self.builders
373
+ or
374
+ # Check symbols
375
+ tok in EquationFactory.symbols
376
+ or
377
+ # Check ignored characters
378
+ tok in EquationFactory.ignore
379
+ ):
380
+ args.remove(tok)
381
+
382
+ return args
383
+
384
+
385
+ # End class EquationFactory
386
+
387
+
388
+ class BaseBuilder(object):
389
+ """Class for building equations.
390
+
391
+ Equation builder objects can be composed like a normal function where the
392
+ arguments can be other BaseBuilder instances or constants.
393
+
394
+ Attributes
395
+ literal -- The equation Literal being built by this instance.
396
+ """
397
+
398
+ def __init__(self):
399
+ """Initialize."""
400
+ self.literal = None
401
+ return
402
+
403
+ def __call__(self, *args):
404
+ """Raises exception for easier debugging."""
405
+ m = "%s (%s) cannot accept arguments" % (
406
+ self.literal.name,
407
+ self.__class__.__name__,
408
+ )
409
+ raise TypeError(m)
410
+
411
+ def getEquation(self):
412
+ """Get the equation built by this object.
413
+
414
+ The equation will given the name "_eq_<root>" where "<root>" is
415
+ the name of the root node.
416
+ """
417
+ # We need to make a name for this, so we name it after its root
418
+ name = "_eq_%s" % self.literal.name
419
+ eq = Equation(name, self.literal)
420
+ return eq
421
+
422
+ def __evalBinary(self, other, OperatorClass, onleft=True):
423
+ """Evaluate a binary function.
424
+
425
+ Other can be an BaseBuilder or a constant.
426
+
427
+ onleft -- Indicates that the operator was passed on the left side
428
+ (default True).
429
+ """
430
+ # Create the Operator
431
+ op = OperatorClass()
432
+
433
+ # onleft takes care of non-commutative operators, and assures that the
434
+ # ordering is preserved.
435
+ if onleft:
436
+ # Add the literals to the operator
437
+ op.addLiteral(self.literal)
438
+
439
+ # Deal with constants
440
+ if isinstance(other, BaseBuilder):
441
+ literal = other.literal
442
+ elif isinstance(other, Literal):
443
+ literal = other
444
+ else:
445
+ literal = literals.Argument(value=other, const=True)
446
+ op.addLiteral(literal)
447
+
448
+ if not onleft:
449
+ # Add the literals to the operator
450
+ op.addLiteral(self.literal)
451
+
452
+ # Create a new OperatorBuilder for the Operator
453
+ opbuilder = OperatorBuilder(op.name)
454
+ opbuilder.literal = op
455
+ return opbuilder
456
+
457
+ def __evalUnary(self, OperatorClass):
458
+ """Evaluate a unary function."""
459
+ op = OperatorClass()
460
+ op.addLiteral(self.literal)
461
+ opbuilder = OperatorBuilder(op.name)
462
+ opbuilder.literal = op
463
+ return opbuilder
464
+
465
+ def __add__(self, other):
466
+ return self.__evalBinary(other, literals.AdditionOperator)
467
+
468
+ def __radd__(self, other):
469
+ return self.__evalBinary(other, literals.AdditionOperator, False)
470
+
471
+ def __sub__(self, other):
472
+ return self.__evalBinary(other, literals.SubtractionOperator)
473
+
474
+ def __rsub__(self, other):
475
+ return self.__evalBinary(other, literals.SubtractionOperator, False)
476
+
477
+ def __mul__(self, other):
478
+ return self.__evalBinary(other, literals.MultiplicationOperator)
479
+
480
+ def __rmul__(self, other):
481
+ return self.__evalBinary(other, literals.MultiplicationOperator, False)
482
+
483
+ def __truediv__(self, other):
484
+ return self.__evalBinary(other, literals.DivisionOperator)
485
+
486
+ def __rtruediv__(self, other):
487
+ return self.__evalBinary(other, literals.DivisionOperator, False)
488
+
489
+ # Python 2 Compatibility -------------------------------------------------
490
+
491
+ if six.PY2:
492
+ __div__ = __truediv__
493
+ __rdiv__ = __rtruediv__
494
+
495
+ # ------------------------------------------------------------------------
496
+
497
+ def __pow__(self, other):
498
+ return self.__evalBinary(other, literals.ExponentiationOperator)
499
+
500
+ def __rpow__(self, other):
501
+ return self.__evalBinary(other, literals.ExponentiationOperator, False)
502
+
503
+ def __mod__(self, other):
504
+ return self.__evalBinary(other, literals.RemainderOperator)
505
+
506
+ def __rmod__(self, other):
507
+ return self.__evalBinary(other, literals.RemainderOperator, False)
508
+
509
+ def __neg__(self):
510
+ return self.__evalUnary(literals.NegationOperator)
511
+
512
+
513
+ # These are used by the class.
514
+
515
+
516
+ class ArgumentBuilder(BaseBuilder):
517
+ """BaseBuilder wrapper around an Argument literal.
518
+
519
+ Equation builder objects can be composed like a normal function where the
520
+ arguments can be other BaseBuilder instances or constants.
521
+
522
+ Attributes
523
+ literal -- The Argument wrapped by this instance.
524
+ """
525
+
526
+ def __init__(self, value=None, name=None, const=False, arg=None):
527
+ """Create an ArgumentBuilder instance, containing a new Argument.
528
+
529
+ Arguments
530
+ value -- The value of the wrapped Argument (float, default None)
531
+ name -- The name of the wrapped Argument (string, default None)
532
+ const -- Flag indicating whether the Argument is constant (bool,
533
+ default False)
534
+ arg -- A pre-defined Argument to use. If this is None (default),
535
+ then a new Argument will be created from value, name and
536
+ const.
537
+ """
538
+ BaseBuilder.__init__(self)
539
+ if arg is None:
540
+ self.literal = literals.Argument(
541
+ value=value, name=name, const=const
542
+ )
543
+ else:
544
+ self.literal = arg
545
+ return
546
+
547
+
548
+ # end class ArgumentBuilder
549
+
550
+
551
+ class OperatorBuilder(BaseBuilder):
552
+ """BaseBuilder wrapper around an Operator literal.
553
+
554
+ Attributes
555
+ literal -- The Operator wrapped by this instance.
556
+ name -- The name of the operator to be wrapped
557
+ """
558
+
559
+ def __init__(self, name, op=None):
560
+ """Wrap an Operator or a function by name.
561
+
562
+ Arguments
563
+ name -- The name of the wrapped Operator
564
+ op -- If specified, this sets the literal attribute as this
565
+ operator (default None). Otherwise, the name is assumed to
566
+ be that of a numpy ufunc, which is used to specify the
567
+ Operator.
568
+ """
569
+ BaseBuilder.__init__(self)
570
+ self.name = name
571
+ self.literal = op
572
+ return
573
+
574
+ def __call__(self, *args):
575
+ """Call the operator builder.
576
+
577
+ This creates a new builder that encapsulates the operation.
578
+
579
+ args -- Arguments of the operation.
580
+
581
+ Raises ValueError if self.literal.nin >= 0 and len(args) != op.nin
582
+ """
583
+ newobj = OperatorBuilder(self.name)
584
+
585
+ # If all we have is a name, then we assume that it is the name of a
586
+ # numpy operator, and we use the corresponding Operator.
587
+ if self.literal is None:
588
+ ufunc = getattr(numpy, self.name)
589
+ self.literal = literals.UFuncOperator(ufunc)
590
+ # Here the Operator is already specified. We can copy its attributes
591
+ # to a new Operator inside of the new OperatorBuilder.
592
+ op = literals.makeOperator(
593
+ name=self.literal.name,
594
+ symbol=self.literal.symbol,
595
+ nin=self.literal.nin,
596
+ nout=self.literal.nout,
597
+ operation=self.literal.operation,
598
+ )
599
+ newobj.literal = op
600
+
601
+ # Now that we have a literal, let's check our inputs
602
+ literal = newobj.literal
603
+ if literal.nin >= 0 and len(args) != literal.nin:
604
+ raise ValueError(
605
+ "%s takes %i arguments (%i given)"
606
+ % (self.literal, self.literal.nin, len(args))
607
+ )
608
+
609
+ # Wrap scalar arguments
610
+ for i, arg in enumerate(args):
611
+ # Wrap the argument if it is not already
612
+ if not isinstance(arg, BaseBuilder):
613
+ name = self.name + "_%i" % i
614
+ arg = ArgumentBuilder(value=arg, name=name, const=True)
615
+ newobj.literal.addLiteral(arg.literal)
616
+
617
+ return newobj
618
+
619
+
620
+ # end class OperatorBuilder
621
+
622
+ # Utility functions
623
+
624
+
625
+ def wrapArgument(name, arg):
626
+ """Wrap an Argument as a builder."""
627
+ argbuilder = ArgumentBuilder(arg=arg)
628
+ return argbuilder
629
+
630
+
631
+ def wrapOperator(name, op):
632
+ """Wrap an Operator as a builder."""
633
+ opbuilder = OperatorBuilder(name, op)
634
+ return opbuilder
635
+
636
+
637
+ def wrapFunction(name, func, nin=2, nout=1):
638
+ """Wrap a function in an OperatorBuilder instance.
639
+
640
+ name -- The name of the function
641
+ func -- A callable python object
642
+ nin -- The number of input arguments (default 2)
643
+ nout -- The number of return values (default 1)
644
+
645
+ Returns the OperatorBuilder instance that wraps the function.
646
+ """
647
+ op = literals.makeOperator(
648
+ name=name, symbol=name, nin=nin, nout=nout, operation=func
649
+ )
650
+
651
+ # Create the OperatorBuilder
652
+ opbuilder = OperatorBuilder(name, op)
653
+
654
+ return opbuilder
655
+
656
+
657
+ def getBuilder(name):
658
+ """Get an operator from the global builders dictionary."""
659
+ return _builders[name]
660
+
661
+
662
+ def __wrapNumpyOperators():
663
+ """Export all numpy operators as OperatorBuilder instances in the module
664
+ namespace."""
665
+ for name in dir(numpy):
666
+ op = getattr(numpy, name)
667
+ if isinstance(op, numpy.ufunc):
668
+ _builders[name] = OperatorBuilder(name)
669
+ return
670
+
671
+
672
+ __wrapNumpyOperators()
673
+
674
+
675
+ # Register other functions as well
676
+ def __wrapSrFitOperators():
677
+ """Export all non-base operators from the
678
+ diffpy.srfit.equation.literals.operators module as OperatorBuilder
679
+ instances in the module namespace."""
680
+ opmod = literals.operators
681
+ excluded_types = set((opmod.CustomOperator, opmod.UFuncOperator))
682
+
683
+ # check if opmod member should be wrapped as OperatorBuilder
684
+ def _is_exported_type(cls):
685
+ return (
686
+ inspect.isclass(cls)
687
+ and issubclass(cls, opmod.Operator)
688
+ and not inspect.isabstract(cls)
689
+ and cls not in excluded_types
690
+ )
691
+
692
+ # create OperatorBuilder objects
693
+ for nm, opclass in inspect.getmembers(opmod, _is_exported_type):
694
+ op = opclass()
695
+ assert op.name, "Unnamed Operator should never appear here."
696
+ _builders[op.name] = OperatorBuilder(op.name, op)
697
+ return
698
+
699
+
700
+ __wrapSrFitOperators()
701
+
702
+ # End of file