Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-05-29 04:20:42

0001 //============================================================================
0002 // Name        : LutNeuronLayerFixedPoint.h
0003 // Author      : Karol Bunkowski
0004 // Created on: Mar 12, 2021
0005 // Version     :
0006 // Copyright   : All right reserved
0007 // Description : Fixed point LUT layer
0008 //============================================================================
0009 
0010 #ifndef L1Trigger_L1TMuonOverlapPhase2_LutNeuronlayerFixedPoint_h
0011 #define L1Trigger_L1TMuonOverlapPhase2_LutNeuronlayerFixedPoint_h
0012 
0013 #include <ap_fixed.h>
0014 #include <ap_int.h>
0015 #include <array>
0016 #include <limits>
0017 #include <iomanip>
0018 #include <cassert>
0019 
0020 #include <boost/property_tree/ptree.hpp>
0021 
0022 #include "L1Trigger/L1TMuonOverlapPhase2/interface/LutNetworkFixedPointCommon.h"
0023 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0024 #include "FWCore/Utilities/interface/Exception.h"
0025 
0026 namespace lutNN {
0027   // constexpr for ceil(log2) from stackoverflow
0028   constexpr size_t floorlog2(size_t i) {
0029     if (!(i > 0))
0030       throw cms::Exception("Incorrect input")
0031           << "Argument of floorlog2 must be grater than 0, while " << i << " used.\n";
0032     return i == 1 ? 0 : 1 + floorlog2(i >> 1);
0033   }
0034   constexpr size_t ceillog2(size_t i) {
0035     if (!(i > 0))
0036       throw cms::Exception("Incorrect input")
0037           << "Argument of ceillog2 must be grater than 0, while " << i << " used.\n";
0038     return i == 1 ? 0 : floorlog2(i - 1) + 1;
0039   }
0040 
0041   template <int input_I, int input_F, size_t inputSize, int lut_I, int lut_F, int neurons, int output_I>
0042   class LutNeuronLayerFixedPoint {
0043   public:
0044     static constexpr int input_W = input_I + input_F;
0045     static constexpr int lut_W = lut_I + lut_F;
0046 
0047     //the lut out values sum
0048     //static const int lutOutSum_I = lut_I + ceil(log2(inputSize)); //MB: ceil(log2(inputSize)) is not constexpr which makes issue for code-checks
0049     static constexpr int lutOutSum_I = lut_I + ceillog2(inputSize);
0050     static constexpr int lutOutSum_W = lutOutSum_I + lut_F;
0051 
0052     static constexpr int output_W = output_I + lut_F;
0053 
0054     //static_assert( (1<<input_I) <= lutSize);
0055     static constexpr size_t lutSize = 1 << input_I;
0056 
0057     typedef std::array<ap_ufixed<input_W, input_I, AP_TRN, AP_SAT>, inputSize> inputArrayType;
0058 
0059     typedef std::array<ap_fixed<lutOutSum_W, lutOutSum_I>, neurons> lutSumArrayType;
0060 
0061     LutNeuronLayerFixedPoint() {  //FIXME initialise name(name)
0062       //static_assert(lut_I <= (output_I - ceil(log2(inputSize)) ), "not correct lut_I, output_I  and inputSize"); //TODO
0063 
0064       LogTrace("l1tOmtfEventPrint") << "Constructing LutNeuronLayerFixedPoint " << name << "\n     input_I  "
0065                                     << std::setw(2) << input_I << "    input_F " << std::setw(2) << input_F
0066                                     << " input_W " << std::setw(2) << input_W << " inputSize " << std::setw(2)
0067                                     << inputSize << "\n       lut_I  " << std::setw(2) << lut_I << "      lut_F "
0068                                     << std::setw(2) << lut_F << "   lut_W " << std::setw(2) << lut_W << "   lutSize "
0069                                     << std::setw(2) << lutSize << "\n lutOutSum_I " << std::setw(2) << lutOutSum_I
0070                                     << " lutOutSum_W " << std::setw(2) << lutOutSum_W << "\n    output_I "
0071                                     << std::setw(2) << output_I << "    output_W " << std::setw(2) << output_W
0072                                     << "\n neurons " << std::setw(2) << neurons << "\n outOffset " << outOffset << " = "
0073                                     << std::hex << outOffset << " width " << outOffset.width << std::dec;
0074     }
0075 
0076     virtual ~LutNeuronLayerFixedPoint() {}
0077 
0078     void setName(std::string name) { this->name = name; }
0079 
0080     auto& getLutArray() { return lutArray; }
0081 
0082     void setLutArray(
0083         const std::array<std::array<std::array<ap_fixed<output_W, output_I>, lutSize>, neurons>, inputSize>& lutArray) {
0084       this->lutArray = lutArray;
0085     }
0086 
0087     void save(boost::property_tree::ptree& tree, std::string keyPath) {
0088       PUT_VAR(tree, keyPath + "." + name, input_I)
0089       PUT_VAR(tree, keyPath + "." + name, input_F)
0090       PUT_VAR(tree, keyPath + "." + name, inputSize)
0091       PUT_VAR(tree, keyPath + "." + name, lut_I)
0092       PUT_VAR(tree, keyPath + "." + name, lut_F)
0093       PUT_VAR(tree, keyPath + "." + name, neurons)
0094       PUT_VAR(tree, keyPath + "." + name, output_I)
0095 
0096       for (unsigned int iInput = 0; iInput < lutArray.size(); iInput++) {
0097         for (unsigned int iNeuron = 0; iNeuron < lutArray[iInput].size(); iNeuron++) {
0098           auto& lut = lutArray.at(iInput).at(iNeuron);
0099           std::ostringstream ostr;
0100           for (auto& a : lut) {
0101             ostr << std::fixed << std::setprecision(19) << a.to_float() << ", ";
0102           }
0103           tree.put(keyPath + "." + name + ".lutArray." + std::to_string(iInput) + "." + std::to_string(iNeuron),
0104                    ostr.str());
0105         }
0106       }
0107     }
0108 
0109     void load(boost::property_tree::ptree& tree, std::string keyPath) {
0110       CHECK_VAR(tree, keyPath + "." + name, input_I)
0111       CHECK_VAR(tree, keyPath + "." + name, input_F)
0112       CHECK_VAR(tree, keyPath + "." + name, inputSize)
0113       CHECK_VAR(tree, keyPath + "." + name, lut_I)
0114       CHECK_VAR(tree, keyPath + "." + name, lut_F)
0115       CHECK_VAR(tree, keyPath + "." + name, neurons)
0116       CHECK_VAR(tree, keyPath + "." + name, output_I)
0117 
0118       for (unsigned int iInput = 0; iInput < lutArray.size(); iInput++) {
0119         for (unsigned int iNeuron = 0; iNeuron < lutArray[iInput].size(); iNeuron++) {
0120           auto& lut = lutArray.at(iInput).at(iNeuron);
0121           auto str = tree.get<std::string>(keyPath + "." + name + ".lutArray." + std::to_string(iInput) + "." +
0122                                            std::to_string(iNeuron));
0123 
0124           std::stringstream ss(str);
0125           std::string item;
0126 
0127           for (auto& a : lut) {
0128             if (std::getline(ss, item, ',')) {
0129               a = std::stof(item, nullptr);
0130             } else {
0131               throw std::runtime_error(
0132                   "LutNeuronLayerFixedPoint::read: number of items get from file is smaller than lut size");
0133             }
0134           }
0135         }
0136       }
0137     }
0138 
0139     lutSumArrayType& runWithInterpolation(const inputArrayType& inputArray) {
0140       for (unsigned int iNeuron = 0; iNeuron < lutOutSumArray.size(); iNeuron++) {
0141         auto& lutOutSum = lutOutSumArray.at(iNeuron);
0142         lutOutSum = 0;
0143         for (unsigned int iInput = 0; iInput < inputArray.size(); iInput++) {
0144           auto address = inputArray.at(iInput).to_uint();  //address in principle is unsigned
0145           auto& lut = lutArray.at(iInput).at(iNeuron);
0146 
0147           auto addresPlus1 = address + 1;
0148           if (addresPlus1 >= lut.size())
0149             addresPlus1 = address;
0150 
0151           auto derivative = lut.at(addresPlus1) - lut.at(address);  // must be signed
0152 
0153           //N.B. the address and fractionalPart is the same for all neurons, what matters for the firmware
0154           ap_ufixed<input_W - input_I, 0> fractionalPart = inputArray.at(iInput);
0155 
0156           auto result = lut.at(address) + fractionalPart * derivative;
0157           lutOutSum += result;
0158         }
0159 
0160         lutOutSumArray.at(iNeuron) = lutOutSum;
0161       }
0162 
0163       return lutOutSumArray;
0164     }
0165 
0166     //Output without offset
0167     auto& getLutOutSum() { return lutOutSumArray; }
0168 
0169     //converts the output values from signed to unsigned by adding the offset = 1 << (output_I-1)
0170     //these values can be then directly used as inputs of the next LUT layer
0171     auto& getOutWithOffset() {
0172       for (unsigned int iOut = 0; iOut < lutOutSumArray.size(); iOut++) {
0173         outputArray[iOut] = lutOutSumArray[iOut] + outOffset;
0174       }
0175 
0176       return outputArray;
0177     }
0178 
0179     auto getName() { return name; }
0180 
0181   private:
0182     lutSumArrayType lutOutSumArray;
0183     std::array<ap_ufixed<output_W, output_I, AP_TRN, AP_SAT>, neurons> outputArray;
0184 
0185     ap_uint<output_I> outOffset = 1 << (output_I - 1);
0186 
0187     std::array<std::array<std::array<ap_fixed<lut_W, lut_I>, lutSize>, neurons>, inputSize>
0188         lutArray;  //[inputNum][outputNum =  neuronNum][address]
0189 
0190     std::string name;
0191   };
0192 
0193 } /* namespace lutNN */
0194 
0195 #endif /* L1Trigger_L1TMuonOverlapPhase2_LutNeuronlayerFixedPoint_h */