Coverage for python/lsst/pipe/base/graph/quantumNode.py: 63%

Shortcuts 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

52 statements  

1# This file is part of pipe_base. 

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/>. 

21from __future__ import annotations 

22import uuid 

23 

24__all__ = ("QuantumNode", "NodeId", "BuildId") 

25 

26from dataclasses import dataclass 

27from pydantic import BaseModel 

28from typing import Dict, NewType, Optional, Tuple 

29 

30from ..pipeline import TaskDef 

31from lsst.daf.butler import (Quantum, DatasetRef, SerializedQuantum, DimensionUniverse, DimensionRecord, 

32 DimensionRecordsAccumulator) 

33 

34BuildId = NewType("BuildId", str) 

35 

36 

37def _hashDsRef(ref: DatasetRef) -> int: 

38 return hash((ref.datasetType, ref.dataId)) 

39 

40 

41@dataclass(frozen=True, eq=True) 

42class NodeId: 

43 """Deprecated, this class is used with QuantumGraph save formats of 

44 1 and 2 when unpicking objects and must be retained until those formats 

45 are considered unloadable. 

46 

47 This represents an unique identifier of a node within an individual 

48 construction of a `QuantumGraph`. This identifier will stay constant 

49 through a pickle, and any `QuantumGraph` methods that return a new 

50 `QuantumGraph`. 

51 

52 A `NodeId` will not be the same if a new graph is built containing the same 

53 information in a `QuantumNode`, or even built from exactly the same inputs. 

54 

55 `NodeId`s do not play any role in deciding the equality or identity (hash) 

56 of a `QuantumNode`, and are mainly useful in debugging or working with 

57 various subsets of the same graph. 

58 

59 This interface is a convenance only, and no guarantees on long term 

60 stability are made. New implementations might change the `NodeId`, or 

61 provide more or less guarantees. 

62 """ 

63 number: int 

64 """The unique position of the node within the graph assigned at graph 

65 creation. 

66 """ 

67 buildId: BuildId 

68 """Unique identifier created at the time the originating graph was created 

69 """ 

70 

71 

72@dataclass(frozen=True) 

73class QuantumNode: 

74 """This class represents a node in the quantum graph. 

75 

76 The quantum attribute represents the data that is to be processed at this 

77 node. 

78 """ 

79 quantum: Quantum 

80 """The unit of data that is to be processed by this graph node""" 

81 taskDef: TaskDef 

82 """Definition of the task that will process the `Quantum` associated with 

83 this node. 

84 """ 

85 nodeId: uuid.UUID 

86 """The unique position of the node within the graph assigned at graph 

87 creation. 

88 """ 

89 

90 def __post_init__(self): 

91 # use setattr here to preserve the frozenness of the QuantumNode 

92 self._precomputedHash: int 

93 object.__setattr__(self, "_precomputedHash", hash((self.taskDef.label, self.quantum))) 

94 

95 def __eq__(self, other: object) -> bool: 

96 if not isinstance(other, QuantumNode): 

97 return False 

98 if self.quantum != other.quantum: 

99 return False 

100 return self.taskDef == other.taskDef 

101 

102 def __hash__(self) -> int: 

103 """For graphs it is useful to have a more robust hash than provided 

104 by the default quantum id based hashing 

105 """ 

106 return self._precomputedHash 

107 

108 def __repr__(self): 

109 """Make more human readable string representation. 

110 """ 

111 return (f"{self.__class__.__name__}(quantum={self.quantum}, " 

112 f"taskDef={self.taskDef}, nodeId={self.nodeId})") 

113 

114 def to_simple(self, accumulator: Optional[DimensionRecordsAccumulator] = None) -> SerializedQuantumNode: 

115 return SerializedQuantumNode(quantum=self.quantum.to_simple(accumulator=accumulator), 

116 taskLabel=self.taskDef.label, 

117 nodeId=self.nodeId) 

118 

119 @classmethod 

120 def from_simple(cls, simple: SerializedQuantumNode, taskDefMap: Dict[str, TaskDef], 

121 universe: DimensionUniverse, 

122 recontitutedDimensions: Optional[Dict[int, Tuple[str, DimensionRecord]]] = None 

123 ) -> QuantumNode: 

124 return QuantumNode(quantum=Quantum.from_simple(simple.quantum, universe, 

125 reconstitutedDimensions=recontitutedDimensions), 

126 taskDef=taskDefMap[simple.taskLabel], 

127 nodeId=simple.nodeId) 

128 

129 

130class SerializedQuantumNode(BaseModel): 

131 quantum: SerializedQuantum 

132 taskLabel: str 

133 nodeId: uuid.UUID 

134 

135 @classmethod 

136 def direct(cls, *, quantum, taskLabel, nodeId): 

137 node = SerializedQuantumNode.__new__(cls) 

138 setter = object.__setattr__ 

139 setter(node, 'quantum', SerializedQuantum.direct(**quantum)) 

140 setter(node, 'taskLabel', taskLabel) 

141 setter(node, 'nodeId', uuid.UUID(nodeId)) 

142 setter(node, '__fields_set__', {"quantum", "taskLabel", "nodeId"}) 

143 return node