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
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
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
24__all__ = ("QuantumNode", "NodeId", "BuildId")
26from dataclasses import dataclass
27from pydantic import BaseModel
28from typing import Dict, NewType, Optional, Tuple
30from ..pipeline import TaskDef
31from lsst.daf.butler import (Quantum, DatasetRef, SerializedQuantum, DimensionUniverse, DimensionRecord,
32 DimensionRecordsAccumulator)
34BuildId = NewType("BuildId", str)
37def _hashDsRef(ref: DatasetRef) -> int:
38 return hash((ref.datasetType, ref.dataId))
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.
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`.
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.
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.
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 """
72@dataclass(frozen=True)
73class QuantumNode:
74 """This class represents a node in the quantum graph.
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 """
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)))
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
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
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})")
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)
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)
130class SerializedQuantumNode(BaseModel):
131 quantum: SerializedQuantum
132 taskLabel: str
133 nodeId: uuid.UUID
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