Brush C++ API
A flexible interpretable machine learning framework
Toggle main menu visibility
Loading...
Searching...
No Matches
node.cpp
Go to the documentation of this file.
1
#include "
node.h
"
2
3
namespace
Brush
{
4
5
ostream&
operator<<
(ostream& os,
const
NodeType
& nt)
6
{
7
os <<
"nt: "
<< nt << endl;
8
os <<
NodeTypeName
.at(nt);
9
return
os;
10
}
11
12
ostream&
operator<<
(ostream& os,
const
Node
& n)
13
{
14
os << n.
get_name
();
15
return
os;
16
}
17
20
auto
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
25
if
(
Is<NodeType::Terminal>
(
node_type
))
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
}
36
else
if
(
Is<NodeType::MeanLabel>
(
node_type
))
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
}
42
else
if
(
Is<NodeType::OffsetSum>
(
node_type
))
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
58
string
Node::get_model
(
const
vector<string>& children)
const
noexcept
59
{
60
if
(children.empty())
61
return
get_name
();
62
else
if
(
Is<NodeType::SplitBest>
(
node_type
)){
63
return
fmt::format(
"If({}>={:.2f},{},{})"
,
64
feature
,
65
W
,
66
children.at(0),
67
children.at(1)
68
);
69
}
70
else
if
(
Is<NodeType::SplitOn>
(
node_type
)){
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
}
88
else
if
(
Is<NodeType::OffsetSum>
(
node_type
)){
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
120
void
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
142
using
NT
=
NodeType
;
143
void
init_node_with_default_signature
(
Node
& node)
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
,
168
NT::Logabs
,
169
NT::Log1p
,
170
NT::Sqrt
,
171
NT::Sqrtabs
,
172
NT::Square
,
173
NT::Logistic
,
174
NT::OffsetSum
,
// unary version
175
NT::CustomUnaryOp
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
,
186
NT::SplitBest
,
187
NT::CustomSplit
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
{
197
node.
set_signature
<
Signature
<
ArrayXb
(
ArrayXb
,
ArrayXb
)>>();
198
}
199
else
if
(
Is
<
200
NT::Not
201
>(n))
202
{
203
node.
set_signature
<
Signature
<
ArrayXb
(
ArrayXb
)>>();
204
}
205
else
if
(
Is
<
206
NT::Geq
207
>(n))
208
{
209
node.
set_signature
<
Signature
<
ArrayXb
(ArrayXf,ArrayXf)>>();
210
}
211
else
if
(
Is
<
212
NT::Equals
213
>(n))
214
{
215
node.
set_signature
<
Signature
<
ArrayXb
(
ArrayXi
,
ArrayXi
)>>();
216
}
217
else
if
(
Is
<
218
NT::Before
,
219
NT::After
,
220
NT::During
221
>(n))
222
{
223
node.
set_signature
<
Signature
<
TimeSeriesf
(
TimeSeriesf
,
TimeSeriesf
)>>();
224
}
225
else
if
(
Is
<
226
NT::Count
227
>(n))
228
{
229
node.
set_signature
<
Signature
<ArrayXf(
TimeSeriesf
)>>();
230
}
231
else
if
(
Is
<
232
NT::ArgMax
233
>(n))
234
{
235
node.
set_signature
<
Signature
<
ArrayXi
(ArrayXXf)>>();
236
}
237
else
if
(
Is
<
238
NT::Min
,
239
NT::Max
,
240
NT::Mean
,
241
NT::Median
,
242
NT::Sum
,
243
// NT::OffsetSum,
244
NT::Prod
,
245
NT::Softmax
,
246
// NT::SplitOn,
247
NT::SplitBest
248
>(n))
249
{
250
auto
msg = fmt::format(
"Can't infer arguments for {} from json."
251
" Please provide them.\n"
,n);
252
HANDLE_ERROR_THROW
(msg);
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'
:
274
node.
set_signature
<
Signature
<
ArrayXi
()>>();
275
break
;
276
case
'B'
:
277
node.
set_signature
<
Signature
<
ArrayXb
()>>();
278
break
;
279
default
:
280
node.
set_signature
<
Signature
<ArrayXf()>>();
281
}
282
}
283
else
if
(
Is<NT::MeanLabel>
(n))
284
node.
set_signature
<
Signature
<
ArrayXb
()>>();
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
:
290
node.
set_signature
<
Signature
<
ArrayXb
()>>();
291
break
;
292
case
DataType::ArrayI
:
293
node.
set_signature
<
Signature
<
ArrayXi
()>>();
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
305
void
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
321
p.
name
=
NodeTypeName
[p.
node_type
];
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
357
init_node_with_default_signature
(p);
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
}
HANDLE_ERROR_THROW
#define HANDLE_ERROR_THROW(err)
Definition
error.h:27
Brush::Data::TimeSeriesf
TimeSeries< float > TimeSeriesf
Definition
types.h:112
Brush
< nsga2 selection operator for getting the front
Definition
bandit.cpp:3
Brush::operator<<
ostream & operator<<(ostream &os, DataType n)
Brush::NodeType
NodeType
Definition
nodetype.h:31
Brush::NodeType::Log1p
@ Log1p
Definition
nodetype.h:49
Brush::NodeType::Sin
@ Sin
Definition
nodetype.h:40
Brush::NodeType::Equals
@ Equals
Definition
nodetype.h:90
Brush::NodeType::Tan
@ Tan
Definition
nodetype.h:42
Brush::NodeType::Abs
@ Abs
Definition
nodetype.h:34
Brush::NodeType::Softmax
@ Softmax
Definition
nodetype.h:70
Brush::NodeType::Prod
@ Prod
Definition
nodetype.h:65
Brush::NodeType::Or
@ Or
Definition
nodetype.h:85
Brush::NodeType::Mean
@ Mean
Definition
nodetype.h:63
Brush::NodeType::Cosh
@ Cosh
Definition
nodetype.h:39
Brush::NodeType::Div
@ Div
Definition
nodetype.h:76
Brush::NodeType::ArgMax
@ ArgMax
Definition
nodetype.h:102
Brush::NodeType::OffsetSum
@ OffsetSum
Definition
nodetype.h:67
Brush::NodeType::CustomUnaryOp
@ CustomUnaryOp
Definition
nodetype.h:107
Brush::NodeType::Geq
@ Geq
Definition
nodetype.h:91
Brush::NodeType::Mul
@ Mul
Definition
nodetype.h:75
Brush::NodeType::Median
@ Median
Definition
nodetype.h:64
Brush::NodeType::Max
@ Max
Definition
nodetype.h:62
Brush::NodeType::During
@ During
Definition
nodetype.h:58
Brush::NodeType::Asin
@ Asin
Definition
nodetype.h:36
Brush::NodeType::Min
@ Min
Definition
nodetype.h:61
Brush::NodeType::After
@ After
Definition
nodetype.h:57
Brush::NodeType::Sqrtabs
@ Sqrtabs
Definition
nodetype.h:51
Brush::NodeType::Pow
@ Pow
Definition
nodetype.h:77
Brush::NodeType::Before
@ Before
Definition
nodetype.h:56
Brush::NodeType::Logabs
@ Logabs
Definition
nodetype.h:48
Brush::NodeType::Sinh
@ Sinh
Definition
nodetype.h:41
Brush::NodeType::Logistic
@ Logistic
Definition
nodetype.h:53
Brush::NodeType::Sum
@ Sum
Definition
nodetype.h:66
Brush::NodeType::Not
@ Not
Definition
nodetype.h:86
Brush::NodeType::Sqrt
@ Sqrt
Definition
nodetype.h:50
Brush::NodeType::Ceil
@ Ceil
Definition
nodetype.h:44
Brush::NodeType::And
@ And
Definition
nodetype.h:84
Brush::NodeType::Atan
@ Atan
Definition
nodetype.h:37
Brush::NodeType::Exp
@ Exp
Definition
nodetype.h:46
Brush::NodeType::Tanh
@ Tanh
Definition
nodetype.h:43
Brush::NodeType::Log
@ Log
Definition
nodetype.h:47
Brush::NodeType::Square
@ Square
Definition
nodetype.h:52
Brush::NodeType::CustomSplit
@ CustomSplit
Definition
nodetype.h:109
Brush::NodeType::Sub
@ Sub
Definition
nodetype.h:74
Brush::NodeType::Count
@ Count
Definition
nodetype.h:104
Brush::NodeType::Add
@ Add
Definition
nodetype.h:73
Brush::NodeType::Acos
@ Acos
Definition
nodetype.h:35
Brush::NodeType::Floor
@ Floor
Definition
nodetype.h:45
Brush::NodeType::SplitBest
@ SplitBest
Definition
nodetype.h:80
Brush::NodeType::Cos
@ Cos
Definition
nodetype.h:38
Brush::ArrayXb
Eigen::Array< bool, Eigen::Dynamic, 1 > ArrayXb
Definition
types.h:39
Brush::from_json
void from_json(const json &j, Fitness &f)
Definition
fitness.cpp:31
Brush::DataType::ArrayI
@ ArrayI
Definition
types.h:145
Brush::DataType::ArrayF
@ ArrayF
Definition
types.h:146
Brush::DataType::ArrayB
@ ArrayB
Definition
types.h:144
Brush::Is
auto Is(NodeType nt) -> bool
Definition
node.h:317
Brush::to_json
void to_json(json &j, const Fitness &f)
Definition
fitness.cpp:6
Brush::init_node_with_default_signature
void init_node_with_default_signature(Node &node)
Definition
node.cpp:143
Brush::NT
NodeType NT
Definition
node.cpp:142
Brush::NodeTypeName
std::map< NodeType, std::string > NodeTypeName
Definition
nodetype.cpp:81
Brush::ArrayXi
Eigen::Array< int, Eigen::Dynamic, 1 > ArrayXi
Definition
types.h:40
node.h
Brush::Node
class holding the data for a node in a tree.
Definition
node.h:89
Brush::Node::center_op
bool center_op
whether to center the operator in pretty printing
Definition
node.h:127
Brush::Node::arg_types
std::vector< DataType > arg_types
argument data types
Definition
node.h:99
Brush::Node::set_signature
void set_signature()
Definition
node.h:166
Brush::Node::feature_type
DataType feature_type
feature type for terminals or splitting nodes
Definition
node.h:310
Brush::Node::weight_is_fixed
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
Brush::Node::node_type
NodeType node_type
the node type
Definition
node.h:94
Brush::Node::get_feature_type
DataType get_feature_type() const
Definition
node.h:288
Brush::Node::ret_type
DataType ret_type
return data type
Definition
node.h:97
Brush::Node::get_feature
string get_feature() const
Definition
node.h:285
Brush::Node::sig_hash
std::size_t sig_hash
a hash of the signature
Definition
node.h:101
Brush::Node::init
void init()
Definition
node.h:175
Brush::Node::prob_change
float prob_change
chance of node being selected for variation. This will take into account if the node is fixed,...
Definition
node.h:119
Brush::Node::get_name
string get_name(bool include_weight=true) const noexcept
gets a string version of the node for printing.
Definition
node.cpp:20
Brush::Node::W
float W
the weights of the node. also used for splitting thresholds.
Definition
node.h:122
Brush::Node::is_weighted
bool is_weighted
whether this node is weighted (ignored in nodes that must have weights, such as meanLabel,...
Definition
node.h:116
Brush::Node::node_is_fixed
bool node_is_fixed
whether the node is replaceable. Weights are still optimized.
Definition
node.h:112
Brush::Node::feature
string feature
feature name for terminals or splitting nodes
Definition
node.h:307
Brush::Node::name
string name
full name of the node, with types
Definition
node.h:92
Brush::Node::sig_dual_hash
std::size_t sig_dual_hash
a hash of the dual of the signature (for NLS)
Definition
node.h:103
Brush::Node::get_model
string get_model(const vector< string > &) const noexcept
Definition
node.cpp:58
Brush::Signature
Definition
signatures.h:125
src
program
node.cpp
Generated by
1.18.0