Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of daf_butler. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21 

22from __future__ import annotations 

23 

24 

25__all__ = ("DimensionPacker",) 

26 

27from abc import ABCMeta, abstractmethod 

28from typing import Any, Optional, Tuple, Type, TYPE_CHECKING, Union 

29 

30from lsst.utils import doImport 

31 

32from ..config import Config 

33from .graph import DimensionGraph 

34from .coordinate import DataCoordinate, DataId 

35 

36if TYPE_CHECKING: # Imports needed only for type annotations; may be circular. 36 ↛ 37line 36 didn't jump to line 37, because the condition on line 36 was never true

37 from .universe import DimensionUniverse 

38 

39 

40class DimensionPacker(metaclass=ABCMeta): 

41 """An abstract base class for bidirectional mappings between a 

42 `DataCoordinate` and a packed integer ID. 

43 

44 Parameters 

45 ---------- 

46 fixed : `DataCoordinate` 

47 Expanded data ID for the dimensions whose values must remain fixed 

48 (to these values) in all calls to `pack`, and are used in the results 

49 of calls to `unpack`. ``fixed.hasRecords()`` must return `True`. 

50 dimensions : `DimensionGraph` 

51 The dimensions of data IDs packed by this instance. 

52 """ 

53 

54 def __init__(self, fixed: DataCoordinate, dimensions: DimensionGraph): 

55 self.fixed = fixed 

56 self.dimensions = dimensions 

57 

58 @property 

59 def universe(self) -> DimensionUniverse: 

60 """A graph containing all known dimensions (`DimensionUniverse`). 

61 """ 

62 return self.fixed.universe 

63 

64 @property 

65 @abstractmethod 

66 def maxBits(self) -> int: 

67 """The maximum number of nonzero bits in the packed ID returned by 

68 `~DimensionPacker.pack` (`int`). 

69 

70 Must be implemented by all concrete derived classes. May return 

71 `None` to indicate that there is no maximum. 

72 """ 

73 raise NotImplementedError() 

74 

75 @abstractmethod 

76 def _pack(self, dataId: DataCoordinate) -> int: 

77 """Abstract implementation for `~DimensionPacker.pack`. 

78 

79 Must be implemented by all concrete derived classes. 

80 

81 Parameters 

82 ---------- 

83 dataId : `DataCoordinate` 

84 Dictionary-like object identifying (at least) all packed 

85 dimensions associated with this packer. Guaranteed to be a true 

86 `DataCoordinate`, not an informal data ID 

87 

88 Returns 

89 ------- 

90 packed : `int` 

91 Packed integer ID. 

92 """ 

93 raise NotImplementedError() 

94 

95 def pack(self, dataId: DataId, *, returnMaxBits: bool = False, 

96 **kwargs: Any) -> Union[Tuple[int, int], int]: 

97 """Pack the given data ID into a single integer. 

98 

99 Parameters 

100 ---------- 

101 dataId : `DataId` 

102 Data ID to pack. Values for any keys also present in the "fixed" 

103 data ID passed at construction must be the same as the values 

104 passed at construction. 

105 returnMaxBits : `bool` 

106 If `True`, return a tuple of ``(packed, self.maxBits)``. 

107 **kwargs 

108 Additional keyword arguments forwarded to 

109 `DataCoordinate.standardize`. 

110 

111 Returns 

112 ------- 

113 packed : `int` 

114 Packed integer ID. 

115 maxBits : `int`, optional 

116 Maximum number of nonzero bits in ``packed``. Not returned unless 

117 ``returnMaxBits`` is `True`. 

118 

119 Notes 

120 ----- 

121 Should not be overridden by derived class 

122 (`~DimensionPacker._pack` should be overridden instead). 

123 """ 

124 dataId = DataCoordinate.standardize(dataId, **kwargs) 

125 packed = self._pack(dataId) 

126 if returnMaxBits: 

127 return packed, self.maxBits 

128 else: 

129 return packed 

130 

131 @abstractmethod 

132 def unpack(self, packedId: int) -> DataCoordinate: 

133 """Unpack an ID produced by `pack` into a full `DataCoordinate`. 

134 

135 Must be implemented by all concrete derived classes. 

136 

137 Parameters 

138 ---------- 

139 packedId : `int` 

140 The result of a call to `~DimensionPacker.pack` on either 

141 ``self`` or an identically-constructed packer instance. 

142 

143 Returns 

144 ------- 

145 dataId : `DataCoordinate` 

146 Dictionary-like ID that uniquely identifies all covered 

147 dimensions. 

148 """ 

149 raise NotImplementedError() 

150 

151 # Class attributes below are shadowed by instance attributes, and are 

152 # present just to hold the docstrings for those instance attributes. 

153 

154 fixed: DataCoordinate 

155 """The dimensions provided to the packer at construction 

156 (`DataCoordinate`) 

157 

158 The packed ID values are only unique and reversible with these 

159 dimensions held fixed. ``fixed.hasRecords() is True`` is guaranteed. 

160 """ 

161 

162 dimensions: DimensionGraph 

163 """The dimensions of data IDs packed by this instance (`DimensionGraph`). 

164 """ 

165 

166 

167class DimensionPackerFactory: 

168 """A factory class for `DimensionPacker` instances that can be constructed 

169 from configuration. 

170 

171 This class is primarily intended for internal use by `DimensionUniverse`. 

172 """ 

173 

174 def __init__(self, fixed: DimensionGraph, dimensions: DimensionGraph, clsName: str): 

175 self.fixed = fixed 

176 self.dimensions = dimensions 

177 self._clsName = clsName 

178 self._cls: Optional[Type[DimensionPacker]] = None 

179 

180 @classmethod 

181 def fromConfig(cls, universe: DimensionUniverse, config: Config) -> DimensionPackerFactory: 

182 """Construct a `DimensionPackerFactory` from a piece of dimension 

183 configuration. 

184 

185 Parameters 

186 ---------- 

187 universe : `DimensionGraph` 

188 All dimension objects known to the `Registry`. 

189 config : `Config` 

190 A dict-like `Config` node corresponding to a single entry 

191 in the ``packers`` section of a `DimensionConfig`. 

192 """ 

193 fixed = DimensionGraph(universe=universe, names=config["fixed"]) 

194 dimensions = DimensionGraph(universe=universe, names=config["dimensions"]) 

195 clsName = config["cls"] 

196 return cls(fixed=fixed, dimensions=dimensions, clsName=clsName) 

197 

198 def __call__(self, fixed: DataCoordinate) -> DimensionPacker: 

199 """Construct a `DimensionPacker` instance for the given fixed data ID. 

200 

201 Parameters 

202 ---------- 

203 fixed : `DataCoordinate` 

204 Data ID that provides values for the "fixed" dimensions of the 

205 packer. Must be expanded with all metadata known to the 

206 `Registry`. ``fixed.hasRecords()`` must return `True`. 

207 """ 

208 assert fixed.graph.issuperset(self.fixed) 

209 if self._cls is None: 

210 self._cls = doImport(self._clsName) 

211 return self._cls(fixed, self.dimensions) 

212 

213 # Class attributes below are shadowed by instance attributes, and are 

214 # present just to hold the docstrings for those instance attributes. 

215 

216 fixed: DimensionGraph 

217 """The dimensions provided to new packers at construction 

218 (`DimensionGraph`) 

219 

220 The packed ID values are only unique and reversible with these 

221 dimensions held fixed. 

222 """ 

223 

224 dimensions: DimensionGraph 

225 """The dimensions of data IDs packed by the instances constructed by this 

226 factory (`DimensionGraph`). 

227 """