9#include "trg/cdc/NeuroTrigger3DH.h"
16#include "trg/cdc/dataobjects/CDCTrigger3DHTrack.h"
17#include "framework/gearbox/Unit.h"
18#include "framework/datastore/StoreArray.h"
19#include "framework/logging/Logger.h"
20#include "cdc/geometry/CDCGeometryPar.h"
25void NeuroTrigger3DH::initialize()
27 std::array<std::vector<int>, m_nSL> getWireLayerID;
31 for (
int i = 0; i < 8; ++i) {
32 getWireLayerID[0].push_back(layer++);
35 for (
unsigned short superLayerIdx = 1; superLayerIdx < m_nSL; ++superLayerIdx) {
36 for (
int i = 0; i < 6; ++i) {
37 getWireLayerID[superLayerIdx].push_back(layer++);
43 for (
unsigned short superLayerIdx = 0; superLayerIdx < m_nSL; ++superLayerIdx) {
44 if (superLayerIdx == 0) {
45 m_radiusWireLayer[superLayerIdx][0] = cdc.senseWireR(getWireLayerID[superLayerIdx][3]);
46 m_radiusWireLayer[superLayerIdx][1] = cdc.senseWireR(getWireLayerID[superLayerIdx][4]);
48 m_radiusWireLayer[superLayerIdx][0] = cdc.senseWireR(getWireLayerID[superLayerIdx][2]);
49 m_radiusWireLayer[superLayerIdx][1] = cdc.senseWireR(getWireLayerID[superLayerIdx][3]);
51 m_nWires[superLayerIdx] = cdc.nWiresInLayer(getWireLayerID[superLayerIdx][0]);
54 m_cumulativeWires[0] = 0;
55 for (
unsigned short superLayerIdx = 1; superLayerIdx <= m_nSL; ++superLayerIdx) {
56 m_cumulativeWires[superLayerIdx] = m_cumulativeWires[superLayerIdx - 1] + m_nWires[superLayerIdx - 1];
61void NeuroTrigger3DH::initializeCollections(std::string hitCollectionName)
63 m_segmentHits.isRequired(hitCollectionName);
64 m_hitCollectionName = hitCollectionName;
71 m_neuroParameters3DH = mlp.getNeuroParameters();
75void NeuroTrigger3DH::createIntWeights()
77 const std::vector<float>& floatWeights = m_MLP.getFloatWeights();
78 float minFloatWeight = *std::min_element(floatWeights.begin(), floatWeights.end());
79 float maxFloatWeight = *std::max_element(floatWeights.begin(), floatWeights.end());
80 float maxAbsoluteWeight = std::max(std::abs(minFloatWeight), std::abs(maxFloatWeight));
83 int shift =
static_cast<int>(std::ceil(std::log2(maxAbsoluteWeight)));
84 int fractionalWeightBits = m_precisionWeights - shift;
87 std::vector<int32_t> intWeights(floatWeights.size());
88 std::transform(floatWeights.begin(), floatWeights.end(), intWeights.begin(),
89 [fractionalWeightBits](
float w) { return static_cast<int32_t>(std::round(w * (1 << fractionalWeightBits))); });
91 m_intWeights = intWeights;
92 m_fractionalWeightBits = fractionalWeightBits;
98 const double omega = track.getOmega();
99 const double phi = track.getPhi0();
100 for (
unsigned short superLayerIdx = 0; superLayerIdx < m_nSL; ++superLayerIdx) {
101 for (
int priorityLayer = 0; priorityLayer < 2; ++priorityLayer) {
102 double radiusWireLayer = m_radiusWireLayer[superLayerIdx][priorityLayer];
103 double alpha = (radiusWireLayer * std::abs(omega) < 2.0)
104 ? std::asin(radiusWireLayer * omega / 2.0)
106 double referenceID = (phi - alpha) * m_nWires[superLayerIdx] / (2.0 * M_PI);
107 m_alpha[superLayerIdx][priorityLayer] = alpha;
108 m_referenceID[superLayerIdx][priorityLayer] = std::remainder(referenceID, m_nWires[superLayerIdx]);
114void NeuroTrigger3DH::calculateTrackParametersFixedPrecision(
const CDCTrigger3DHTrack& track)
116 const long scalePhi = 1L << m_precisionPhi;
117 const long scaleAlpha = 1L << m_precisionAlpha;
118 const long scaleFactor = 1L << m_precisionScaleFactor;
119 const long scaleID = 1L << m_precisionReferenceID;
121 const double omega = track.getOmega();
122 const long phiFixed = std::round(track.getPhi0() * scalePhi);
124 for (
unsigned short int superLayerIdx = 0; superLayerIdx < m_nSL; ++superLayerIdx) {
125 const long scaledWireFactor = std::round((m_nWires[superLayerIdx] / (2.0 * M_PI)) * scaleFactor);
126 for (
int priorityLayer = 0; priorityLayer < 2; ++priorityLayer) {
127 const double radius = m_radiusWireLayer[superLayerIdx][priorityLayer];
128 long alphaFixed = (radius * std::abs(omega) < 2.0)
129 ? std::round(std::asin(radius * omega / 2.0) * scaleAlpha)
130 : std::round(M_PI_2 * scaleAlpha);
131 long deltaPhi = phiFixed - alphaFixed;
132 long refIDFixed = (deltaPhi * scaledWireFactor) / (1L << (m_precisionPhi + m_precisionScaleFactor - m_precisionReferenceID));
133 m_alpha[superLayerIdx][priorityLayer] =
static_cast<double>(alphaFixed) / scaleAlpha;
134 m_referenceID[superLayerIdx][priorityLayer] =
static_cast<double>(refIDFixed) / scaleID;
142 m_T0 = getLowestTime(track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName));
154 int lowestTime = 9999;
155 for (
unsigned int hitIdx = 0; hitIdx < trackSegmentHits.
size(); ++hitIdx) {
156 if (trackSegmentHits.
weight(hitIdx) < 0)
continue;
157 if (trackSegmentHits[hitIdx]->priorityTime() < lowestTime) {
158 lowestTime = trackSegmentHits[hitIdx]->priorityTime();
167 RelationVector<CDCTriggerSegmentHit> trackSegmentHits =
168 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
169 std::vector<size_t> allHitIDs;
170 for (
size_t hitIdx = 0; hitIdx < trackSegmentHits.
size(); ++hitIdx) {
171 if (trackSegmentHits.
weight(hitIdx) >= 0) {
172 allHitIDs.push_back(trackSegmentHits[hitIdx]->getArrayIndex());
179std::vector<float> NeuroTrigger3DH::getInputVector(
const std::vector<size_t>& hitIds)
const
181 const size_t nInput = m_neuroParameters3DH.nInput;
183 if (nInput != m_nStandardInputNodes && nInput != m_nExtendedInputNodes) {
184 B2FATAL(
"Invalid neural network input size");
187 constexpr unsigned short tMax = 256;
189 const bool hasExtendedInput = (nInput == m_nExtendedInputNodes);
190 const size_t featuresPerHit = nInput / m_nSL;
191 const size_t patternSize = 11;
193 std::vector<float> inputVector(nInput, 0.0f);
194 for (
const size_t hitIdx : hitIds) {
195 const CDCTriggerSegmentHit* trackSegmentHit = m_segmentHits[hitIdx];
197 const unsigned short superLayerIdx = trackSegmentHit->
getISuperLayer();
198 const size_t priorityLayer = (priority == 3) ? 0 : 1;
199 const size_t baseIdx = featuresPerHit * superLayerIdx;
201 const float relativeID = scaleRelativeID(getRelativeID(*trackSegmentHit), superLayerIdx);
202 const float alpha = m_alpha[superLayerIdx][priorityLayer];
203 const float scaledDriftTime = getScaledDriftTime(*trackSegmentHit, tMax);
205 inputVector[baseIdx] = relativeID;
206 inputVector[baseIdx + 1] = scaledDriftTime;
207 inputVector[baseIdx + 2] = alpha;
209 if (m_hasT0 && hasExtendedInput) {
211 for (
size_t j = 0; j < patternSize; ++j) {
212 inputVector[baseIdx + 3 + j] = (hitPattern >> j) & 1;
224 const unsigned int priorityLayer = (priority == 3) ? 0 : 1;
226 constexpr std::array<short, 4> priorityOffsetFactor = {0, -1, 1, 0};
228 - m_cumulativeWires[superLayerIdx]
229 + 0.5 * priorityOffsetFactor[priority]
230 - m_referenceID[superLayerIdx][priorityLayer];
231 return std::remainder(relativeID, m_nWires[superLayerIdx]);
235float NeuroTrigger3DH::scaleRelativeID(
const double relativeID,
const unsigned superLayerIdx)
const
238 constexpr std::array<float, 18> relevantID =
239 {-1.5, 1.5, -7.5, -0.5, -1.5, 1.5, 0.5, 7.5, -1.5, 1.5, -8.5, 0.5, -2.5, 1.5, -0.5, 10.5, -3.5, 2.5};
240 const float idMin = relevantID[2 * superLayerIdx];
241 const float idMax = relevantID[2 * superLayerIdx + 1];
242 const float range = idMax - idMin;
243 float scale = 2.0f / range;
245 scale = std::pow(2.0f, std::floor(std::log2(scale)));
246 const float offset = (idMin + idMax) * 0.5f;
247 return scale *
static_cast<float>(relativeID - offset);
251float NeuroTrigger3DH::getScaledDriftTime(
const CDCTriggerSegmentHit& trackSegmentHit,
const unsigned short maxTime)
const
253 const float scaleT = std::pow(2.0f, std::floor(std::log2(1.0f / maxTime)));
254 int driftTime = (m_hasT0) ? trackSegmentHit.
priorityTime() - m_T0 : 0;
255 driftTime = std::clamp(driftTime, 0,
static_cast<int>(maxTime));
257 constexpr std::array<short, 4> leftRightFactor = {0, -1, 1, 0};
258 float scaledDriftTime = leftRightFactor[trackSegmentHit.
getLeftRight()] * driftTime * scaleT;
259 return scaledDriftTime;
263std::vector<float> NeuroTrigger3DH::runMLP(
const std::vector<float>& input)
const
265 const CDCTrigger3DHMLP& network = m_MLP;
266 const std::vector<float>& weights = network.getFloatWeights();
268 std::vector<float> layerInput = input;
269 std::vector<float> layerOutput;
270 size_t weightIndex = 0;
272 const size_t nLayers = network.getNumberOfLayers();
273 for (
size_t layerIdx = 1; layerIdx < nLayers; ++layerIdx) {
274 layerInput.push_back(1.0f);
275 const size_t nInputs = layerInput.size();
276 const size_t nOutputs = network.getNumberOfNodes(layerIdx);
277 layerOutput.assign(nOutputs, 0.0f);
278 for (
size_t outIdx = 0; outIdx < nOutputs; ++outIdx) {
279 for (
size_t inputIdx = 0; inputIdx < nInputs; ++inputIdx) {
280 layerOutput[outIdx] += layerInput[inputIdx] * weights[weightIndex++];
282 layerOutput[outIdx] = std::tanh(layerOutput[outIdx]);
284 layerInput = layerOutput;
286 return unscaleTarget(layerOutput);
290std::vector<float> NeuroTrigger3DH::runMLPFixedPrecision(
const std::vector<float>& input)
const
292 const CDCTrigger3DHMLP& network = m_MLP;
293 const int fractionalInputBits = m_precisionInputs - 1;
296 std::vector<int32_t> layerInput(input.size());
297 std::transform(input.begin(), input.end(), layerInput.begin(),
298 [](
float i) { return static_cast<int32_t>(std::round(i * (1 << fractionalInputBits))); });
300 std::vector<int32_t> layerOutput;
301 size_t weightIndex = 0;
302 const size_t nLayers = network.getNumberOfLayers();
303 for (
size_t layerIdx = 1; layerIdx < nLayers; ++layerIdx) {
305 unsigned int scalingShift;
307 layerInput.push_back(1 << fractionalInputBits);
308 scalingShift = fractionalInputBits + m_fractionalWeightBits - TanhLUT::LUT_FRAC_BITS;
310 layerInput.push_back(1 << TanhLUT::LUT_FRAC_BITS);
311 scalingShift = TanhLUT::LUT_FRAC_BITS + m_fractionalWeightBits - TanhLUT::LUT_FRAC_BITS;
313 const size_t nOutputs = network.getNumberOfNodes(layerIdx);
314 const size_t nInputs = layerInput.size();
315 layerOutput.assign(nOutputs, 0);
316 for (
size_t outIdx = 0; outIdx < nOutputs; ++outIdx) {
317 int64_t mulAccSum = 0;
318 for (
size_t inputIdx = 0; inputIdx < nInputs; ++inputIdx) {
319 mulAccSum +=
static_cast<int64_t
>(layerInput[inputIdx]) *
static_cast<int64_t
>(m_intWeights[weightIndex++]);
321 int mulAccSumSign = (mulAccSum < 0) ? -1 : 1;
325 const int high = scalingShift + TanhLUT::LUT_FRAC_BITS + TanhLUT::MAX_FLOAT_SHIFT + TanhLUT::HEADROOM_BITS;
326 const int low = scalingShift;
327 uint32_t lutInput =
static_cast<uint32_t
>(std::abs(extractBits(mulAccSum, high, low)));
330 if (lutInput >= TanhLUT::LUT_SATURATION) {
331 outFix =
static_cast<int32_t
>(mulAccSumSign * ((1 << TanhLUT::LUT_FRAC_BITS) - 1));
334 size_t lutIdx =
static_cast<size_t>((lutInput * TanhLUT::LUT_SIZE) / TanhLUT::LUT_SATURATION);
335 int32_t lutVal = TanhLUT::get(lutIdx);
336 outFix =
static_cast<int32_t
>(mulAccSumSign * lutVal);
338 layerOutput[outIdx] = outFix;
340 layerInput = layerOutput;
343 std::vector<float> output(layerOutput.size(), 0.0f);
344 std::transform(layerOutput.begin(), layerOutput.end(), output.begin(),
345 [](int32_t val) { return val / static_cast<float>(1 << TanhLUT::LUT_FRAC_BITS); });
346 return unscaleTarget(output);
350std::vector<float> NeuroTrigger3DH::unscaleTarget(
const std::vector<float>& target)
const
352 std::vector<float> unscaled(target.size());
353 for (
size_t i = 0; i < target.size(); ++i) {
354 float min = m_neuroParameters3DH.outputScale[2 * i];
355 float max = m_neuroParameters3DH.outputScale[2 * i + 1];
360 float range = max - min;
361 unscaled[i] = (target[i] + 1.0f) * range * 0.5f + min;
367std::vector<float> NeuroTrigger3DH::scaleTarget(
const std::vector<float>& target)
const
369 std::vector<float> scaled(target.size());
370 for (
size_t i = 0; i < target.size(); ++i) {
371 float min = m_neuroParameters3DH.outputScale[2 * i];
372 float max = m_neuroParameters3DH.outputScale[2 * i + 1];
377 float range = max - min;
378 scaled[i] = 2.0f * (target[i] - min) / range - 1.0f;
Combination of several CDCHits to a track segment hit for the trigger.
short priorityTime() const
get hit time of priority cell in trigger clocks
unsigned short getPriorityPosition() const
get position of the priority cell within the track segment (0: no hit, 3: 1st priority,...
unsigned int gethitpattern() const
get hit pattern in a segment hit
unsigned short getSegmentID() const
get continuous ID of the track segment [0, 2335]
unsigned short getISuperLayer() const
get super layer number.
unsigned short getLeftRight() const
get position of the priority cell relative to the track (0: no hit, 1: right, 2: left,...
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
Class for type safe access to objects that are referred to in relations.
size_t size() const
Get number of relations.
float weight(int index) const
Get weight with index.
static const double deg
degree to radians
Abstract base class for different kinds of events.