LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
LSSTDataManagementBasePackage
algorithmRegistry.py
Go to the documentation of this file.
1 import collections
2 
3 import lsst.pex.config as pexConf
4 from . import algorithmsLib
5 
6 @pexConf.wrap(algorithmsLib.AlgorithmControl)
7 class AlgorithmConfig(pexConf.Config):
8  pass
9 
10 @pexConf.wrap(algorithmsLib.CentroidControl)
12  pass
13 
14 @pexConf.wrap(algorithmsLib.ShapeControl)
16  pass
17 
18 @pexConf.wrap(algorithmsLib.FluxControl)
20  pass
21 
22 class AlgorithmRegistry(pexConf.Registry):
23  """A customized registry for source measurement algorithms.
24 
25  Using a customizated registry allows us to avoid a lot of the
26  boilerplate that would otherwise be necessary when implementing a
27  new source measurement algorithm.
28 
29  First, this registry class has the ability to views and associated
30  Fields that can only refer to algorithms with a particular
31  intermediate base class, all while referencing the same underlying
32  registry. This allows some fields to be restricted to a
33  particular subclass while others are not:
34 
35  class MyConfig(Config):
36  any = AlgorithmRegistry.all.makeField(
37  "field that accepts any algorithm"
38  )
39  centroids = AlgorithmRegistry.filter(CentroidConfig).makeField(
40  "only allows centroiders"
41  )
42 
43  The only instance of this registry is the class attribute 'all'
44  (hence 'AlgorithmRegistry.all', above). The 'filter' class method
45  is used to create filtered views into it. Note that the base
46  class is a config class, not a control class.
47 
48  Second, because all algorithms must have swigged C++ control
49  classes, and these are essentially duplicates of the Config
50  classes users interact with in a RegistryField, the Config classes
51  themselves can be created transparently from the control classes
52  when the control class is registered. This is only done if the
53  control class does not already have a ConfigClass attribute and no
54  explicit ConfigClass argument is passed to the register method.
55 
56  Third, the registry provides a customized instance dict for its
57  associated registry fields, which ensures the list of active
58  algorithms is always sorted according to their config's 'order'
59  class member.
60 
61  The 'configurable' held in this registry is a callable that
62  returns a control object instance, and takes no additional
63  arguments. This simply calls config.makeControl() and sets the
64  control object's name data member (the config does not have a name
65  field, since it would be confusing to allow it to differ from the
66  registration name).
67 
68  All config classes registered must have a makeControl() method
69  that returns a control instance (this is usually provided by using
70  pex.config.makeConfigClass on the swigged control class). """
71 
72  class SubclassRegistryView(collections.Mapping):
73  """A read-only view into registry that filters out items whose
74  config classes don't inherit from a particular base class."""
75 
76  def __init__(self, registry, base):
77  self.registry = registry
78  self.base = base
79 
80  def __getitem__(self, k):
81  t = self.registry[k]
82  if not issubclass(t.ConfigClass, self.base):
83  raise TypeError("%r is not a subclass of %r" % (t.ConfigClass, self.base))
84  return t
85 
86  def __len__(self):
87  return len(tuple(iter(self)))
88 
89  def __iter__(self):
90  return (k for k, v in self.registry.iteritems() if issubclass(v.ConfigClass, self.base))
91 
92  def __contains__(self, k):
93  v = self.registry.get(k)
94  return v is not None and issubclass(v.ConfigClass, self.base)
95 
96  def makeField(self, doc, default=None, optional=False, multi=False):
97  return pexConf.RegistryField(doc, self, default, optional, multi)
98 
99  class Configurable(object):
100  """Class used as the actual element in the registry; a
101  callable that returns a swigged C++ control object with its
102  name set from the registry when called."""
103 
104  __slots__ = "ConfigClass", "name"
105 
106  def __init__(self, name, ConfigClass):
107  self.name = name
108  self.ConfigClass = ConfigClass
109 
110  def __call__(self, config):
111  ctrl = config.makeControl()
112  ctrl.name = self.name
113  return ctrl
114 
115  def __new__(cls):
116  if hasattr(cls, "all"):
117  raise TypeError("AlgorithmRegistry should be a singleton, and must "\
118  "not be copied (this is probably a bug in pex_config).")
119  return pexConf.Registry.__new__(cls, AlgorithmConfig)
120 
121  @classmethod
122  def register(cls, name, target, ConfigClass=None):
123  """Register an AlgorithmControl subclass.
124 
125  This is a class method, so you can either use it on the
126  registry instance or its class (this works because the
127  registry is a singleton, so the class knows to use cls.all as
128  the instance).
129 
130  If it does not have a ConfigClass attribute pointing to the
131  corresponding Config class, a config class will be created
132  using pex.config.makeConfigClass. A new config class will
133  also be created if the ConfigClass attribute was inherited
134  from a base class,
135 
136  @param[in] name Name the algorithm will be registered
137  with; also the name of the source fields
138  it will fill.
139  @param[in] target An AlgorithmControl subclass.
140  @param[in] ConfigClass A Config class to be paired with the
141  control class.
142  """
143  self = cls.all
144  if not issubclass(target, algorithmsLib.AlgorithmControl):
145  raise TypeError("Registry targets must be subclasses of AlgorithmControl")
146  if ConfigClass is None:
147  if hasattr(target, "ConfigClass") and (not hasattr(target.__base__, "ConfigClass")
148  or target.ConfigClass != target.__base__.ConfigClass):
149  ConfigClass = target.ConfigClass # class attributes
150  else:
151  if not hasattr(target.__base__, "ConfigClass"):
152  raise ValueError("Cannot create a config class for %s unless its base class "
153  "has a ConfigClass attribute." % target)
154  ConfigClass = pexConf.makeConfigClass(target, base=target.__base__.ConfigClass, module=2)
155  target = self.Configurable(name, ConfigClass)
156  pexConf.Registry.register(self, name, target)
157 
158  def duplicate(self, oldName, newName):
159  """Register an existing algorithm class with a new name."""
160  old = self[oldName]
161  target = self.Configurable(newName, old.ConfigClass)
162  pexConf.Registry.register(self, newName, target)
163 
164  def makeField(self, doc, default=None, optional=False, multi=False):
165  return pexConf.RegistryField(doc, self, default, optional, multi)
166 
167  @classmethod
168  def filter(cls, base):
169  """Return a lazy read-only view that only contains items with
170  the given Config (not Control) base class.
171  """
172  return cls.SubclassRegistryView(cls.all, base)
173 
174 AlgorithmRegistry.all = AlgorithmRegistry()
175 
176 AlgorithmRegistry.register("correctfluxes", algorithmsLib.CorrectFluxesControl)
177 AlgorithmRegistry.register("classification.extendedness", algorithmsLib.ClassificationControl)
178 AlgorithmRegistry.register("flags.pixel", algorithmsLib.PixelFlagControl)
179 AlgorithmRegistry.register("skycoord", algorithmsLib.SkyCoordControl)
180 AlgorithmRegistry.register("centroid.gaussian", algorithmsLib.GaussianCentroidControl)
181 AlgorithmRegistry.register("centroid.naive", algorithmsLib.NaiveCentroidControl)
182 AlgorithmRegistry.register("centroid.sdss", algorithmsLib.SdssCentroidControl)
183 AlgorithmRegistry.register("centroid.record", algorithmsLib.RecordCentroidControl)
184 AlgorithmRegistry.register("shape.sdss", algorithmsLib.SdssShapeControl)
185 AlgorithmRegistry.register("flux.aperture", algorithmsLib.ApertureFluxControl)
186 AlgorithmRegistry.register("flux.aperture.elliptical", algorithmsLib.EllipticalApertureFluxControl)
187 AlgorithmRegistry.register("flux.peakLikelihood", algorithmsLib.PeakLikelihoodFluxControl)
188 AlgorithmRegistry.register("flux.gaussian", algorithmsLib.GaussianFluxControl)
189 AlgorithmRegistry.register("flux.naive", algorithmsLib.NaiveFluxControl)
190 AlgorithmRegistry.register("flux.psf", algorithmsLib.PsfFluxControl)
191 
192 # Here's an example on how to declare a measurement config more manually, and add a property to the Config.
193 @pexConf.wrap(algorithmsLib.SincFluxControl)
195  def _get_radius(self): return self.radius2
196  def _set_radius(self, r): self.radius2 = r
197  radius = property(_get_radius, _set_radius, doc="synonym for radius2")
198 AlgorithmRegistry.register("flux.sinc", target=algorithmsLib.SincFluxControl, ConfigClass=SincFluxConfig)
int iter