From 090c867eb81c00e5fd039bca3e5e0d24f922ef1b Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Wed, 15 Oct 2025 18:59:22 +0200 Subject: [PATCH 01/23] Alternative dot print which helps understand the DAG using any dot graph creator --- include/expression_dag.hh | 53 +++++++++++++++++++++++++++++++++++++++ include/parser.hh | 1 + 2 files changed, 54 insertions(+) diff --git a/include/expression_dag.hh b/include/expression_dag.hh index da08bb5..2b44a47 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -102,6 +102,7 @@ public: /** * Print ScalarExpression graph in the dot format. + * Useful for debugging */ void print_in_dot() { std::map i_node; @@ -131,6 +132,58 @@ public: std::cout << "Node: " << node->op_name_ << "_" << node->result_idx_ << " " << node->result_storage << std::endl; } + /** + * Print ScalarExpression graph in the common dot format. + * Useful for understanding the DAG + */ + void print_in_dot2() { + sort_nodes(); + + std::cout << "\n" << "----- begin cut here -----" << "\n"; + std::cout << "digraph Expr {" << "\n"; + + std::cout << "/* definitions */" << "\n"; + std::cout << "edge [dir=back]" << "\n"; + for (uint i = 0; i < sorted.size(); ++i) { + _print_dot_node_definition(sorted[i]); + } + std::cout << "/* end of definitions */" << "\n"; + + for (uint i = 0; i < sorted.size(); ++i) { + for (uint in = 0; in < sorted[i]->n_inputs_; ++in) { + std::cout << " "; + _print_dot_node(sorted[i]); + std::cout << "\n -> "; + _print_dot_node(sorted[i]->inputs_[in]); + std::cout << "\n\n"; + } + } + std::cout << "}" << "\n"; + std::cout << "----- end cut here -----" << "\n"; + std::cout.flush(); + } + void _print_dot_node(ScalarNodePtr node) { + std::cout << node->op_name_ << "_" << (int)node.get() << "__" << node->result_storage;// << std::endl; + } + + void _print_dot_node_definition(ScalarNodePtr node) { + _print_dot_node(node); + std::cout << ' '; + + if (node->result_storage == ResultStorage::constant) { + std::cout << "[shape=circle,label=\"const " << *node->values_ << "\"]" << std::endl; + } + else if (node->result_storage == ResultStorage::constant_bool) { + std::cout << "[shape=circle,label=\"const " << *node->values_ << "\"]" << std::endl; + } + else if (node->result_storage == ResultStorage::expr_result) { + std::cout << "[shape=box,label=\"" << node->op_name_ << " " << node->result_idx_ << "\"]" << std::endl; + } + else { + std::cout << "[label=\"" << node->op_name_ << "\"]" << std::endl; + } + } + private: void _print_i_node(uint i) { diff --git a/include/parser.hh b/include/parser.hh index ca8bde1..ade2953 100644 --- a/include/parser.hh +++ b/include/parser.hh @@ -190,6 +190,7 @@ public: details::ExpressionDAG se(result_array_.elements()); //se.print_in_dot(); + //se.print_in_dot2(); processor = ProcessorBase::create_processor(se, max_vec_size, simd_size, arena); } From cb9414e2a97cdce2772d349c661d44a661c3e6e9 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 19 Oct 2025 16:58:29 +0200 Subject: [PATCH 02/23] Better cast --- include/expression_dag.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/expression_dag.hh b/include/expression_dag.hh index 2b44a47..64f95e3 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -163,7 +163,7 @@ public: std::cout.flush(); } void _print_dot_node(ScalarNodePtr node) { - std::cout << node->op_name_ << "_" << (int)node.get() << "__" << node->result_storage;// << std::endl; + std::cout << node->op_name_ << "_" << (uintptr_t)node.get() << "__" << node->result_storage;// << std::endl; } void _print_dot_node_definition(ScalarNodePtr node) { From 95d91c38f64a1659c60b1ad6162c432d9a410c90 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Mon, 20 Oct 2025 19:03:11 +0200 Subject: [PATCH 03/23] Improved print_in_dot2. If the symbols_ map is supplied, it will also print the variable and constant names in dot --- include/expression_dag.hh | 130 +++++++++++++++++++++++++++++++++----- include/parser.hh | 1 + 2 files changed, 115 insertions(+), 16 deletions(-) diff --git a/include/expression_dag.hh b/include/expression_dag.hh index 64f95e3..b57cfc5 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -15,6 +15,7 @@ #include "config.hh" #include "scalar_node.hh" #include "assert.hh" +#include "array.hh" namespace bparser { @@ -40,6 +41,8 @@ private: /// Result nodes, given as input. NodeVec results; + typedef std::pair InvDotNameAndScalar; + typedef std::map InvDotMap; /** * Used in the setup_result_storage to note number of unclosed nodes @@ -134,27 +137,45 @@ public: /** * Print ScalarExpression graph in the common dot format. - * Useful for understanding the DAG + * Useful for understanding the DAG. */ void print_in_dot2() { + print_in_dot2(InvDotMap()); + } + + /** + * Print ScalarExpression graph in the common dot format. + * Useful for understanding the DAG. Using the parser's map of var. Name -> Array find the inverse ScalarNodePtr -> var. Name + */ + void print_in_dot2(const std::map& symbols) { + print_in_dot2(create_inverse_map(symbols)); + } + + /** + * Print ScalarExpression graph in the common dot format. + * Useful for understanding the DAG. Using the map of ScalarNodePtr -> variableName + */ + void print_in_dot2(const InvDotMap& names) { + sort_nodes(); std::cout << "\n" << "----- begin cut here -----" << "\n"; std::cout << "digraph Expr {" << "\n"; std::cout << "/* definitions */" << "\n"; + std::cout << "edge [dir=back]" << "\n"; for (uint i = 0; i < sorted.size(); ++i) { - _print_dot_node_definition(sorted[i]); + _print_dot_node_definition(sorted[i],names); } std::cout << "/* end of definitions */" << "\n"; for (uint i = 0; i < sorted.size(); ++i) { for (uint in = 0; in < sorted[i]->n_inputs_; ++in) { std::cout << " "; - _print_dot_node(sorted[i]); + _print_dot_node_id(sorted[i]); std::cout << "\n -> "; - _print_dot_node(sorted[i]->inputs_[in]); + _print_dot_node_id(sorted[i]->inputs_[in]); std::cout << "\n\n"; } } @@ -162,30 +183,107 @@ public: std::cout << "----- end cut here -----" << "\n"; std::cout.flush(); } - void _print_dot_node(ScalarNodePtr node) { + + //Create a map of ScalarNodePtr -> (variable name, is_scalar) + InvDotMap create_inverse_map(const std::map& symbols) const { + InvDotMap inv_map; + if (symbols.empty()) return inv_map; + for (const auto& s : symbols) + { + for (const auto& n : s.second.elements()) { + inv_map[n] = std::pair(s.first, s.second.shape().empty()); + } + } + return inv_map; + } + + +private: + //Print the vertice identifier for dot + void _print_dot_node_id(const ScalarNodePtr& node) const { std::cout << node->op_name_ << "_" << (uintptr_t)node.get() << "__" << node->result_storage;// << std::endl; } - void _print_dot_node_definition(ScalarNodePtr node) { - _print_dot_node(node); + //Print how the vertice should look in dot + void _print_dot_node_definition(const ScalarNodePtr& node, const InvDotMap& invmap) const { + _print_dot_node_id(node); std::cout << ' '; - if (node->result_storage == ResultStorage::constant) { - std::cout << "[shape=circle,label=\"const " << *node->values_ << "\"]" << std::endl; + if (node->result_storage == ResultStorage::constant) { // Constant + std::cout << "[shape=circle,"; + + try { //If the constant has a name + std::string name(invmap.at(node).first); + std::cout << "label=\"" << name << ": " << *node->values_ << "\",group=\"" << name << '"'; + } + catch (std::out_of_range) { //No name + std::cout << "label=\"" << "const " << *node->values_ << '"'; + } + std::cout << "]" << std::endl; + } + + else if (node->result_storage == ResultStorage::constant_bool) { //Constant bool + std::cout << "[shape=circle,"; + + try { //If the constant has a name + std::string name(invmap.at(node).first); + std::cout << "label=\"" << name << ": " << *node->values_ << "\",group=\"" << name << '"'; + } + catch (std::out_of_range) { //No name + std::cout << "label=\"" << "const " << *node->values_ << '"'; + } + std::cout << "]" << std::endl; } - else if (node->result_storage == ResultStorage::constant_bool) { - std::cout << "[shape=circle,label=\"const " << *node->values_ << "\"]" << std::endl; + + else if (node->result_storage == ResultStorage::expr_result) { //Result + std::cout << "[shape=box,label=\"" << node->op_name_ << " [" << node->result_idx_ << "]" << "\"]" << std::endl; } - else if (node->result_storage == ResultStorage::expr_result) { - std::cout << "[shape=box,label=\"" << node->op_name_ << " " << node->result_idx_ << "\"]" << std::endl; + + else if (node->result_storage == ResultStorage::value) { // Value + + std::cout << "[shape=circle,"; + try { + std::string name(invmap.at(node).first); + bool scalar(invmap.at(node).second); + if (scalar) { + std::cout << "label=\"" << name << '"'; + } + else { + std::cout << "label=<" << name << "i" << '>'; + } + std::cout << ",group=\"" << name << '"'; + } + catch (std::out_of_range) { + std::cout << "label=<var>"; + } + + std::cout << "]" << std::endl; + } + + else if (node->result_storage == ResultStorage::value_copy) { //Value copy + std::cout << "[shape=circle,"; + try { + std::string name(invmap.at(node).first); + bool scalar(invmap.at(node).second); + if (scalar) { + std::cout << "label=\"" << name << '"'; + } + else { + std::cout << "label=<" << name << "i" << '>'; + } + std::cout << ",group=\"" << name << '"'; + } + catch (std::out_of_range) { + std::cout << "label=<var_cp>"; + } + std::cout << "]" << std::endl; } - else { + + else {//Temporary & other //Temporary & other std::cout << "[label=\"" << node->op_name_ << "\"]" << std::endl; } } - -private: void _print_i_node(uint i) { std::cout << sorted[i]->op_name_ << "_" << i << "_"<< sorted[i]->result_idx_; } diff --git a/include/parser.hh b/include/parser.hh index ade2953..781dbc8 100644 --- a/include/parser.hh +++ b/include/parser.hh @@ -191,6 +191,7 @@ public: //se.print_in_dot(); //se.print_in_dot2(); + //se.print_in_dot2(symbols_); processor = ProcessorBase::create_processor(se, max_vec_size, simd_size, arena); } From 12b37e909f8d269efbbdb8630c3715eab8b2774e Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Mon, 20 Oct 2025 19:08:09 +0200 Subject: [PATCH 04/23] More appropriate catch blocks --- include/expression_dag.hh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/expression_dag.hh b/include/expression_dag.hh index b57cfc5..ab32ccb 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -216,7 +216,7 @@ private: std::string name(invmap.at(node).first); std::cout << "label=\"" << name << ": " << *node->values_ << "\",group=\"" << name << '"'; } - catch (std::out_of_range) { //No name + catch (const std::out_of_range&) { //No name std::cout << "label=\"" << "const " << *node->values_ << '"'; } std::cout << "]" << std::endl; @@ -229,7 +229,7 @@ private: std::string name(invmap.at(node).first); std::cout << "label=\"" << name << ": " << *node->values_ << "\",group=\"" << name << '"'; } - catch (std::out_of_range) { //No name + catch (const std::out_of_range&) { //No name std::cout << "label=\"" << "const " << *node->values_ << '"'; } std::cout << "]" << std::endl; @@ -253,7 +253,7 @@ private: } std::cout << ",group=\"" << name << '"'; } - catch (std::out_of_range) { + catch (const std::out_of_range&) { std::cout << "label=<var>"; } @@ -273,7 +273,7 @@ private: } std::cout << ",group=\"" << name << '"'; } - catch (std::out_of_range) { + catch (const std::out_of_range&) { std::cout << "label=<var_cp>"; } std::cout << "]" << std::endl; From b44c9d7afdf64972293df751b071aae683e626b3 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sat, 1 Nov 2025 17:23:38 +0100 Subject: [PATCH 05/23] Matrix and vector norms --- include/array.hh | 87 +++++++++++++++++++++++++++++++++++++++ include/grammar.impl.hh | 4 ++ include/scalar_wrapper.hh | 37 +++++++++++++---- test/test_parser.cc | 8 ++++ 4 files changed, 129 insertions(+), 7 deletions(-) diff --git a/include/array.hh b/include/array.hh index b590b27..4f0c174 100644 --- a/include/array.hh +++ b/include/array.hh @@ -1216,6 +1216,93 @@ public: //return full_({}, *wrap_array(a).trace()); } + static Array norm1(const Array& a) { + switch (a.shape().size()) { + case 0: //scalar + Throw() << "Norms are not for scalar values" << "\n"; + break; + case 1: //vector + { + + Shape s; //empty Shape for scalar + Array r(s); + r.elements_[0U] = *wrap_array(a).lpNorm<1>(); + return r; + } + case 2: //matrix + { + Shape s; //empty Shape for scalar + Array r(s); + r.elements_[0U] = *wrap_array(a).colwise().lpNorm<1>().maxCoeff(); + return r; + } + default: + Throw() << "Norms are not avaiable for ND tensors" << "\n"; + } + } + + static Array norm2(const Array& a) { + switch (a.shape().size()) { + case 0: //scalar + Throw() << "Norms are not for scalar values" << "\n"; + break; + case 1: //vector + { + //Euclidean norm + Shape s; //empty Shape for scalar + Array r(s); + r.elements_[0U] = *wrap_array(a).norm(); + return r; + } + case 2: //matrix + { + //Spectral norm + Throw() << "norm2(matrix) not yet implemented" << "\n"; + Shape s; //empty Shape for scalar + Array r(s); + //r.elements_[0U] = *wrap_array(a); + return r; + } + default: + Throw() << "Norms are not avaiable for ND tensors" << "\n"; + } + } + + static Array normfro(const Array& a) { + if (a.shape().size() != 2) { + Throw() << "Frobenius norm is only defined for matrices" << "\n"; + } + + Shape s; + Array r(s); + r.elements_[0U] = *wrap_array(a).norm(); + return r; + } + + static Array norminf(const Array& a) { + switch (a.shape().size()) { + case 0: //scalar + Throw() << "Norms are not for scalar values" << "\n"; + break; + case 1: //vector + { + Shape s; //empty Shape for scalar + Array r(s); + r.elements_[0U] = *wrap_array(a).lpNorm(); + return r; + } + case 2: //matrix + { + Shape s; //empty Shape for scalar + Array r(s); + r.elements_[0U] = *wrap_array(a).rowwise().lpNorm<1>().maxCoeff(); + return r; + } + default: + Throw() << "Norms are not avaiable for ND tensors" << "\n"; + } + } + static Array flatten(const Array &tensor) { uint n_elements = shape_size(tensor.shape()); Shape res_shape(1, n_elements); diff --git a/include/grammar.impl.hh b/include/grammar.impl.hh index 8a5ad5c..082592f 100644 --- a/include/grammar.impl.hh +++ b/include/grammar.impl.hh @@ -181,6 +181,10 @@ struct grammar : qi::grammar { FN("maximum", binary_array<_max_>()) FN("diag" , &Array::diag) FN("tr" , &Array::trace) + FN("norm1" , &Array::norm1) + FN("norm2" , &Array::norm2) + FN("normfro", &Array::normfro) + FN("norminf", &Array::norminf) ; unary_op.add diff --git a/include/scalar_wrapper.hh b/include/scalar_wrapper.hh index 54dda38..5273e6d 100644 --- a/include/scalar_wrapper.hh +++ b/include/scalar_wrapper.hh @@ -49,8 +49,7 @@ namespace bparser { } inline bool operator==(const ScalarWrapper& b) const { - if (((***this).result_storage == constant && (**b).result_storage == constant ) || - ((***this).result_storage == constant_bool && (**b).result_storage == constant_bool) ) + if ((*this).is_constant() && (*this).have_same_result_storage(b)) return *(***this).values_ == *(**b).values_; return false; } @@ -78,6 +77,14 @@ namespace bparser { protected: ScalarNodePtr node; + inline bool is_constant() const { + return (***this).result_storage == constant || + (***this).result_storage == constant_bool; + } + + inline bool have_same_result_storage(const ScalarWrapper& b)const { + return (***this).result_storage == (**b).result_storage; + } }; //ScalarWrapper @@ -91,18 +98,25 @@ namespace bparser { } \ using std::OP; - /* +#define BIN_OP(OP) \ + inline ScalarWrapper OP(const ScalarWrapper& a,const ScalarWrapper& b) { \ + return ScalarWrapper::bin_op<_##OP##_>(a,b); \ + } \ + using std::OP; + + UN_OP(abs) //https://eigen.tuxfamily.org/dox/namespaceEigen.html#a54cc34b64b4935307efc06d56cd531df inline ScalarWrapper abs2(const ScalarWrapper& s) { return s*s; - }; - */ + } + - //UN_OP(sqrt) + UN_OP(sqrt) //UN_OP(exp) //UN_OP(log) + //UN_OP(log2) //UN_OP(log10) //UN_OP(sin) //UN_OP(sinh) @@ -116,9 +130,18 @@ namespace bparser { //UN_OP(ceil) //UN_OP(floor) + BIN_OP(max) + inline ScalarWrapper maxi(const ScalarWrapper& a, const ScalarWrapper& b) { + return ScalarWrapper::bin_op<_max_>(a, b); + } + BIN_OP(min) + inline ScalarWrapper mini(const ScalarWrapper& a, const ScalarWrapper& b) { + return ScalarWrapper::bin_op<_min_>(a, b); + } - + //BIN_OP(atan2) + //BIN_OP(pow) } //details } //bparser diff --git a/test/test_parser.cc b/test/test_parser.cc index 28b3c20..df23f82 100644 --- a/test/test_parser.cc +++ b/test/test_parser.cc @@ -261,6 +261,14 @@ void test_expression() { BP_ASSERT(test_expr("tr([[1,9,9],[9,1,9],[9,9,1]])", { 3 }, {})); + BP_ASSERT(test_expr("norm1([-4,-3,-2,-1,0,1,2,3,4])", {20}, {})); + BP_ASSERT(test_expr("norm1([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 7 }, {})); + BP_ASSERT(test_expr("norm2([-4,-3,-2,-1,0,1,2,3,4])", { 7.745966692414834 }, {})); + //BP_ASSERT(test_expr("norm2([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 7.3484692283495345 }, {})); + BP_ASSERT(test_expr("normfro([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 7.745966692414834 }, {})); + BP_ASSERT(test_expr("norminf([-4,-3,-2,-1,0,1,2,3,4])", { 4 }, {})); + BP_ASSERT(test_expr("norminf([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 9 }, {})); + BP_ASSERT(test_expr("abs(-1)+abs(0)+abs(1)", {2})); BP_ASSERT(test_expr("floor(-3.5)", {-4}, {})); BP_ASSERT(test_expr("ceil(-3.5)", {-3}, {})); From 5bb3f9ac2146beabed02ed407985677471cd6394 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 9 Nov 2025 17:37:58 +0100 Subject: [PATCH 06/23] InverseMap now holds the indices of the node which is then printed to dot --- include/expression_dag.hh | 190 +++++++++++++++++++++++++------------- 1 file changed, 126 insertions(+), 64 deletions(-) diff --git a/include/expression_dag.hh b/include/expression_dag.hh index ab32ccb..43a194f 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -41,8 +41,8 @@ private: /// Result nodes, given as input. NodeVec results; - typedef std::pair InvDotNameAndScalar; - typedef std::map InvDotMap; + typedef std::pair InvDotNameAndIndices; + typedef std::map InvDotMap; /** * Used in the setup_result_storage to note number of unclosed nodes @@ -190,9 +190,12 @@ public: if (symbols.empty()) return inv_map; for (const auto& s : symbols) { - for (const auto& n : s.second.elements()) { - inv_map[n] = std::pair(s.first, s.second.shape().empty()); + for (MultiIdx idx(s.second.range()); idx.valid();idx.inc_src()) { + inv_map[s.second[idx]] = InvDotNameAndIndices(s.first, idx.indices()); } + /*for (const auto& n : s.second.elements()) { + inv_map[n] = InvDotNameAndIndices(s.first, s.second.shape().empty()); + }*/ } return inv_map; } @@ -209,79 +212,138 @@ private: _print_dot_node_id(node); std::cout << ' '; - if (node->result_storage == ResultStorage::constant) { // Constant - std::cout << "[shape=circle,"; - - try { //If the constant has a name - std::string name(invmap.at(node).first); - std::cout << "label=\"" << name << ": " << *node->values_ << "\",group=\"" << name << '"'; - } - catch (const std::out_of_range&) { //No name - std::cout << "label=\"" << "const " << *node->values_ << '"'; + switch (node->result_storage) { + case ResultStorage::constant: { // Constant + std::cout << "[shape=circle,"; + + try { //If the constant has a name + std::string name(invmap.at(node).first); + const MultiIdx::VecUint indices(invmap.at(node).second); + bool scalar(indices.empty()); + if (scalar) { + std::cout << "label=\"" << name << '=' << *node->values_ << '\"'; + } + else { + MultiIdx::VecUint::size_type size(indices.size()); + std::cout << "label=\"" << name << "["; + for (MultiIdx::VecUint::size_type i = 0; i < size; i++) { + std::cout << indices.at(i); + if (i != size - 1) { + std::cout << ','; + } + } + std::cout << "]"; + std::cout << '=' << *node->values_ << '\"'; + } + std::cout << ", group = \"" << name << '\"'; + } + catch (const std::out_of_range&) { //No name + std::cout << "label=\"" << "const " << *node->values_ << '"'; + } + std::cout << "]" << std::endl; + break; } - std::cout << "]" << std::endl; - } - else if (node->result_storage == ResultStorage::constant_bool) { //Constant bool - std::cout << "[shape=circle,"; - - try { //If the constant has a name - std::string name(invmap.at(node).first); - std::cout << "label=\"" << name << ": " << *node->values_ << "\",group=\"" << name << '"'; - } - catch (const std::out_of_range&) { //No name - std::cout << "label=\"" << "const " << *node->values_ << '"'; + case ResultStorage::constant_bool: { //Constant bool + std::cout << "[shape=circle,"; + + try { //If the constant has a name + std::string name(invmap.at(node).first); + const MultiIdx::VecUint indices(invmap.at(node).second); + bool scalar(indices.empty()); + if (scalar) { + std::cout << "label=\"" << name << '=' << *node->values_ << '\"'; + } + else { + MultiIdx::VecUint::size_type size(indices.size()); + std::cout << "label=\"" << name << "["; + for (MultiIdx::VecUint::size_type i = 0; i < size; i++) { + std::cout << indices.at(i); + if (i != size - 1) { + std::cout << ','; + } + } + std::cout << "]"; + std::cout << '=' << *node->values_ << '\"'; + } + std::cout << ", group = \"" << name << '\"'; + } + catch (const std::out_of_range&) { //No name + std::cout << "label=\"" << "const " << *node->values_ << '"'; + } + std::cout << "]" << std::endl; + break; } - std::cout << "]" << std::endl; - } - else if (node->result_storage == ResultStorage::expr_result) { //Result - std::cout << "[shape=box,label=\"" << node->op_name_ << " [" << node->result_idx_ << "]" << "\"]" << std::endl; - } - - else if (node->result_storage == ResultStorage::value) { // Value + case ResultStorage::expr_result: { //Result + std::cout << "[shape=box,label=\"" << node->op_name_ << " [" << node->result_idx_ << "]" << "\"]" << std::endl; + break; + } - std::cout << "[shape=circle,"; - try { - std::string name(invmap.at(node).first); - bool scalar(invmap.at(node).second); - if (scalar) { - std::cout << "label=\"" << name << '"'; + case ResultStorage::value: { // Value + std::cout << "[shape=circle,"; + try { + std::string name(invmap.at(node).first); + const MultiIdx::VecUint indices(invmap.at(node).second); + bool scalar(indices.empty()); + if (scalar) { + std::cout << "label=\"" << name << '"'; + } + else { + MultiIdx::VecUint::size_type size(indices.size()); + std::cout << "label=\"" << name << "["; + for (MultiIdx::VecUint::size_type i = 0; i < size; i++) { + std::cout << indices.at(i); + if (i != size - 1) { + std::cout << ','; + } + } + std::cout << "]\""; + } + std::cout << ",group=\"" << name << '"'; } - else { - std::cout << "label=<" << name << "i" << '>'; + catch (const std::out_of_range&) { + std::cout << "label=<var>"; } - std::cout << ",group=\"" << name << '"'; - } - catch (const std::out_of_range&) { - std::cout << "label=<var>"; + + std::cout << "]" << std::endl; + break; } - - std::cout << "]" << std::endl; - } - else if (node->result_storage == ResultStorage::value_copy) { //Value copy - std::cout << "[shape=circle,"; - try { - std::string name(invmap.at(node).first); - bool scalar(invmap.at(node).second); - if (scalar) { - std::cout << "label=\"" << name << '"'; + case ResultStorage::value_copy: { //Value copy + std::cout << "[shape=circle,"; + try { + std::string name(invmap.at(node).first); + MultiIdx::VecUint indices(invmap.at(node).second); + bool scalar(indices.empty()); + if (scalar) { + std::cout << "label=\"" << name << '"'; + } + else { + MultiIdx::VecUint::size_type size(indices.size()); + std::cout << "label=\"" << name << "["; + for (MultiIdx::VecUint::size_type i = 0; i < size; i++) { + std::cout << indices.at(i); + if (i != size - 1) { + std::cout << ','; + } + } + std::cout << "]\""; + } + std::cout << ",group=\"" << name << '"'; } - else { - std::cout << "label=<" << name << "i" << '>'; + catch (const std::out_of_range&) { + std::cout << "label=<var_cp>"; } - std::cout << ",group=\"" << name << '"'; - } - catch (const std::out_of_range&) { - std::cout << "label=<var_cp>"; + std::cout << "]" << std::endl; + break; } - std::cout << "]" << std::endl; - } - else {//Temporary & other //Temporary & other - std::cout << "[label=\"" << node->op_name_ << "\"]" << std::endl; - } + default: { //Temporary & other + std::cout << "[label=\"" << node->op_name_ << "\"]" << std::endl; + break; + } + } //switch } void _print_i_node(uint i) { From 69be136a871486f1369004034cac09e7b60ffa40 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 9 Nov 2025 19:26:46 +0100 Subject: [PATCH 07/23] Attempt at computing spectral norm using eigenvalues. Requires comparison operators and static cast to double, not possible for Bparser --- include/array.hh | 14 +++++++---- include/scalar_wrapper.hh | 50 ++++++++++++++++++++++++++++++++++++++- test/test_parser.cc | 2 +- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/include/array.hh b/include/array.hh index 4f0c174..cbd88fd 100644 --- a/include/array.hh +++ b/include/array.hh @@ -1257,11 +1257,17 @@ public: case 2: //matrix { //Spectral norm - Throw() << "norm2(matrix) not yet implemented" << "\n"; - Shape s; //empty Shape for scalar + Throw() << "norm2(matrix) is not yet possible" << "\n"; + /*Shape s; //empty Shape for scalar Array r(s); - //r.elements_[0U] = *wrap_array(a); - return r; + + Eigen::MatrixX m( wrap_array(a) ); + + r.elements_[0U] = *details::sqrt((m.adjoint()*m).eigenvalues().real().maxCoeff()); + //computing eigenvalues would require static cast to double and comparison operators (<,<=,>,>=,!=,==) + //something which we cannot support + return r;*/ + break; } default: Throw() << "Norms are not avaiable for ND tensors" << "\n"; diff --git a/include/scalar_wrapper.hh b/include/scalar_wrapper.hh index 5273e6d..8a07d46 100644 --- a/include/scalar_wrapper.hh +++ b/include/scalar_wrapper.hh @@ -12,6 +12,7 @@ #include "scalar_node.hh" #include +//#include //impossible namespace bparser { namespace details { @@ -23,6 +24,10 @@ namespace bparser { ScalarWrapper(double d) : node(ScalarNode::create_const(d)) { ; } ScalarWrapper(ScalarNodePtr existing_ptr) : node(existing_ptr) { ; } + inline ScalarWrapper operator+() const { + return ScalarWrapper(*this); + } + inline ScalarWrapper operator-() const { return un_op<_minus_>(*this); } @@ -36,14 +41,29 @@ namespace bparser { return bin_op<_add_>(*this, b); } + inline ScalarWrapper& operator-=(const ScalarWrapper& b) { + node = bin_op<_sub_>(*this, b).get(); + return *this; + } + inline ScalarWrapper operator-(const ScalarWrapper& b) const { return bin_op<_sub_>(*this, b); } + inline ScalarWrapper& operator*=(const ScalarWrapper& b) { + node = bin_op<_mul_>(*this, b).get(); + return *this; + } + inline ScalarWrapper operator*(const ScalarWrapper& b) const { return bin_op<_mul_>(*this, b); } + inline ScalarWrapper& operator/=(const ScalarWrapper& b) { + node = bin_op<_div_>(*this, b).get(); + return *this; + } + inline ScalarWrapper operator/(const ScalarWrapper& b) const { return bin_op<_div_>(*this, b); } @@ -53,6 +73,34 @@ namespace bparser { return *(***this).values_ == *(**b).values_; return false; } + /* These do not make any sense with what we are trying to achieve + inline bool operator!=(const ScalarWrapper& b) const { + return !((*this) == b); + } + + inline bool operator<(const ScalarWrapper& b) const { + if ((*this).is_constant() && (*this).have_same_result_storage(b)) + return *(***this).values_ < *(**b).values_; + return false; + } + + inline bool operator<=(const ScalarWrapper& b) const { + if ((*this).is_constant() && (*this).have_same_result_storage(b)) + return *(***this).values_ <= *(**b).values_; + return false; + } + + inline bool operator>=(const ScalarWrapper& b) const { + if ((*this).is_constant() && (*this).have_same_result_storage(b)) + return *(***this).values_ >= *(**b).values_; + return false; + } + + inline bool operator>(const ScalarWrapper& b) const { + if ((*this).is_constant() && (*this).have_same_result_storage(b)) + return *(***this).values_ > *(**b).values_; + return false; + }*/ inline ScalarNodePtr operator*() const { //dereference @@ -111,7 +159,7 @@ namespace bparser { inline ScalarWrapper abs2(const ScalarWrapper& s) { return s*s; } - + UN_OP(sqrt) //UN_OP(exp) diff --git a/test/test_parser.cc b/test/test_parser.cc index df23f82..e226692 100644 --- a/test/test_parser.cc +++ b/test/test_parser.cc @@ -264,7 +264,7 @@ void test_expression() { BP_ASSERT(test_expr("norm1([-4,-3,-2,-1,0,1,2,3,4])", {20}, {})); BP_ASSERT(test_expr("norm1([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 7 }, {})); BP_ASSERT(test_expr("norm2([-4,-3,-2,-1,0,1,2,3,4])", { 7.745966692414834 }, {})); - //BP_ASSERT(test_expr("norm2([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 7.3484692283495345 }, {})); + //BP_ASSERT(test_expr("norm2([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 7.3484692283495345 }, {})); //Spectral norm uses eigenvalues/singular values. Eigen uses comparison operators in the algorithm. Bparser does not like that BP_ASSERT(test_expr("normfro([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 7.745966692414834 }, {})); BP_ASSERT(test_expr("norminf([-4,-3,-2,-1,0,1,2,3,4])", { 4 }, {})); BP_ASSERT(test_expr("norminf([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 9 }, {})); From 8b5e0eb4c73eb7e251cbd7e74b0bf137c885a6f4 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 23 Nov 2025 12:36:56 +0100 Subject: [PATCH 08/23] min, max, sum of all coefficients --- include/array.hh | 21 +++++++++++++++++++++ include/grammar.impl.hh | 3 +++ test/test_parser.cc | 9 +++++++++ 3 files changed, 33 insertions(+) diff --git a/include/array.hh b/include/array.hh index cbd88fd..57df92f 100644 --- a/include/array.hh +++ b/include/array.hh @@ -1309,6 +1309,27 @@ public: } } + static Array max(const Array& a) { + Shape s; + Array r(s); + r.elements_[0U] = *wrap_array(flatten(a)).maxCoeff(); + return r; + } + + static Array min(const Array& a) { + Shape s; + Array r(s); + r.elements_[0U] = *wrap_array(flatten(a)).minCoeff(); + return r; + } + + static Array sum(const Array& a) { + Shape s; + Array r(s); + r.elements_[0U] = *wrap_array(flatten(a)).sum(); + return r; + } + static Array flatten(const Array &tensor) { uint n_elements = shape_size(tensor.shape()); Shape res_shape(1, n_elements); diff --git a/include/grammar.impl.hh b/include/grammar.impl.hh index 082592f..410055b 100644 --- a/include/grammar.impl.hh +++ b/include/grammar.impl.hh @@ -179,12 +179,15 @@ struct grammar : qi::grammar { FN("power" , binary_array<_pow_>()) FN("minimum", binary_array<_min_>()) FN("maximum", binary_array<_max_>()) + FN("min" , &Array::min) + FN("max" , &Array::max) FN("diag" , &Array::diag) FN("tr" , &Array::trace) FN("norm1" , &Array::norm1) FN("norm2" , &Array::norm2) FN("normfro", &Array::normfro) FN("norminf", &Array::norminf) + FN("sum" , &Array::sum) ; unary_op.add diff --git a/test/test_parser.cc b/test/test_parser.cc index e226692..457f1a6 100644 --- a/test/test_parser.cc +++ b/test/test_parser.cc @@ -304,6 +304,15 @@ void test_expression() { // BP_ASSERT(test_expr("norm([2, 3])", {5})); BP_ASSERT(test_expr("minimum([1,2,3], [0,4,3])", {0,2,3})); BP_ASSERT(test_expr("maximum([1,2,3], [0,4,3])", {1,4,3})); + BP_ASSERT(test_expr("min([1,2,3])", {1})); + BP_ASSERT(test_expr("min([-3,-2,-1,0,1,2,3])", { -3 })); + BP_ASSERT(test_expr("min([[[8,7],[6,5]],[[4,3],[2,1]]])", { 1 })); + BP_ASSERT(test_expr("max([1,2,3])", { 3 })); + BP_ASSERT(test_expr("max([-3,-2,-1,0,1,2,3])", { 3 })); + BP_ASSERT(test_expr("max([[[8,7],[6,5]],[[4,3],[2,1]]])", { 8 })); + BP_ASSERT(test_expr("sum([1,2,3])", { 6 })); + BP_ASSERT(test_expr("sum([-3,-2,-1,0,1,2,3])", { 0 })); + BP_ASSERT(test_expr("sum([[[8,7],[6,5]],[[4,3],[2,1]]])", { 1+2+3+4+5+6+7+8 })); /** * All bool tests have defined: From c061f775255963732f5e51e5cc4edd0efa05ec9d Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 23 Nov 2025 16:50:00 +0100 Subject: [PATCH 09/23] cross product for vectors --- include/array.hh | 57 ++++++++++++++++++++++++++++++++++----- include/grammar.impl.hh | 1 + include/scalar_wrapper.hh | 1 + test/test_parser.cc | 7 +++++ 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/include/array.hh b/include/array.hh index 57df92f..b6378de 100644 --- a/include/array.hh +++ b/include/array.hh @@ -859,12 +859,12 @@ public: return result; } - + typedef Eigen::MatrixX WrappedArray; //Wraps the ScalarNodes of an Array into an Eigen Matrix of ScalarWrappers. //Vectors will be column vectors. Eigen does not support vectors without orientation. //Cannot wrap scalars. To wrap scalars, use the bparser::details::ScalarWrapper constructor - static Eigen::MatrixX wrap_array(const bparser::Array& a) { + static WrappedArray wrap_array(const bparser::Array& a) { MultiIdx idx(a.range()); return wrap_array(a, idx); } @@ -872,7 +872,7 @@ public: //Wraps the ScalarNodes of an Array accessed via MultiIdx.idx_trg() created from supplied MultiIdxRange into an Eigen Matrix of ScalarWrapper //Vectors will be column vectors. Eigen does not support vectors without orientation. //Cannot wrap scalars. To wrap scalars, use the bparser::details::ScalarWrapper constructor - static Eigen::MatrixX wrap_array(const bparser::Array& a, MultiIdxRange& range) { + static WrappedArray wrap_array(const bparser::Array& a, MultiIdxRange& range) { MultiIdx idx (range); return wrap_array(a, idx); } @@ -880,7 +880,7 @@ public: //Wraps the ScalarNodes of an Array accessed via MultiIdx.idx_trg() into an Eigen Matrix of ScalarWrapper //Vectors will be column vectors. Eigen does not support vectors without orientation. //Cannot wrap scalars. To wrap scalars, use the bparser::details::ScalarWrapper constructor - static Eigen::MatrixX wrap_array(const bparser::Array& a, MultiIdx& index) { + static WrappedArray wrap_array(const bparser::Array& a, MultiIdx& index) { using namespace details; Shape trg_shape = index.range_.target_shape(); @@ -1127,15 +1127,15 @@ public: //std::cout << print_shape(result_shape) << std::endl; Array result(result_shape); - bool should_transpose = a.shape().size() == 1; + //bool should_transpose = a.shape().size() == 1; for (MultiIdx result_idx(result.range()), a_idx(a_range), b_idx(b_range); result_idx.valid(); ) { - Eigen::MatrixX m_a = wrap_array(a, a_idx); - Eigen::MatrixX m_b = wrap_array(b, b_idx); + WrappedArray m_a = wrap_array(a, a_idx); + WrappedArray m_b = wrap_array(b, b_idx); Array matmult = unwrap_array(m_a * m_b); @@ -1330,6 +1330,49 @@ public: return r; } + static Array cross(const Array& a, const Array& b) { + Shape a_shape(a.shape()); + Shape b_shape(b.shape()); + if (a_shape.size() != 1 && a_shape.size() != 2) + Throw() << "Array a of cross product has wrong dimensions"; + if (b_shape.size() != 1 && b_shape.size() != 2) + Throw() << "Array b of cross product has wrong dimensions"; + if (a_shape.back() != 2 && a_shape.back() != 3) + Throw() << "Array a of cross product doesn't have the right amount of elements"; + if (b_shape.back() != 2 && b_shape.back() != 3) + Throw() << "Array b of cross product doesn't have the right amount of elements"; + + //for (MultiIdx) //TODO: Support multiple vector cross-products + //{ + WrappedArray m_a = wrap_array(a); + WrappedArray m_b = wrap_array(b); + + if (m_a.cols() == 1) m_a.transposeInPlace(); //col -> row + if (m_b.cols() == 1) m_b.transposeInPlace(); //col -> row + + if (m_a.cols() == 2 && m_b.cols() == 3) { + m_a.conservativeResize(Eigen::NoChange, 3); + m_a(0, 2) = details::ScalarWrapper(details::ScalarNode::create_zero()); + } + else if (m_b.cols() == 2 && m_a.cols() == 3) { + m_b.conservativeResize(Eigen::NoChange, 3); + m_b(0, 2) = details::ScalarWrapper(details::ScalarNode::create_zero()); + } + + WrappedArray cross; + if (m_a.cols() == 2 && m_b.cols() == 2) { + //cross = Eigen::Ref>(m_a).cross(Eigen::Ref>(m_b)); // Only in Eigen 5.0.0+ + cross = WrappedArray(1, 1); + cross(0, 0) = (m_a(0, 0) * m_b(0, 1) - m_b(0, 0) * m_a(0, 1)); + } + else { + cross = Eigen::Ref>(m_a).cross(Eigen::Ref>(m_b)); + } + Array arr = unwrap_array(cross, true); + //} + return Array(arr); + } + static Array flatten(const Array &tensor) { uint n_elements = shape_size(tensor.shape()); Shape res_shape(1, n_elements); diff --git a/include/grammar.impl.hh b/include/grammar.impl.hh index 410055b..c03be4a 100644 --- a/include/grammar.impl.hh +++ b/include/grammar.impl.hh @@ -188,6 +188,7 @@ struct grammar : qi::grammar { FN("normfro", &Array::normfro) FN("norminf", &Array::norminf) FN("sum" , &Array::sum) + FN("cross" , &Array::cross) ; unary_op.add diff --git a/include/scalar_wrapper.hh b/include/scalar_wrapper.hh index 8a07d46..7aa050f 100644 --- a/include/scalar_wrapper.hh +++ b/include/scalar_wrapper.hh @@ -12,6 +12,7 @@ #include "scalar_node.hh" #include +#include //#include //impossible namespace bparser { diff --git a/test/test_parser.cc b/test/test_parser.cc index 457f1a6..4083d47 100644 --- a/test/test_parser.cc +++ b/test/test_parser.cc @@ -314,6 +314,13 @@ void test_expression() { BP_ASSERT(test_expr("sum([-3,-2,-1,0,1,2,3])", { 0 })); BP_ASSERT(test_expr("sum([[[8,7],[6,5]],[[4,3],[2,1]]])", { 1+2+3+4+5+6+7+8 })); + BP_ASSERT(test_expr("cross([1,2,3],[4,5,6])", {-3, 6, -3}, {3})); + BP_ASSERT(test_expr("cross([1,2],[4,5,6])", { 12, -6, -3 }, { 3 })); + BP_ASSERT(test_expr("cross([1,2,0],[4,5,6])", { 12, -6, -3 }, { 3 })); + BP_ASSERT(test_expr("cross([1,2],[4,5])", { -3 }, {})); + //BP_ASSERT(test_expr("cross([[1,2,3],[4,5,6]," + + // "[[4,5,6],[1,2,3]] )")); + /** * All bool tests have defined: * v1 - scalar array == [88..134] From 591eb620692d80b19d33466ddf66c5210630329a Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 7 Dec 2025 19:17:54 +0100 Subject: [PATCH 10/23] Created two more Make targets, to generete and run an expanded DAG from bparsed string --- .gitignore | 5 +- CMakeLists.txt | 28 +++++++++ include/expression_dag.hh | 115 ++++++++++++++++++++++++++++++++++-- nitpick/nitpick_def.cc | 54 +++++++++++++++++ nitpick/nitpick_generate.cc | 16 +++++ nitpick/nitpick_include.hh | 10 ++++ nitpick/nitpick_run.cc | 19 ++++++ 7 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 nitpick/nitpick_def.cc create mode 100644 nitpick/nitpick_generate.cc create mode 100644 nitpick/nitpick_include.hh create mode 100644 nitpick/nitpick_run.cc diff --git a/.gitignore b/.gitignore index dd5dd11..3485587 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ build/* .cproject .project .vs -CMakeSettings.json \ No newline at end of file +CMakeSettings.json + +# Autogenerated files +/nitpick/autogenerated.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index ae5cbef..f029243 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,3 +290,31 @@ define_test(test_grammar bparser) define_test(test_processor bparser) #is it broken? -LV define_test(test_speed bparser) define_test(test_simd) + +set(src_name "nitpick_generate") +set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") +set(nit_binary "${src_name}_bin") +set(nit_name "${src_name}") + +add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) +add_dependencies(${nit_binary} bparser) +target_link_libraries(${nit_binary} bparser) + +add_custom_target(${nit_name} +COMMAND "$" +DEPENDS ${nit_binary} +WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/nitpick") + +set(src_name "nitpick_run") +set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") +set(nit_binary "${src_name}_bin") +set(nit_name "${src_name}") + +add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) +add_dependencies(${nit_binary} bparser) +target_link_libraries(${nit_binary} bparser) + +add_custom_target(${nit_name} +COMMAND "$" +DEPENDS ${nit_binary} +WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/nitpick") diff --git a/include/expression_dag.hh b/include/expression_dag.hh index ab32ccb..c69e279 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -43,6 +43,7 @@ private: typedef std::pair InvDotNameAndScalar; typedef std::map InvDotMap; + typedef std::unordered_map CXXVarMap; /** * Used in the setup_result_storage to note number of unclosed nodes @@ -173,9 +174,9 @@ public: for (uint i = 0; i < sorted.size(); ++i) { for (uint in = 0; in < sorted[i]->n_inputs_; ++in) { std::cout << " "; - _print_dot_node_id(sorted[i]); + std::cout << _get_dot_node_id(sorted[i]); std::cout << "\n -> "; - _print_dot_node_id(sorted[i]->inputs_[in]); + std::cout << _get_dot_node_id(sorted[i]->inputs_[in]); std::cout << "\n\n"; } } @@ -183,6 +184,53 @@ public: std::cout << "----- end cut here -----" << "\n"; std::cout.flush(); } + + std::string print_in_cxx(/*const CXXVarMap& map*/) { + std::ostringstream result; + NodeVec result_nodes; + + result << "//AUTOGENERATED This file has been autogenerated by bparser::ExpressionDAG::print_in_cxx" << "\n"; + result << "\n"; + result << "#ifndef NITPICK_INCLUDE_ONLY" << "\n"; + result << "#include \"parser.hh\"" << "\n"; + result << "using namespace bparser;" << "\n"; + result << "using namespace bparser::details;" << "\n"; + result << "int main(){ //This is here only to stop any compiler warnings, do not run this file as is" << "\n"; + result << "#endif //NITPICK_INCLUDE_ONLY" << "\n"; + result << "\n"; + + //Print nodes + for (uint i = sorted.size(); i-- > 0U; ) { // N-1,N-2,... 0 + + result << _get_cxx_node_definition(sorted[i]); + //result << "\n"; + if (sorted[i]->result_storage == expr_result) { + result_nodes.push_back(sorted[i]); + } + } + result << "\n\n"; + //Print results + for (uint i = 0U; i < result_nodes.size(); ++i) { + result << "ScalarNodePtr " << "r" << i << " = " << _get_cxx_result(result_nodes[i]); + } + //Print final dag + result << "ExpressionDAG se({"; + for (uint i = 0U; i < result_nodes.size(); ++i) { + result << "r" << i; + if (i < result_nodes.size() - 1) { + result << ", "; + } + } + result << "}); //Do not rename this DAG, it is used later in the nitpick_run.cc file"; + + result << "\n"; + result << "#ifndef NITPICK_INCLUDE_ONLY" << "\n"; + result << "} //main" << "\n"; + result << "#endif //NITPICK_INCLUDE_ONLY" << "\n"; + //std::cout << result.str(); + return result.str(); + + } //Create a map of ScalarNodePtr -> (variable name, is_scalar) InvDotMap create_inverse_map(const std::map& symbols) const { @@ -200,13 +248,13 @@ public: private: //Print the vertice identifier for dot - void _print_dot_node_id(const ScalarNodePtr& node) const { - std::cout << node->op_name_ << "_" << (uintptr_t)node.get() << "__" << node->result_storage;// << std::endl; + std::string _get_dot_node_id(const ScalarNodePtr& node) const { + return (std::ostringstream() << node->op_name_ << "_" << (uintptr_t)node.get() << "__" << node->result_storage).str();// << std::endl; } //Print how the vertice should look in dot void _print_dot_node_definition(const ScalarNodePtr& node, const InvDotMap& invmap) const { - _print_dot_node_id(node); + std::cout << _get_dot_node_id(node); std::cout << ' '; if (node->result_storage == ResultStorage::constant) { // Constant @@ -284,6 +332,63 @@ private: } } + std::string _get_cxx_node_id(const ScalarNodePtr& node) { + return _get_dot_node_id(node); + } + + std::string _get_cxx_input_ids(const ScalarNodePtr& node) { + std::string result; + for (uint in = 0; in < node->n_inputs_; ++in) { + result += _get_cxx_node_id(node->inputs_[in]); + if (in < node->n_inputs_ - 1) { + result += ", "; + } + } + return result; + } + + std::string _get_cxx_node_definition(const ScalarNodePtr& node) { + std::ostringstream result; + + result << "ScalarNodePtr " << _get_cxx_node_id(node) << " = "; + switch (node->result_storage) + { + case ResultStorage::value: + case ResultStorage::constant:{ + result << "ScalarNode::create_const(" << *node->values_ << ");\n"; + break; + } + case ResultStorage::constant_bool: { + result << "ScalarNode::create_const_bool(" << *node->values_ << ");\n"; + break; + } + /*case ResultStorage::value: { + result << "ScalarNode::create_const(" << *node->values_ << ");\n"; + break; + } + case ResultStorage::value_copy: { + result << "ScalarNode::create_const(" << *node->values_ << ");\n"; + break; + }*/ + case ResultStorage::expr_result: + case ResultStorage::temporary: { + result << "ScalarNode::create<_" << node->op_name_ << "_>(" << _get_cxx_input_ids(node) << ");\n"; + break; + } + /*case ResultStorage::expr_result: { + result << "ScalarNode::create_result(" << _get_cxx_node_id(node->inputs_[0]) << ", " << "???" << ");\n"; + break; + }*/ + default: + break; + } + return result.str(); + } + + std::string _get_cxx_result(const ScalarNodePtr& node/*, std::string idx*/) { + return "ScalarNode::create_result(" + _get_cxx_node_id(node) + ", " + "???" + ");\n"; + } + void _print_i_node(uint i) { std::cout << sorted[i]->op_name_ << "_" << i << "_"<< sorted[i]->result_idx_; } diff --git a/nitpick/nitpick_def.cc b/nitpick/nitpick_def.cc new file mode 100644 index 0000000..ca16fb6 --- /dev/null +++ b/nitpick/nitpick_def.cc @@ -0,0 +1,54 @@ +/* + * nitpick_def.c + * + * Created on: Dec 7, 2025 + * Author: LV + */ + +/* +* Prepare an environment for both DAG generation and then subsequent running +*/ + +#ifndef NITPICK_INCLUDE_ONLY +#include "nitpick_include.hh" +#endif //NITPICK_INCLUDE_ONLY + +using namespace bparser; + +#ifndef NITPICK_INCLUDE_ONLY +int main() { +#endif //NITPICK_INCLUDE_ONLY + + // Define own value vectors, preferably aligned. + constexpr uint vec_size = 8; + + double v1[vec_size * 3]; + for (uint i = 0; i < vec_size * 3; ++i) { + v1[i] = i; + } + double v2[vec_size * 3]; + for (uint i = 0; i < vec_size * 3; ++i) { + v2[i] = 2; + } + double vres[vec_size * 3]; + + // Create parser, give the size of the value spaces. + // That is maximal allocated space. Actual values and + // active subset can be changed between evaluations of the expression. + Parser p(vec_size); + // parse an expression. + p.parse("1 * v1 + cs1 * v2"); + + // "cs1" constant with shape {}, i.e. scalar and values {2}. + p.set_constant("cs1", {}, { 2 }); + // "cv1" vector constant with shape {3} + p.set_constant("cv1", { 3 }, { 1, 2, 3 }); + // "v1" variable with shape {3}; v1 is pointer to the value space + p.set_variable("v1", { 3 }, v1); + p.set_variable("v2", { 3 }, v2); + // Set the result variable (the last line of the expression) + p.set_variable("_result_", { 3 }, vres); + +#ifndef NITPICK_INCLUDE_ONLY +} +#endif //NITPICK_INCLUDE_ONLY \ No newline at end of file diff --git a/nitpick/nitpick_generate.cc b/nitpick/nitpick_generate.cc new file mode 100644 index 0000000..35fafc8 --- /dev/null +++ b/nitpick/nitpick_generate.cc @@ -0,0 +1,16 @@ +#include "nitpick_include.hh" +#include + +int main() { +#define NITPICK_INCLUDE_ONLY +#include "nitpick_def.cc" + // Compile the expression into internal processor. + p.compile(); + + ExpressionDAG dag(p.result_array().elements()); + + std::ofstream file(NITPICK_AUTOGENERATED_FILE_NAME); + file << dag.print_in_cxx(); + file.close(); + std::cout << "File " << std::filesystem::current_path() << " " << NITPICK_AUTOGENERATED_FILE_NAME << " created"; +} \ No newline at end of file diff --git a/nitpick/nitpick_include.hh b/nitpick/nitpick_include.hh new file mode 100644 index 0000000..d457b2e --- /dev/null +++ b/nitpick/nitpick_include.hh @@ -0,0 +1,10 @@ +#ifndef NITPICK_INCLUDE_HH +#define NITPICK_INCLUDE_HH + +#define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.cc" //Keep the same in .gitignore + +#include "parser.hh" +#include +#include + +#endif //NITPICK_INCLUDE_HH \ No newline at end of file diff --git a/nitpick/nitpick_run.cc b/nitpick/nitpick_run.cc new file mode 100644 index 0000000..6c78e89 --- /dev/null +++ b/nitpick/nitpick_run.cc @@ -0,0 +1,19 @@ +#include "nitpick_include.hh" + +int main() { +// only get us the variables +#define NITPICK_INCLUDE_ONLY +#include "nitpick_def.cc" //include all the variables used to generate the file + +#include NITPICK_AUTOGENERATED_FILE_NAME + + ProcessorBase* processor = ProcessorBase::create_processor(se, vec_size, bparser::get_simd_size(), nullptr); + + std::vector subset = { 0, 1 }; + + processor->set_subset(subset); + processor->run(); + + // Result in the 'vres' value space. + //std::cout << print_vec(vres, 3 * vec_size); +} \ No newline at end of file From 717f3c9aea7ae28b732e896bd76ab316bee511c3 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Mon, 29 Dec 2025 15:41:52 +0100 Subject: [PATCH 11/23] Nitpick working on basic example --- include/expression_dag.hh | 47 ++++++++++++++++++++++--------------- include/test_tools.hh | 2 +- nitpick/nitpick_def.cc | 29 ++++++++++++++++------- nitpick/nitpick_generate.cc | 4 +++- nitpick/nitpick_include.hh | 34 +++++++++++++++++++++++++++ nitpick/nitpick_run.cc | 6 +++-- 6 files changed, 91 insertions(+), 31 deletions(-) diff --git a/include/expression_dag.hh b/include/expression_dag.hh index c69e279..5b51776 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -43,7 +43,7 @@ private: typedef std::pair InvDotNameAndScalar; typedef std::map InvDotMap; - typedef std::unordered_map CXXVarMap; + typedef std::unordered_map CXXVarMap; /** * Used in the setup_result_storage to note number of unclosed nodes @@ -185,7 +185,7 @@ public: std::cout.flush(); } - std::string print_in_cxx(/*const CXXVarMap& map*/) { + std::string print_in_cxx(const CXXVarMap& map) { std::ostringstream result; NodeVec result_nodes; @@ -195,14 +195,14 @@ public: result << "#include \"parser.hh\"" << "\n"; result << "using namespace bparser;" << "\n"; result << "using namespace bparser::details;" << "\n"; - result << "int main(){ //This is here only to stop any compiler warnings, do not run this file as is" << "\n"; + result << "int main(){ //This is here only to stop any IDE warnings, do not run this file as is" << "\n"; result << "#endif //NITPICK_INCLUDE_ONLY" << "\n"; result << "\n"; //Print nodes for (uint i = sorted.size(); i-- > 0U; ) { // N-1,N-2,... 0 - result << _get_cxx_node_definition(sorted[i]); + result << _get_cxx_node_definition(sorted[i],map); //result << "\n"; if (sorted[i]->result_storage == expr_result) { result_nodes.push_back(sorted[i]); @@ -211,7 +211,7 @@ public: result << "\n\n"; //Print results for (uint i = 0U; i < result_nodes.size(); ++i) { - result << "ScalarNodePtr " << "r" << i << " = " << _get_cxx_result(result_nodes[i]); + result << "ScalarNodePtr " << "r" << i << " = " << _get_cxx_result(result_nodes[i],map); } //Print final dag result << "ExpressionDAG se({"; @@ -332,11 +332,11 @@ private: } } - std::string _get_cxx_node_id(const ScalarNodePtr& node) { + std::string _get_cxx_node_id(const ScalarNodePtr& node) const { return _get_dot_node_id(node); } - std::string _get_cxx_input_ids(const ScalarNodePtr& node) { + std::string _get_cxx_input_ids(const ScalarNodePtr& node) const { std::string result; for (uint in = 0; in < node->n_inputs_; ++in) { result += _get_cxx_node_id(node->inputs_[in]); @@ -347,29 +347,38 @@ private: return result; } - std::string _get_cxx_node_definition(const ScalarNodePtr& node) { + std::string _get_cxx_node_definition(const ScalarNodePtr& node, const CXXVarMap& map) const { std::ostringstream result; result << "ScalarNodePtr " << _get_cxx_node_id(node) << " = "; switch (node->result_storage) { - case ResultStorage::value: - case ResultStorage::constant:{ - result << "ScalarNode::create_const(" << *node->values_ << ");\n"; - break; + case ResultStorage::constant: { + if (map.count(node->values_) == 1){ + result << "ScalarNode::create_const(node_map[\"" << map.at(node->values_) << "\"]);\n"; + } + else { + result << "ScalarNode::create_const(" << *node->values_ << ");\n"; } + break; + } case ResultStorage::constant_bool: { - result << "ScalarNode::create_const_bool(" << *node->values_ << ");\n"; + if (map.count(node->values_) == 1) { + result << "ScalarNode::create_const_bool(node_map[\"" << map.at(node->values_) << "\"]);\n"; + } + else { + result << "ScalarNode::create_const_bool(" << *node->values_ << ");\n"; + } break; } - /*case ResultStorage::value: { - result << "ScalarNode::create_const(" << *node->values_ << ");\n"; + case ResultStorage::value: { + result << "ScalarNode::create_value(node_map[\"" << map.at(node->values_) << "\"]);\n"; break; } case ResultStorage::value_copy: { - result << "ScalarNode::create_const(" << *node->values_ << ");\n"; + result << "ScalarNode::create_val_copy(node_map[\"" << map.at(node->values_) << "\"]);\n"; break; - }*/ + } case ResultStorage::expr_result: case ResultStorage::temporary: { result << "ScalarNode::create<_" << node->op_name_ << "_>(" << _get_cxx_input_ids(node) << ");\n"; @@ -385,8 +394,8 @@ private: return result.str(); } - std::string _get_cxx_result(const ScalarNodePtr& node/*, std::string idx*/) { - return "ScalarNode::create_result(" + _get_cxx_node_id(node) + ", " + "???" + ");\n"; + std::string _get_cxx_result(const ScalarNodePtr& node, const CXXVarMap& map) const { + return "ScalarNode::create_result(" + _get_cxx_node_id(node) + ", node_map[\"" + map.at(node->values_) + "\"]);\n"; } void _print_i_node(uint i) { diff --git a/include/test_tools.hh b/include/test_tools.hh index 498812d..6e53dce 100644 --- a/include/test_tools.hh +++ b/include/test_tools.hh @@ -8,7 +8,7 @@ #ifndef INCLUDE_TEST_TOOLS_HH_ #define INCLUDE_TEST_TOOLS_HH_ - +#include "config.hh" #include "assert.hh" #include diff --git a/nitpick/nitpick_def.cc b/nitpick/nitpick_def.cc index ca16fb6..a8e6286 100644 --- a/nitpick/nitpick_def.cc +++ b/nitpick/nitpick_def.cc @@ -20,7 +20,15 @@ int main() { #endif //NITPICK_INCLUDE_ONLY // Define own value vectors, preferably aligned. + // These variable names are used in macros and/or autogenerated files. Renaming them will cause issues constexpr uint vec_size = 8; + constexpr uint max_vec_size = vec_size; + + // These variable names are used in macros and/or autogenerated files. Renaming them will cause issues + std::unordered_map inv_map; //given to the print_in_cxx function to generate the file + std::unordered_map node_map; //is used by the generated file to use the same double pointers as the parser + + //Memory allocation double v1[vec_size * 3]; for (uint i = 0; i < vec_size * 3; ++i) { @@ -30,24 +38,29 @@ int main() { for (uint i = 0; i < vec_size * 3; ++i) { v2[i] = 2; } - double vres[vec_size * 3]; + constexpr int vres_size = vec_size * 3; + double vres[vres_size]; - // Create parser, give the size of the value spaces. + // Create parser, give the size of the value spaces. // That is maximal allocated space. Actual values and // active subset can be changed between evaluations of the expression. - Parser p(vec_size); + // This variable name is used in macros and/or autogenerated files. Renaming it will cause issues + Parser p(max_vec_size); + + // parse an expression. p.parse("1 * v1 + cs1 * v2"); + //TODO: Create the SET_CONSTANT macro // "cs1" constant with shape {}, i.e. scalar and values {2}. - p.set_constant("cs1", {}, { 2 }); + p.set_constant("cs1", {}, {2}); // "cv1" vector constant with shape {3} - p.set_constant("cv1", { 3 }, { 1, 2, 3 }); + p.set_constant("cv1", {3}, {1, 2, 3}); // "v1" variable with shape {3}; v1 is pointer to the value space - p.set_variable("v1", { 3 }, v1); - p.set_variable("v2", { 3 }, v2); + P_SET_VARIABLE(v1, { 3 }, v1); + P_SET_VARIABLE(v2, { 3 }, v2); // Set the result variable (the last line of the expression) - p.set_variable("_result_", { 3 }, vres); + P_SET_VARIABLE(_result_, { 3 }, vres); #ifndef NITPICK_INCLUDE_ONLY } diff --git a/nitpick/nitpick_generate.cc b/nitpick/nitpick_generate.cc index 35fafc8..bf7c418 100644 --- a/nitpick/nitpick_generate.cc +++ b/nitpick/nitpick_generate.cc @@ -2,15 +2,17 @@ #include int main() { + #define NITPICK_INCLUDE_ONLY #include "nitpick_def.cc" + // Compile the expression into internal processor. p.compile(); ExpressionDAG dag(p.result_array().elements()); std::ofstream file(NITPICK_AUTOGENERATED_FILE_NAME); - file << dag.print_in_cxx(); + file << dag.print_in_cxx(inv_map); file.close(); std::cout << "File " << std::filesystem::current_path() << " " << NITPICK_AUTOGENERATED_FILE_NAME << " created"; } \ No newline at end of file diff --git a/nitpick/nitpick_include.hh b/nitpick/nitpick_include.hh index d457b2e..f8d7a7f 100644 --- a/nitpick/nitpick_include.hh +++ b/nitpick/nitpick_include.hh @@ -3,8 +3,42 @@ #define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.cc" //Keep the same in .gitignore +#define P_SET_(TYPE, NAME, SHAPE, POINTER) \ + Array NAME##_array = Array::value(POINTER, max_vec_size, SHAPE); \ + \ + for (MultiIdx idx(NAME##_array.range()); idx.valid(); idx.inc_src()) { \ + std::string var_name = get_var_name(#NAME, idx.indices()); \ + inv_map[NAME##_array[idx]->values_] = var_name; \ + node_map[var_name] = NAME##_array[idx]->values_; \ + } \ + p.set_##TYPE(#NAME, SHAPE, POINTER); + +#define P_SET_VARIABLE(NAME, SHAPE, POINTER) P_SET_(variable, NAME, SHAPE, POINTER) +//#define P_SET_CONSTANT(NAME, SHAPE, POINTER) P_SET_(constant, NAME, SHAPE, POINTER) +#define P_SET_VAR_COPY(NAME, SHAPE, POINTER) P_SET_(var_copy, NAME, SHAPE, POINTER) + +#include "test_tools.hh" #include "parser.hh" #include #include +std::string get_var_name(const std::string & name, const bparser::MultiIdx::VecUint & indices); + +// v1, (1,2,3) => "v1__1_2_3" +std::string get_var_name(const std::string& name, const bparser::MultiIdx::VecUint& indices) { + bparser::MultiIdx::VecUint::size_type size(indices.size()); + + std::ostringstream result; + result << name; + result << "__"; + for (bparser::MultiIdx::VecUint::size_type i = 0; i < size; i++) { + result << indices.at(i); + if (i != size - 1) { + result << '_'; + } + } + return result.str(); +} + + #endif //NITPICK_INCLUDE_HH \ No newline at end of file diff --git a/nitpick/nitpick_run.cc b/nitpick/nitpick_run.cc index 6c78e89..aa5d6b0 100644 --- a/nitpick/nitpick_run.cc +++ b/nitpick/nitpick_run.cc @@ -7,13 +7,15 @@ int main() { #include NITPICK_AUTOGENERATED_FILE_NAME - ProcessorBase* processor = ProcessorBase::create_processor(se, vec_size, bparser::get_simd_size(), nullptr); + ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), nullptr); std::vector subset = { 0, 1 }; processor->set_subset(subset); processor->run(); + std::cout << "Result: \n"; + // Result in the 'vres' value space. - //std::cout << print_vec(vres, 3 * vec_size); + std::cout << print_vec(vres, vres_size); } \ No newline at end of file From 955e006cb70e843161b6549be6d76a4af0882d2e Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 4 Jan 2026 19:00:22 +0100 Subject: [PATCH 12/23] included generation time and date for autogenerated.cc --- include/expression_dag.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/expression_dag.hh b/include/expression_dag.hh index 5b51776..b70770e 100644 --- a/include/expression_dag.hh +++ b/include/expression_dag.hh @@ -190,6 +190,7 @@ public: NodeVec result_nodes; result << "//AUTOGENERATED This file has been autogenerated by bparser::ExpressionDAG::print_in_cxx" << "\n"; + result << "// " << __DATE__ << " " << __TIME__ << "\n"; result << "\n"; result << "#ifndef NITPICK_INCLUDE_ONLY" << "\n"; result << "#include \"parser.hh\"" << "\n"; @@ -354,7 +355,7 @@ private: switch (node->result_storage) { case ResultStorage::constant: { - if (map.count(node->values_) == 1){ + if (map.count(node->values_) == 1){ //this will not work, each Array::const has its own pointers result << "ScalarNode::create_const(node_map[\"" << map.at(node->values_) << "\"]);\n"; } else { From c6ba90e39e7a798e366d552771fb4df6d1aa8866 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sun, 4 Jan 2026 19:01:04 +0100 Subject: [PATCH 13/23] P_SET_CONSTANT macro added, does not fully work --- nitpick/nitpick_def.cc | 8 +++++--- nitpick/nitpick_generate.cc | 1 + nitpick/nitpick_include.hh | 33 +++++++++++++++++++++++---------- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/nitpick/nitpick_def.cc b/nitpick/nitpick_def.cc index a8e6286..ba486ed 100644 --- a/nitpick/nitpick_def.cc +++ b/nitpick/nitpick_def.cc @@ -40,6 +40,9 @@ int main() { } constexpr int vres_size = vec_size * 3; double vres[vres_size]; + for (uint i = 0; i < vres_size; ++i) { + vres[i] = NAN; + } // Create parser, give the size of the value spaces. // That is maximal allocated space. Actual values and @@ -51,11 +54,10 @@ int main() { // parse an expression. p.parse("1 * v1 + cs1 * v2"); - //TODO: Create the SET_CONSTANT macro // "cs1" constant with shape {}, i.e. scalar and values {2}. - p.set_constant("cs1", {}, {2}); + P_SET_CONSTANT(cs1, {}, {2}); // "cv1" vector constant with shape {3} - p.set_constant("cv1", {3}, {1, 2, 3}); + P_SET_CONSTANT(cv1, {3}, ARG({1, 2, 3})); // "v1" variable with shape {3}; v1 is pointer to the value space P_SET_VARIABLE(v1, { 3 }, v1); P_SET_VARIABLE(v2, { 3 }, v2); diff --git a/nitpick/nitpick_generate.cc b/nitpick/nitpick_generate.cc index bf7c418..46e424d 100644 --- a/nitpick/nitpick_generate.cc +++ b/nitpick/nitpick_generate.cc @@ -10,6 +10,7 @@ int main() { p.compile(); ExpressionDAG dag(p.result_array().elements()); + //dag.print_in_dot2(); std::ofstream file(NITPICK_AUTOGENERATED_FILE_NAME); file << dag.print_in_cxx(inv_map); diff --git a/nitpick/nitpick_include.hh b/nitpick/nitpick_include.hh index f8d7a7f..eab3ba5 100644 --- a/nitpick/nitpick_include.hh +++ b/nitpick/nitpick_include.hh @@ -3,40 +3,53 @@ #define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.cc" //Keep the same in .gitignore +#define ARG(...) __VA_ARGS__ + #define P_SET_(TYPE, NAME, SHAPE, POINTER) \ - Array NAME##_array = Array::value(POINTER, max_vec_size, SHAPE); \ + Array NAME##_array = Array::value(POINTER, max_vec_size, SHAPE); \ \ for (MultiIdx idx(NAME##_array.range()); idx.valid(); idx.inc_src()) { \ std::string var_name = get_var_name(#NAME, idx.indices()); \ - inv_map[NAME##_array[idx]->values_] = var_name; \ + inv_map[NAME##_array[idx]->values_] = var_name; \ node_map[var_name] = NAME##_array[idx]->values_; \ } \ p.set_##TYPE(#NAME, SHAPE, POINTER); -#define P_SET_VARIABLE(NAME, SHAPE, POINTER) P_SET_(variable, NAME, SHAPE, POINTER) -//#define P_SET_CONSTANT(NAME, SHAPE, POINTER) P_SET_(constant, NAME, SHAPE, POINTER) -#define P_SET_VAR_COPY(NAME, SHAPE, POINTER) P_SET_(var_copy, NAME, SHAPE, POINTER) +#define P_SET_C(TYPE, NAME, SHAPE, VALUES) \ + Array NAME##_array = Array::constant(VALUES, SHAPE); \ + \ + for (MultiIdx idx(NAME##_array.range()); idx.valid(); idx.inc_src()) { \ + std::string var_name = get_var_name(#NAME, idx.indices()); \ + inv_map[NAME##_array[idx]->values_] = var_name; \ + node_map[var_name] = NAME##_array[idx]->values_; \ + } \ + p.set_##TYPE(#NAME, SHAPE, VALUES); + +//this has different pointers than the parser's Array::constant, so it does not work + +#define P_SET_VARIABLE(NAME, SHAPE, POINTER) P_SET_(variable, NAME, ARG(SHAPE), POINTER) +#define P_SET_CONSTANT(NAME, SHAPE, VALUES) P_SET_C(constant, NAME, ARG(SHAPE), ARG(VALUES)) +#define P_SET_VAR_COPY(NAME, SHAPE, POINTER) P_SET_(var_copy, NAME, ARG(SHAPE), POINTER) #include "test_tools.hh" #include "parser.hh" #include #include -std::string get_var_name(const std::string & name, const bparser::MultiIdx::VecUint & indices); - -// v1, (1,2,3) => "v1__1_2_3" +// v1, (1,2,3) => "v1[1,2,3]" std::string get_var_name(const std::string& name, const bparser::MultiIdx::VecUint& indices) { bparser::MultiIdx::VecUint::size_type size(indices.size()); std::ostringstream result; result << name; - result << "__"; + result << "["; for (bparser::MultiIdx::VecUint::size_type i = 0; i < size; i++) { result << indices.at(i); if (i != size - 1) { - result << '_'; + result << ','; } } + result << "]"; return result.str(); } From e2c6d3214e824c80e3fc1988873f19e5227f3fc2 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Wed, 7 Jan 2026 15:26:20 +0100 Subject: [PATCH 14/23] Generation and running of generated files via preprocessor variables DEF_FILE and GEN_FILE Renamed NITPICK_INLCLUDE_ONLY to NITPICK_IDE_IGNROE --- .gitignore | 2 - CMakeLists.txt | 60 ++++++++++++++++--- .../nitpick_def.cc => cases/basic_expr_def.cc | 28 ++++----- cases/norm2_def.cc | 20 +++++++ include/dag_printer.hh | 8 +-- nitpick/example_def.cc | 22 +++++++ nitpick/nitpick_common.cc | 17 ++++++ nitpick/nitpick_generate.cc | 12 ++-- nitpick/nitpick_include.hh | 32 +++++++++- nitpick/nitpick_run.cc | 14 +++-- 10 files changed, 173 insertions(+), 42 deletions(-) rename nitpick/nitpick_def.cc => cases/basic_expr_def.cc (67%) create mode 100644 cases/norm2_def.cc create mode 100644 nitpick/example_def.cc create mode 100644 nitpick/nitpick_common.cc diff --git a/.gitignore b/.gitignore index 3485587..c9ca503 100644 --- a/.gitignore +++ b/.gitignore @@ -41,5 +41,3 @@ build/* .vs CMakeSettings.json -# Autogenerated files -/nitpick/autogenerated.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index f029243..168a52d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,7 +199,7 @@ message(STATUS "=======================================================\n\n") message(STATUS "VCL2_INCLUDE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}/third_party/VCL_v2") -set(BPARSER_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include ${Boost_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/third_party/VCL_v2 ${EIGEN3_INCLUDE_DIR}) +set(BPARSER_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/cases ${Boost_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/third_party/VCL_v2 ${EIGEN3_INCLUDE_DIR}) if(NOT PROJECT_IS_TOP_LEVEL) set(BPARSER_INCLUDES ${BPARSER_INCLUDES} PARENT_SCOPE) endif() @@ -291,30 +291,76 @@ define_test(test_processor bparser) #is it broken? -LV define_test(test_speed bparser) define_test(test_simd) +macro(define_nit_gen make_name def_file gen_file) set(src_name "nitpick_generate") set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") -set(nit_binary "${src_name}_bin") -set(nit_name "${src_name}") +set(nit_name "${src_name}_${make_name}") +set(nit_binary "${nit_name}_bin") add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) add_dependencies(${nit_binary} bparser) target_link_libraries(${nit_binary} bparser) +#set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") +set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") add_custom_target(${nit_name} COMMAND "$" DEPENDS ${nit_binary} -WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/nitpick") +WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") +endmacro() +macro(define_nit_run make_name def_file gen_file) set(src_name "nitpick_run") set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") -set(nit_binary "${src_name}_bin") -set(nit_name "${src_name}") +set(nit_name "${src_name}_${make_name}") +set(nit_binary "${nit_name}_bin") add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) add_dependencies(${nit_binary} bparser) target_link_libraries(${nit_binary} bparser) +#set_target_properties(${nit_binary} PROPERTIES COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") +set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") add_custom_target(${nit_name} COMMAND "$" DEPENDS ${nit_binary} -WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/nitpick") +WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") +endmacro() + +macro(define_nit make_name def_file gen_file) +define_nit_gen(${make_name} ${def_file} ${gen_file}) + +define_nit_run(${make_name} ${def_file} ${gen_file}) +endmacro() + +define_nit(basic_expr basic_expr_def.cc basic_expr_gen.cc) +define_nit(norm2 norm2_def.cc norm2_gen.cc) + + +#set(src_name "nitpick_generate") +#set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") +#set(nit_binary "${src_name}_bin") +#set(nit_name "${src_name}") + +#add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) +#add_dependencies(${nit_binary} bparser) +#target_link_libraries(${nit_binary} bparser) + +#add_custom_target(${nit_name} +#COMMAND "$" +#DEPENDS ${nit_binary} +#WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/nitpick") + +#set(src_name "nitpick_run") +#set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") +#set(nit_binary "${src_name}_bin") +#set(nit_name "${src_name}") + +#add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) +#add_dependencies(${nit_binary} bparser) +#target_link_libraries(${nit_binary} bparser) + +#add_custom_target(${nit_name} +#COMMAND "$" +#DEPENDS ${nit_binary} +#WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/nitpick") diff --git a/nitpick/nitpick_def.cc b/cases/basic_expr_def.cc similarity index 67% rename from nitpick/nitpick_def.cc rename to cases/basic_expr_def.cc index ba486ed..7f4eaa1 100644 --- a/nitpick/nitpick_def.cc +++ b/cases/basic_expr_def.cc @@ -1,32 +1,24 @@ -/* - * nitpick_def.c - * - * Created on: Dec 7, 2025 - * Author: LV - */ - /* * Prepare an environment for both DAG generation and then subsequent running */ -#ifndef NITPICK_INCLUDE_ONLY + +#ifndef NITPICK_IDE_IGNORE #include "nitpick_include.hh" -#endif //NITPICK_INCLUDE_ONLY +#endif //NITPICK_IDE_IGNORE -using namespace bparser; -#ifndef NITPICK_INCLUDE_ONLY + +#ifndef NITPICK_IDE_IGNORE int main() { -#endif //NITPICK_INCLUDE_ONLY +#endif //NITPICK_IDE_IGNORE // Define own value vectors, preferably aligned. // These variable names are used in macros and/or autogenerated files. Renaming them will cause issues constexpr uint vec_size = 8; constexpr uint max_vec_size = vec_size; - // These variable names are used in macros and/or autogenerated files. Renaming them will cause issues - std::unordered_map inv_map; //given to the print_in_cxx function to generate the file - std::unordered_map node_map; //is used by the generated file to use the same double pointers as the parser + //Memory allocation @@ -44,7 +36,7 @@ int main() { vres[i] = NAN; } - // Create parser, give the size of the value spaces. + // Create parser, give the size of the value spaces. // That is maximal allocated space. Actual values and // active subset can be changed between evaluations of the expression. // This variable name is used in macros and/or autogenerated files. Renaming it will cause issues @@ -64,6 +56,6 @@ int main() { // Set the result variable (the last line of the expression) P_SET_VARIABLE(_result_, { 3 }, vres); -#ifndef NITPICK_INCLUDE_ONLY +#ifndef NITPICK_IDE_IGNORE } -#endif //NITPICK_INCLUDE_ONLY \ No newline at end of file +#endif //NITPICK_IDE_IGNORE \ No newline at end of file diff --git a/cases/norm2_def.cc b/cases/norm2_def.cc new file mode 100644 index 0000000..3559b86 --- /dev/null +++ b/cases/norm2_def.cc @@ -0,0 +1,20 @@ +#ifndef NITPICK_IDE_IGNORE +# include "nitpick_include.hh" +#endif //This block will not be compiled during generation/run. But it helps the IDE understand the code + +constexpr uint vec_size = 8; +constexpr uint max_vec_size = vec_size; + +//MEM_ALLOC(name, 3*vec_size, 2) //2,2,2,2,2,2... +//MEM_ALLOC_INDEX(namei, 3 * vec_size) // 0,1,2,3,4,5... +MEM_ALLOC_LINEAR(v1, 3 * vec_size) // 1,2,3,4,5,6... +constexpr uint vres_size = 3 * vec_size; +MEM_ALLOC(vres, vres_size, NAN) + +Parser p(max_vec_size); + +p.parse("norm2(v1)"); + +P_SET_VARIABLE(v1, ARG({3}), v1) +P_SET_VARIABLE(_result_, { }, vres); + diff --git a/include/dag_printer.hh b/include/dag_printer.hh index ff39f4b..e931fcc 100644 --- a/include/dag_printer.hh +++ b/include/dag_printer.hh @@ -110,12 +110,12 @@ namespace details { result << "//AUTOGENERATED This file has been autogenerated by bparser::details::DagPrinter::print_in_cxx" << "\n"; result << "// " << __DATE__ << " " << __TIME__ << "\n"; result << "\n"; - result << "#ifndef NITPICK_INCLUDE_ONLY" << "\n"; + result << "#ifndef NITPICK_IDE_IGNORE" << "\n"; result << "#include \"parser.hh\"" << "\n"; result << "using namespace bparser;" << "\n"; result << "using namespace bparser::details;" << "\n"; result << "int main(){ //This is here only to stop any IDE warnings, do not run this file as is" << "\n"; - result << "#endif //NITPICK_INCLUDE_ONLY" << "\n"; + result << "#endif //NITPICK_IDE_IGNORE" << "\n"; result << "\n"; //Print nodes @@ -143,9 +143,9 @@ namespace details { result << "}); //Do not rename this DAG, it is used later in the nitpick_run.cc file"; result << "\n"; - result << "#ifndef NITPICK_INCLUDE_ONLY" << "\n"; + result << "#ifndef NITPICK_IDE_IGNORE" << "\n"; result << "} //main" << "\n"; - result << "#endif //NITPICK_INCLUDE_ONLY" << "\n"; + result << "#endif //NITPICK_IDE_IGNORE" << "\n"; //std::cout << result.str(); return result.str(); diff --git a/nitpick/example_def.cc b/nitpick/example_def.cc new file mode 100644 index 0000000..829aab1 --- /dev/null +++ b/nitpick/example_def.cc @@ -0,0 +1,22 @@ +// vec size and p names must stay the same! + +#ifndef NITPICK_IDE_IGNORE +# include "nitpick_include.hh" +#endif //This block will not be compiled during generation/run. But it helps the IDE understand the code + +constexpr uint vec_size = 8; +constexpr uint max_vec_size = vec_size; + +//MEM_ALLOC(name, 3*vec_size, 2) //2,2,2,2,2,2... +//MEM_ALLOC_INDEX(namei, 3 * vec_size) // 0,1,2,3,4,5... +//MEM_ALLOC_LINEAR(namel, 3 * vec_size) // 1,2,3,4,5,6... +constexpr uint vres_size = 3 * vec_size; +MEM_ALLOC(vres, vres_size, NAN) + +Parser p(max_vec_size); + +//P_SET_CONSTANT(name, ARG({2,2}), ARG({1,2,3,4})) //2x2 matrix +//P_SET_VARIABLE(v1, ARG({3}), namei) //vector from namei double* +P_SET_VARIABLE(_result_, { }, vres); + +p.parse("1+1"); \ No newline at end of file diff --git a/nitpick/nitpick_common.cc b/nitpick/nitpick_common.cc new file mode 100644 index 0000000..5747e84 --- /dev/null +++ b/nitpick/nitpick_common.cc @@ -0,0 +1,17 @@ +/* + * nitpick_common.cc + * Included in the nitpick_run.cc and nitpick_generate.cc files, do not compile + * + * Created on: Jan 7, 2026 + * Author: LV + */ + +#ifndef NITPICK_IDE_IGNORE +#include "parser.h" //this is here to help the IDE, header file includes are in nitpick_include.hh +#endif //NITPICK_IDE_IGNORE + +using namespace bparser; + +// These variable names are used in macros and/or autogenerated files. Renaming them will cause issues +std::unordered_map inv_map; //given to the print_in_cxx function to generate the file +std::unordered_map node_map; //is used by the generated file to use the same double pointers as the parser \ No newline at end of file diff --git a/nitpick/nitpick_generate.cc b/nitpick/nitpick_generate.cc index 8ea0790..8fee154 100644 --- a/nitpick/nitpick_generate.cc +++ b/nitpick/nitpick_generate.cc @@ -1,19 +1,23 @@ #include "nitpick_include.hh" +#include #include int main() { -#define NITPICK_INCLUDE_ONLY -#include "nitpick_def.cc" +#define NITPICK_IDE_IGNORE +#include "nitpick_common.cc" +#include NITPICK_DEF_FILE + + //p is in NITPICK_DEF_FILE // Compile the expression into internal processor. p.compile(); ExpressionDAG dag(p.result_array().elements()); //dag.print_in_dot2(); - std::ofstream file(NITPICK_AUTOGENERATED_FILE_NAME); + std::ofstream file(NITPICK_GEN_FILE); file << DagPrinter(dag).print_in_cxx(inv_map); file.close(); - std::cout << "File " << std::filesystem::current_path() << " " << NITPICK_AUTOGENERATED_FILE_NAME << " created"; + std::cout << "File " << std::filesystem::current_path() << " " << NITPICK_GEN_FILE << " created from " << NITPICK_DEF_FILE; } \ No newline at end of file diff --git a/nitpick/nitpick_include.hh b/nitpick/nitpick_include.hh index eab3ba5..a08e4db 100644 --- a/nitpick/nitpick_include.hh +++ b/nitpick/nitpick_include.hh @@ -1,7 +1,24 @@ #ifndef NITPICK_INCLUDE_HH #define NITPICK_INCLUDE_HH -#define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.cc" //Keep the same in .gitignore +#define NITPICK_FALLBACK_DEF_FILE_NAME "example_def.cc" +#define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.cc" + +#ifndef NITPICK_DEF_FILE +# ifdef DEF_FILE +# define NITPICK_DEF_FILE DEF_FILE +# else +# define NITPICK_DEF_FILE NITPICK_FALLBACK_DEF_FILE_NAME +# endif +#endif + +#ifndef NITPICK_GEN_FILE +# ifdef GEN_FILE +# define NITPICK_GEN_FILE GEN_FILE +# else +# define NITPICK_GEN_FILE NITPICK_AUTOGENERATED_FILE_NAME +# endif +#endif #define ARG(...) __VA_ARGS__ @@ -27,14 +44,23 @@ //this has different pointers than the parser's Array::constant, so it does not work -#define P_SET_VARIABLE(NAME, SHAPE, POINTER) P_SET_(variable, NAME, ARG(SHAPE), POINTER) #define P_SET_CONSTANT(NAME, SHAPE, VALUES) P_SET_C(constant, NAME, ARG(SHAPE), ARG(VALUES)) +#define P_SET_VARIABLE(NAME, SHAPE, POINTER) P_SET_(variable, NAME, ARG(SHAPE), POINTER) #define P_SET_VAR_COPY(NAME, SHAPE, POINTER) P_SET_(var_copy, NAME, ARG(SHAPE), POINTER) + +#define MEM_ALLOC(NAME, SIZE, VALUE) \ + double NAME[(SIZE)]; \ + for (uint i = 0; i < (SIZE); ++i) { \ + NAME[i] = (VALUE); \ + } + +#define MEM_ALLOC_INDEX(NAME, SIZE) MEM_ALLOC(NAME, SIZE, i) +#define MEM_ALLOC_LINEAR(NAME, SIZE) MEM_ALLOC(NAME, SIZE, i+1) + #include "test_tools.hh" #include "parser.hh" #include -#include // v1, (1,2,3) => "v1[1,2,3]" std::string get_var_name(const std::string& name, const bparser::MultiIdx::VecUint& indices) { diff --git a/nitpick/nitpick_run.cc b/nitpick/nitpick_run.cc index e8fe33f..370d860 100644 --- a/nitpick/nitpick_run.cc +++ b/nitpick/nitpick_run.cc @@ -2,18 +2,24 @@ int main() { // only get us the variables -#define NITPICK_INCLUDE_ONLY -#include "nitpick_def.cc" //include all the variables used to generate the file +#define NITPICK_IDE_IGNORE +#include "nitpick_common.cc" -#include NITPICK_AUTOGENERATED_FILE_NAME + //include all the variables used to generate the file +#include NITPICK_DEF_FILE + //include the generated file +#include NITPICK_GEN_FILE + //se is in the NITPICK_GEN_FILE + //max_vec_size is in NITPICK_DEF_FILE ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), nullptr); - std::vector subset = { 0, 1 }; //ctverice doubluu + std::vector subset = { 0, 1 }; //ctverice doubluu //TODO: move or expand processor->set_subset(subset); processor->run(); + //TODO: std::cout << "Result: \n"; // Result in the 'vres' value space. From c11ee3e57e98b2601718f2d7b7edf917af5a0a68 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Tue, 20 Jan 2026 18:44:05 +0100 Subject: [PATCH 15/23] Reworked nitpick to use the new ExprCase class rather than macros --- CMakeLists.txt | 68 ++++++------- cases/basic_expr_def.cc | 61 ------------ cases/basic_expr_def.hh | 25 +++++ cases/basic_expr_gen.hh | 36 +++++++ cases/norm2_def.cc | 20 ---- cases/norm2_def.hh | 13 +++ cases/norm2_gen.hh | 26 +++++ include/dag_printer.hh | 28 +++--- nitpick/example_def.cc | 22 ----- nitpick/example_def.hh | 12 +++ nitpick/exprcase.hh | 188 ++++++++++++++++++++++++++++++++++++ nitpick/nitpick_common.cc | 17 ---- nitpick/nitpick_generate.cc | 26 ++++- nitpick/nitpick_include.hh | 61 +----------- 14 files changed, 374 insertions(+), 229 deletions(-) delete mode 100644 cases/basic_expr_def.cc create mode 100644 cases/basic_expr_def.hh create mode 100644 cases/basic_expr_gen.hh delete mode 100644 cases/norm2_def.cc create mode 100644 cases/norm2_def.hh create mode 100644 cases/norm2_gen.hh delete mode 100644 nitpick/example_def.cc create mode 100644 nitpick/example_def.hh create mode 100644 nitpick/exprcase.hh delete mode 100644 nitpick/nitpick_common.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 168a52d..2bf9705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,49 +292,49 @@ define_test(test_speed bparser) define_test(test_simd) macro(define_nit_gen make_name def_file gen_file) -set(src_name "nitpick_generate") -set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") -set(nit_name "${src_name}_${make_name}") -set(nit_binary "${nit_name}_bin") - -add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) -add_dependencies(${nit_binary} bparser) -target_link_libraries(${nit_binary} bparser) -#set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") -set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") - -add_custom_target(${nit_name} -COMMAND "$" -DEPENDS ${nit_binary} -WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") + set(src_name "nitpick_generate") + set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") + set(nit_name "${src_name}_${make_name}") + set(nit_binary "${nit_name}_bin") + + add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) + add_dependencies(${nit_binary} bparser) + target_link_libraries(${nit_binary} bparser) + #set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") + set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") + + add_custom_target(${nit_name} + COMMAND "$" + DEPENDS ${nit_binary} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") endmacro() macro(define_nit_run make_name def_file gen_file) -set(src_name "nitpick_run") -set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") -set(nit_name "${src_name}_${make_name}") -set(nit_binary "${nit_name}_bin") - -add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) -add_dependencies(${nit_binary} bparser) -target_link_libraries(${nit_binary} bparser) -#set_target_properties(${nit_binary} PROPERTIES COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") -set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") - -add_custom_target(${nit_name} -COMMAND "$" -DEPENDS ${nit_binary} -WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") + set(src_name "nitpick_run") + set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") + set(nit_name "${src_name}_${make_name}") + set(nit_binary "${nit_name}_bin") + + add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) + add_dependencies(${nit_binary} bparser) + target_link_libraries(${nit_binary} bparser) + #set_target_properties(${nit_binary} PROPERTIES COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") + set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") + + add_custom_target(${nit_name} + COMMAND "$" + DEPENDS ${nit_binary} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") endmacro() macro(define_nit make_name def_file gen_file) -define_nit_gen(${make_name} ${def_file} ${gen_file}) + define_nit_gen(${make_name} ${def_file} ${gen_file}) -define_nit_run(${make_name} ${def_file} ${gen_file}) + define_nit_run(${make_name} ${def_file} ${gen_file}) endmacro() -define_nit(basic_expr basic_expr_def.cc basic_expr_gen.cc) -define_nit(norm2 norm2_def.cc norm2_gen.cc) +define_nit(basic_expr basic_expr_def.hh basic_expr_gen.hh) +define_nit(norm2 norm2_def.hh norm2_gen.hh) #set(src_name "nitpick_generate") diff --git a/cases/basic_expr_def.cc b/cases/basic_expr_def.cc deleted file mode 100644 index 7f4eaa1..0000000 --- a/cases/basic_expr_def.cc +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Prepare an environment for both DAG generation and then subsequent running -*/ - - -#ifndef NITPICK_IDE_IGNORE -#include "nitpick_include.hh" -#endif //NITPICK_IDE_IGNORE - - - -#ifndef NITPICK_IDE_IGNORE -int main() { -#endif //NITPICK_IDE_IGNORE - - // Define own value vectors, preferably aligned. - // These variable names are used in macros and/or autogenerated files. Renaming them will cause issues - constexpr uint vec_size = 8; - constexpr uint max_vec_size = vec_size; - - - - //Memory allocation - - double v1[vec_size * 3]; - for (uint i = 0; i < vec_size * 3; ++i) { - v1[i] = i; - } - double v2[vec_size * 3]; - for (uint i = 0; i < vec_size * 3; ++i) { - v2[i] = 2; - } - constexpr int vres_size = vec_size * 3; - double vres[vres_size]; - for (uint i = 0; i < vres_size; ++i) { - vres[i] = NAN; - } - - // Create parser, give the size of the value spaces. - // That is maximal allocated space. Actual values and - // active subset can be changed between evaluations of the expression. - // This variable name is used in macros and/or autogenerated files. Renaming it will cause issues - Parser p(max_vec_size); - - - // parse an expression. - p.parse("1 * v1 + cs1 * v2"); - - // "cs1" constant with shape {}, i.e. scalar and values {2}. - P_SET_CONSTANT(cs1, {}, {2}); - // "cv1" vector constant with shape {3} - P_SET_CONSTANT(cv1, {3}, ARG({1, 2, 3})); - // "v1" variable with shape {3}; v1 is pointer to the value space - P_SET_VARIABLE(v1, { 3 }, v1); - P_SET_VARIABLE(v2, { 3 }, v2); - // Set the result variable (the last line of the expression) - P_SET_VARIABLE(_result_, { 3 }, vres); - -#ifndef NITPICK_IDE_IGNORE -} -#endif //NITPICK_IDE_IGNORE \ No newline at end of file diff --git a/cases/basic_expr_def.hh b/cases/basic_expr_def.hh new file mode 100644 index 0000000..c227bf0 --- /dev/null +++ b/cases/basic_expr_def.hh @@ -0,0 +1,25 @@ +/* +* Prepare an environment for both DAG generation and then subsequent running +*/ + +#ifndef NITPICK_IDE_IGNORE +#include "nitpick_include.hh" +#endif //NITPICK_IDE_IGNORE + +void def(ExprCase& c) { + + // parse an expression. + c.parse("1 * v1 + cs1 * v2"); + + // "cs1" constant with shape {}, i.e. scalar and values {2}. + c.set_constant("cs1", {}, {2}); + // "cv1" vector constant with shape {3} + //P_SET_CONSTANT(cv1, {3}, ARG({1, 2, 3})); + // "v1" variable with shape {3}; v1 is pointer to the value space + c.set_variable("v1", {3}); + c.set_variable("v2", {3}); + // Set the result variable (the last line of the expression) + c.set_result_shape({ 3 }); + + +} diff --git a/cases/basic_expr_gen.hh b/cases/basic_expr_gen.hh new file mode 100644 index 0000000..fc596b2 --- /dev/null +++ b/cases/basic_expr_gen.hh @@ -0,0 +1,36 @@ +//AUTOGENERATED This file has been autogenerated by bparser::details::DagPrinter::print_in_cxx +// Jan 20 2026 17:52:25 + +#ifndef NITPICK_IDE_IGNORE +#include "nitpick_include.hh" +using namespace bparser; +using namespace bparser::details; +#endif //NITPICK_IDE_IGNORE //This is here only to stop any IDE warnings + +ExpressionDAG gen(const NodeMap& node_map){ //Do not rename this function, it is used later in the nitpick_run.cc file + ScalarNodePtr Const_16__1 = ScalarNode::create_const(1); + ScalarNodePtr Value_15__2 = ScalarNode::create_value(node_map.at("v1[0]")); + ScalarNodePtr mul_14__3 = ScalarNode::create<_mul_>(Const_16__1, Value_15__2); + ScalarNodePtr Const_13__1 = ScalarNode::create_const(2); + ScalarNodePtr Value_12__2 = ScalarNode::create_value(node_map.at("v2[0]")); + ScalarNodePtr mul_11__3 = ScalarNode::create<_mul_>(Const_13__1, Value_12__2); + ScalarNodePtr add_10__4 = ScalarNode::create<_add_>(mul_14__3, mul_11__3); + ScalarNodePtr Value_9__2 = ScalarNode::create_value(node_map.at("v1[1]")); + ScalarNodePtr mul_8__3 = ScalarNode::create<_mul_>(Const_16__1, Value_9__2); + ScalarNodePtr Value_7__2 = ScalarNode::create_value(node_map.at("v2[1]")); + ScalarNodePtr mul_6__3 = ScalarNode::create<_mul_>(Const_13__1, Value_7__2); + ScalarNodePtr add_5__4 = ScalarNode::create<_add_>(mul_8__3, mul_6__3); + ScalarNodePtr Value_4__2 = ScalarNode::create_value(node_map.at("v1[2]")); + ScalarNodePtr mul_3__3 = ScalarNode::create<_mul_>(Const_16__1, Value_4__2); + ScalarNodePtr Value_2__2 = ScalarNode::create_value(node_map.at("v2[2]")); + ScalarNodePtr mul_1__3 = ScalarNode::create<_mul_>(Const_13__1, Value_2__2); + ScalarNodePtr add_0__4 = ScalarNode::create<_add_>(mul_3__3, mul_1__3); + + + ScalarNodePtr r0 = ScalarNode::create_result(add_10__4, node_map.at("_result_[0]")); + ScalarNodePtr r1 = ScalarNode::create_result(add_5__4, node_map.at("_result_[1]")); + ScalarNodePtr r2 = ScalarNode::create_result(add_0__4, node_map.at("_result_[2]")); + ExpressionDAG se({r0, r1, r2}); + + return se; +} //gen diff --git a/cases/norm2_def.cc b/cases/norm2_def.cc deleted file mode 100644 index 3559b86..0000000 --- a/cases/norm2_def.cc +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NITPICK_IDE_IGNORE -# include "nitpick_include.hh" -#endif //This block will not be compiled during generation/run. But it helps the IDE understand the code - -constexpr uint vec_size = 8; -constexpr uint max_vec_size = vec_size; - -//MEM_ALLOC(name, 3*vec_size, 2) //2,2,2,2,2,2... -//MEM_ALLOC_INDEX(namei, 3 * vec_size) // 0,1,2,3,4,5... -MEM_ALLOC_LINEAR(v1, 3 * vec_size) // 1,2,3,4,5,6... -constexpr uint vres_size = 3 * vec_size; -MEM_ALLOC(vres, vres_size, NAN) - -Parser p(max_vec_size); - -p.parse("norm2(v1)"); - -P_SET_VARIABLE(v1, ARG({3}), v1) -P_SET_VARIABLE(_result_, { }, vres); - diff --git a/cases/norm2_def.hh b/cases/norm2_def.hh new file mode 100644 index 0000000..f2dd8d9 --- /dev/null +++ b/cases/norm2_def.hh @@ -0,0 +1,13 @@ + +#ifndef NITPICK_IDE_IGNORE +# include "nitpick/nitpick_include.hh" +#endif // NITPICK_IDE_IGNORE + +void def(ExprCase& c) { + +c.parse("norm2(v1)"); + +c.set_variable("v1", { 3 }); +c.set_result_shape({ }); +} + diff --git a/cases/norm2_gen.hh b/cases/norm2_gen.hh new file mode 100644 index 0000000..74f2ef5 --- /dev/null +++ b/cases/norm2_gen.hh @@ -0,0 +1,26 @@ +//AUTOGENERATED This file has been autogenerated by bparser::details::DagPrinter::print_in_cxx +// Jan 20 2026 18:42:12 + +#ifndef NITPICK_IDE_IGNORE +#include "nitpick_include.hh" +using namespace bparser; +using namespace bparser::details; +#endif //NITPICK_IDE_IGNORE //This is here only to stop any IDE warnings +ExpressionDAG gen(const NodeMap& node_map){ //Do not rename this function, it is used later in the nitpick_run.cc file + +ScalarNodePtr Value_8__2 = ScalarNode::create_value(node_map.at("v1[0]")); +ScalarNodePtr mul_7__3 = ScalarNode::create<_mul_>(Value_8__2, Value_8__2); +ScalarNodePtr Value_6__2 = ScalarNode::create_value(node_map.at("v1[1]")); +ScalarNodePtr mul_5__3 = ScalarNode::create<_mul_>(Value_6__2, Value_6__2); +ScalarNodePtr add_4__3 = ScalarNode::create<_add_>(mul_7__3, mul_5__3); +ScalarNodePtr Value_3__2 = ScalarNode::create_value(node_map.at("v1[2]")); +ScalarNodePtr mul_2__3 = ScalarNode::create<_mul_>(Value_3__2, Value_3__2); +ScalarNodePtr add_1__3 = ScalarNode::create<_add_>(add_4__3, mul_2__3); +ScalarNodePtr sqrt_0__4 = ScalarNode::create<_sqrt_>(add_1__3); + + +ScalarNodePtr r0 = ScalarNode::create_result(sqrt_0__4, node_map.at("_result_[]")); +ExpressionDAG se({r0}); + +return se; +} //gen diff --git a/include/dag_printer.hh b/include/dag_printer.hh index e931fcc..bb2e2ae 100644 --- a/include/dag_printer.hh +++ b/include/dag_printer.hh @@ -106,16 +106,16 @@ namespace details { std::string print_in_cxx(const CXXVarMap& map) { std::ostringstream result; ExpressionDAG::NodeVec result_nodes; - + //TODO: Make a function returning se and getting node_map result << "//AUTOGENERATED This file has been autogenerated by bparser::details::DagPrinter::print_in_cxx" << "\n"; result << "// " << __DATE__ << " " << __TIME__ << "\n"; result << "\n"; result << "#ifndef NITPICK_IDE_IGNORE" << "\n"; - result << "#include \"parser.hh\"" << "\n"; + result << "#include \"nitpick_include.hh\"" << "\n"; result << "using namespace bparser;" << "\n"; result << "using namespace bparser::details;" << "\n"; - result << "int main(){ //This is here only to stop any IDE warnings, do not run this file as is" << "\n"; - result << "#endif //NITPICK_IDE_IGNORE" << "\n"; + result << "#endif //NITPICK_IDE_IGNORE //This is here only to stop any IDE warnings" << "\n"; + result << "ExpressionDAG gen(const NodeMap& node_map){ //Do not rename this function, it is used later in the nitpick_run.cc file" << "\n"; result << "\n"; //Print nodes @@ -140,12 +140,12 @@ namespace details { result << ", "; } } - result << "}); //Do not rename this DAG, it is used later in the nitpick_run.cc file"; - + result << "});" << "\n"; result << "\n"; - result << "#ifndef NITPICK_IDE_IGNORE" << "\n"; - result << "} //main" << "\n"; - result << "#endif //NITPICK_IDE_IGNORE" << "\n"; + result << "return se;" << "\n"; + + result << "} //gen" << "\n"; + //std::cout << result.str(); return result.str(); @@ -340,7 +340,7 @@ namespace details { { case ResultStorage::constant: { if (map.count(node->values_) == 1) { //this will not work, each Array::const has its own pointers - result << "ScalarNode::create_const(node_map[\"" << map.at(node->values_) << "\"]);\n"; + result << "ScalarNode::create_const(node_map.at(\"" << map.at(node->values_) << "\"));\n"; } else { result << "ScalarNode::create_const(" << *node->values_ << ");\n"; @@ -349,7 +349,7 @@ namespace details { } case ResultStorage::constant_bool: { if (map.count(node->values_) == 1) { - result << "ScalarNode::create_const_bool(node_map[\"" << map.at(node->values_) << "\"]);\n"; + result << "ScalarNode::create_const_bool(node_map.at(\"" << map.at(node->values_) << "\"));\n"; } else { result << "ScalarNode::create_const_bool(" << *node->values_ << ");\n"; @@ -357,11 +357,11 @@ namespace details { break; } case ResultStorage::value: { - result << "ScalarNode::create_value(node_map[\"" << map.at(node->values_) << "\"]);\n"; + result << "ScalarNode::create_value(node_map.at(\"" << map.at(node->values_) << "\"));\n"; break; } case ResultStorage::value_copy: { - result << "ScalarNode::create_val_copy(node_map[\"" << map.at(node->values_) << "\"]);\n"; + result << "ScalarNode::create_val_copy(node_map.at(\"" << map.at(node->values_) << "\"));\n"; break; } case ResultStorage::expr_result: @@ -380,7 +380,7 @@ namespace details { } std::string _get_cxx_result(const ScalarNodePtr& node, const CXXVarMap& map) const { - return "ScalarNode::create_result(" + _get_cxx_node_id(node) + ", node_map[\"" + map.at(node->values_) + "\"]);\n"; + return "ScalarNode::create_result(" + _get_cxx_node_id(node) + ", node_map.at(\"" + map.at(node->values_) + "\"));\n"; } }; //DagPrinter diff --git a/nitpick/example_def.cc b/nitpick/example_def.cc deleted file mode 100644 index 829aab1..0000000 --- a/nitpick/example_def.cc +++ /dev/null @@ -1,22 +0,0 @@ -// vec size and p names must stay the same! - -#ifndef NITPICK_IDE_IGNORE -# include "nitpick_include.hh" -#endif //This block will not be compiled during generation/run. But it helps the IDE understand the code - -constexpr uint vec_size = 8; -constexpr uint max_vec_size = vec_size; - -//MEM_ALLOC(name, 3*vec_size, 2) //2,2,2,2,2,2... -//MEM_ALLOC_INDEX(namei, 3 * vec_size) // 0,1,2,3,4,5... -//MEM_ALLOC_LINEAR(namel, 3 * vec_size) // 1,2,3,4,5,6... -constexpr uint vres_size = 3 * vec_size; -MEM_ALLOC(vres, vres_size, NAN) - -Parser p(max_vec_size); - -//P_SET_CONSTANT(name, ARG({2,2}), ARG({1,2,3,4})) //2x2 matrix -//P_SET_VARIABLE(v1, ARG({3}), namei) //vector from namei double* -P_SET_VARIABLE(_result_, { }, vres); - -p.parse("1+1"); \ No newline at end of file diff --git a/nitpick/example_def.hh b/nitpick/example_def.hh new file mode 100644 index 0000000..5fafc77 --- /dev/null +++ b/nitpick/example_def.hh @@ -0,0 +1,12 @@ + +#ifndef NITPICK_IDE_IGNORE +# include "nitpick_include.hh" +#endif //This block will not be compiled during generation/run. But it helps the IDE understand the code + +void def(ExprCase& c) { + +c.parse("1+1"); + +//c.set_variable("v1" {3}); +c.set_result_shape({}); +} \ No newline at end of file diff --git a/nitpick/exprcase.hh b/nitpick/exprcase.hh new file mode 100644 index 0000000..c0c283d --- /dev/null +++ b/nitpick/exprcase.hh @@ -0,0 +1,188 @@ +/* + * exprcase.hh + * Holds the memory and other config for generating and running DAGs + * + * Created on: Jan 20, 2026 + * Author: LV + */ + + +#ifndef EXPRCASE_HH +#define EXPRCASE_HH + +#include "arena_resource.hh" +#include "parser.hh" + +using namespace bparser; + +namespace bparser { + + typedef std::unordered_map NodeMap; + + class ExprCase { + public: + enum VarType { + Variable, + VarCopy, + Const + }; + + using string = std::string; + using Shape = bparser::Shape; + typedef std::pair VarInfo; + + + + public: //Constructors + ExprCase(uint max_vec_size, void* buffer, size_t buffer_size, size_t simd_size) : + max_vec_size(max_vec_size), + arena(std::make_shared(buffer, buffer_size, simd_size)), + parser(Parser(max_vec_size)) + { + ; + } + + ExprCase(uint max_vec_size, void* buffer, size_t buffer_size) : + ExprCase(max_vec_size, buffer, buffer_size, bparser::get_simd_size()) + { + ; + } + protected: + + uint max_vec_size; + + PatchArenaPtr arena; + Parser parser; + std::string parseQuery; + + std::unordered_map variables; + std::unordered_map> variable_consts; + + std::unordered_map inv_map; //given to the print_in_cxx function to generate the file + NodeMap node_map; //is used by the generated file to use the same double pointers as the parser + + + public: //Methods for the *_def.hh files + void set_variable(string name, Shape shape){ + variables[name] = VarInfo(Variable, shape); + } + void set_var_copy(string name, Shape shape){ + variables[name] = VarInfo(VarCopy, shape); + } + void set_constant(string name, Shape shape, std::vector values){ + variables[name] = VarInfo(Const, shape); + variable_consts[name] = values; + } + void set_result_shape(Shape shape) { + variables["_result_"] = VarInfo(Variable, shape); + } + void parse(string query) { + parseQuery = query; + + parser.parse(query); + } + + public: //Methods for the gen/run files + + void deallocate() { + arena->reset(); + } + + void allocate(double vec_size) { + for (const auto& [varName, varInfo] : variables) { + VarType t = varInfo.first; + Shape shape = varInfo.second; + double n_values = numel(shape); + double* ptr = arena->allocate_simd(n_values * vec_size); + + if (t == Const) { + map_const(varName, variable_consts[varName], shape); + } + else { + map_variable(varName, ptr, shape); + } + + + if (t == Variable) { + parser.set_variable(varName, shape, ptr); + }else if (t == VarCopy) { + parser.set_var_copy(varName, shape, ptr); + } + else if (t == Const) { + parser.set_constant(varName, shape, variable_consts[varName]); + } + + } + } + + PatchArenaPtr get_patch_arena() const { + return arena; + } + string get_query() const { + return parseQuery; + } + + const std::unordered_map& get_inv_map() const{ + return inv_map; + } + const NodeMap& get_node_map() const{ + return node_map; + } + + Parser& get_parser() { + return parser; + } + + protected: //Helper methods + + //Put the variable names and ptrs in the maps for gen/run use + void map_variable(string name,double* pointer, const Shape& shape) { + Array array = Array::value(pointer, max_vec_size, shape); + + for (MultiIdx idx(array.range()); idx.valid(); idx.inc_src()) { + + std::string var_name = get_var_name(name, idx.indices()); + inv_map[array[idx]->values_] = var_name; + node_map[var_name] = array[idx]->values_; + } + } + + void map_const(string name, const std::vector& values, const Shape& shape) { + Array array = Array::constant(values, shape); //useless + + for (MultiIdx idx(array.range()); idx.valid(); idx.inc_src()) { + + std::string var_name = get_var_name(name, idx.indices()); + //inv_map[array[idx]->values_] = var_name; + //node_map[var_name] = array[idx]->values_; + } + } + + inline double numel(const Shape& s) { + double res = 1; + for (const uint& n : s) { + res *= n; + } + return res; + } + + // v1, (1,2,3) => "v1[1,2,3]" + std::string get_var_name(const std::string& name, const bparser::MultiIdx::VecUint& indices) { + bparser::MultiIdx::VecUint::size_type size(indices.size()); + + std::ostringstream result; + result << name; + result << "["; + for (bparser::MultiIdx::VecUint::size_type i = 0; i < size; i++) { + result << indices.at(i); + if (i != size - 1) { + result << ','; + } + } + result << "]"; + return result.str(); + } + }; +} + +#endif //EXPRCASE_HH \ No newline at end of file diff --git a/nitpick/nitpick_common.cc b/nitpick/nitpick_common.cc deleted file mode 100644 index 5747e84..0000000 --- a/nitpick/nitpick_common.cc +++ /dev/null @@ -1,17 +0,0 @@ -/* - * nitpick_common.cc - * Included in the nitpick_run.cc and nitpick_generate.cc files, do not compile - * - * Created on: Jan 7, 2026 - * Author: LV - */ - -#ifndef NITPICK_IDE_IGNORE -#include "parser.h" //this is here to help the IDE, header file includes are in nitpick_include.hh -#endif //NITPICK_IDE_IGNORE - -using namespace bparser; - -// These variable names are used in macros and/or autogenerated files. Renaming them will cause issues -std::unordered_map inv_map; //given to the print_in_cxx function to generate the file -std::unordered_map node_map; //is used by the generated file to use the same double pointers as the parser \ No newline at end of file diff --git a/nitpick/nitpick_generate.cc b/nitpick/nitpick_generate.cc index 8fee154..68c5119 100644 --- a/nitpick/nitpick_generate.cc +++ b/nitpick/nitpick_generate.cc @@ -1,23 +1,39 @@ #include "nitpick_include.hh" #include #include +#define NITPICK_IDE_IGNORE + +#include NITPICK_DEF_FILE + +using namespace bparser; int main() { -#define NITPICK_IDE_IGNORE -#include "nitpick_common.cc" + const uint max_vec_size = 20000; + const size_t buffer_size = sizeof(double) * max_vec_size; -#include NITPICK_DEF_FILE + void* buffer = ::operator new(buffer_size); + + + //double buffer[buffer_size]; + ExprCase exprcase(max_vec_size,(void*)buffer, buffer_size); + + //def is in NITPICK_DEF_FILE + def(exprcase); + + exprcase.allocate(1); + Parser& p = exprcase.get_parser(); - //p is in NITPICK_DEF_FILE // Compile the expression into internal processor. p.compile(); + //p.compile(exprcase.get_patch_arena()); //Add arena from ExprCase ExpressionDAG dag(p.result_array().elements()); //dag.print_in_dot2(); std::ofstream file(NITPICK_GEN_FILE); - file << DagPrinter(dag).print_in_cxx(inv_map); + file << DagPrinter(dag).print_in_cxx(exprcase.get_inv_map()); file.close(); std::cout << "File " << std::filesystem::current_path() << " " << NITPICK_GEN_FILE << " created from " << NITPICK_DEF_FILE; + ::operator delete(buffer); } \ No newline at end of file diff --git a/nitpick/nitpick_include.hh b/nitpick/nitpick_include.hh index a08e4db..a313670 100644 --- a/nitpick/nitpick_include.hh +++ b/nitpick/nitpick_include.hh @@ -1,8 +1,8 @@ #ifndef NITPICK_INCLUDE_HH #define NITPICK_INCLUDE_HH -#define NITPICK_FALLBACK_DEF_FILE_NAME "example_def.cc" -#define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.cc" +#define NITPICK_FALLBACK_DEF_FILE_NAME "example_def.hh" +#define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.hh" #ifndef NITPICK_DEF_FILE # ifdef DEF_FILE @@ -20,64 +20,13 @@ # endif #endif -#define ARG(...) __VA_ARGS__ - -#define P_SET_(TYPE, NAME, SHAPE, POINTER) \ - Array NAME##_array = Array::value(POINTER, max_vec_size, SHAPE); \ - \ - for (MultiIdx idx(NAME##_array.range()); idx.valid(); idx.inc_src()) { \ - std::string var_name = get_var_name(#NAME, idx.indices()); \ - inv_map[NAME##_array[idx]->values_] = var_name; \ - node_map[var_name] = NAME##_array[idx]->values_; \ - } \ - p.set_##TYPE(#NAME, SHAPE, POINTER); - -#define P_SET_C(TYPE, NAME, SHAPE, VALUES) \ - Array NAME##_array = Array::constant(VALUES, SHAPE); \ - \ - for (MultiIdx idx(NAME##_array.range()); idx.valid(); idx.inc_src()) { \ - std::string var_name = get_var_name(#NAME, idx.indices()); \ - inv_map[NAME##_array[idx]->values_] = var_name; \ - node_map[var_name] = NAME##_array[idx]->values_; \ - } \ - p.set_##TYPE(#NAME, SHAPE, VALUES); - -//this has different pointers than the parser's Array::constant, so it does not work - -#define P_SET_CONSTANT(NAME, SHAPE, VALUES) P_SET_C(constant, NAME, ARG(SHAPE), ARG(VALUES)) -#define P_SET_VARIABLE(NAME, SHAPE, POINTER) P_SET_(variable, NAME, ARG(SHAPE), POINTER) -#define P_SET_VAR_COPY(NAME, SHAPE, POINTER) P_SET_(var_copy, NAME, ARG(SHAPE), POINTER) - - -#define MEM_ALLOC(NAME, SIZE, VALUE) \ - double NAME[(SIZE)]; \ - for (uint i = 0; i < (SIZE); ++i) { \ - NAME[i] = (VALUE); \ - } - -#define MEM_ALLOC_INDEX(NAME, SIZE) MEM_ALLOC(NAME, SIZE, i) -#define MEM_ALLOC_LINEAR(NAME, SIZE) MEM_ALLOC(NAME, SIZE, i+1) - #include "test_tools.hh" #include "parser.hh" #include +#include "exprcase.hh" -// v1, (1,2,3) => "v1[1,2,3]" -std::string get_var_name(const std::string& name, const bparser::MultiIdx::VecUint& indices) { - bparser::MultiIdx::VecUint::size_type size(indices.size()); - - std::ostringstream result; - result << name; - result << "["; - for (bparser::MultiIdx::VecUint::size_type i = 0; i < size; i++) { - result << indices.at(i); - if (i != size - 1) { - result << ','; - } - } - result << "]"; - return result.str(); -} +//void def(ExprCase c); +//ExpressionDAG gen(const NodeMap& node_map); #endif //NITPICK_INCLUDE_HH \ No newline at end of file From 285c200f2cbc030c7d52023aa35802f6c179a232 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Tue, 20 Jan 2026 18:44:23 +0100 Subject: [PATCH 16/23] Running with the ExprCase class --- nitpick/nitpick_run.cc | 58 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/nitpick/nitpick_run.cc b/nitpick/nitpick_run.cc index 370d860..e252620 100644 --- a/nitpick/nitpick_run.cc +++ b/nitpick/nitpick_run.cc @@ -1,27 +1,63 @@ #include "nitpick_include.hh" - -int main() { +#include // only get us the variables #define NITPICK_IDE_IGNORE -#include "nitpick_common.cc" +using namespace bparser; //include all the variables used to generate the file #include NITPICK_DEF_FILE //include the generated file #include NITPICK_GEN_FILE - //se is in the NITPICK_GEN_FILE - //max_vec_size is in NITPICK_DEF_FILE - ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), nullptr); +int main() { + + const uint max_vec_size = 200000; + const size_t buffer_size = sizeof(double) * max_vec_size; + + const uint N = 2048U; + + void* buffer = ::operator new(buffer_size); + ExprCase exprcase(max_vec_size, (void*)buffer, buffer_size); + + def(exprcase); + + + + + std::vector subset = { 0, 1, 2, 3 }; //ctverice doubluu //TODO: autocreate from vec_size + std::vector vec_sizes({ 16U, 64U, 256U, 1024U }); + + PatchArenaPtr arena = exprcase.get_patch_arena(); + for (uint vec_size : vec_sizes) { + uint n_repeats = N / vec_size; + std::cout << "Running \"" << exprcase.get_query() << "\" " << n_repeats << "x for vec_size: " << vec_size << std::endl; + exprcase.allocate(vec_size); + + //Get autogenerated dag; + ExpressionDAG se = gen(exprcase.get_node_map()); + + //ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), arena); //get arena from ExprCase + ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), nullptr); + processor->set_subset(subset); - std::vector subset = { 0, 1 }; //ctverice doubluu //TODO: move or expand + auto start_time = std::chrono::high_resolution_clock::now(); + for (uint i_rep = 0; i_rep < n_repeats; i_rep++) { + processor->run(); + } + auto end_time = std::chrono::high_resolution_clock::now(); - processor->set_subset(subset); - processor->run(); + double time = std::chrono::duration_cast>(end_time - start_time).count(); + std::cout << "Finished in " << time << std::endl; + //processor.get_arena(); + delete processor; //ERROR from shared_ptr + exprcase.deallocate(); + } + //auto start_time = std::chrono::high_resolution_clock::now(); //TODO: - std::cout << "Result: \n"; + //std::cout << "Result: \n"; // Result in the 'vres' value space. - std::cout << print_vec(vres, vres_size); + //std::cout << print_vec(vres, vres_size); + ::operator delete(buffer); } \ No newline at end of file From 16013b6eeddf29c35e76168be153a670dcf02507 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Thu, 22 Jan 2026 14:33:36 +0100 Subject: [PATCH 17/23] Ternary ScalarNode::create, mul_add (MAC) and an attempt to use it with norm2_gen_edit.hh --- CMakeLists.txt | 1 + cases/norm2_gen_edit.hh | 27 +++++++++++++++++++++++++++ include/processor.hh | 2 +- include/scalar_node.hh | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 cases/norm2_gen_edit.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bf9705..deb8c69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,6 +335,7 @@ endmacro() define_nit(basic_expr basic_expr_def.hh basic_expr_gen.hh) define_nit(norm2 norm2_def.hh norm2_gen.hh) +define_nit_run(norm2_mac norm2_def.hh norm2_gen_edit.hh) #set(src_name "nitpick_generate") diff --git a/cases/norm2_gen_edit.hh b/cases/norm2_gen_edit.hh new file mode 100644 index 0000000..59cb024 --- /dev/null +++ b/cases/norm2_gen_edit.hh @@ -0,0 +1,27 @@ +//AUTOGENERATED This file has been autogenerated by bparser::details::DagPrinter::print_in_cxx +// Jan 20 2026 18:42:12 +// And edited on Jan 22 to use multiply-accumulate (fma) + +#ifndef NITPICK_IDE_IGNORE +#include "nitpick_include.hh" +using namespace bparser; +using namespace bparser::details; +#endif //NITPICK_IDE_IGNORE //This is here only to stop any IDE warnings +ExpressionDAG gen(const NodeMap& node_map){ //Do not rename this function, it is used later in the nitpick_run.cc file + +ScalarNodePtr Value_8__2 = ScalarNode::create_value(node_map.at("v1[0]")); +ScalarNodePtr Value_6__2 = ScalarNode::create_value(node_map.at("v1[1]")); +ScalarNodePtr Value_3__2 = ScalarNode::create_value(node_map.at("v1[2]")); +ScalarNodePtr mul_7__3 = ScalarNode::create<_mul_>(Value_8__2, Value_8__2); +ScalarNodePtr muladd_4__3 = ScalarNode::create<_muladd_>(Value_6__2, Value_6__2, mul_7__3); +//ScalarNodePtr add_4__3 = ScalarNode::create<_add_>(mul_7__3, mul_5__3); +ScalarNodePtr muladd_2__3 = ScalarNode::create<_muladd_>(Value_3__2, Value_3__2, muladd_4__3); +//ScalarNodePtr add_1__3 = ScalarNode::create<_add_>(add_5__3, mul_2__3); +ScalarNodePtr sqrt_0__4 = ScalarNode::create<_sqrt_>(muladd_2__3); + + +ScalarNodePtr r0 = ScalarNode::create_result(sqrt_0__4, node_map.at("_result_[]")); +ExpressionDAG se({r0}); + +return se; +} //gen diff --git a/include/processor.hh b/include/processor.hh index b16e018..3754cfc 100644 --- a/include/processor.hh +++ b/include/processor.hh @@ -431,7 +431,7 @@ struct Processor : public ProcessorBase { CODE(_copy_); CODE(_ifelse_); CODE(_log2_); -// CODE(__); + CODE(_muladd_); // CODE(__); // CODE(__); // CODE(__); diff --git a/include/scalar_node.hh b/include/scalar_node.hh index 44faedb..1547f55 100644 --- a/include/scalar_node.hh +++ b/include/scalar_node.hh @@ -86,6 +86,9 @@ struct ScalarNode { template static ScalarNodePtr create(ScalarNodePtr a, ScalarNodePtr b); + template + static ScalarNodePtr create(ScalarNodePtr a, ScalarNodePtr b, ScalarNodePtr c); + ScalarNode() @@ -728,6 +731,20 @@ inline void _ifelse_::eval(double &res, double a, double b, double c) { } UNARY_FN(_log2_, 52, log2); +inline double mul_add(double a, double b, double c) { + return std::fma(a, b, c); +} +using ::mul_add; //+ VCL mul_add + +struct _muladd_ : public ScalarNode { + static const char op_code = 53; + static const char n_eval_args = 4; + template + inline static void eval(VecType& res, VecType a, VecType b, VecType c) { + res = mul_add(a, b, c); // a * b + c + } +}; + /*********************** * Construction Nodes. @@ -808,6 +825,26 @@ ScalarNodePtr ScalarNode::create(ScalarNodePtr a, ScalarNodePtr b) { return node_ptr; } +template +ScalarNodePtr ScalarNode::create(ScalarNodePtr a, ScalarNodePtr b, ScalarNodePtr c) { + std::shared_ptr node_ptr = std::make_shared(); + node_ptr->op_code_ = T::op_code; + node_ptr->set_name(typeid(T).name()); + node_ptr->add_input(a); + node_ptr->add_input(b); + node_ptr->add_input(c); + if (T::n_eval_args == 3) { + // Note: in place operations are not supported + node_ptr->result_storage = none; + } + else { + BP_ASSERT(T::n_eval_args == 4); + node_ptr->result_storage = temporary; + } + + return node_ptr; +} + inline ScalarNodePtr ScalarNode::create_ifelse(ScalarNodePtr a, ScalarNodePtr b, ScalarNodePtr c) { std::shared_ptr<_ifelse_> node_ptr = std::make_shared<_ifelse_>(); node_ptr->op_code_ = _ifelse_::op_code; From 5a48e221ca17b3f3ef7c3a0263d116945d4b03e8 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Thu, 22 Jan 2026 14:34:01 +0100 Subject: [PATCH 18/23] More attempts at getting this nitpick_run to work --- nitpick/nitpick_run.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/nitpick/nitpick_run.cc b/nitpick/nitpick_run.cc index e252620..17f331e 100644 --- a/nitpick/nitpick_run.cc +++ b/nitpick/nitpick_run.cc @@ -11,8 +11,8 @@ using namespace bparser; int main() { - const uint max_vec_size = 200000; - const size_t buffer_size = sizeof(double) * max_vec_size; + const uint max_vec_size = 2000; + const size_t buffer_size = sizeof(double) * max_vec_size + 50000; const uint N = 2048U; @@ -21,8 +21,9 @@ int main() { def(exprcase); - - + if (exprcase.get_query().empty()) { + Throw() << "No expression was set!"; + } std::vector subset = { 0, 1, 2, 3 }; //ctverice doubluu //TODO: autocreate from vec_size std::vector vec_sizes({ 16U, 64U, 256U, 1024U }); @@ -36,8 +37,8 @@ int main() { //Get autogenerated dag; ExpressionDAG se = gen(exprcase.get_node_map()); - //ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), arena); //get arena from ExprCase - ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), nullptr); + ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), arena); //get arena from ExprCase + //ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), nullptr); processor->set_subset(subset); auto start_time = std::chrono::high_resolution_clock::now(); @@ -49,7 +50,8 @@ int main() { double time = std::chrono::duration_cast>(end_time - start_time).count(); std::cout << "Finished in " << time << std::endl; //processor.get_arena(); - delete processor; //ERROR from shared_ptr + delete processor; + exprcase.deallocate(); } //auto start_time = std::chrono::high_resolution_clock::now(); From f1314133eed8368a987753fb3605037d998d3a39 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Thu, 22 Jan 2026 15:14:13 +0100 Subject: [PATCH 19/23] Fixed the nitpick_run.cc error --- nitpick/exprcase.hh | 12 ++++++------ nitpick/nitpick_generate.cc | 8 +++++++- nitpick/nitpick_run.cc | 25 +++++++++++++------------ 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/nitpick/exprcase.hh b/nitpick/exprcase.hh index c0c283d..d5f9e99 100644 --- a/nitpick/exprcase.hh +++ b/nitpick/exprcase.hh @@ -53,7 +53,7 @@ namespace bparser { PatchArenaPtr arena; Parser parser; - std::string parseQuery; + std::string parseExpr; std::unordered_map variables; std::unordered_map> variable_consts; @@ -76,10 +76,10 @@ namespace bparser { void set_result_shape(Shape shape) { variables["_result_"] = VarInfo(Variable, shape); } - void parse(string query) { - parseQuery = query; + void parse(string expression) { + parseExpr = expression; - parser.parse(query); + parser.parse(expression); } public: //Methods for the gen/run files @@ -118,8 +118,8 @@ namespace bparser { PatchArenaPtr get_patch_arena() const { return arena; } - string get_query() const { - return parseQuery; + string get_expression() const { + return parseExpr; } const std::unordered_map& get_inv_map() const{ diff --git a/nitpick/nitpick_generate.cc b/nitpick/nitpick_generate.cc index 68c5119..1d76ac6 100644 --- a/nitpick/nitpick_generate.cc +++ b/nitpick/nitpick_generate.cc @@ -21,6 +21,10 @@ int main() { //def is in NITPICK_DEF_FILE def(exprcase); + if (exprcase.get_expression().empty()) { + Throw() << "No expression was set!"; + } + exprcase.allocate(1); Parser& p = exprcase.get_parser(); @@ -35,5 +39,7 @@ int main() { file << DagPrinter(dag).print_in_cxx(exprcase.get_inv_map()); file.close(); std::cout << "File " << std::filesystem::current_path() << " " << NITPICK_GEN_FILE << " created from " << NITPICK_DEF_FILE; + ::operator delete(buffer); -} \ No newline at end of file + +} //main \ No newline at end of file diff --git a/nitpick/nitpick_run.cc b/nitpick/nitpick_run.cc index 17f331e..50f8aba 100644 --- a/nitpick/nitpick_run.cc +++ b/nitpick/nitpick_run.cc @@ -12,24 +12,25 @@ using namespace bparser; int main() { const uint max_vec_size = 2000; - const size_t buffer_size = sizeof(double) * max_vec_size + 50000; + const size_t buffer_size = sizeof(double) * max_vec_size + 500000; - const uint N = 2048U; + const uint N = 1073741824U; //2^30 void* buffer = ::operator new(buffer_size); ExprCase exprcase(max_vec_size, (void*)buffer, buffer_size); def(exprcase); - if (exprcase.get_query().empty()) { + if (exprcase.get_expression().empty()) { Throw() << "No expression was set!"; } - std::vector subset = { 0, 1, 2, 3 }; //ctverice doubluu //TODO: autocreate from vec_size + std::vector subset = { 0, 1, 2, 3, 4,5,6,7,8,9,10,11,12 }; //ctverice doubluu //TODO: autocreate from vec_size std::vector vec_sizes({ 16U, 64U, 256U, 1024U }); PatchArenaPtr arena = exprcase.get_patch_arena(); for (uint vec_size : vec_sizes) { + uint n_repeats = N / vec_size; std::cout << "Running \"" << exprcase.get_query() << "\" " << n_repeats << "x for vec_size: " << vec_size << std::endl; exprcase.allocate(vec_size); @@ -38,7 +39,6 @@ int main() { ExpressionDAG se = gen(exprcase.get_node_map()); ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), arena); //get arena from ExprCase - //ProcessorBase* processor = ProcessorBase::create_processor(se, max_vec_size, bparser::get_simd_size(), nullptr); processor->set_subset(subset); auto start_time = std::chrono::high_resolution_clock::now(); @@ -49,17 +49,18 @@ int main() { double time = std::chrono::duration_cast>(end_time - start_time).count(); std::cout << "Finished in " << time << std::endl; - //processor.get_arena(); - delete processor; - exprcase.deallocate(); - } - //auto start_time = std::chrono::high_resolution_clock::now(); - //TODO: + exprcase.deallocate(); //processor is allocated in arena + } //for + + + //TODO: have result //std::cout << "Result: \n"; // Result in the 'vres' value space. //std::cout << print_vec(vres, vres_size); + ::operator delete(buffer); -} \ No newline at end of file + +} //main \ No newline at end of file From 2a439c5c70aee50020b774f95fe5c8fab5c3cb04 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sat, 31 Jan 2026 16:45:05 +0100 Subject: [PATCH 20/23] Moved nitpick and cases to test folder Simplified nitpick related macros in CMakeLists.txt Provided a README to test/cases --- .gitignore | 2 + CMakeLists.txt | 39 +++++------------- test/cases/README.md | 41 +++++++++++++++++++ {cases => test/cases}/basic_expr_def.hh | 0 .../cases/basic_expr_gen_ref.hh | 0 {cases => test/cases}/norm2_def.hh | 0 {cases => test/cases}/norm2_gen_edit.hh | 0 .../cases/norm2_gen_ref.hh | 0 {nitpick => test/nitpick}/example_def.hh | 0 {nitpick => test/nitpick}/exprcase.hh | 0 {nitpick => test/nitpick}/nitpick_generate.cc | 0 {nitpick => test/nitpick}/nitpick_include.hh | 7 +--- {nitpick => test/nitpick}/nitpick_run.cc | 0 13 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 test/cases/README.md rename {cases => test/cases}/basic_expr_def.hh (100%) rename cases/basic_expr_gen.hh => test/cases/basic_expr_gen_ref.hh (100%) rename {cases => test/cases}/norm2_def.hh (100%) rename {cases => test/cases}/norm2_gen_edit.hh (100%) rename cases/norm2_gen.hh => test/cases/norm2_gen_ref.hh (100%) rename {nitpick => test/nitpick}/example_def.hh (100%) rename {nitpick => test/nitpick}/exprcase.hh (100%) rename {nitpick => test/nitpick}/nitpick_generate.cc (100%) rename {nitpick => test/nitpick}/nitpick_include.hh (65%) rename {nitpick => test/nitpick}/nitpick_run.cc (100%) diff --git a/.gitignore b/.gitignore index c9ca503..370ad6c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ build/* .vs CMakeSettings.json +# Autogenerated files +test/cases/*_gen.hh \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index deb8c69..95b7771 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -291,51 +291,34 @@ define_test(test_processor bparser) #is it broken? -LV define_test(test_speed bparser) define_test(test_simd) -macro(define_nit_gen make_name def_file gen_file) - set(src_name "nitpick_generate") - set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") +macro(define_nit_target make_name def_file gen_file src_name) + set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/test/nitpick/${src_name}.cc") set(nit_name "${src_name}_${make_name}") set(nit_binary "${nit_name}_bin") add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) add_dependencies(${nit_binary} bparser) target_link_libraries(${nit_binary} bparser) - #set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") add_custom_target(${nit_name} COMMAND "$" DEPENDS ${nit_binary} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test/cases") endmacro() -macro(define_nit_run make_name def_file gen_file) - set(src_name "nitpick_run") - set(nit_source "${CMAKE_CURRENT_SOURCE_DIR}/nitpick/${src_name}.cc") - set(nit_name "${src_name}_${make_name}") - set(nit_binary "${nit_name}_bin") - - add_executable(${nit_binary} EXCLUDE_FROM_ALL ${nit_source} ) - add_dependencies(${nit_binary} bparser) - target_link_libraries(${nit_binary} bparser) - #set_target_properties(${nit_binary} PROPERTIES COMPILE_DEFINITIONS "DEF_FILE=$;GEN_FILE=$") - set_property(TARGET ${nit_binary} PROPERTY COMPILE_DEFINITIONS "DEF_FILE=\"${def_file}\";GEN_FILE=\"${gen_file}\"") - - add_custom_target(${nit_name} - COMMAND "$" - DEPENDS ${nit_binary} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cases") -endmacro() +macro(define_nit make_name) + set(nit_def_file "${make_name}_def.hh") + set(nit_gen_file "${make_name}_gen") -macro(define_nit make_name def_file gen_file) - define_nit_gen(${make_name} ${def_file} ${gen_file}) + define_nit_target(${make_name} ${nit_def_file} "${nit_gen_file}.hh" "nitpick_generate") #generate the _gen.hh file - define_nit_run(${make_name} ${def_file} ${gen_file}) + define_nit_target(${make_name} ${nit_def_file} "${nit_gen_file}_ref.hh" "nitpick_run") #run the _gen_ref.hh file + define_nit_target("${make_name}_edit" ${nit_def_file} "${nit_gen_file}_edit.hh" "nitpick_run") #run the _gen_edit.hh file endmacro() -define_nit(basic_expr basic_expr_def.hh basic_expr_gen.hh) -define_nit(norm2 norm2_def.hh norm2_gen.hh) -define_nit_run(norm2_mac norm2_def.hh norm2_gen_edit.hh) +define_nit(basic_expr) +define_nit(norm2) #set(src_name "nitpick_generate") diff --git a/test/cases/README.md b/test/cases/README.md new file mode 100644 index 0000000..f1ccd99 --- /dev/null +++ b/test/cases/README.md @@ -0,0 +1,41 @@ + +# Nitpick test cases +The nitpick test cases allows developers to time and compare different Directed Acyclic Graphs (DAG) of ScalarNodes for various bparser expressions. + +## File structure + +There are 4 different files for each case: +* `_def.hh` - Defines what the parser expression is, what are the variable shapes and names and the result shape +* `_gen.hh` - Autogenerated file containing a reconstruction of the parser's DAG. This file is in .gitignore +* `_gen_ref.hh` - The autogenerated file before being edited. +Most likely identical to `_gen.hh` but the autogenerated file can look different now than how it would generate before. +* `_gen_edit.hh` - Former file, but edited. The DAG may be constructed more optimally or is using different ScalarNode operations + +## Adding a case + +1. Create a `_def.hh` file. + * This file must define the function `void def(ExprCase&)` + * You must call the `ExprCase::parse` method to set the bparser's expression. + * You must specify the result shape using the `ExprCase::set_result_shape` method. + * You may use the `ExprCase::set_variable` and `ExprCase::set_var_copy` methods. + They are similar to `Parser::set_variable` and `Parser::set_var_copy`, except you don't need a double pointer. + * You may use `#ifndef NITPICK_IDE_IGNORE #endif` to trick the IDE into thinking something exists and/or is included. This block will not actually be compiled. +2. Declare the case in CMakeLists.txt + * Add `define_nit()` near similar definitions +3. Generate the file + * Make the `nitpick_generate_` target +4. Copy the autogenerated file + * First copy should have `_ref` at the end of the filename + * Second copy should have `_edit` at the end of the filename +5. Run and compare + * Use the `nitpick_run_` and `nitpick_run__edit` Make targets to run and time the original or edited file + + +## Timing +The nitpick_run.cc file will repeatedly: +1. Allocate \ × vec_size for each variable set in the ExprCase object where vec_size changes every iteration +2. Run and time the autogenerated DAG for N ÷ vec_size times, where N is a very high constant integer + * This is to spend more time computing small vec_sizes +3. Deallocate memory from step 1. + +This is done for every vec_size which is an array of integers set in the nitpick_run.cc file \ No newline at end of file diff --git a/cases/basic_expr_def.hh b/test/cases/basic_expr_def.hh similarity index 100% rename from cases/basic_expr_def.hh rename to test/cases/basic_expr_def.hh diff --git a/cases/basic_expr_gen.hh b/test/cases/basic_expr_gen_ref.hh similarity index 100% rename from cases/basic_expr_gen.hh rename to test/cases/basic_expr_gen_ref.hh diff --git a/cases/norm2_def.hh b/test/cases/norm2_def.hh similarity index 100% rename from cases/norm2_def.hh rename to test/cases/norm2_def.hh diff --git a/cases/norm2_gen_edit.hh b/test/cases/norm2_gen_edit.hh similarity index 100% rename from cases/norm2_gen_edit.hh rename to test/cases/norm2_gen_edit.hh diff --git a/cases/norm2_gen.hh b/test/cases/norm2_gen_ref.hh similarity index 100% rename from cases/norm2_gen.hh rename to test/cases/norm2_gen_ref.hh diff --git a/nitpick/example_def.hh b/test/nitpick/example_def.hh similarity index 100% rename from nitpick/example_def.hh rename to test/nitpick/example_def.hh diff --git a/nitpick/exprcase.hh b/test/nitpick/exprcase.hh similarity index 100% rename from nitpick/exprcase.hh rename to test/nitpick/exprcase.hh diff --git a/nitpick/nitpick_generate.cc b/test/nitpick/nitpick_generate.cc similarity index 100% rename from nitpick/nitpick_generate.cc rename to test/nitpick/nitpick_generate.cc diff --git a/nitpick/nitpick_include.hh b/test/nitpick/nitpick_include.hh similarity index 65% rename from nitpick/nitpick_include.hh rename to test/nitpick/nitpick_include.hh index a313670..a22067f 100644 --- a/nitpick/nitpick_include.hh +++ b/test/nitpick/nitpick_include.hh @@ -1,14 +1,11 @@ #ifndef NITPICK_INCLUDE_HH #define NITPICK_INCLUDE_HH -#define NITPICK_FALLBACK_DEF_FILE_NAME "example_def.hh" -#define NITPICK_AUTOGENERATED_FILE_NAME "autogenerated.hh" - #ifndef NITPICK_DEF_FILE # ifdef DEF_FILE # define NITPICK_DEF_FILE DEF_FILE # else -# define NITPICK_DEF_FILE NITPICK_FALLBACK_DEF_FILE_NAME +# error [Nitpick] DEF_FILE not defined # endif #endif @@ -16,7 +13,7 @@ # ifdef GEN_FILE # define NITPICK_GEN_FILE GEN_FILE # else -# define NITPICK_GEN_FILE NITPICK_AUTOGENERATED_FILE_NAME +# error [Nitpick] GEN_FILE not defined # endif #endif diff --git a/nitpick/nitpick_run.cc b/test/nitpick/nitpick_run.cc similarity index 100% rename from nitpick/nitpick_run.cc rename to test/nitpick/nitpick_run.cc From d32746f634092ad0021dace0c1254dbc7aa23b93 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Sat, 31 Jan 2026 16:51:05 +0100 Subject: [PATCH 21/23] added basic test/nitpick/README.md --- test/nitpick/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/nitpick/README.md diff --git a/test/nitpick/README.md b/test/nitpick/README.md new file mode 100644 index 0000000..2e1c33b --- /dev/null +++ b/test/nitpick/README.md @@ -0,0 +1,8 @@ +# Nitpick + +## Files + +* `exprcase.hh` - Defines the ExprCase class used in the test cases. It is referenced in every test/case/*_def.hh file +* `nitpick_include.hh` - Common includes and macros, shared by both .cc files. Throws preprocessor error if DEF_FILE or GEN_FILE is not defined +* `nitpick_generate.cc` - Takes the provided DEF_FILE, uses DagPrinter to generate the output and writes to GEN_FILE +* `nitpick_run.cc` - Takes the provided DEF_FILE and GEN_FILE and runs and times the expression for different vec_sizes \ No newline at end of file From 94c3805d16a0f441cd909de2c504d2d01a03ce52 Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Thu, 5 Feb 2026 15:04:03 +0100 Subject: [PATCH 22/23] Matrix transpose --- include/array.hh | 18 ++++++++++++++++++ include/grammar.impl.hh | 8 ++++++++ test/test_parser.cc | 2 ++ 3 files changed, 28 insertions(+) diff --git a/include/array.hh b/include/array.hh index b6378de..0dbb81c 100644 --- a/include/array.hh +++ b/include/array.hh @@ -1373,6 +1373,24 @@ public: return Array(arr); } + static Array transpose(const Array& a) { + switch (a.shape().size()) { + case 0: //scalar + Throw() << "Cannot transpose a scalar" << "\n"; + break; + case 1: //vector + { + Throw() << "bparser vectors do not have an orientation and thus cannot be transposed" << "\n"; + } + case 2: //matrix + { + return unwrap_array(wrap_array(a).transpose()); + } + default: + Throw() << "Cannot transpose ND tensors" << "\n"; + } + } + static Array flatten(const Array &tensor) { uint n_elements = shape_size(tensor.shape()); Shape res_shape(1, n_elements); diff --git a/include/grammar.impl.hh b/include/grammar.impl.hh index c03be4a..a5f31de 100644 --- a/include/grammar.impl.hh +++ b/include/grammar.impl.hh @@ -98,6 +98,7 @@ struct grammar : qi::grammar { multiplicative_expr, signed_optional, signed_expr, + post_expr, power, array_constr, array_constr_list_opt, @@ -129,6 +130,7 @@ struct grammar : qi::grammar { reserved, func, unary_op, + unary_op_post, not_op, additive_op, multiplicative_op, @@ -196,6 +198,10 @@ struct grammar : qi::grammar { FN("-", unary_array<_minus_>()) ; + unary_op_post.add + FN(".T", &Array::transpose) + ; + additive_op.add FN("+", binary_array<_add_>()) FN("-", binary_array<_sub_>()) @@ -315,6 +321,8 @@ struct grammar : qi::grammar { RULE(power) = primary[qi::_val = qi::_1] >> -(power_op > signed_optional)[qi::_val = ast::make_binary(qi::_1, qi::_val, qi::_2)]; + RULE(post_expr) = (subscriptable >> unary_op_post)[qi::_val = ast::make_unary(qi::_2, qi::_1)]; //TODO: Do properly, this does not work -LV + RULE(primary) = literal_number | const_lit | subscription; RULE(subscriptable) = array_constr | enclosure | call | identifier; diff --git a/test/test_parser.cc b/test/test_parser.cc index 4083d47..88cac70 100644 --- a/test/test_parser.cc +++ b/test/test_parser.cc @@ -269,6 +269,8 @@ void test_expression() { BP_ASSERT(test_expr("norminf([-4,-3,-2,-1,0,1,2,3,4])", { 4 }, {})); BP_ASSERT(test_expr("norminf([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 9 }, {})); + //BP_ASSERT(test_expr("[[1,2],[3,4]].T", {1, 3, 2, 4}, { 2,2 })); + BP_ASSERT(test_expr("abs(-1)+abs(0)+abs(1)", {2})); BP_ASSERT(test_expr("floor(-3.5)", {-4}, {})); BP_ASSERT(test_expr("ceil(-3.5)", {-3}, {})); From fe6e75f9ea0ef9ca81b74008efed165bab40a3ec Mon Sep 17 00:00:00 2001 From: lukas-valenta-tul Date: Thu, 5 Feb 2026 16:20:11 +0100 Subject: [PATCH 23/23] sym, dev functions --- include/array.hh | 55 ++++++++++++++++++++++++++++++++++++++++- include/grammar.impl.hh | 2 ++ test/test_parser.cc | 5 +++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/include/array.hh b/include/array.hh index 0dbb81c..5ea5093 100644 --- a/include/array.hh +++ b/include/array.hh @@ -1373,6 +1373,8 @@ public: return Array(arr); } + // [[ 1, 2 ], -> [[ 1, 3 ], + // [ 3, 4 ]] [ 2, 4 ]] static Array transpose(const Array& a) { switch (a.shape().size()) { case 0: //scalar @@ -1380,7 +1382,7 @@ public: break; case 1: //vector { - Throw() << "bparser vectors do not have an orientation and thus cannot be transposed" << "\n"; + Throw() << "Cannot transpose vector. BParser vectors do not have an orientation" << "\n"; } case 2: //matrix { @@ -1391,6 +1393,57 @@ public: } } + static Array sym(const Array& a) { + switch (a.shape().size()) { + case 0: //scalar + Throw() << "Cannot sym a scalar" << "\n"; + break; + case 1: //vector + { + Throw() << "Cannot sym a vector" << "\n"; + } + case 2: //matrix + { + if (a.shape()[0] != a.shape()[1]) { + Throw() << "Cannot sym non-square matrix" << "\n"; + } + + WrappedArray m_a(wrap_array(a)); + using namespace details; + ScalarWrapper two(ScalarNode::create_const(2)); + return unwrap_array( (m_a + m_a.transpose())/two ); + } + default: + Throw() << "Cannot sym ND tensors" << "\n"; + } + } + + static Array dev(const Array& a) { + switch (a.shape().size()) { + case 0: //scalar + Throw() << "Cannot dev a scalar" << "\n"; + break; + case 1: //vector + { + Throw() << "Cannot dev a vector" << "\n"; + } + case 2: //matrix + { + if (a.shape()[0] != a.shape()[1]) { + Throw() << "Cannot dev non-square matrix" << "\n"; + } + WrappedArray m_a(wrap_array(a)); + using namespace details; + ScalarWrapper D((int)m_a.rows()); + WrappedArray I(WrappedArray::Identity(m_a.rows(), m_a.cols())); + return unwrap_array( m_a - ( (m_a.trace()/D ) * I ) ); + } + default: + Throw() << "Cannot dev ND tensors" << "\n"; + } + } + + static Array flatten(const Array &tensor) { uint n_elements = shape_size(tensor.shape()); Shape res_shape(1, n_elements); diff --git a/include/grammar.impl.hh b/include/grammar.impl.hh index a5f31de..0260960 100644 --- a/include/grammar.impl.hh +++ b/include/grammar.impl.hh @@ -191,6 +191,8 @@ struct grammar : qi::grammar { FN("norminf", &Array::norminf) FN("sum" , &Array::sum) FN("cross" , &Array::cross) + FN("sym" , &Array::sym) + FN("dev" , &Array::dev) ; unary_op.add diff --git a/test/test_parser.cc b/test/test_parser.cc index 88cac70..836455b 100644 --- a/test/test_parser.cc +++ b/test/test_parser.cc @@ -269,7 +269,10 @@ void test_expression() { BP_ASSERT(test_expr("norminf([-4,-3,-2,-1,0,1,2,3,4])", { 4 }, {})); BP_ASSERT(test_expr("norminf([[-4,-3,-2],[-1,0,1],[2,3,4]])", { 9 }, {})); - //BP_ASSERT(test_expr("[[1,2],[3,4]].T", {1, 3, 2, 4}, { 2,2 })); + //BP_ASSERT(test_expr("[[1,2],[3,4]].T", {1, 3, 2, 4}, { 2,2 })); //Fix .T before uncommenting + //BP_ASSERT(test_expr("a = sym([[1,2],[3,4]]); a.T == a", { 1,1,1,1 }, { 2,2 })); + BP_ASSERT(test_expr("dev([[1,2],[3,4]])", { 1-2.5, 2, 3, 4-2.5 }, {2,2})); + BP_ASSERT(test_expr("tr(dev([[1,2],[3,4]]))", { 0 }, {})); BP_ASSERT(test_expr("abs(-1)+abs(0)+abs(1)", {2})); BP_ASSERT(test_expr("floor(-3.5)", {-4}, {}));