CMS 3D CMS Logo

conifer.h
Go to the documentation of this file.
1 #ifndef L1TRIGGER_PHASE2L1PARTICLEFLOW_CONNIFER_H
2 #define L1TRIGGER_PHASE2L1PARTICLEFLOW_CONNIFER_H
3 #include "nlohmann/json.hpp"
4 #include <fstream>
5 #ifdef CMSSW_GIT_HASH
7 #else
8 #include <stdexcept>
9 #endif
10 
11 namespace conifer {
12 
13  /* ---
14 * Balanced tree reduce implementation.
15 * Reduces an array of inputs to a single value using the template binary operator 'Op',
16 * for example summing all elements with Op_add, or finding the maximum with Op_max
17 * Use only when the input array is fully unrolled. Or, slice out a fully unrolled section
18 * before applying and accumulate the result over the rolled dimension.
19 * Required for emulation to guarantee equality of ordering.
20 * --- */
21  constexpr int floorlog2(int x) { return (x < 2) ? 0 : 1 + floorlog2(x / 2); }
22 
23  template <int B>
24  constexpr int pow(int x) {
25  return x == 0 ? 1 : B * pow<B>(x - 1);
26  }
27 
28  constexpr int pow2(int x) { return pow<2>(x); }
29 
30  template <class T, class Op>
31  T reduce(std::vector<T> x, Op op) {
32  int N = x.size();
33  int leftN = pow2(floorlog2(N - 1)) > 0 ? pow2(floorlog2(N - 1)) : 0;
34  //static constexpr int rightN = N - leftN > 0 ? N - leftN : 0;
35  if (N == 1) {
36  return x.at(0);
37  } else if (N == 2) {
38  return op(x.at(0), x.at(1));
39  } else {
40  std::vector<T> left(x.begin(), x.begin() + leftN);
41  std::vector<T> right(x.begin() + leftN, x.end());
42  return op(reduce<T, Op>(left, op), reduce<T, Op>(right, op));
43  }
44  }
45 
46  template <class T>
47  class OpAdd {
48  public:
49  T operator()(T a, T b) { return a + b; }
50  };
51 
52  template <class T, class U>
53  class DecisionTree {
54  private:
55  std::vector<int> feature;
56  std::vector<int> children_left;
57  std::vector<int> children_right;
58  std::vector<T> threshold_;
59  std::vector<U> value_;
60  std::vector<double> threshold;
61  std::vector<double> value;
62 
63  public:
64  U decision_function(std::vector<T> x) const {
65  /* Do the prediction */
66  int i = 0;
67  while (feature[i] != -2) { // continue until reaching leaf
68  bool comparison = x[feature[i]] <= threshold_[i];
69  i = comparison ? children_left[i] : children_right[i];
70  }
71  return value_[i];
72  }
73 
74  void init_() {
75  /* Since T, U types may not be readable from the JSON, read them to double and the cast them here */
77  threshold.begin(), threshold.end(), std::back_inserter(threshold_), [](double t) -> T { return (T)t; });
78  std::transform(value.begin(), value.end(), std::back_inserter(value_), [](double v) -> U { return (U)v; });
79  }
80 
81  // Define how to read this class to/from JSON
83 
84  }; // class DecisionTree
85 
86  template <class T, class U, bool useAddTree = false>
87  class BDT {
88  private:
89  unsigned int n_classes;
90  unsigned int n_trees;
91  unsigned int n_features;
92  std::vector<double> init_predict;
93  std::vector<U> init_predict_;
94  // vector of decision trees: outer dimension tree, inner dimension class
95  std::vector<std::vector<DecisionTree<T, U>>> trees;
97 
98  public:
99  // Define how to read this class to/from JSON
101 
103  /* Construct the BDT from conifer cpp backend JSON file */
104  std::ifstream ifs(filename);
106  from_json(j, *this);
107  /* Do some transformation to initialise things into the proper emulation T, U types */
108  if (n_classes == 2)
109  n_classes = 1;
110  std::transform(init_predict.begin(), init_predict.end(), std::back_inserter(init_predict_), [](double ip) -> U {
111  return (U)ip;
112  });
113  for (unsigned int i = 0; i < n_trees; i++) {
114  for (unsigned int j = 0; j < n_classes; j++) {
115  trees.at(i).at(j).init_();
116  }
117  }
118  }
119 
120  std::vector<U> decision_function(std::vector<T> x) const {
121  /* Do the prediction */
122 #ifdef CMSSW_GIT_HASH
123  if (x.size() != n_features) {
124  throw cms::Exception("RuntimeError")
125  << "Conifer : Size of feature vector mismatches expected n_features" << std::endl;
126  }
127 #else
128  if (x.size() != n_features) {
129  throw std::runtime_error("Conifer : Size of feature vector mismatches expected n_features");
130  }
131 #endif
132  std::vector<U> values;
133  std::vector<std::vector<U>> values_trees;
134  values_trees.resize(n_classes);
135  values.resize(n_classes, U(0));
136  for (unsigned int i = 0; i < n_classes; i++) {
137  std::transform(trees.begin(),
138  trees.end(),
139  std::back_inserter(values_trees.at(i)),
140  [&i, &x](std::vector<DecisionTree<T, U>> tree_v) { return tree_v.at(i).decision_function(x); });
141  if (useAddTree) {
142  values.at(i) = init_predict_.at(i);
143  values.at(i) += reduce<U, OpAdd<U>>(values_trees.at(i), add);
144  } else {
145  values.at(i) = std::accumulate(values_trees.at(i).begin(), values_trees.at(i).end(), U(init_predict_.at(i)));
146  }
147  }
148 
149  return values;
150  }
151 
152  std::vector<double> _decision_function_double(std::vector<double> x) const {
153  /* Do the prediction with data in/out as double, cast to T, U before prediction */
154  std::vector<T> xt;
155  std::transform(x.begin(), x.end(), std::back_inserter(xt), [](double xi) -> T { return (T)xi; });
156  std::vector<U> y = decision_function(xt);
157  std::vector<double> yd;
158  std::transform(y.begin(), y.end(), std::back_inserter(yd), [](U yi) -> double { return (double)yi; });
159  return yd;
160  }
161 
162  }; // class BDT
163 
164 } // namespace conifer
165 
166 #endif
vector< string > parse(string line, const string &delimiter)
std::vector< int > feature
Definition: conifer.h:55
std::vector< double > value
Definition: conifer.h:61
Definition: APVGainStruct.h:7
nlohmann::json json
T operator()(T a, T b)
Definition: conifer.h:49
constexpr int pow(int x)
Definition: conifer.h:24
OpAdd< U > add
Definition: conifer.h:96
std::vector< double > init_predict
Definition: conifer.h:92
std::vector< T > threshold_
Definition: conifer.h:58
unsigned int n_classes
Definition: conifer.h:89
std::vector< int > children_right
Definition: conifer.h:57
T reduce(std::vector< T > x, Op op)
Definition: conifer.h:31
std::vector< U > decision_function(std::vector< T > x) const
Definition: conifer.h:120
constexpr int floorlog2(int x)
Definition: conifer.h:21
Definition: value.py:1
unsigned int n_trees
Definition: conifer.h:90
std::vector< double > threshold
Definition: conifer.h:60
BDT(std::string filename)
Definition: conifer.h:102
NLOHMANN_DEFINE_TYPE_INTRUSIVE(BDT, n_classes, n_trees, n_features, init_predict, trees)
std::vector< U > init_predict_
Definition: conifer.h:93
void from_json(const nlohmann::json &nlohmann_json_j, mkfit::LayerControl &nlohmann_json_t)
#define N
Definition: blowfish.cc:9
double b
Definition: hdecay.h:120
constexpr int pow2(int x)
Definition: conifer.h:28
double a
Definition: hdecay.h:121
NLOHMANN_DEFINE_TYPE_INTRUSIVE(DecisionTree, feature, children_left, children_right, threshold, value)
unsigned int n_features
Definition: conifer.h:91
float x
std::vector< U > value_
Definition: conifer.h:59
std::vector< std::vector< DecisionTree< T, U > > > trees
Definition: conifer.h:95
long double T
std::vector< int > children_left
Definition: conifer.h:56
std::vector< double > _decision_function_double(std::vector< double > x) const
Definition: conifer.h:152
U decision_function(std::vector< T > x) const
Definition: conifer.h:64
unsigned transform(const HcalDetId &id, unsigned transformCode)