Brush C++ API
A flexible interpretable machine learning framework
Loading...
Searching...
No Matches
node.h
Go to the documentation of this file.
1/* Brush
2copyright 2020 William La Cava
3license: GNU/GPL v3
4
5Node class design heavily inspired by Operon, (c) Heal Research
6https://github.com/heal-research/operon/
7*/
8
9#ifndef NODE_H
10#define NODE_H
11
12#include "../data/data.h"
13#include "nodetype.h"
14#include "../util/utils.h"
15#include <iostream>
16// #include "nodes/base.h"
17// #include "nodes/dx.h"
18// #include "nodes/split.h"
19// #include "nodes/terminal.h"
21
22/*
23Node overhaul:
24
25- Incorporating new design principles, learning much from operon:
26 - make Node trivial, so that it is easily copied around.
27 - use Enums and maps to define node information. This kind of abandons the
28 object oriented approach taken thus far, but it should make extensibility
29 easier and performance better in the long run.
30 - Leverage ceres for parameter optimization. No more defining analytical
31 derivatives for every function. Let ceres do that.
32 - sidenote: not sure ceres can handle the data flow of split nodes.
33 need to figure out.
34 - this also suggests turning TimeSeries back into EigenSparse matrices.
35 - forget all the runtime node generation. It saves space at the cost of
36 unclear code. I might as well just define all the nodes that are available,
37 plainly. At run-time this will be faster.
38 - keep an eye towards extensibility by defining a custom node registration
39 function that works.
40*/
41
42using Brush::DataType;
44
45namespace Brush{
46
47template <DataType... T>
48inline auto Isnt(DataType dt) -> bool { return !((dt == T) || ...); }
49
50template<DataType DT>
51inline auto IsWeighable() noexcept -> bool {
57 >(DT);
58}
59inline auto IsWeighable(DataType dt) noexcept -> bool {
65 >(dt);
66}
67
69 std::size_t operator()(std::vector<uint32_t> const& vec) const {
70 std::size_t seed = vec.size();
71 for(auto& i : vec) {
72 seed ^= i + 0x9e3779b9 + (seed << 6) + (seed >> 2);
73 }
74 return seed;
75 }
76 std::size_t operator()(std::vector<Brush::DataType> const& vec) const {
77 std::size_t seed = vec.size();
78 for(auto& i : vec) {
79 seed ^= uint32_t(i) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
80 }
81 return seed;
82 }
83};
84
89struct Node {
90
92 string name;
95
99 std::vector<DataType> arg_types;
101 std::size_t sig_hash;
103 std::size_t sig_dual_hash;
104
105 // TODO: node_is_fixed, weight_is_fixed, and is_weighted accessed via getters/setters
106
107 // The three flags below will help determine how to handle the node during mutation,
108 // and can also be changed by the locking mechanism. prob_change is user-defined and
109 // also interact with the flags
110
120
122 float W;
123
125 // Retained for backwards-compatible program JSON. This has not being implemented yet
126 // While doing some random_state tests, i realized that this feature was being set to true sometimes, undesirable. So we make sure to initialize it as false always
127 bool center_op = false;
128
129 // /// @brief a node hash / unique ID for the node, except weights
130 // size_t node_hash;
132 using HashTuple = std::tuple<
133 UnderlyingNodeType, // node type
134 size_t, // sig_hash
135 bool, // is_weighted
136 string, // feature
137 bool, // node_is_fixed
138 bool, // weight_is_fixed
139 int // rounded W
140 // float // prob_change
141 >;
142
143 // Node(){init();};
144 Node() = default;
145
151 template<typename S>
152 explicit Node(NodeType type, S signature, bool weighted=false, string feature_name="") noexcept
153 : node_type(type)
154 , name(NodeTypeName[type])
155 , ret_type(S::get_ret_type())
157 , sig_hash(S::hash())
158 , sig_dual_hash(S::Dual::hash())
159 , is_weighted(weighted)
160 , feature(feature_name)
161 {
162 init();
163 }
164
165 template<typename S>
167 {
168 ret_type = S::get_ret_type();
169 arg_types = S::get_arg_types();
170 sig_hash = S::hash();
171 sig_dual_hash = S::Dual::hash();
172 // set_node_hash();
173 }
174
175 void init(){
176 center_op = false;
177
178 // starting weights with neutral element of the operation. offsetsum
179 // is the only node that does not multiply the weight --- instead, it adds it,
180 // so we need to handle it differently
181 W = (node_type == NodeType::OffsetSum) ? 0.0 : 1.0;
182
183 // set_node_hash();
184
185 // everything is unlocked. Special nodes (like the logistic root)
186 // should be fixed during its creation (check `vary` source code)
187 node_is_fixed=false;
188 weight_is_fixed=false;
189
190 set_prob_change(1.0);
191
192 // TODO: confirm that this is really necessary (intializing this variable) and transform this line into a ternary if so
193 // cant weight an boolean terminal
194 this->is_weighted = IsWeighable(this->ret_type);
195 }
196
200 string get_name(bool include_weight=true) const noexcept;
201 string get_model(const vector<string>&) const noexcept;
202
203 // get return type and argument types.
204 inline DataType get_ret_type() const { return ret_type; };
205 inline std::size_t args_type() const { return sig_hash; };
206 inline auto get_arg_types() const { return arg_types; };
207 inline size_t get_arg_count() const { return arg_types.size(); };
208
209 // void set_node_hash(){
210 // node_hash = std::hash<HashTuple>{}(HashTuple{
211 // NodeTypes::GetIndex(node_type),
212 // sig_hash,
213 // is_weighted,
214 // feature,
215 // fixed,
216 // W,
217 // prob_change
218 // });
219 // // fmt::print("nodetype:{}; hash tuple:{}; node_hash={}\n", node_type, tmp, node_hash);
220 // }
221 size_t get_node_hash(bool include_const=true) const {
222 // we ignore the constant only on simplification, because it will be tuned anyways
223
224 return std::hash<HashTuple>{}(HashTuple{
226 sig_hash,
228 feature,
231 // include weights only if we want exact matches.
232 // but we will indicate that constants are on using get is weighted,
233 // just so we differentiate whether the weight exists or is ignored
234 include_const ? int(W * 100) : (get_is_weighted() ? 1 : 0)
235 });
236 }
237
238 //comparison operators
239 inline auto operator==(const Node& rhs) const noexcept -> bool
240 {
241 // obs: this is declared as a member operator, so the lhs is implicit.
242 // If i were to declare this outsize class definition, then I would have
243 // to include lhs in the function signature
244
245 /* return CalculatedHashValue == rhs.CalculatedHashValue; */
246 return get_node_hash() == rhs.get_node_hash();
247 /* return (*this) == rhs; */
248 }
249
250 inline auto operator!=(const Node& rhs) const noexcept -> bool
251 {
252 return !((*this) == rhs);
253 }
254
255 inline auto operator<(const Node& rhs) const noexcept -> bool
256 {
257 /* return std::tie(HashValue, CalculatedHashValue) < std::tie(rhs.HashValue, rhs.CalculatedHashValue); */
258 return get_node_hash() < rhs.get_node_hash();
259 return (*this) < rhs;
260 }
261
262 inline auto operator<=(const Node& rhs) const noexcept -> bool
263 {
264 return ((*this) == rhs || (*this) < rhs);
265 }
266
267 inline auto operator>(const Node& rhs) const noexcept -> bool
268 {
269 return !((*this) <= rhs);
270 }
271
272 inline auto operator>=(const Node& rhs) const noexcept -> bool
273 {
274 return !((*this) < rhs);
275 }
276
278 // getters and setters
279 //TODO revisit
280 float get_prob_change() const { return node_is_fixed ? 0.0 : this->prob_change;};
281 void set_prob_change(float w){ this->prob_change = w;};
282 float get_prob_keep() const { return node_is_fixed ? 1.0 : 1.0-this->prob_change;};
283
284 inline void set_feature(string f){ feature = f; };
285 inline string get_feature() const { return feature; };
286
287 inline void set_feature_type(DataType ft){ this->feature_type = ft; };
288 inline DataType get_feature_type() const { return this->feature_type; };
289
290 inline void set_keep_split_feature(bool keep){ this->keep_split_feature = keep; };
291 inline bool get_keep_split_feature() const { return this->keep_split_feature; };
292
293 // Some types does not support weights, so we completely ignore the weights
294 // if is not weighable
295 inline bool get_is_weighted() const {
296 if (IsWeighable(this->ret_type))
297 return this->is_weighted;
298 return false;
299 };
300 inline void set_is_weighted(bool is_weighted){
301 if (IsWeighable(this->ret_type))
302 this->is_weighted = is_weighted;
303 };
304
305 // private:
307 string feature;
308
311
313 bool keep_split_feature = false; // TODO: unittests for keep_split_feature
314};
315
316template <NodeType... T>
317inline auto Is(NodeType nt) -> bool { return ((nt == T) || ...); }
318
319template <NodeType... T>
320inline auto Isnt(NodeType nt) -> bool { return !((nt == T) || ...); }
321
322// TODO: I think there are places where I can replace some logic with IsLeaf --> Check that.
323// TODO: create IsConstant, and add Constant and MeanLabel to it.
324inline auto IsLeaf(NodeType nt) noexcept -> bool {
326}
327
328inline auto IsCommutative(NodeType nt) noexcept -> bool {
329 return Is<NodeType::Add,
335 >(nt);
336}
337
338inline auto IsDifferentiable(NodeType nt) noexcept -> bool {
339 return Isnt<
348 NodeType::Not // TODO: should I include OffsetSum here? If I do so, then I should change the logic in the optimizer to not optimize the weight of OffsetSum nodes.
349 >(nt);
350}
351template<NodeType NT>
384
385ostream& operator<<(ostream& os, const Node& n);
386ostream& operator<<(ostream& os, const NodeType& nt);
387
388void from_json(const json &j, Node& p);
389void to_json(json& j, const Node& p);
390} // namespace Brush
391
392// format overload for Nodes
393template <> struct fmt::formatter<Brush::Node>: formatter<string_view> {
394 // parse is inherited from formatter<string_view>.
395 template <typename FormatContext>
396 auto format(Brush::Node x, FormatContext& ctx) const {
397 return formatter<string_view>::format(x.get_name(), ctx);
398 }
399};
400
401
402
403#endif
holds variable type data.
Definition data.h:51
< nsga2 selection operator for getting the front
Definition bandit.cpp:3
ostream & operator<<(ostream &os, DataType n)
std::underlying_type_t< NodeType > UnderlyingNodeType
Definition nodetype.h:114
NodeType
Definition nodetype.h:31
auto Isnt(DataType dt) -> bool
Definition node.h:48
auto IsWeighable() noexcept -> bool
Definition node.h:51
void from_json(const json &j, Fitness &f)
Definition fitness.cpp:31
auto IsLeaf(NodeType nt) noexcept -> bool
Definition node.h:324
auto IsCommutative(NodeType nt) noexcept -> bool
Definition node.h:328
DataType
data types.
Definition types.h:143
auto Is(NodeType nt) -> bool
Definition node.h:317
void to_json(json &j, const Fitness &f)
Definition fitness.cpp:6
NodeType NT
Definition node.cpp:142
std::map< NodeType, std::string > NodeTypeName
Definition nodetype.cpp:81
DataType DT
Definition types.h:188
auto IsDifferentiable(NodeType nt) noexcept -> bool
Definition node.h:338
static auto GetIndex(NodeType type) -> size_t
Definition nodetype.h:127
class holding the data for a node in a tree.
Definition node.h:89
float get_prob_keep() const
Definition node.h:282
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
bool get_keep_split_feature() const
Definition node.h:291
DataType feature_type
feature type for terminals or splitting nodes
Definition node.h:310
DataType get_ret_type() const
Definition node.h:204
void set_keep_split_feature(bool keep)
Definition node.h:290
size_t get_node_hash(bool include_const=true) const
Definition node.h:221
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
float get_prob_change() const
Definition node.h:280
NodeType node_type
the node type
Definition node.h:94
DataType get_feature_type() const
Definition node.h:288
auto operator<=(const Node &rhs) const noexcept -> bool
Definition node.h:262
Node(NodeType type, S signature, bool weighted=false, string feature_name="") noexcept
Constructor used by search space.
Definition node.h:152
DataType ret_type
return data type
Definition node.h:97
auto operator>(const Node &rhs) const noexcept -> bool
Definition node.h:267
void set_is_weighted(bool is_weighted)
Definition node.h:300
auto get_arg_types() const
Definition node.h:206
auto operator==(const Node &rhs) const noexcept -> bool
Definition node.h:239
void set_feature(string f)
Definition node.h:284
string get_feature() const
Definition node.h:285
std::size_t sig_hash
a hash of the signature
Definition node.h:101
bool get_is_weighted() const
Definition node.h:295
void init()
Definition node.h:175
void set_feature_type(DataType ft)
Definition node.h:287
std::size_t args_type() const
Definition node.h:205
float prob_change
chance of node being selected for variation. This will take into account if the node is fixed,...
Definition node.h:119
auto operator>=(const Node &rhs) const noexcept -> bool
Definition node.h:272
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
bool keep_split_feature
fix the SplitBest feature when the node is fixed
Definition node.h:313
size_t get_arg_count() const
Definition node.h:207
string feature
feature name for terminals or splitting nodes
Definition node.h:307
auto operator!=(const Node &rhs) const noexcept -> bool
Definition node.h:250
string name
full name of the node, with types
Definition node.h:92
void set_prob_change(float w)
Definition node.h:281
Node()=default
std::tuple< UnderlyingNodeType, size_t, bool, string, bool, bool, int > HashTuple
tuple type for hashing
Definition node.h:132
auto operator<(const Node &rhs) const noexcept -> bool
Definition node.h:255
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
std::size_t operator()(std::vector< Brush::DataType > const &vec) const
Definition node.h:76
std::size_t operator()(std::vector< uint32_t > const &vec) const
Definition node.h:69
auto format(Brush::Node x, FormatContext &ctx) const
Definition node.h:396