Brush C++ API
A flexible interpretable machine learning framework
Loading...
Searching...
No Matches
node.cpp
Go to the documentation of this file.
1#include "node.h"
2
3namespace Brush {
4
5ostream& operator<<(ostream& os, const NodeType& nt)
6{
7 os << "nt: " << nt << endl;
8 os << NodeTypeName.at(nt);
9 return os;
10}
11
12ostream& operator<<(ostream& os, const Node& n)
13{
14 os << n.get_name();
15 return os;
16}
17
20auto Node::get_name(bool include_weight) const noexcept -> std::string
21{
22 constexpr float atol = 1e-6f;
23 const bool weight_is_one = std::fabs(W - 1.0f) <= atol;
24
26 {
27 if (is_weighted && !weight_is_one && include_weight)
28 return fmt::format("{:.2f}*{}", W, feature);
29 else
30 return feature;
31 }
32 else if (Is<NodeType::Constant>(node_type) && include_weight)
33 {
34 return fmt::format("{:.2f}", W);
35 }
37 {
38 // this will show (MeanLabel) in the terminal name so we can differentiate
39 // a meanLabel from a constant.
40 return fmt::format("{:.2f}", W);
41 }
43 {
44 if (is_weighted && !weight_is_one)
45 return fmt::format("{:.2f}+Add", W);
46
47 return "Sum";
48 }
49 else if (is_weighted && !weight_is_one && include_weight)
50 {
51 return fmt::format("{:.2f}*{}", W, name);
52 }
53
54 return name;
55}
56
57
58string Node::get_model(const vector<string>& children) const noexcept
59{
60 if (children.empty())
61 return get_name();
63 return fmt::format("If({}>={:.2f},{},{})",
64 feature,
65 W,
66 children.at(0),
67 children.at(1)
68 );
69 }
71 if (arg_types.at(0) == DataType::ArrayB)
72 {
73 // booleans dont use thresholds (they are used directly as mask in split)
74 return fmt::format("If({},{},{})",
75 children.at(0),
76 children.at(1),
77 children.at(2)
78 );
79 }
80 // integers or floating points (they have a threshold)
81 return fmt::format("If({}>={:.2f},{},{})",
82 children.at(0),
83 W,
84 children.at(1),
85 children.at(2)
86 );
87 }
89 string args = "";
90
91 if (is_weighted && W != 1.0)
92 args = fmt::format("{:.2f},", W);
93
94 for (int i = 0; i < children.size(); ++i){
95 args += children.at(i);
96 if (i < children.size()-1)
97 args += ",";
98 }
99
100 return fmt::format("Add({})", args);
101 }
102 else{
103 string args = "";
104 for (int i = 0; i < children.size(); ++i){
105 args += children.at(i);
106 if (i < children.size()-1)
107 args += ",";
108 }
109
110 return fmt::format("{}({})", get_name(), args);
111 }
112
113}
114
116// serialization
117// serialization for Node
118// using json = nlohmann::json;
119
120void to_json(json& j, const Node& p)
121{
122 j = json{
123 {"name", p.name},
124 // center_op is always set to false --> not implemented yet
125 {"center_op", false},
126 {"node_is_fixed", p.node_is_fixed},
127 {"weight_is_fixed", p.weight_is_fixed},
128 {"prob_change", p.prob_change},
129 {"is_weighted", p.is_weighted},
130 {"W", p.W},
131 {"node_type", p.node_type},
132 {"sig_hash", p.sig_hash},
133 {"sig_dual_hash", p.sig_dual_hash},
134 {"ret_type", p.ret_type},
135 {"arg_types", p.arg_types},
136 {"feature", p.get_feature()},
137 {"feature_type", p.get_feature_type()}
138 // {"node_hash", p.get_node_hash()}
139 };
140}
141
142using NT = NodeType;
144{
145 // if (Is<
146 // NT::Add,
147 // NT::Mul,
148 // NT::Min,
149 // NT::Max
150 // >(nt))
151 // return Signature<ArrayXf(ArrayXf,ArrayXf)>{};
152 NT n = node.node_type;
153 if (Is<
154 NT::Abs,
155 NT::Acos,
156 NT::Asin,
157 NT::Atan,
158 NT::Cos,
159 NT::Cosh,
160 NT::Sin,
161 NT::Sinh,
162 NT::Tan,
163 NT::Tanh,
164 NT::Ceil,
165 NT::Floor,
166 NT::Exp,
167 NT::Log,
169 NT::Log1p,
170 NT::Sqrt,
174 NT::OffsetSum, // unary version
176 >(n))
177 {
178 node.set_signature<Signature<ArrayXf(ArrayXf)>>();
179 }
180 else if (Is<
181 NT::Add,
182 NT::Sub,
183 NT::Mul,
184 NT::Div,
185 NT::Pow,
188 >(n))
189 {
190 node.set_signature<Signature<ArrayXf(ArrayXf,ArrayXf)>>();
191 }
192 else if (Is<
193 NT::And,
194 NT::Or
195 >(n))
196 {
198 }
199 else if (Is<
200 NT::Not
201 >(n))
202 {
204 }
205 else if (Is<
206 NT::Geq
207 >(n))
208 {
209 node.set_signature<Signature<ArrayXb(ArrayXf,ArrayXf)>>();
210 }
211 else if (Is<
213 >(n))
214 {
216 }
217 else if (Is<
219 NT::After,
221 >(n))
222 {
224 }
225 else if (Is<
227 >(n))
228 {
229 node.set_signature<Signature<ArrayXf(TimeSeriesf)>>();
230 }
231 else if (Is<
233 >(n))
234 {
235 node.set_signature<Signature<ArrayXi(ArrayXXf)>>();
236 }
237 else if (Is<
238 NT::Min,
239 NT::Max,
240 NT::Mean,
242 NT::Sum,
243 // NT::OffsetSum,
244 NT::Prod,
246 // NT::SplitOn,
248 >(n))
249 {
250 auto msg = fmt::format("Can't infer arguments for {} from json."
251 " Please provide them.\n",n);
253 }
254 else if (Is<NT::SplitOn>(n))
255 {
256 // lets make split on always defaults to floats (so it will work
257 // regardless of datatype).
258 // This only matters for weakly defined nodes when doing manual
259 // construction of brush programs as json objects, and this behavior
260 // can be ignored by avoiding the need of generating the signature
261 // (that is, defining the node with missing hash values and ret types)
262 node.set_signature<Signature<ArrayXf(ArrayXb,ArrayXf,ArrayXf)>>();
263 }
264 else if (Is<NT::Constant>(n))
265 {
266 // "feature" starts with "const"
267 char last_char = node.feature.back();
268
269 switch (last_char) {
270 case 'F':
271 node.set_signature<Signature<ArrayXf()>>();
272 break;
273 case 'I':
275 break;
276 case 'B':
278 break;
279 default:
280 node.set_signature<Signature<ArrayXf()>>();
281 }
282 }
283 else if (Is<NT::MeanLabel>(n))
285 else if (Is<NT::Terminal>(n))
286 {
287 // For terminals, use feature_type to determine the correct signature
288 switch (node.get_feature_type()) {
289 case DataType::ArrayB:
291 break;
292 case DataType::ArrayI:
294 break;
295 case DataType::ArrayF:
296 default:
297 node.set_signature<Signature<ArrayXf()>>();
298 break;
299 }
300 }
301 else
302 node.set_signature<Signature<ArrayXf()>>();
303}
304
305void from_json(const json &j, Node& p)
306{
307 // This serialization tries to build the nodes with the fewest information possible,
308 // so interface is easier when doing manual generation of trees.
309
310 // First we start with required information, then we set the optional ones
311 // (they can be inferred from the required ones)
312
313 if (j.contains("node_type"))
314 j.at("node_type").get_to(p.node_type);
315 else
316 HANDLE_ERROR_THROW("Node json must contain node_type");
317
318 if (j.contains("name"))
319 j.at("name").get_to(p.name);
320 else
322
323 // used in split nodes
324 if (j.contains("feature"))
325 {
326 j.at("feature").get_to(p.feature);
327 }
328
329 if (j.contains("feature_type"))
330 {
331 j.at("feature_type").get_to(p.feature_type);
332 }
333
334 // if node has a ret_type and arg_types, get them. if not we need to make
335 // a signature
336 bool make_signature=false;
337
338 if (j.contains("ret_type"))
339 j.at("ret_type").get_to(p.ret_type);
340 else
341 make_signature=true;
342 if (j.contains("arg_types"))
343 j.at("arg_types").get_to(p.arg_types);
344 else
345 make_signature=true;
346 if (j.contains("sig_hash"))
347 j.at("sig_hash").get_to(p.sig_hash);
348 else
349 make_signature=true;
350 if (j.contains("sig_dual_hash"))
351 j.at("sig_dual_hash").get_to(p.sig_dual_hash);
352 else
353 make_signature=true;
354
355 if (make_signature){
356 p.is_weighted = false; // TODO: remove this line
358 }
359
360 // after this point we set attributes that are modified in init
361 p.init();
362 // center_op is intentionally ignored
363 p.center_op = false;
364
365 // these below needs to be set after init(), since `init` sets these values
366 if (j.contains("node_is_fixed"))
367 {
368 j.at("node_is_fixed").get_to(p.node_is_fixed);
369 }
370
371 if (j.contains("weight_is_fixed"))
372 {
373 j.at("weight_is_fixed").get_to(p.weight_is_fixed);
374 }
375
376 if (j.contains("is_weighted"))
377 {
378 j.at("is_weighted").get_to(p.is_weighted);
379 }
380
381 if (j.contains("prob_change"))
382 {
383 j.at("prob_change").get_to(p.prob_change);
384 }
385
386 if (j.contains("W"))
387 {
388 j.at("W").get_to(p.W);
389 }
390}
391
392}
#define HANDLE_ERROR_THROW(err)
Definition error.h:27
TimeSeries< float > TimeSeriesf
Definition types.h:112
< nsga2 selection operator for getting the front
Definition bandit.cpp:3
ostream & operator<<(ostream &os, DataType n)
NodeType
Definition nodetype.h:31
Eigen::Array< bool, Eigen::Dynamic, 1 > ArrayXb
Definition types.h:39
void from_json(const json &j, Fitness &f)
Definition fitness.cpp:31
auto Is(NodeType nt) -> bool
Definition node.h:317
void to_json(json &j, const Fitness &f)
Definition fitness.cpp:6
void init_node_with_default_signature(Node &node)
Definition node.cpp:143
NodeType NT
Definition node.cpp:142
std::map< NodeType, std::string > NodeTypeName
Definition nodetype.cpp:81
Eigen::Array< int, Eigen::Dynamic, 1 > ArrayXi
Definition types.h:40
class holding the data for a node in a tree.
Definition node.h:89
bool center_op
whether to center the operator in pretty printing
Definition node.h:127
std::vector< DataType > arg_types
argument data types
Definition node.h:99
void set_signature()
Definition node.h:166
DataType feature_type
feature type for terminals or splitting nodes
Definition node.h:310
bool weight_is_fixed
whether the weight should be kept during variation. Notice that weight_is_fixed alows us to fix the w...
Definition node.h:114
NodeType node_type
the node type
Definition node.h:94
DataType get_feature_type() const
Definition node.h:288
DataType ret_type
return data type
Definition node.h:97
string get_feature() const
Definition node.h:285
std::size_t sig_hash
a hash of the signature
Definition node.h:101
void init()
Definition node.h:175
float prob_change
chance of node being selected for variation. This will take into account if the node is fixed,...
Definition node.h:119
string get_name(bool include_weight=true) const noexcept
gets a string version of the node for printing.
Definition node.cpp:20
float W
the weights of the node. also used for splitting thresholds.
Definition node.h:122
bool is_weighted
whether this node is weighted (ignored in nodes that must have weights, such as meanLabel,...
Definition node.h:116
bool node_is_fixed
whether the node is replaceable. Weights are still optimized.
Definition node.h:112
string feature
feature name for terminals or splitting nodes
Definition node.h:307
string name
full name of the node, with types
Definition node.h:92
std::size_t sig_dual_hash
a hash of the dual of the signature (for NLS)
Definition node.h:103
string get_model(const vector< string > &) const noexcept
Definition node.cpp:58