From b6fc74f2a2b0bd3fd2cb2a353cf20314587de86a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Sun, 15 Feb 2026 00:49:51 +0400 Subject: [PATCH 01/34] init commit --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 384 ++++++++++ experimental/test/test_CFL_AllPaths.c | 679 ++++++++++++++++++ 2 files changed, 1063 insertions(+) create mode 100644 experimental/algorithm/LAGraph_CFL_AllPaths.c create mode 100644 experimental/test/test_CFL_AllPaths.c diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c new file mode 100644 index 0000000000..c4c5d25ea5 --- /dev/null +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -0,0 +1,384 @@ +//------------------------------------------------------------------------------ +// LAGraph_CFL_reachability.c: Context-Free Language Reachability Matrix-Based +// Algorithm +// ------------------------------------------------------------------------------ +// +// LAGraph, (c) 2019-2024 by The LAGraph Contributors, All Rights Reserved. +// SPDX-License-Identifier: BSD-2-Clause + +// Contributed by Ilhom Kombaev, Semyon Grigoriev, St. Petersburg State University. + +//------------------------------------------------------------------------------ + +// Code is based on the "A matrix-based CFPQ algorithm" described in the +// following paper: * Rustam Azimov, Semyon Grigorev, "Context-Free Path +// Querying Using Linear Algebra", URL: +// https://disser.spbu.ru/files/2022/disser_azimov.pdf + +#define LG_FREE_WORK \ + { \ + LAGraph_Free ((void **) &nnzs, msg) ; \ + GrB_free(&true_scalar); \ + GrB_free(&identity_matrix); \ + LAGraph_Free ((void **) &T, msg); \ + LAGraph_Free ((void **) &indexes, msg); \ + LAGraph_Free((void**) &t_empty_flags, NULL); \ + LAGraph_Free((void**) &eps_rules, NULL); \ + LAGraph_Free((void**) &term_rules, NULL); \ + LAGraph_Free((void**) &bin_rules, NULL); \ + } + +#define LG_FREE_ALL \ + { \ + for (int64_t i = 0; i < nonterms_count; i++) { \ + GrB_free(&T[i]); \ + } \ + \ + LG_FREE_WORK; \ + } + +#include "LG_internal.h" +#include + +#define ERROR_RULE(msg,i) \ + { \ + LG_ASSERT_MSGF(false, GrB_INVALID_VALUE, \ + "Rule with index %" PRId64 " is invalid. ", msg, i); \ + } + +#define ADD_TO_MSG(...) \ + { \ + if (msg_len == 0) { \ + msg_len += \ + snprintf(msg, LAGRAPH_MSG_LEN, \ + "LAGraph failure (file %s, line %d): ", \ + __FILE__, __LINE__); \ + } \ + if (msg_len < LAGRAPH_MSG_LEN) { \ + msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ + __VA_ARGS__); \ + } \ + } + +#define ADD_INDEX_TO_ERROR_RULE(rule, i) \ + { \ + rule.len_indexes_str += snprintf( \ + rule.indexes_str + rule.len_indexes_str, \ + LAGRAPH_MSG_LEN - rule.len_indexes_str, \ + rule.count == 0 ? "%" PRId64 : ", %" PRId64, i); \ + rule.count++; \ + } + + + +// LAGraph_CFL_reachability: Context-Free Language Reachability Matrix-Based Algorithm +// +// This function determines the set of vertex pairs (u, v) in a graph (represented by +// adjacency matrices) such that there is a path from u to v, where the edge labels form a +// word from the language generated by the context-free grammar (represented by `rules`). +// +// Terminals and non-terminals are enumerated by integers starting from zero. +// The start non-terminal is the non-terminal with index 0. +// +// Example: +// +// Graph: +// ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ +// │ 0 ├───► 1 ├───► 2 ├───► 3 ├───► 4 │ +// └───┘ a └─┬─┘ a └─▲─┘ b └───┘ b └───┘ +// │ │ +// │ ┌───┐ │ +// a└─► 5 ├─┘b +// └───┘ +// +// Grammar: S -> aSb | ab +// +// There are paths from node [1] to node [3] and from node [1] to node [2] that form the +// word "ab" ([1]-a->[2]-b->[3] and [1]-a->[5]-b->[2]). The word "ab" is in the language +// generated by our context-free grammar, so the pairs (1, 3) and (1, 2) will be included +// in the result. +// +// Note: It doesn't matter how many paths exist from node [A] to node [B] that form a word +// in the language. If at least one path exists, the pair ([A], [B]) will be included in +// the result. +// +// In contrast, the path from node [1] to node [4] forms the word "abb" +// ([1]-a->[2]-b->[3]-b->[4]) and the word "abbb" ([1]-a->[5]-b->[2]-b->[3]-b->[4]). +// The words "aab" and "abbb" are not in the language, so the pair (1, 4) will not be +// included in the result. +// +// With this graph and grammar, we obtain the following results: +// (0, 4) - because there exists a path (0-1-2-3-4) that forms the word "aabb" +// (1, 3) - because there exists a path (1-2-3) that forms "ab" +// (1, 2) - because there exists a path (1-5-2) that forms the word "ab" +// (0, 3) - because there exists a path (0-1-5-2-3) that forms the word "aabb" + +GrB_Info LAGraph_CFL_reachability +( + // Output + GrB_Matrix *outputs, // Array of matrices containing results. + // The size of the array must be equal to nonterms_count. + // + // outputs[k]: (i, j) = true if and only if there is a path + // from node i to node j whose edge labels form a word + // derivable from the non-terminal 'k' of the specified CFG. + // Input + const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. + // The length of this array is equal to the count of + // terminals (terms_count). + // + // adj_matrices[t]: (i, j) == 1 if and only if there + // is an edge between nodes i and j with the label of + // the terminal corresponding to index 't' (where t is + // in the range [0, terms_count - 1]). + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +) +{ + +#if LAGRAPH_SUITESPARSE + // Declare workspace and clear the msg string, if not NULL + GrB_Matrix *T; + bool *t_empty_flags = NULL ; // t_empty_flags[i] == true <=> T[i] is empty + GrB_Matrix identity_matrix = NULL; + uint64_t *nnzs = NULL; + LG_CLEAR_MSG; + size_t msg_len = 0; // For error formatting + bool iso_flag = false; + GrB_Index *indexes = NULL; + + // Arrays for processing rules + size_t *eps_rules = NULL, eps_rules_count = 0; // [Variable -> eps] + size_t *term_rules = NULL, term_rules_count = 0; // [Variable -> term] + size_t *bin_rules = NULL, bin_rules_count = 0; // [Variable -> AB] + + GrB_Scalar true_scalar; + GrB_Scalar_new(&true_scalar, GrB_BOOL); + GrB_Scalar_setElement_BOOL(true_scalar, true); + + LG_TRY(LAGraph_Calloc((void **) &T, nonterms_count, sizeof(GrB_Matrix), msg)); + LG_TRY(LAGraph_Calloc((void **) &t_empty_flags, nonterms_count, sizeof(bool), msg)) ; + + LG_ASSERT_MSG(terms_count > 0, GrB_INVALID_VALUE, + "The number of terminals must be greater than zero."); + LG_ASSERT_MSG(nonterms_count > 0, GrB_INVALID_VALUE, + "The number of non-terminals must be greater than zero."); + LG_ASSERT_MSG(rules_count > 0, GrB_INVALID_VALUE, + "The number of rules must be greater than zero."); + LG_ASSERT_MSG(outputs != NULL, GrB_NULL_POINTER, "The outputs array cannot be null."); + LG_ASSERT_MSG(rules != NULL, GrB_NULL_POINTER, "The rules array cannot be null."); + LG_ASSERT_MSG(adj_matrices != NULL, GrB_NULL_POINTER, + "The adjacency matrices array cannot be null."); + + // Find null adjacency matrices + bool found_null = false; + for (int64_t i = 0; i < terms_count; i++) { + if (adj_matrices[i] != NULL) + continue; + + if (!found_null) { + ADD_TO_MSG("Adjacency matrices with these indexes are null: "); + ADD_TO_MSG("%" PRId64, i); + } else { + ADD_TO_MSG("%" PRId64, i); + } + + found_null = true; + } + + if (found_null) { + LG_FREE_ALL; + return GrB_NULL_POINTER; + } + + GrB_Index n; + GRB_TRY(GrB_Matrix_ncols(&n, adj_matrices[0])); + + // Create nonterms matrices + for (int64_t i = 0; i < nonterms_count; i++) { + GRB_TRY(GrB_Matrix_new(&T[i], GrB_BOOL, n, n)); + t_empty_flags[i] = true; + } + + LG_TRY(LAGraph_Calloc((void **) &eps_rules, rules_count, sizeof(size_t), msg)) ; + LG_TRY(LAGraph_Calloc((void **) &term_rules, rules_count, sizeof(size_t), msg)) ; + LG_TRY(LAGraph_Calloc((void **) &bin_rules, rules_count, sizeof(size_t), msg)) ; + + // Process rules + typedef struct { + size_t count; + size_t len_indexes_str; + char indexes_str[LAGRAPH_MSG_LEN]; + } rule_error_s; + rule_error_s term_err = {0}; + rule_error_s nonterm_err = {0}; + rule_error_s invalid_err = {0}; + for (int64_t i = 0; i < rules_count; i++) { + LAGraph_rule_WCNF rule = rules[i]; + + bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; + bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; + bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; + + // Check that all rules are well-formed + if (rule.nonterm < 0 || rule.nonterm >= nonterms_count) { + ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); + } + + // [Variable -> eps] + if (is_rule_eps) { + eps_rules[eps_rules_count++] = i; + + continue; + } + + // [Variable -> term] + if (is_rule_term) { + term_rules[term_rules_count++] = i; + + if (rule.prod_A < -1 || rule.prod_A >= terms_count) { + ADD_INDEX_TO_ERROR_RULE(term_err, i); + } + + continue; + } + + // [Variable -> A B] + if (is_rule_bin) { + bin_rules[bin_rules_count++] = i; + + if (rule.prod_A < -1 || rule.prod_A >= nonterms_count || rule.prod_B < -1 || + rule.prod_B >= nonterms_count) { + ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); + } + + continue; + } + + // [Variable -> _ B] + ADD_INDEX_TO_ERROR_RULE(invalid_err, i); + } + + if (term_err.count + nonterm_err.count + invalid_err.count > 0) { + ADD_TO_MSG("Count of invalid rules: %" PRId64 ".\n", + (int64_t) (term_err.count + nonterm_err.count + invalid_err.count)); + + if (nonterm_err.count > 0) { + ADD_TO_MSG("Non-terminals must be in range [0, nonterms_count). "); + ADD_TO_MSG("Indexes of invalid rules: %s\n", nonterm_err.indexes_str) + } + if (term_err.count > 0) { + ADD_TO_MSG("Terminals must be in range [-1, nonterms_count). "); + ADD_TO_MSG("Indexes of invalid rules: %s\n", term_err.indexes_str) + } + if (invalid_err.count > 0) { + ADD_TO_MSG("[Variable -> _ B] type of rule is not acceptable. "); + ADD_TO_MSG("Indexes of invalid rules: %.120s\n", invalid_err.indexes_str) + } + + LG_FREE_ALL; + return GrB_INVALID_VALUE; + } + + // Rule [Variable -> term] + for (int64_t i = 0; i < term_rules_count; i++) { + LAGraph_rule_WCNF term_rule = rules[term_rules[i]]; + GrB_Index adj_matrix_nnz = 0; + GRB_TRY(GrB_Matrix_nvals(&adj_matrix_nnz, adj_matrices[term_rule.prod_A])); + + if (adj_matrix_nnz == 0) { + continue; + } + + GxB_eWiseUnion( + T[term_rule.nonterm], GrB_NULL, GrB_NULL, GxB_PAIR_BOOL, + T[term_rule.nonterm], true_scalar, adj_matrices[term_rule.prod_A], true_scalar, GrB_NULL + ); + + t_empty_flags[term_rule.nonterm] = false; + + #ifdef DEBUG_CFL_REACHBILITY + GxB_Matrix_iso(&iso_flag, T[term_rule.nonterm]); + printf("[TERM] eWiseUnion: NONTERM: %d (ISO: %d)\n", term_rule.nonterm, iso_flag); + #endif + } + + GrB_Vector v_diag; + GRB_TRY(GrB_Vector_new(&v_diag, GrB_BOOL, n)); + GRB_TRY(GrB_Vector_assign_BOOL(v_diag, GrB_NULL, GrB_NULL, true, GrB_ALL, n, NULL)); + GRB_TRY(GrB_Matrix_diag(&identity_matrix, v_diag, 0)); + GRB_TRY(GrB_free(&v_diag)); + + // Rule [Variable -> eps] + for (int64_t i = 0; i < eps_rules_count; i++) { + LAGraph_rule_WCNF eps_rule = rules[eps_rules[i]]; + + GxB_eWiseUnion ( + T[eps_rule.nonterm],GrB_NULL,GxB_PAIR_BOOL,GxB_PAIR_BOOL, + T[eps_rule.nonterm],true_scalar,identity_matrix,true_scalar,GrB_NULL + ); + + t_empty_flags[eps_rule.nonterm] = false; + + #ifdef DEBUG_CFL_REACHBILITY + GxB_Matrix_iso(&iso_flag, T[eps_rule.nonterm]); + printf("[EPS] eWiseUnion: NONTERM: %d (ISO: %d)\n", + eps_rule.nonterm, iso_flag); + #endif + } + + // Rule [Variable -> Variable1 Variable2] + LG_TRY(LAGraph_Calloc((void **) &nnzs, nonterms_count, sizeof(uint64_t), msg)); + bool changed = true; + while (changed) { + changed = false; + for (int64_t i = 0; i < bin_rules_count; i++) { + LAGraph_rule_WCNF bin_rule = rules[bin_rules[i]]; + + // If one of matrices is empty then their product will be empty + if (t_empty_flags[bin_rule.prod_A] || t_empty_flags[bin_rule.prod_B]) { + continue; + } + + GrB_BinaryOp acc_op = t_empty_flags[bin_rule.nonterm] ? GrB_NULL : GxB_ANY_BOOL; + GRB_TRY(GrB_mxm(T[bin_rule.nonterm], GrB_NULL, acc_op, + GxB_ANY_PAIR_BOOL, T[bin_rule.prod_A], T[bin_rule.prod_B], + GrB_NULL)) + + GrB_Index new_nnz; + GRB_TRY(GrB_Matrix_nvals(&new_nnz, T[bin_rule.nonterm])); + if (new_nnz != 0) t_empty_flags[bin_rule.nonterm] = false; + + changed = changed || (nnzs[bin_rule.nonterm] != new_nnz); + nnzs[bin_rule.nonterm] = new_nnz; + + #ifdef DEBUG_CFL_REACHBILITY + GxB_Matrix_iso(&iso_flag, T[bin_rule.nonterm]); + printf("[TERM1 TERM2] MULTIPLY, S: %d, A: %d, B: %d, " + "I: %" PRId64 " (ISO: %d)\n", + bin_rule.nonterm, bin_rule.prod_A, bin_rule.prod_B, i, iso_flag); + #endif + + } + } + + #ifdef DEBUG_CFL_REACHBILITY + for (int64_t i = 0; i < nonterms_count; i++) { + printf("MATRIX WITH INDEX %" PRId64 ":\n", i); + GxB_print(T[i], GxB_SUMMARY); + } + #endif + + for (int64_t i = 0; i < nonterms_count; i++) { + outputs[i] = T[i]; + } + + LG_FREE_WORK; + return GrB_SUCCESS; +#else + return (GrB_NOT_IMPLEMENTED) ; +#endif +} diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c new file mode 100644 index 0000000000..d7d77c0041 --- /dev/null +++ b/experimental/test/test_CFL_AllPaths.c @@ -0,0 +1,679 @@ +//------------------------------------------------------------------------------ +// LAGraph/experimental/test/LAGraph_CFL_reachability.c: test cases for Context-Free +// Language Reachability Matrix-Based Algorithm +//------------------------------------------------------------------------------ +// +// LAGraph, (c) 2019-2024 by The LAGraph Contributors, All Rights Reserved. +// SPDX-License-Identifier: BSD-2-Clause + +// Contributed by Ilhom Kombaev, Semyon Grigoriev, St. Petersburg State University. + +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include + +#define run_algorithm() \ + LAGraph_CFL_reachability(outputs, adj_matrices, grammar.terms_count, \ + grammar.nonterms_count, grammar.rules, grammar.rules_count, \ + msg) + +#define check_error(error) \ + { \ + retval = run_algorithm(); \ + TEST_CHECK(retval == error); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ + } + +#define check_result(result) \ + { \ + char *expected = output_to_str(0); \ + TEST_CHECK(strcmp(result, expected) == 0); \ + TEST_MSG("Wrong result. Actual: %s", expected); \ + LAGraph_Free ((void **) &expected, msg); \ + } + +typedef struct { + size_t nonterms_count; + size_t terms_count; + size_t rules_count; + LAGraph_rule_WCNF *rules; +} grammar_t; + +GrB_Matrix *adj_matrices = NULL; +int n_adj_matrices = 0 ; +GrB_Matrix *outputs = NULL; +grammar_t grammar = {0, 0, 0, NULL}; +char msg[LAGRAPH_MSG_LEN]; + +void setup() { LAGraph_Init(msg); } + +void teardown(void) { LAGraph_Finalize(msg); } + +void init_outputs() +{ + LAGraph_Calloc ((void **) &outputs, + grammar.nonterms_count, sizeof(GrB_Matrix), msg) ; +} + +char *output_to_str(size_t nonterm) { + GrB_Index nnz = 0; + OK(GrB_Matrix_nvals(&nnz, outputs[nonterm])); + GrB_Index *row = NULL ; + GrB_Index *col = NULL ; + bool *val = NULL ; + LAGraph_Malloc ((void **) &row, nnz, sizeof (GrB_Index), msg) ; + LAGraph_Malloc ((void **) &col, nnz, sizeof (GrB_Index), msg) ; + LAGraph_Malloc ((void **) &val, nnz, sizeof (GrB_Index), msg) ; + + OK(GrB_Matrix_extractTuples(row, col, val, &nnz, outputs[nonterm])); + + // 11 - size of " (%ld, %ld)" + char *result_str = NULL ; + LAGraph_Malloc ((void **) &result_str, 11*nnz, sizeof (char), msg) ; + + result_str[0] = '\0'; + for (size_t i = 0; i < nnz; i++) { + sprintf(result_str + strlen(result_str), i == 0 ? + "(%" PRIu64 ", %" PRIu64 ")" : " (%" PRIu64 ", %" PRIu64 ")", + row[i], col[i]); + } + + LAGraph_Free ((void **) &row, msg); + LAGraph_Free ((void **) &col, msg); + LAGraph_Free ((void **) &val, msg); + + return result_str; +} + +void free_workspace() { + + if (adj_matrices != NULL) + { + for (size_t i = 0; i < n_adj_matrices ; i++) + { + GrB_free(&adj_matrices[i]); + } + } + LAGraph_Free ((void **) &adj_matrices, msg); + + if (outputs != NULL) + { + for (size_t i = 0; i < grammar.nonterms_count; i++) + { + GrB_free(&outputs[i]); + } + } + LAGraph_Free ((void **) &outputs, msg); + + LAGraph_Free ((void **) &grammar.rules, msg); + grammar = (grammar_t){0, 0, 0, NULL}; +} + +//==================== +// Grammars +//==================== + +// S -> aSb | ab in WCNF +// +// Terms: [0 a] [1 b] +// Nonterms: [0 S] [1 A] [2 B] [3 C] +// S -> AB [0 1 2 0] +// S -> AC [0 1 3 0] +// C -> SB [3 0 2 0] +// A -> a [1 0 -1 0] +// B -> b [2 1 -1 0] +void init_grammar_aSb() { + LAGraph_rule_WCNF *rules = NULL ; + LAGraph_Calloc ((void **) &rules, 5, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 1, 3, 0}; + rules[2] = (LAGraph_rule_WCNF){3, 0, 2, 0}; + rules[3] = (LAGraph_rule_WCNF){1, 0, -1, 0}; + rules[4] = (LAGraph_rule_WCNF){2, 1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 4, .terms_count = 2, .rules_count = 5, .rules = rules}; +} + +// S -> aS | a | eps in WCNF +// +// Terms: [0 a] +// Nonterms: [0 S] +// S -> SS [0 0 0 0] +// S -> a [0 0 -1 0] +// S -> eps [0 -1 -1 0] +void init_grammar_aS() { + LAGraph_rule_WCNF *rules = NULL ; + LAGraph_Calloc ((void **) &rules, 3, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 0, 0, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 0, -1, 0}; + rules[2] = (LAGraph_rule_WCNF){0, -1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 1, .terms_count = 1, .rules_count = 3, .rules = rules}; +} + +// Complex grammar +// aaaabbbb or aaabbb +// +// Terms: [0 a] [1 b] +// Nonterms: [0 S] [n Sn] +// S -> S1 S2 [0 1 2 0] +// S -> S15 S16 [0 15 16 0] +// S1 -> S3 S4 [1 3 4 0] +// S2 -> S5 S6 [2 5 6 0] +// S3 -> S7 S8 [3 7 8 0] +// S4 -> S9 S10 [4 9 10 0] +// S5 -> S11 S12 [5 11 12 0] +// S6 -> S13 S14 [6 13 14 0] +// S16 -> S17 S18 [16 17 18 0] +// S17 -> S19 S20 [17 19 20 0] +// S18 -> S21 S22 [18 21 22 0] +// S22 -> S23 S24 [22 23 24 0] +// S7 -> a [7 0 -1 0] +// S8 -> a [8 0 -1 0] +// S9 -> a [9 0 -1 0] +// S10 -> a [10 0 -1 0] +// S11 -> b [11 1 -1 0] +// S12 -> b [12 1 -1 0] +// S13 -> b [13 1 -1 0] +// S14 -> b [14 1 -1 0] +// S15 -> a [15 0 -1 0] +// S19 -> a [19 0 -1 0] +// S20 -> a [20 0 -1 0] +// S21 -> b [21 1 -1 0] +// S23 -> b [23 1 -1 0] +// S24 -> b [24 1 -1 0] +void init_grammar_complex() { + LAGraph_rule_WCNF *rules = NULL ; + LAGraph_Calloc ((void **) &rules, 26, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 15, 16, 0}; + rules[2] = (LAGraph_rule_WCNF){1, 3, 4, 0}; + rules[3] = (LAGraph_rule_WCNF){2, 5, 6, 0}; + rules[4] = (LAGraph_rule_WCNF){3, 7, 8, 0}; + rules[5] = (LAGraph_rule_WCNF){4, 9, 10, 0}; + rules[6] = (LAGraph_rule_WCNF){5, 11, 12, 0}; + rules[7] = (LAGraph_rule_WCNF){6, 13, 14, 0}; + rules[8] = (LAGraph_rule_WCNF){16, 17, 18, 0}; + rules[9] = (LAGraph_rule_WCNF){17, 19, 20, 0}; + rules[10] = (LAGraph_rule_WCNF){18, 21, 22, 0}; + rules[11] = (LAGraph_rule_WCNF){22, 23, 24, 0}; + rules[12] = (LAGraph_rule_WCNF){7, 0, -1, 0}; + rules[13] = (LAGraph_rule_WCNF){8, 0, -1, 0}; + rules[14] = (LAGraph_rule_WCNF){9, 0, -1, 0}; + rules[15] = (LAGraph_rule_WCNF){10, 0, -1, 0}; + rules[16] = (LAGraph_rule_WCNF){11, 1, -1, 0}; + rules[17] = (LAGraph_rule_WCNF){12, 1, -1, 0}; + rules[18] = (LAGraph_rule_WCNF){13, 1, -1, 0}; + rules[19] = (LAGraph_rule_WCNF){14, 1, -1, 0}; + rules[20] = (LAGraph_rule_WCNF){15, 0, -1, 0}; + rules[21] = (LAGraph_rule_WCNF){19, 0, -1, 0}; + rules[22] = (LAGraph_rule_WCNF){20, 0, -1, 0}; + rules[23] = (LAGraph_rule_WCNF){21, 1, -1, 0}; + rules[24] = (LAGraph_rule_WCNF){23, 1, -1, 0}; + rules[25] = (LAGraph_rule_WCNF){24, 1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 25, .terms_count = 2, .rules_count = 26, .rules = rules}; +} + +//==================== +// Graphs +//==================== + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 0 +// 0 -b-> 3 +// 3 -b-> 0 +void init_graph_double_cycle() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 4, 4)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 4, 4)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 3 +// 3 -a-> 4 +// 3 -b-> 5 +// 4 -b-> 3 +// 5 -b-> 6 +// 6 -b-> 7 +void init_graph_1() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 8, 8)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 8, 8)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 3)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 4)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 5)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 4, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 6)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 7)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: +// +// 0 -a-> 2 +// 1 -a-> 2 +// 3 -a-> 5 +// 4 -a-> 5 +// 2 -a-> 6 +// 5 -a-> 6 +// 2 -b-> 0 +// 2 -b-> 1 +// 5 -b-> 3 +// 5 -b-> 4 +// 6 -b-> 2 +// 6 -b-> 5 +void init_graph_tree() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 7, 7)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 7, 7)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 5)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 4, 5)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 6)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 5, 6)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 1)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 4)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 2)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 5)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 0 +void init_graph_one_cycle() { + LAGraph_Calloc ((void **) &adj_matrices, 1, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 1 ; + + GrB_Matrix adj_matrix_a; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); + + adj_matrices[0] = adj_matrix_a; +} + +// Graph: + +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -b-> 3 +// 3 -b-> 4 +void init_graph_line() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 5, 5); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 5, 5); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 4)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: + +// 0 -a-> 0 +// 0 -b-> 1 +// 1 -c-> 2 +void init_graph_2() { + LAGraph_Calloc ((void **) &adj_matrices, 3, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 3 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b, adj_matrix_c; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 3, 3); + GrB_Matrix_new(&adj_matrix_c, GrB_BOOL, 3, 3); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_c, true, 1, 2)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + adj_matrices[2] = adj_matrix_c; +} + +// Graph: + +// 0 -a-> 1 +// 1 -a-> 0 +// 0 -b-> 0 +void init_graph_3() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: + +// 0 -b-> 1 +// 1 -b-> 0 +void init_graph_4() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 1, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +//==================== +// Tests with valid result +//==================== + +void test_CFL_reachability_cycle(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_graph_one_cycle(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2)"); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_two_cycle(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 0) (0, 3) (1, 0) (1, 3) (2, 0) (2, 3)"); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_labels_more_than_nonterms(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_2(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 1)"); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_complex_grammar(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_complex(); + init_graph_1(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 7) (1, 6)"); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_tree(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_tree(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 0) (0, 1) (0, 3) (0, 4) (1, 0) (1, 1) (1, 3) (1, 4) (2, 2) (2, 5) " + "(3, 0) (3, 1) (3, 3) (3, 4) (4, 0) (4, 1) (4, 3) (4, 4) (5, 2) (5, 5)"); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_line(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_line(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 4) (1, 3)"); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_two_nodes_cycle(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_3(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 0) (1, 0)"); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_with_empty_adj_matrix(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_graph_4(); + init_outputs() ; + + OK(run_algorithm()); + check_result("(0, 0) (1, 1)"); + + free_workspace(); + teardown(); +#endif +} + +//==================== +// Tests with invalid result +//==================== + +void test_CFL_reachability_invalid_rules(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + + // Rule [Variable -> _ B] + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = -1, .prod_B = 1, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [_ -> A B] + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = -1, .prod_A = 1, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [C -> A B], where C >= nonterms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 10, .prod_A = 1, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [S -> A B], where A >= nonterms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [C -> t], where t >= terms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = -1, .index = 0}; + check_error(GrB_INVALID_VALUE); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_reachability_null_pointers(void) { +#if LAGRAPH_SUITESPARSE + + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + +// adj_matrices[0] = NULL; +// adj_matrices[1] = NULL; + GrB_free(&adj_matrices[0]); + GrB_free(&adj_matrices[1]); + + check_error(GrB_NULL_POINTER); + +// adj_matrices = NULL; + LAGraph_Free ((void **) &adj_matrices, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + +// outputs = NULL; + LAGraph_Free ((void **) &outputs, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + +// grammar.rules = NULL; + LAGraph_Free ((void **) &grammar.rules, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + teardown(); +#endif +} + +TEST_LIST = {{"CFL_reachability_complex_grammar", test_CFL_reachability_complex_grammar}, + {"CFL_reachability_cycle", test_CFL_reachability_cycle}, + {"CFL_reachability_two_cycle", test_CFL_reachability_two_cycle}, + {"CFL_reachability_labels_more_than_nonterms", + test_CFL_reachability_labels_more_than_nonterms}, + {"CFL_reachability_tree", test_CFL_reachability_tree}, + {"CFL_reachability_line", test_CFL_reachability_line}, + {"CFL_reachability_two_nodes_cycle", test_CFL_reachability_two_nodes_cycle}, + {"CFG_reach_basic_invalid_rules", test_CFL_reachability_invalid_rules}, + {"test_CFL_reachability_with_empty_adj_matrix", test_CFL_reachability_with_empty_adj_matrix}, + #if !defined ( GRAPHBLAS_HAS_CUDA ) + {"CFG_reachability_null_pointers", test_CFL_reachability_null_pointers}, + #endif + {NULL, NULL}}; + From 15c7f7746b9aa83906e088430c5e20f4b5432824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Mon, 2 Mar 2026 22:11:00 +0400 Subject: [PATCH 02/34] init commit --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 384 ------- .../LAGraph_CFL_extract_single_path.c | 227 ++++ ...LAGraph_CFL_extract_single_path_internal.c | 227 ++++ .../algorithm/LAGraph_CFL_reachability.c | 308 +----- .../algorithm/LAGraph_CFL_single_path.c | 219 ++++ experimental/algorithm/LAGraph_CFPQ_core.c | 325 ++++++ .../test/test_CFL_extract_single_path.c | 997 ++++++++++++++++++ ..._CFL_AllPaths.c => test_CFL_single_path.c} | 429 +++++--- include/LAGraphX.h | 139 +++ 9 files changed, 2412 insertions(+), 843 deletions(-) delete mode 100644 experimental/algorithm/LAGraph_CFL_AllPaths.c create mode 100644 experimental/algorithm/LAGraph_CFL_extract_single_path.c create mode 100644 experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c create mode 100644 experimental/algorithm/LAGraph_CFL_single_path.c create mode 100644 experimental/algorithm/LAGraph_CFPQ_core.c create mode 100644 experimental/test/test_CFL_extract_single_path.c rename experimental/test/{test_CFL_AllPaths.c => test_CFL_single_path.c} (58%) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c deleted file mode 100644 index c4c5d25ea5..0000000000 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ /dev/null @@ -1,384 +0,0 @@ -//------------------------------------------------------------------------------ -// LAGraph_CFL_reachability.c: Context-Free Language Reachability Matrix-Based -// Algorithm -// ------------------------------------------------------------------------------ -// -// LAGraph, (c) 2019-2024 by The LAGraph Contributors, All Rights Reserved. -// SPDX-License-Identifier: BSD-2-Clause - -// Contributed by Ilhom Kombaev, Semyon Grigoriev, St. Petersburg State University. - -//------------------------------------------------------------------------------ - -// Code is based on the "A matrix-based CFPQ algorithm" described in the -// following paper: * Rustam Azimov, Semyon Grigorev, "Context-Free Path -// Querying Using Linear Algebra", URL: -// https://disser.spbu.ru/files/2022/disser_azimov.pdf - -#define LG_FREE_WORK \ - { \ - LAGraph_Free ((void **) &nnzs, msg) ; \ - GrB_free(&true_scalar); \ - GrB_free(&identity_matrix); \ - LAGraph_Free ((void **) &T, msg); \ - LAGraph_Free ((void **) &indexes, msg); \ - LAGraph_Free((void**) &t_empty_flags, NULL); \ - LAGraph_Free((void**) &eps_rules, NULL); \ - LAGraph_Free((void**) &term_rules, NULL); \ - LAGraph_Free((void**) &bin_rules, NULL); \ - } - -#define LG_FREE_ALL \ - { \ - for (int64_t i = 0; i < nonterms_count; i++) { \ - GrB_free(&T[i]); \ - } \ - \ - LG_FREE_WORK; \ - } - -#include "LG_internal.h" -#include - -#define ERROR_RULE(msg,i) \ - { \ - LG_ASSERT_MSGF(false, GrB_INVALID_VALUE, \ - "Rule with index %" PRId64 " is invalid. ", msg, i); \ - } - -#define ADD_TO_MSG(...) \ - { \ - if (msg_len == 0) { \ - msg_len += \ - snprintf(msg, LAGRAPH_MSG_LEN, \ - "LAGraph failure (file %s, line %d): ", \ - __FILE__, __LINE__); \ - } \ - if (msg_len < LAGRAPH_MSG_LEN) { \ - msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ - __VA_ARGS__); \ - } \ - } - -#define ADD_INDEX_TO_ERROR_RULE(rule, i) \ - { \ - rule.len_indexes_str += snprintf( \ - rule.indexes_str + rule.len_indexes_str, \ - LAGRAPH_MSG_LEN - rule.len_indexes_str, \ - rule.count == 0 ? "%" PRId64 : ", %" PRId64, i); \ - rule.count++; \ - } - - - -// LAGraph_CFL_reachability: Context-Free Language Reachability Matrix-Based Algorithm -// -// This function determines the set of vertex pairs (u, v) in a graph (represented by -// adjacency matrices) such that there is a path from u to v, where the edge labels form a -// word from the language generated by the context-free grammar (represented by `rules`). -// -// Terminals and non-terminals are enumerated by integers starting from zero. -// The start non-terminal is the non-terminal with index 0. -// -// Example: -// -// Graph: -// ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ -// │ 0 ├───► 1 ├───► 2 ├───► 3 ├───► 4 │ -// └───┘ a └─┬─┘ a └─▲─┘ b └───┘ b └───┘ -// │ │ -// │ ┌───┐ │ -// a└─► 5 ├─┘b -// └───┘ -// -// Grammar: S -> aSb | ab -// -// There are paths from node [1] to node [3] and from node [1] to node [2] that form the -// word "ab" ([1]-a->[2]-b->[3] and [1]-a->[5]-b->[2]). The word "ab" is in the language -// generated by our context-free grammar, so the pairs (1, 3) and (1, 2) will be included -// in the result. -// -// Note: It doesn't matter how many paths exist from node [A] to node [B] that form a word -// in the language. If at least one path exists, the pair ([A], [B]) will be included in -// the result. -// -// In contrast, the path from node [1] to node [4] forms the word "abb" -// ([1]-a->[2]-b->[3]-b->[4]) and the word "abbb" ([1]-a->[5]-b->[2]-b->[3]-b->[4]). -// The words "aab" and "abbb" are not in the language, so the pair (1, 4) will not be -// included in the result. -// -// With this graph and grammar, we obtain the following results: -// (0, 4) - because there exists a path (0-1-2-3-4) that forms the word "aabb" -// (1, 3) - because there exists a path (1-2-3) that forms "ab" -// (1, 2) - because there exists a path (1-5-2) that forms the word "ab" -// (0, 3) - because there exists a path (0-1-5-2-3) that forms the word "aabb" - -GrB_Info LAGraph_CFL_reachability -( - // Output - GrB_Matrix *outputs, // Array of matrices containing results. - // The size of the array must be equal to nonterms_count. - // - // outputs[k]: (i, j) = true if and only if there is a path - // from node i to node j whose edge labels form a word - // derivable from the non-terminal 'k' of the specified CFG. - // Input - const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. - // The length of this array is equal to the count of - // terminals (terms_count). - // - // adj_matrices[t]: (i, j) == 1 if and only if there - // is an edge between nodes i and j with the label of - // the terminal corresponding to index 't' (where t is - // in the range [0, terms_count - 1]). - int64_t terms_count, // The total number of terminal symbols in the CFG. - int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. - const LAGraph_rule_WCNF *rules, // The rules of the CFG. - int64_t rules_count, // The total number of rules in the CFG. - char *msg // Message string for error reporting. -) -{ - -#if LAGRAPH_SUITESPARSE - // Declare workspace and clear the msg string, if not NULL - GrB_Matrix *T; - bool *t_empty_flags = NULL ; // t_empty_flags[i] == true <=> T[i] is empty - GrB_Matrix identity_matrix = NULL; - uint64_t *nnzs = NULL; - LG_CLEAR_MSG; - size_t msg_len = 0; // For error formatting - bool iso_flag = false; - GrB_Index *indexes = NULL; - - // Arrays for processing rules - size_t *eps_rules = NULL, eps_rules_count = 0; // [Variable -> eps] - size_t *term_rules = NULL, term_rules_count = 0; // [Variable -> term] - size_t *bin_rules = NULL, bin_rules_count = 0; // [Variable -> AB] - - GrB_Scalar true_scalar; - GrB_Scalar_new(&true_scalar, GrB_BOOL); - GrB_Scalar_setElement_BOOL(true_scalar, true); - - LG_TRY(LAGraph_Calloc((void **) &T, nonterms_count, sizeof(GrB_Matrix), msg)); - LG_TRY(LAGraph_Calloc((void **) &t_empty_flags, nonterms_count, sizeof(bool), msg)) ; - - LG_ASSERT_MSG(terms_count > 0, GrB_INVALID_VALUE, - "The number of terminals must be greater than zero."); - LG_ASSERT_MSG(nonterms_count > 0, GrB_INVALID_VALUE, - "The number of non-terminals must be greater than zero."); - LG_ASSERT_MSG(rules_count > 0, GrB_INVALID_VALUE, - "The number of rules must be greater than zero."); - LG_ASSERT_MSG(outputs != NULL, GrB_NULL_POINTER, "The outputs array cannot be null."); - LG_ASSERT_MSG(rules != NULL, GrB_NULL_POINTER, "The rules array cannot be null."); - LG_ASSERT_MSG(adj_matrices != NULL, GrB_NULL_POINTER, - "The adjacency matrices array cannot be null."); - - // Find null adjacency matrices - bool found_null = false; - for (int64_t i = 0; i < terms_count; i++) { - if (adj_matrices[i] != NULL) - continue; - - if (!found_null) { - ADD_TO_MSG("Adjacency matrices with these indexes are null: "); - ADD_TO_MSG("%" PRId64, i); - } else { - ADD_TO_MSG("%" PRId64, i); - } - - found_null = true; - } - - if (found_null) { - LG_FREE_ALL; - return GrB_NULL_POINTER; - } - - GrB_Index n; - GRB_TRY(GrB_Matrix_ncols(&n, adj_matrices[0])); - - // Create nonterms matrices - for (int64_t i = 0; i < nonterms_count; i++) { - GRB_TRY(GrB_Matrix_new(&T[i], GrB_BOOL, n, n)); - t_empty_flags[i] = true; - } - - LG_TRY(LAGraph_Calloc((void **) &eps_rules, rules_count, sizeof(size_t), msg)) ; - LG_TRY(LAGraph_Calloc((void **) &term_rules, rules_count, sizeof(size_t), msg)) ; - LG_TRY(LAGraph_Calloc((void **) &bin_rules, rules_count, sizeof(size_t), msg)) ; - - // Process rules - typedef struct { - size_t count; - size_t len_indexes_str; - char indexes_str[LAGRAPH_MSG_LEN]; - } rule_error_s; - rule_error_s term_err = {0}; - rule_error_s nonterm_err = {0}; - rule_error_s invalid_err = {0}; - for (int64_t i = 0; i < rules_count; i++) { - LAGraph_rule_WCNF rule = rules[i]; - - bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; - bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; - bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; - - // Check that all rules are well-formed - if (rule.nonterm < 0 || rule.nonterm >= nonterms_count) { - ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); - } - - // [Variable -> eps] - if (is_rule_eps) { - eps_rules[eps_rules_count++] = i; - - continue; - } - - // [Variable -> term] - if (is_rule_term) { - term_rules[term_rules_count++] = i; - - if (rule.prod_A < -1 || rule.prod_A >= terms_count) { - ADD_INDEX_TO_ERROR_RULE(term_err, i); - } - - continue; - } - - // [Variable -> A B] - if (is_rule_bin) { - bin_rules[bin_rules_count++] = i; - - if (rule.prod_A < -1 || rule.prod_A >= nonterms_count || rule.prod_B < -1 || - rule.prod_B >= nonterms_count) { - ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); - } - - continue; - } - - // [Variable -> _ B] - ADD_INDEX_TO_ERROR_RULE(invalid_err, i); - } - - if (term_err.count + nonterm_err.count + invalid_err.count > 0) { - ADD_TO_MSG("Count of invalid rules: %" PRId64 ".\n", - (int64_t) (term_err.count + nonterm_err.count + invalid_err.count)); - - if (nonterm_err.count > 0) { - ADD_TO_MSG("Non-terminals must be in range [0, nonterms_count). "); - ADD_TO_MSG("Indexes of invalid rules: %s\n", nonterm_err.indexes_str) - } - if (term_err.count > 0) { - ADD_TO_MSG("Terminals must be in range [-1, nonterms_count). "); - ADD_TO_MSG("Indexes of invalid rules: %s\n", term_err.indexes_str) - } - if (invalid_err.count > 0) { - ADD_TO_MSG("[Variable -> _ B] type of rule is not acceptable. "); - ADD_TO_MSG("Indexes of invalid rules: %.120s\n", invalid_err.indexes_str) - } - - LG_FREE_ALL; - return GrB_INVALID_VALUE; - } - - // Rule [Variable -> term] - for (int64_t i = 0; i < term_rules_count; i++) { - LAGraph_rule_WCNF term_rule = rules[term_rules[i]]; - GrB_Index adj_matrix_nnz = 0; - GRB_TRY(GrB_Matrix_nvals(&adj_matrix_nnz, adj_matrices[term_rule.prod_A])); - - if (adj_matrix_nnz == 0) { - continue; - } - - GxB_eWiseUnion( - T[term_rule.nonterm], GrB_NULL, GrB_NULL, GxB_PAIR_BOOL, - T[term_rule.nonterm], true_scalar, adj_matrices[term_rule.prod_A], true_scalar, GrB_NULL - ); - - t_empty_flags[term_rule.nonterm] = false; - - #ifdef DEBUG_CFL_REACHBILITY - GxB_Matrix_iso(&iso_flag, T[term_rule.nonterm]); - printf("[TERM] eWiseUnion: NONTERM: %d (ISO: %d)\n", term_rule.nonterm, iso_flag); - #endif - } - - GrB_Vector v_diag; - GRB_TRY(GrB_Vector_new(&v_diag, GrB_BOOL, n)); - GRB_TRY(GrB_Vector_assign_BOOL(v_diag, GrB_NULL, GrB_NULL, true, GrB_ALL, n, NULL)); - GRB_TRY(GrB_Matrix_diag(&identity_matrix, v_diag, 0)); - GRB_TRY(GrB_free(&v_diag)); - - // Rule [Variable -> eps] - for (int64_t i = 0; i < eps_rules_count; i++) { - LAGraph_rule_WCNF eps_rule = rules[eps_rules[i]]; - - GxB_eWiseUnion ( - T[eps_rule.nonterm],GrB_NULL,GxB_PAIR_BOOL,GxB_PAIR_BOOL, - T[eps_rule.nonterm],true_scalar,identity_matrix,true_scalar,GrB_NULL - ); - - t_empty_flags[eps_rule.nonterm] = false; - - #ifdef DEBUG_CFL_REACHBILITY - GxB_Matrix_iso(&iso_flag, T[eps_rule.nonterm]); - printf("[EPS] eWiseUnion: NONTERM: %d (ISO: %d)\n", - eps_rule.nonterm, iso_flag); - #endif - } - - // Rule [Variable -> Variable1 Variable2] - LG_TRY(LAGraph_Calloc((void **) &nnzs, nonterms_count, sizeof(uint64_t), msg)); - bool changed = true; - while (changed) { - changed = false; - for (int64_t i = 0; i < bin_rules_count; i++) { - LAGraph_rule_WCNF bin_rule = rules[bin_rules[i]]; - - // If one of matrices is empty then their product will be empty - if (t_empty_flags[bin_rule.prod_A] || t_empty_flags[bin_rule.prod_B]) { - continue; - } - - GrB_BinaryOp acc_op = t_empty_flags[bin_rule.nonterm] ? GrB_NULL : GxB_ANY_BOOL; - GRB_TRY(GrB_mxm(T[bin_rule.nonterm], GrB_NULL, acc_op, - GxB_ANY_PAIR_BOOL, T[bin_rule.prod_A], T[bin_rule.prod_B], - GrB_NULL)) - - GrB_Index new_nnz; - GRB_TRY(GrB_Matrix_nvals(&new_nnz, T[bin_rule.nonterm])); - if (new_nnz != 0) t_empty_flags[bin_rule.nonterm] = false; - - changed = changed || (nnzs[bin_rule.nonterm] != new_nnz); - nnzs[bin_rule.nonterm] = new_nnz; - - #ifdef DEBUG_CFL_REACHBILITY - GxB_Matrix_iso(&iso_flag, T[bin_rule.nonterm]); - printf("[TERM1 TERM2] MULTIPLY, S: %d, A: %d, B: %d, " - "I: %" PRId64 " (ISO: %d)\n", - bin_rule.nonterm, bin_rule.prod_A, bin_rule.prod_B, i, iso_flag); - #endif - - } - } - - #ifdef DEBUG_CFL_REACHBILITY - for (int64_t i = 0; i < nonterms_count; i++) { - printf("MATRIX WITH INDEX %" PRId64 ":\n", i); - GxB_print(T[i], GxB_SUMMARY); - } - #endif - - for (int64_t i = 0; i < nonterms_count; i++) { - outputs[i] = T[i]; - } - - LG_FREE_WORK; - return GrB_SUCCESS; -#else - return (GrB_NOT_IMPLEMENTED) ; -#endif -} diff --git a/experimental/algorithm/LAGraph_CFL_extract_single_path.c b/experimental/algorithm/LAGraph_CFL_extract_single_path.c new file mode 100644 index 0000000000..0014758273 --- /dev/null +++ b/experimental/algorithm/LAGraph_CFL_extract_single_path.c @@ -0,0 +1,227 @@ +#define LG_FREE_ALL \ + { \ + LAGraph_Free((void **)&output->paths, NULL); \ + output->count = 0; \ + output->capacity = 0; \ + } + +#include "LG_internal.h" +#include + +#define ADD_TO_MSG(...) \ + { \ + if (msg_len == 0) \ + { \ + msg_len += \ + snprintf(msg, LAGRAPH_MSG_LEN, \ + "LAGraph failure (file %s, line %d): ", \ + __FILE__, __LINE__); \ + } \ + if (msg_len < LAGRAPH_MSG_LEN) \ + { \ + msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ + __VA_ARGS__); \ + } \ + } + +#define ADD_INDEX_TO_ERROR_RULE(rule, i) \ + { \ + rule.len_indexes_str += snprintf( \ + rule.indexes_str + rule.len_indexes_str, \ + LAGRAPH_MSG_LEN - rule.len_indexes_str, \ + rule.count == 0 ? "%" PRId64 : ", %" PRId64, i); \ + rule.count++; \ + } + +GrB_Info LAGraph_CFL_extract_single_path( + // Output + PathArray *output, + // Input + GrB_Index *start, + GrB_Index *end, + int32_t nonterm, + const GrB_Matrix *adj_matrices, + const GrB_Matrix *T, + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +) +{ + LG_CLEAR_MSG; + size_t msg_len = 0; // For error formatting + GrB_Info info; + output->count = 0; + output->paths = NULL; + // Initial capacity 1, since most often looking for exactly 1 path with a fixed start and end + output->capacity = 1; + + LG_ASSERT_MSG(terms_count > 0, GrB_INVALID_VALUE, + "The number of terminals must be greater than zero."); + LG_ASSERT_MSG(nonterms_count > 0, GrB_INVALID_VALUE, + "The number of non-terminals must be greater than zero."); + LG_ASSERT_MSG(rules_count > 0, GrB_INVALID_VALUE, + "The number of rules must be greater than zero."); + LG_ASSERT_MSG(nonterm < nonterms_count, GrB_INVALID_VALUE, + "The start non-terminal must be no greater than the number of non-terminals."); + LG_ASSERT_MSG(T != NULL, GrB_NULL_POINTER, "The T array cannot be null."); + LG_ASSERT_MSG(rules != NULL, GrB_NULL_POINTER, "The rules array cannot be null."); + LG_ASSERT_MSG(adj_matrices != NULL, GrB_NULL_POINTER, + "The adjacency matrices array cannot be null."); + + // Find null adjacency matrices + bool found_null = false; + for (int64_t i = 0; i < terms_count; i++) + { + if (adj_matrices[i] != NULL) + continue; + + if (!found_null) + { + ADD_TO_MSG("Adjacency matrices with these indexes are null:"); + } + ADD_TO_MSG(" %" PRId64, i); + found_null = true; + } + + if (found_null) + { + LG_FREE_ALL; + return GrB_NULL_POINTER; + } + + // Find null T matrices + found_null = false; + for (int64_t i = 0; i < nonterms_count; i++) + { + if (T[i] != NULL) + continue; + + if (!found_null) + { + ADD_TO_MSG("T matrices with these indexes are null:"); + } + ADD_TO_MSG(" %" PRId64, i); + + found_null = true; + } + if (found_null) + { + LG_FREE_ALL; + return GrB_NULL_POINTER; + } + + // Check the rules + typedef struct + { + size_t count; + size_t len_indexes_str; + char indexes_str[LAGRAPH_MSG_LEN]; + } rule_error_s; + rule_error_s term_err = {0}; + rule_error_s nonterm_err = {0}; + rule_error_s invalid_err = {0}; + for (int64_t i = 0; i < rules_count; i++) + { + LAGraph_rule_WCNF rule = rules[i]; + + bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; + bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; + bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; + + // Check that all rules are well-formed + if (rule.nonterm < 0 || rule.nonterm >= nonterms_count) + { + ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); + } + + // [Variable -> term] + if (is_rule_term) + { + if (rule.prod_A < -1 || rule.prod_A >= terms_count) + { + ADD_INDEX_TO_ERROR_RULE(term_err, i); + } + continue; + } + + // [Variable -> A B] + if (is_rule_bin) + { + if (rule.prod_A < -1 || rule.prod_A >= nonterms_count || rule.prod_B < -1 || + rule.prod_B >= nonterms_count) + { + ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); + } + continue; + } + + // [Variable -> _ B] + ADD_INDEX_TO_ERROR_RULE(invalid_err, i); + } + + if (term_err.count + nonterm_err.count + invalid_err.count > 0) + { + ADD_TO_MSG("Count of invalid rules: %" PRId64 ".\n", + (int64_t)(term_err.count + nonterm_err.count + invalid_err.count)); + + if (nonterm_err.count > 0) + { + ADD_TO_MSG("Non-terminals must be in range [0, nonterms_count). "); + ADD_TO_MSG("Indexes of invalid rules: %s\n", nonterm_err.indexes_str) + } + if (term_err.count > 0) + { + ADD_TO_MSG("Terminals must be in range [-1, nonterms_count). "); + ADD_TO_MSG("Indexes of invalid rules: %s\n", term_err.indexes_str) + } + if (invalid_err.count > 0) + { + ADD_TO_MSG("[Variable -> _ B] type of rule is not acceptable. "); + ADD_TO_MSG("Indexes of invalid rules: %.120s\n", invalid_err.indexes_str) + } + + LG_FREE_ALL; + return GrB_INVALID_VALUE; + } + + GrB_Index n; + GRB_TRY(GrB_Matrix_nrows(&n, adj_matrices[0])); + + GrB_Index start_begin = (start == NULL) ? 0 : *start; + GrB_Index start_end = (start == NULL) ? n - 1 : *start; + GrB_Index end_begin = (end == NULL) ? 0 : *end; + GrB_Index end_end = (end == NULL) ? n - 1 : *end; + + LG_TRY(LAGraph_Malloc((void **)&output->paths, output->capacity, sizeof(Path), msg)); + + for (GrB_Index st = start_begin; st <= start_end; st++) + { + for (GrB_Index en = end_begin; en <= end_end; en++) + { + Path path; + + // Function that extracts one path with fixed start and end + info = LAGraph_CFL_extract_single_path_internal(&path, st, en, nonterm, adj_matrices, T, terms_count, nonterms_count, rules, rules_count, msg); + if (info == GrB_SUCCESS) + { + if (output->count == output->capacity) + { + LG_TRY(LAGraph_Realloc((void **)&output->paths, output->capacity * 2, output->capacity, + sizeof(Path), msg)); + output->capacity *= 2; + } + output->paths[output->count++] = path; + } + else if (info != GrB_NO_VALUE) // GrB_NO_VALUE is the absence of a path, not an error + { + LG_FREE_ALL; + return info; + } + } + } + + LG_FREE_WORK; + return GrB_SUCCESS; +} diff --git a/experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c b/experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c new file mode 100644 index 0000000000..766998730a --- /dev/null +++ b/experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c @@ -0,0 +1,227 @@ +#define LG_FREE_WORK \ + { \ + LAGraph_Free((void **)&eps_rules, NULL); \ + LAGraph_Free((void **)&term_rules, NULL); \ + LAGraph_Free((void **)&bin_rules, NULL); \ + } + +#define LG_FREE_ALL \ + { \ + LAGraph_Free((void **)&output->path, NULL); \ + output->len = 0; \ + LG_FREE_WORK; \ + } + +#include "LG_internal.h" +#include + +#define ADD_TO_MSG(...) \ + { \ + if (msg_len == 0) \ + { \ + msg_len += \ + snprintf(msg, LAGRAPH_MSG_LEN, \ + "LAGraph failure (file %s, line %d): ", \ + __FILE__, __LINE__); \ + } \ + if (msg_len < LAGRAPH_MSG_LEN) \ + { \ + msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ + __VA_ARGS__); \ + } \ + } + +GrB_Info LAGraph_CFL_extract_single_path_internal( + // Output + Path *output, + // Input + GrB_Index start, + GrB_Index end, + int32_t nonterm, + const GrB_Matrix *adj_matrices, + const GrB_Matrix *T, + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +) +{ + LG_CLEAR_MSG; + size_t msg_len = 0; // For error formatting + output->len = 0; + output->path = NULL; + // Arrays for processing rules + size_t *eps_rules = NULL, eps_rules_count = 0; // [Variable -> eps] + size_t *term_rules = NULL, term_rules_count = 0; // [Variable -> term] + size_t *bin_rules = NULL, bin_rules_count = 0; // [Variable -> AB] + + LG_TRY(LAGraph_Calloc((void **)&eps_rules, rules_count, sizeof(size_t), msg)); + LG_TRY(LAGraph_Calloc((void **)&term_rules, rules_count, sizeof(size_t), msg)); + LG_TRY(LAGraph_Calloc((void **)&bin_rules, rules_count, sizeof(size_t), msg)); + + // Internal function: assumes all inputs have been validated by the public wrapper + + // Process rules + for (int64_t i = 0; i < rules_count; i++) + { + LAGraph_rule_WCNF rule = rules[i]; + + bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; + bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; + bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; + + // [Variable -> eps] + if (is_rule_eps) + { + eps_rules[eps_rules_count++] = i; + continue; + } + + // [Variable -> term] + if (is_rule_term) + { + term_rules[term_rules_count++] = i; + continue; + } + + // [Variable -> A B] + if (is_rule_bin) + { + bin_rules[bin_rules_count++] = i; + continue; + } + } + + PathIndex index; + GrB_Info info = GrB_Matrix_extractElement_UDT(&index, T[nonterm], start, end); + if (info == GrB_SUCCESS) // Such a path exists + { + if (index.height == 1) + { + if (start == end) // Height = 1 and start = end is an empty eps-path + { + for (size_t i = 0; i < eps_rules_count; i++) + { + LAGraph_rule_WCNF term_rule = rules[eps_rules[i]]; + if (term_rule.nonterm == nonterm) + { + LG_FREE_WORK; + return GrB_SUCCESS; + } + } + } + // Height = 1 and different vertices is a term-path + for (int64_t i = 0; i < terms_count; i++) + { + bool edge; + if (GrB_Matrix_extractElement_BOOL(&edge, adj_matrices[i], start, end) == GrB_SUCCESS) + { + for (size_t j = 0; j < term_rules_count; j++) + { + LAGraph_rule_WCNF term_rule = rules[term_rules[j]]; + if (term_rule.nonterm == nonterm && term_rule.prod_A == i) + { + LG_TRY(LAGraph_Calloc((void **)&output->path, 1, sizeof(Edge), msg)); + output->len = 1; + output->path[0] = (Edge){start, i, end}; + LG_FREE_WORK; + return GrB_SUCCESS; + } + } + } + } + // If couldn't find rules for outputting an empty or terminal path, + // then the path were looking for doesn't match the rules + LG_FREE_WORK; + ADD_TO_MSG("The extracted path does not match the input grammar."); + return GrB_NO_VALUE; + } + // Rules of the form Nonterm -> Nonterm * Nonterm are traversed recursively and merged + for (size_t i = 0; i < bin_rules_count; i++) + { + LAGraph_rule_WCNF term_rule = rules[bin_rules[i]]; + if (term_rule.nonterm != nonterm) + { + continue; + } + PathIndex indexB, indexC; + if ((info = GrB_Matrix_extractElement_UDT(&indexB, T[term_rule.prod_A], start, index.middle)) != GrB_SUCCESS) + { + // If haven't found such a piece of the path, then continue. + if (info != GrB_NO_VALUE) + { + LG_FREE_WORK; + return info; + } + + continue; + } + if ((info = GrB_Matrix_extractElement_UDT(&indexC, T[term_rule.prod_B], index.middle, end)) != GrB_SUCCESS) + { + if (info != GrB_NO_VALUE) + { + LG_FREE_WORK; + return info; + } + continue; + } + + // Height compliance check + int32_t maxH = (indexB.height > indexC.height ? indexB.height : indexC.height); + if (index.height != maxH + 1) + { + continue; + } + + Path left, right; + // If didn't find the path, try the other rules. + if ((info = LAGraph_CFL_extract_single_path_internal(&left, start, index.middle, term_rule.prod_A, adj_matrices, T, terms_count, nonterms_count, rules, rules_count, msg)) != GrB_SUCCESS) + { + if (info == GrB_NO_VALUE) + { + continue; + } + LG_FREE_WORK; + return info; + } + if ((info = LAGraph_CFL_extract_single_path_internal(&right, index.middle, end, term_rule.prod_B, adj_matrices, T, terms_count, nonterms_count, rules, rules_count, msg)) != GrB_SUCCESS) + { + if (info == GrB_NO_VALUE) + { + LG_TRY(LAGraph_Free((void **)&left.path, msg)); + continue; + } + LG_TRY(LAGraph_Free((void **)&left.path, msg)); + LG_FREE_WORK; + return info; + } + + output->len = left.len + right.len; + + LG_TRY(LAGraph_Calloc((void **)&output->path, output->len, sizeof(Edge), msg)); + + memcpy(output->path, left.path, left.len * sizeof(Edge)); + memcpy(output->path + left.len, right.path, right.len * sizeof(Edge)); + LG_TRY(LAGraph_Free((void **)&left.path, msg)); + LG_TRY(LAGraph_Free((void **)&right.path, msg)); + LG_FREE_WORK; + return GrB_SUCCESS; + } + + // If couldn't find rules for outputting an path, + // then the path were looking for doesn't match the rules + LG_FREE_WORK; + ADD_TO_MSG("The extracted path does not match the input grammar."); + return GrB_NO_VALUE; + } + // Such a path doesn't exists - return an empty path and GrB_NO_VALUE + else if (info == GrB_NO_VALUE) + { + LG_FREE_WORK; + return GrB_NO_VALUE; + } + // Return some other error + LG_FREE_WORK; + return info; +} diff --git a/experimental/algorithm/LAGraph_CFL_reachability.c b/experimental/algorithm/LAGraph_CFL_reachability.c index c4c5d25ea5..4b1dbce46e 100644 --- a/experimental/algorithm/LAGraph_CFL_reachability.c +++ b/experimental/algorithm/LAGraph_CFL_reachability.c @@ -15,62 +15,14 @@ // Querying Using Linear Algebra", URL: // https://disser.spbu.ru/files/2022/disser_azimov.pdf -#define LG_FREE_WORK \ - { \ - LAGraph_Free ((void **) &nnzs, msg) ; \ - GrB_free(&true_scalar); \ - GrB_free(&identity_matrix); \ - LAGraph_Free ((void **) &T, msg); \ - LAGraph_Free ((void **) &indexes, msg); \ - LAGraph_Free((void**) &t_empty_flags, NULL); \ - LAGraph_Free((void**) &eps_rules, NULL); \ - LAGraph_Free((void**) &term_rules, NULL); \ - LAGraph_Free((void**) &bin_rules, NULL); \ - } - -#define LG_FREE_ALL \ - { \ - for (int64_t i = 0; i < nonterms_count; i++) { \ - GrB_free(&T[i]); \ - } \ - \ - LG_FREE_WORK; \ +#define LG_FREE_WORK \ + { \ + GrB_free(&false_scalar); \ } #include "LG_internal.h" #include -#define ERROR_RULE(msg,i) \ - { \ - LG_ASSERT_MSGF(false, GrB_INVALID_VALUE, \ - "Rule with index %" PRId64 " is invalid. ", msg, i); \ - } - -#define ADD_TO_MSG(...) \ - { \ - if (msg_len == 0) { \ - msg_len += \ - snprintf(msg, LAGRAPH_MSG_LEN, \ - "LAGraph failure (file %s, line %d): ", \ - __FILE__, __LINE__); \ - } \ - if (msg_len < LAGRAPH_MSG_LEN) { \ - msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ - __VA_ARGS__); \ - } \ - } - -#define ADD_INDEX_TO_ERROR_RULE(rule, i) \ - { \ - rule.len_indexes_str += snprintf( \ - rule.indexes_str + rule.len_indexes_str, \ - LAGRAPH_MSG_LEN - rule.len_indexes_str, \ - rule.count == 0 ? "%" PRId64 : ", %" PRId64, i); \ - rule.count++; \ - } - - - // LAGraph_CFL_reachability: Context-Free Language Reachability Matrix-Based Algorithm // // This function determines the set of vertex pairs (u, v) in a graph (represented by @@ -113,8 +65,7 @@ // (1, 2) - because there exists a path (1-5-2) that forms the word "ab" // (0, 3) - because there exists a path (0-1-5-2-3) that forms the word "aabb" -GrB_Info LAGraph_CFL_reachability -( +GrB_Info LAGraph_CFL_reachability( // Output GrB_Matrix *outputs, // Array of matrices containing results. // The size of the array must be equal to nonterms_count. @@ -138,247 +89,16 @@ GrB_Info LAGraph_CFL_reachability char *msg // Message string for error reporting. ) { - -#if LAGRAPH_SUITESPARSE - // Declare workspace and clear the msg string, if not NULL - GrB_Matrix *T; - bool *t_empty_flags = NULL ; // t_empty_flags[i] == true <=> T[i] is empty - GrB_Matrix identity_matrix = NULL; - uint64_t *nnzs = NULL; - LG_CLEAR_MSG; - size_t msg_len = 0; // For error formatting - bool iso_flag = false; - GrB_Index *indexes = NULL; - - // Arrays for processing rules - size_t *eps_rules = NULL, eps_rules_count = 0; // [Variable -> eps] - size_t *term_rules = NULL, term_rules_count = 0; // [Variable -> term] - size_t *bin_rules = NULL, bin_rules_count = 0; // [Variable -> AB] - - GrB_Scalar true_scalar; - GrB_Scalar_new(&true_scalar, GrB_BOOL); - GrB_Scalar_setElement_BOOL(true_scalar, true); - - LG_TRY(LAGraph_Calloc((void **) &T, nonterms_count, sizeof(GrB_Matrix), msg)); - LG_TRY(LAGraph_Calloc((void **) &t_empty_flags, nonterms_count, sizeof(bool), msg)) ; - - LG_ASSERT_MSG(terms_count > 0, GrB_INVALID_VALUE, - "The number of terminals must be greater than zero."); - LG_ASSERT_MSG(nonterms_count > 0, GrB_INVALID_VALUE, - "The number of non-terminals must be greater than zero."); - LG_ASSERT_MSG(rules_count > 0, GrB_INVALID_VALUE, - "The number of rules must be greater than zero."); - LG_ASSERT_MSG(outputs != NULL, GrB_NULL_POINTER, "The outputs array cannot be null."); - LG_ASSERT_MSG(rules != NULL, GrB_NULL_POINTER, "The rules array cannot be null."); - LG_ASSERT_MSG(adj_matrices != NULL, GrB_NULL_POINTER, - "The adjacency matrices array cannot be null."); - - // Find null adjacency matrices - bool found_null = false; - for (int64_t i = 0; i < terms_count; i++) { - if (adj_matrices[i] != NULL) - continue; - - if (!found_null) { - ADD_TO_MSG("Adjacency matrices with these indexes are null: "); - ADD_TO_MSG("%" PRId64, i); - } else { - ADD_TO_MSG("%" PRId64, i); - } - - found_null = true; - } - - if (found_null) { - LG_FREE_ALL; - return GrB_NULL_POINTER; - } - - GrB_Index n; - GRB_TRY(GrB_Matrix_ncols(&n, adj_matrices[0])); - - // Create nonterms matrices - for (int64_t i = 0; i < nonterms_count; i++) { - GRB_TRY(GrB_Matrix_new(&T[i], GrB_BOOL, n, n)); - t_empty_flags[i] = true; - } - - LG_TRY(LAGraph_Calloc((void **) &eps_rules, rules_count, sizeof(size_t), msg)) ; - LG_TRY(LAGraph_Calloc((void **) &term_rules, rules_count, sizeof(size_t), msg)) ; - LG_TRY(LAGraph_Calloc((void **) &bin_rules, rules_count, sizeof(size_t), msg)) ; - - // Process rules - typedef struct { - size_t count; - size_t len_indexes_str; - char indexes_str[LAGRAPH_MSG_LEN]; - } rule_error_s; - rule_error_s term_err = {0}; - rule_error_s nonterm_err = {0}; - rule_error_s invalid_err = {0}; - for (int64_t i = 0; i < rules_count; i++) { - LAGraph_rule_WCNF rule = rules[i]; - - bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; - bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; - bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; - - // Check that all rules are well-formed - if (rule.nonterm < 0 || rule.nonterm >= nonterms_count) { - ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); - } - - // [Variable -> eps] - if (is_rule_eps) { - eps_rules[eps_rules_count++] = i; - - continue; - } - - // [Variable -> term] - if (is_rule_term) { - term_rules[term_rules_count++] = i; - - if (rule.prod_A < -1 || rule.prod_A >= terms_count) { - ADD_INDEX_TO_ERROR_RULE(term_err, i); - } - - continue; - } - - // [Variable -> A B] - if (is_rule_bin) { - bin_rules[bin_rules_count++] = i; - - if (rule.prod_A < -1 || rule.prod_A >= nonterms_count || rule.prod_B < -1 || - rule.prod_B >= nonterms_count) { - ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); - } - - continue; - } - - // [Variable -> _ B] - ADD_INDEX_TO_ERROR_RULE(invalid_err, i); - } - - if (term_err.count + nonterm_err.count + invalid_err.count > 0) { - ADD_TO_MSG("Count of invalid rules: %" PRId64 ".\n", - (int64_t) (term_err.count + nonterm_err.count + invalid_err.count)); - - if (nonterm_err.count > 0) { - ADD_TO_MSG("Non-terminals must be in range [0, nonterms_count). "); - ADD_TO_MSG("Indexes of invalid rules: %s\n", nonterm_err.indexes_str) - } - if (term_err.count > 0) { - ADD_TO_MSG("Terminals must be in range [-1, nonterms_count). "); - ADD_TO_MSG("Indexes of invalid rules: %s\n", term_err.indexes_str) - } - if (invalid_err.count > 0) { - ADD_TO_MSG("[Variable -> _ B] type of rule is not acceptable. "); - ADD_TO_MSG("Indexes of invalid rules: %.120s\n", invalid_err.indexes_str) - } - - LG_FREE_ALL; - return GrB_INVALID_VALUE; - } - - // Rule [Variable -> term] - for (int64_t i = 0; i < term_rules_count; i++) { - LAGraph_rule_WCNF term_rule = rules[term_rules[i]]; - GrB_Index adj_matrix_nnz = 0; - GRB_TRY(GrB_Matrix_nvals(&adj_matrix_nnz, adj_matrices[term_rule.prod_A])); - - if (adj_matrix_nnz == 0) { - continue; - } - - GxB_eWiseUnion( - T[term_rule.nonterm], GrB_NULL, GrB_NULL, GxB_PAIR_BOOL, - T[term_rule.nonterm], true_scalar, adj_matrices[term_rule.prod_A], true_scalar, GrB_NULL - ); - - t_empty_flags[term_rule.nonterm] = false; - - #ifdef DEBUG_CFL_REACHBILITY - GxB_Matrix_iso(&iso_flag, T[term_rule.nonterm]); - printf("[TERM] eWiseUnion: NONTERM: %d (ISO: %d)\n", term_rule.nonterm, iso_flag); - #endif - } - - GrB_Vector v_diag; - GRB_TRY(GrB_Vector_new(&v_diag, GrB_BOOL, n)); - GRB_TRY(GrB_Vector_assign_BOOL(v_diag, GrB_NULL, GrB_NULL, true, GrB_ALL, n, NULL)); - GRB_TRY(GrB_Matrix_diag(&identity_matrix, v_diag, 0)); - GRB_TRY(GrB_free(&v_diag)); - - // Rule [Variable -> eps] - for (int64_t i = 0; i < eps_rules_count; i++) { - LAGraph_rule_WCNF eps_rule = rules[eps_rules[i]]; - - GxB_eWiseUnion ( - T[eps_rule.nonterm],GrB_NULL,GxB_PAIR_BOOL,GxB_PAIR_BOOL, - T[eps_rule.nonterm],true_scalar,identity_matrix,true_scalar,GrB_NULL - ); - - t_empty_flags[eps_rule.nonterm] = false; - - #ifdef DEBUG_CFL_REACHBILITY - GxB_Matrix_iso(&iso_flag, T[eps_rule.nonterm]); - printf("[EPS] eWiseUnion: NONTERM: %d (ISO: %d)\n", - eps_rule.nonterm, iso_flag); - #endif - } - - // Rule [Variable -> Variable1 Variable2] - LG_TRY(LAGraph_Calloc((void **) &nnzs, nonterms_count, sizeof(uint64_t), msg)); - bool changed = true; - while (changed) { - changed = false; - for (int64_t i = 0; i < bin_rules_count; i++) { - LAGraph_rule_WCNF bin_rule = rules[bin_rules[i]]; - - // If one of matrices is empty then their product will be empty - if (t_empty_flags[bin_rule.prod_A] || t_empty_flags[bin_rule.prod_B]) { - continue; - } - - GrB_BinaryOp acc_op = t_empty_flags[bin_rule.nonterm] ? GrB_NULL : GxB_ANY_BOOL; - GRB_TRY(GrB_mxm(T[bin_rule.nonterm], GrB_NULL, acc_op, - GxB_ANY_PAIR_BOOL, T[bin_rule.prod_A], T[bin_rule.prod_B], - GrB_NULL)) - - GrB_Index new_nnz; - GRB_TRY(GrB_Matrix_nvals(&new_nnz, T[bin_rule.nonterm])); - if (new_nnz != 0) t_empty_flags[bin_rule.nonterm] = false; - - changed = changed || (nnzs[bin_rule.nonterm] != new_nnz); - nnzs[bin_rule.nonterm] = new_nnz; - - #ifdef DEBUG_CFL_REACHBILITY - GxB_Matrix_iso(&iso_flag, T[bin_rule.nonterm]); - printf("[TERM1 TERM2] MULTIPLY, S: %d, A: %d, B: %d, " - "I: %" PRId64 " (ISO: %d)\n", - bin_rule.nonterm, bin_rule.prod_A, bin_rule.prod_B, i, iso_flag); - #endif - - } - } - - #ifdef DEBUG_CFL_REACHBILITY - for (int64_t i = 0; i < nonterms_count; i++) { - printf("MATRIX WITH INDEX %" PRId64 ":\n", i); - GxB_print(T[i], GxB_SUMMARY); - } - #endif - - for (int64_t i = 0; i < nonterms_count; i++) { - outputs[i] = T[i]; - } - + GrB_Scalar false_scalar; + GRB_TRY(GrB_Scalar_new(&false_scalar, GrB_BOOL)); + GRB_TRY(GrB_Scalar_setElement_BOOL(false_scalar, false)); + CFL_Semiring semiring = {.type = GrB_BOOL, + .semiring = GxB_ANY_PAIR_BOOL, + .add = GxB_ANY_BOOL, + .mult = GxB_PAIR_BOOL, + .init_path = GxB_PAIR_BOOL, + .bottom_scalar = false_scalar}; + LG_TRY(LAGraph_CFPQ_core(outputs, adj_matrices, terms_count, nonterms_count, rules, rules_count, &semiring, msg)); LG_FREE_WORK; return GrB_SUCCESS; -#else - return (GrB_NOT_IMPLEMENTED) ; -#endif } diff --git a/experimental/algorithm/LAGraph_CFL_single_path.c b/experimental/algorithm/LAGraph_CFL_single_path.c new file mode 100644 index 0000000000..bea45bcfa9 --- /dev/null +++ b/experimental/algorithm/LAGraph_CFL_single_path.c @@ -0,0 +1,219 @@ +#define LG_FREE_WORK \ + { \ + GrB_free(&bottom_scalar); \ + GrB_free(&IPI_set); \ + GrB_free(&IPI_mult); \ + GrB_free(&PI_set); \ + GrB_free(&PI_mult); \ + GrB_free(&PI_add); \ + GrB_free(&PI_semiring); \ + GrB_free(&Theta); \ + GrB_free(&PI_monoid); \ + } + +#include "LG_internal.h" +#include + +void add_path_index(PathIndex *z, const PathIndex *x, const PathIndex *y) +{ + // If one is ⊥, then we take another. + if (x->height == 0 && x->middle == 0) + { + *z = *y; + } + else if (y->height == 0 && y->middle == 0) + { + *z = *x; + } + // Take the path with the minimum height + else if (x->height < y->height || (x->height == y->height && x->middle <= y->middle)) + { + *z = *x; + } + else + { + *z = *y; + } +} + +void mult_path_index(PathIndex *z, + const PathIndex *x, GrB_Index ix, GrB_Index jx, + const PathIndex + *y, + GrB_Index iy, GrB_Index jy, + const void *theta) +{ + // Сoncatenation with ⊥ gives ⊥ + if ((x->height == 0 && x->middle == 0) || (y->height == 0 && y->middle == 0)) + { + z->height = 0; + z->middle = 0; + } + else + { + z->middle = jx; + z->height = (x->height > y->height ? x->height : y->height) + 1; + } +} + +void set_path_index(PathIndex *z, + const PathIndex *x, GrB_Index ix, GrB_Index jx, + const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, + const void *theta) +{ + if (*edge_exist) + { + z->middle = ix; + z->height = 1; + } + else + { + z->middle = 0; + z->height = 0; + } +} + +#define SET_PATH_INDEX_DEFN \ + "void set_path_index( \n" \ + " PathIndex *z, \n" \ + " const PathIndex *x, GrB_Index ix, GrB_Index jx, \n" \ + " const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, \n" \ + " const void *theta) \n" \ + "{ \n" \ + " if (*edge_exist) \n" \ + " { \n" \ + " z->middle = ix; \n" \ + " z->height = 1; \n" \ + " } \n" \ + " else \n" \ + " { \n" \ + " z->middle = 0; \n" \ + " z->height = 0; \n" \ + " } \n" \ + "}" + +#define MULT_PATH_INDEX_DEFN \ + "void mult_path_index( \n" \ + " PathIndex *z, \n" \ + " const PathIndex *x, GrB_Index ix, GrB_Index jx, \n" \ + " const PathIndex *y, GrB_Index iy, GrB_Index jy, \n" \ + " const void *theta) \n" \ + "{ \n" \ + " if ((x->height == 0 && x->middle == 0) || \n" \ + " (y->height == 0 && y->middle == 0)) \n" \ + " { \n" \ + " z->height = 0; \n" \ + " z->middle = 0; \n" \ + " } \n" \ + " else \n" \ + " { \n" \ + " z->middle = jx; \n" \ + " z->height = (x->height > y->height ? \n" \ + " x->height : y->height) + 1; \n" \ + " } \n" \ + "}" + +GrB_Info LAGraph_CFL_single_path( + // Output + GrB_Matrix *outputs, // Array of matrices containing results. + // The size of the array must be equal to nonterms_count. + // + // outputs[k]: (i, j) contains a PathIndex structure if and only if there is a path + // from node i to node j whose edge labels form a word + // derivable from the non-terminal 'k' of the specified CFG. + // Input + const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. + // The length of this array is equal to the count of + // terminals (terms_count). + // + // adj_matrices[t]: (i, j) == 1 if and only if there + // is an edge between nodes i and j with the label of + // the terminal corresponding to index 't' (where t is + // in the range [0, terms_count - 1]). + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +) +{ + // Semiring components + GrB_Type PI_type = NULL; // Type PathIndex + GrB_BinaryOp PI_add = NULL; + GrB_Monoid PI_monoid = NULL; + GxB_IndexBinaryOp IPI_mult = NULL; + GrB_BinaryOp PI_mult = NULL; + GrB_Semiring PI_semiring = NULL; + GxB_IndexBinaryOp IPI_set = NULL; + GrB_BinaryOp PI_set = NULL; + GrB_Scalar Theta = NULL; + GrB_Scalar bottom_scalar = NULL; + + GRB_TRY(GrB_Type_new(&PI_type, sizeof(PathIndex))); // the memory is not being freed yet + + // Theta cannot be NULL + GRB_TRY(GrB_Scalar_new(&Theta, GrB_BOOL)); + GRB_TRY(GrB_Scalar_setElement_BOOL(Theta, false)); + + PathIndex bottom = {0, 0}; + GRB_TRY(GrB_Scalar_new(&bottom_scalar, PI_type)); + GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); + + // Create semiring + GRB_TRY(GrB_BinaryOp_new( + &PI_add, + (void *)add_path_index, + PI_type, + PI_type, + PI_type)); + + GRB_TRY(GrB_Monoid_new( + &PI_monoid, + PI_add, + (void *)(&bottom))); // ⊥ - neutral element for the addition operation + + GRB_TRY(GxB_IndexBinaryOp_new( + &IPI_mult, + (void *)mult_path_index, + PI_type, + PI_type, + PI_type, + GrB_BOOL, + "mult_path_index", + MULT_PATH_INDEX_DEFN)); + + GRB_TRY(GxB_BinaryOp_new_IndexOp( + &PI_mult, + IPI_mult, + Theta)); + + GRB_TRY(GrB_Semiring_new( + &PI_semiring, + PI_monoid, + PI_mult)); + + GRB_TRY(GxB_IndexBinaryOp_new( + &IPI_set, + (void *)set_path_index, + PI_type, + PI_type, + GrB_BOOL, + GrB_BOOL, + "set_path_index", + SET_PATH_INDEX_DEFN)); + + GRB_TRY(GxB_BinaryOp_new_IndexOp( + &PI_set, + IPI_set, + Theta)); + + CFL_Semiring semiring = {.type = PI_type, + .semiring = PI_semiring, + .add = PI_add, + .mult = PI_mult, + .init_path = PI_set, + .bottom_scalar = bottom_scalar}; + LG_TRY(LAGraph_CFPQ_core(outputs, adj_matrices, terms_count, nonterms_count, rules, rules_count, &semiring, msg)); + LG_FREE_WORK; + return GrB_SUCCESS; +} diff --git a/experimental/algorithm/LAGraph_CFPQ_core.c b/experimental/algorithm/LAGraph_CFPQ_core.c new file mode 100644 index 0000000000..17f10b02b4 --- /dev/null +++ b/experimental/algorithm/LAGraph_CFPQ_core.c @@ -0,0 +1,325 @@ +#define LG_FREE_WORK \ + { \ + LAGraph_Free((void **)&nnzs, NULL); \ + GrB_free(&false_scalar); \ + GrB_free(&identity_matrix); \ + LAGraph_Free((void **)&T, NULL); \ + LAGraph_Free((void **)&indexes, NULL); \ + LAGraph_Free((void **)&t_empty_flags, NULL); \ + LAGraph_Free((void **)&eps_rules, NULL); \ + LAGraph_Free((void **)&term_rules, NULL); \ + LAGraph_Free((void **)&bin_rules, NULL); \ + } + +#define LG_FREE_ALL \ + { \ + for (int64_t i = 0; i < nonterms_count; i++) \ + { \ + GrB_free(&T[i]); \ + } \ + \ + LG_FREE_WORK; \ + } + +#include "LG_internal.h" +#include + +#define ERROR_RULE(msg, i) \ + { \ + LG_ASSERT_MSGF(false, GrB_INVALID_VALUE, \ + "Rule with index %" PRId64 " is invalid. ", msg, i); \ + } + +#define ADD_TO_MSG(...) \ + { \ + if (msg_len == 0) \ + { \ + msg_len += \ + snprintf(msg, LAGRAPH_MSG_LEN, \ + "LAGraph failure (file %s, line %d): ", \ + __FILE__, __LINE__); \ + } \ + if (msg_len < LAGRAPH_MSG_LEN) \ + { \ + msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ + __VA_ARGS__); \ + } \ + } + +#define ADD_INDEX_TO_ERROR_RULE(rule, i) \ + { \ + rule.len_indexes_str += snprintf( \ + rule.indexes_str + rule.len_indexes_str, \ + LAGRAPH_MSG_LEN - rule.len_indexes_str, \ + rule.count == 0 ? "%" PRId64 : ", %" PRId64, i); \ + rule.count++; \ + } + +GrB_Info LAGraph_CFPQ_core( + // Output + GrB_Matrix *outputs, // Array of matrices containing results. + // The size of the array must be equal to nonterms_count. + // + // outputs[k]: (i, j) contains a corresponding semiring value if and only if there is a path + // from node i to node j whose edge labels form a word + // derivable from the non-terminal 'k' of the specified CFG. + // Input + const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. + // The length of this array is equal to the count of + // terminals (terms_count). + // + // adj_matrices[t]: (i, j) == 1 if and only if there + // is an edge between nodes i and j with the label of + // the terminal corresponding to index 't' (where t is + // in the range [0, terms_count - 1]). + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + const CFL_Semiring *semiring, // The algebraic structure that defines operations on matrices for a specific problem + char *msg // Message string for error reporting. +) +{ + +#if LAGRAPH_SUITESPARSE + // Declare workspace and clear the msg string, if not NULL + GrB_Matrix *T; + bool *t_empty_flags = NULL; // t_empty_flags[i] == true <=> T[i] is empty + GrB_Matrix identity_matrix = NULL; + uint64_t *nnzs = NULL; + LG_CLEAR_MSG; + size_t msg_len = 0; // For error formatting + bool iso_flag = false; + GrB_Index *indexes = NULL; + + // Arrays for processing rules + size_t *eps_rules = NULL, eps_rules_count = 0; // [Variable -> eps] + size_t *term_rules = NULL, term_rules_count = 0; // [Variable -> term] + size_t *bin_rules = NULL, bin_rules_count = 0; // [Variable -> AB] + + GrB_Scalar false_scalar; + GRB_TRY(GrB_Scalar_new(&false_scalar, GrB_BOOL)); + GRB_TRY(GrB_Scalar_setElement_BOOL(false_scalar, false)); + + LG_TRY(LAGraph_Calloc((void **)&T, nonterms_count, sizeof(GrB_Matrix), msg)); + LG_TRY(LAGraph_Calloc((void **)&t_empty_flags, nonterms_count, sizeof(bool), msg)); + + LG_ASSERT_MSG(terms_count > 0, GrB_INVALID_VALUE, + "The number of terminals must be greater than zero."); + LG_ASSERT_MSG(nonterms_count > 0, GrB_INVALID_VALUE, + "The number of non-terminals must be greater than zero."); + LG_ASSERT_MSG(rules_count > 0, GrB_INVALID_VALUE, + "The number of rules must be greater than zero."); + LG_ASSERT_MSG(outputs != NULL, GrB_NULL_POINTER, "The outputs array cannot be null."); + LG_ASSERT_MSG(rules != NULL, GrB_NULL_POINTER, "The rules array cannot be null."); + LG_ASSERT_MSG(adj_matrices != NULL, GrB_NULL_POINTER, + "The adjacency matrices array cannot be null."); + LG_ASSERT_MSG(semiring != NULL, GrB_NULL_POINTER, + "The semiring cannot be null."); + + // Find null adjacency matrices + bool found_null = false; + for (int64_t i = 0; i < terms_count; i++) + { + if (adj_matrices[i] != NULL) + continue; + + if (!found_null) + { + ADD_TO_MSG("Adjacency matrices with these indexes are null: "); + ADD_TO_MSG("%" PRId64, i); + } + else + { + ADD_TO_MSG(" %" PRId64, i); + } + + found_null = true; + } + + if (found_null) + { + LG_FREE_ALL; + return GrB_NULL_POINTER; + } + + GrB_Index n; + GRB_TRY(GrB_Matrix_ncols(&n, adj_matrices[0])); + + // Create nonterms matrices + for (int64_t i = 0; i < nonterms_count; i++) + { + GRB_TRY(GrB_Matrix_new(&T[i], semiring->type, n, n)); + t_empty_flags[i] = true; + } + + LG_TRY(LAGraph_Calloc((void **)&eps_rules, rules_count, sizeof(size_t), msg)); + LG_TRY(LAGraph_Calloc((void **)&term_rules, rules_count, sizeof(size_t), msg)); + LG_TRY(LAGraph_Calloc((void **)&bin_rules, rules_count, sizeof(size_t), msg)); + + // Process rules + typedef struct + { + size_t count; + size_t len_indexes_str; + char indexes_str[LAGRAPH_MSG_LEN]; + } rule_error_s; + rule_error_s term_err = {0}; + rule_error_s nonterm_err = {0}; + rule_error_s invalid_err = {0}; + for (int64_t i = 0; i < rules_count; i++) + { + LAGraph_rule_WCNF rule = rules[i]; + + bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; + bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; + bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; + + // Check that all rules are well-formed + if (rule.nonterm < 0 || rule.nonterm >= nonterms_count) + { + ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); + } + + // [Variable -> eps] + if (is_rule_eps) + { + eps_rules[eps_rules_count++] = i; + + continue; + } + + // [Variable -> term] + if (is_rule_term) + { + term_rules[term_rules_count++] = i; + + if (rule.prod_A < -1 || rule.prod_A >= terms_count) + { + ADD_INDEX_TO_ERROR_RULE(term_err, i); + } + + continue; + } + + // [Variable -> A B] + if (is_rule_bin) + { + bin_rules[bin_rules_count++] = i; + + if (rule.prod_A < -1 || rule.prod_A >= nonterms_count || rule.prod_B < -1 || + rule.prod_B >= nonterms_count) + { + ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); + } + + continue; + } + + // [Variable -> _ B] + ADD_INDEX_TO_ERROR_RULE(invalid_err, i); + } + + if (term_err.count + nonterm_err.count + invalid_err.count > 0) + { + ADD_TO_MSG("Count of invalid rules: %" PRId64 ".\n", + (int64_t)(term_err.count + nonterm_err.count + invalid_err.count)); + + if (nonterm_err.count > 0) + { + ADD_TO_MSG("Non-terminals must be in range [0, nonterms_count). "); + ADD_TO_MSG("Indexes of invalid rules: %s\n", nonterm_err.indexes_str) + } + if (term_err.count > 0) + { + ADD_TO_MSG("Terminals must be in range [-1, nonterms_count). "); + ADD_TO_MSG("Indexes of invalid rules: %s\n", term_err.indexes_str) + } + if (invalid_err.count > 0) + { + ADD_TO_MSG("[Variable -> _ B] type of rule is not acceptable. "); + ADD_TO_MSG("Indexes of invalid rules: %.120s\n", invalid_err.indexes_str) + } + + LG_FREE_ALL; + return GrB_INVALID_VALUE; + } + + // Rule [Variable -> term] + for (int64_t i = 0; i < term_rules_count; i++) + { + LAGraph_rule_WCNF term_rule = rules[term_rules[i]]; + GrB_Index adj_matrix_nnz = 0; + GRB_TRY(GrB_Matrix_nvals(&adj_matrix_nnz, adj_matrices[term_rule.prod_A])); + + if (adj_matrix_nnz == 0) + { + continue; + } + GxB_eWiseUnion( + T[term_rule.nonterm], GrB_NULL, GrB_NULL, semiring->init_path, + T[term_rule.nonterm], semiring->bottom_scalar, adj_matrices[term_rule.prod_A], false_scalar, GrB_NULL); + + t_empty_flags[term_rule.nonterm] = false; + } + + GrB_Vector v_diag; + GRB_TRY(GrB_Vector_new(&v_diag, GrB_BOOL, n)); + GRB_TRY(GrB_Vector_assign_BOOL(v_diag, GrB_NULL, GrB_NULL, true, GrB_ALL, n, NULL)); + GRB_TRY(GrB_Matrix_diag(&identity_matrix, v_diag, 0)); + GRB_TRY(GrB_free(&v_diag)); + + // Rule [Variable -> eps] + for (int64_t i = 0; i < eps_rules_count; i++) + { + LAGraph_rule_WCNF eps_rule = rules[eps_rules[i]]; + GrB_BinaryOp acc_op = t_empty_flags[eps_rule.nonterm] ? GrB_NULL : semiring->add; + GxB_eWiseUnion( + T[eps_rule.nonterm], GrB_NULL, acc_op, semiring->init_path, + T[eps_rule.nonterm], semiring->bottom_scalar, identity_matrix, false_scalar, GrB_NULL); + + t_empty_flags[eps_rule.nonterm] = false; + } + + // Rule [Variable -> Variable1 Variable2] + LG_TRY(LAGraph_Calloc((void **)&nnzs, nonterms_count, sizeof(uint64_t), msg)); + bool changed = true; + while (changed) + { + changed = false; + for (int64_t i = 0; i < bin_rules_count; i++) + { + LAGraph_rule_WCNF bin_rule = rules[bin_rules[i]]; + + // If one of matrices is empty then their product will be empty + if (t_empty_flags[bin_rule.prod_A] || t_empty_flags[bin_rule.prod_B]) + { + continue; + } + + GrB_BinaryOp acc_op = t_empty_flags[bin_rule.nonterm] ? GrB_NULL : semiring->add; + GRB_TRY(GrB_mxm(T[bin_rule.nonterm], GrB_NULL, acc_op, + semiring->semiring, T[bin_rule.prod_A], T[bin_rule.prod_B], + GrB_NULL)) + + GrB_Index new_nnz; + GRB_TRY(GrB_Matrix_nvals(&new_nnz, T[bin_rule.nonterm])); + if (new_nnz != 0) + t_empty_flags[bin_rule.nonterm] = false; + + changed = changed || (nnzs[bin_rule.nonterm] != new_nnz); + nnzs[bin_rule.nonterm] = new_nnz; + } + } + + for (int64_t i = 0; i < nonterms_count; i++) + { + outputs[i] = T[i]; + } + + LG_FREE_WORK; + return GrB_SUCCESS; +#else + return (GrB_NOT_IMPLEMENTED); +#endif +} diff --git a/experimental/test/test_CFL_extract_single_path.c b/experimental/test/test_CFL_extract_single_path.c new file mode 100644 index 0000000000..2a3777841e --- /dev/null +++ b/experimental/test/test_CFL_extract_single_path.c @@ -0,0 +1,997 @@ +#include +#include +#include +#include +#include +#include + +#define run_aux_algorithm() \ + LAGraph_CFL_single_path(outputs, adj_matrices, grammar.terms_count, \ + grammar.nonterms_count, grammar.rules, grammar.rules_count, \ + msg) + +#define run_algorithm() \ + LAGraph_CFL_extract_single_path(&path, start, end, 0, adj_matrices, outputs, grammar.terms_count, \ + grammar.nonterms_count, grammar.rules, grammar.rules_count, \ + msg) + +#define check_error(error) \ + { \ + retval = run_algorithm(); \ + TEST_CHECK(retval == error); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ + } + +// Check the path through the string +#define check_result1(expected_ret, result) \ + { \ + retval = run_algorithm(); \ + TEST_CHECK(retval == expected_ret); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ + char *expected = path_to_str(); \ + TEST_CHECK(strcmp(result, expected) == 0); \ + TEST_MSG("Wrong result. Actual: %s", expected); \ + LAGraph_Free((void **)&expected, msg); \ + } + +// Check the path according to its type +#define check_result2(expected_path) \ + { \ + retval = run_algorithm(); \ + if (expected_path == non_exist) \ + { \ + TEST_CHECK(retval == GrB_NO_VALUE); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ + TEST_CHECK(check_empty_path()); \ + TEST_MSG("Wrong result"); \ + } \ + else if (expected_path == empty) \ + { \ + TEST_CHECK(retval == GrB_SUCCESS); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ + TEST_CHECK(check_empty_path()); \ + TEST_MSG("Wrong result"); \ + } \ + else \ + { \ + TEST_CHECK(retval == GrB_SUCCESS); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ + TEST_CHECK(check_non_empty_path()); \ + TEST_MSG("Wrong result"); \ + } \ + } + +typedef struct +{ + size_t nonterms_count; + size_t terms_count; + size_t rules_count; + LAGraph_rule_WCNF *rules; +} grammar_t; + +GrB_Matrix *adj_matrices = NULL; +int n_adj_matrices = 0; +GrB_Matrix *outputs = NULL; +grammar_t grammar = {0, 0, 0, NULL}; +Path path; +char msg[LAGRAPH_MSG_LEN]; +// GrB_Type PI_type = NULL; + +typedef enum // Path type +{ + empty = 0, // Empty path - through a rule with eps + non_empty = 1, // Path with non-zero length + non_exist = 2, // Path does not exist +} Type_of_path; + +void setup() +{ + LAGraph_Init(msg); + // GrB_Type_new(&PI_type, sizeof(PathIndex)); +} + +void teardown(void) { LAGraph_Finalize(msg); } + +void init_outputs() +{ + LAGraph_Calloc((void **)&outputs, + grammar.nonterms_count, sizeof(GrB_Matrix), msg); +} + +bool check_empty_path() +{ + return path.len == 0 && path.path == NULL; +} + +bool check_non_empty_path() +{ + if (path.len == 0) + { + return false; + } + for (size_t i = 0; i < path.len; i++) + { + Edge cur_edge = path.path[i]; + bool edge_exist; + if (GrB_Matrix_extractElement_BOOL(&edge_exist, adj_matrices[cur_edge.label], cur_edge.start, cur_edge.end) != GrB_SUCCESS) + { + return false; + } + } + return true; +} + +char *path_to_str() +{ + char *result_str = NULL; + // 15 - size of "%ld->(%d)->%ld " + // 15 - size of "len: %zu path: " + // we need 16 + len * 15 + LAGraph_Malloc((void **)&result_str, 15 + path.len * 15, sizeof(char), msg); + result_str[0] = '\0'; + + sprintf(result_str + strlen(result_str), "len: %zu path: ", path.len); + + if (path.len > 0) + { + for (size_t i = 0; i < path.len; i++) + { + sprintf(result_str + strlen(result_str), "%" PRIu64 "->(%" PRId32 ")->%" PRIu64 " ", path.path[i].start, path.path[i].label, path.path[i].end); + } + } + + return result_str; +} + +void free_workspace() +{ + + if (adj_matrices != NULL) + { + for (size_t i = 0; i < n_adj_matrices; i++) + { + GrB_free(&adj_matrices[i]); + } + } + LAGraph_Free((void **)&adj_matrices, msg); + + if (outputs != NULL) + { + for (size_t i = 0; i < grammar.nonterms_count; i++) + { + GrB_free(&outputs[i]); + } + } + LAGraph_Free((void **)&outputs, msg); + + LAGraph_Free((void **)&grammar.rules, msg); + grammar = (grammar_t){0, 0, 0, NULL}; +} + +//==================== +// Grammars +//==================== + +// S -> aSb | ab in WCNF +// +// Terms: [0 a] [1 b] +// Nonterms: [0 S] [1 A] [2 B] [3 C] +// S -> AB [0 1 2 0] +// S -> AC [0 1 3 0] +// C -> SB [3 0 2 0] +// A -> a [1 0 -1 0] +// B -> b [2 1 -1 0] +void init_grammar_aSb() +{ + LAGraph_rule_WCNF *rules = NULL; + LAGraph_Calloc((void **)&rules, 5, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 1, 3, 0}; + rules[2] = (LAGraph_rule_WCNF){3, 0, 2, 0}; + rules[3] = (LAGraph_rule_WCNF){1, 0, -1, 0}; + rules[4] = (LAGraph_rule_WCNF){2, 1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 4, .terms_count = 2, .rules_count = 5, .rules = rules}; +} + +// S -> aS | a | eps in WCNF +// +// Terms: [0 a] +// Nonterms: [0 S] +// S -> SS [0 0 0 0] +// S -> a [0 0 -1 0] +// S -> eps [0 -1 -1 0] +void init_grammar_aS() +{ + LAGraph_rule_WCNF *rules = NULL; + LAGraph_Calloc((void **)&rules, 3, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 0, 0, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 0, -1, 0}; + rules[2] = (LAGraph_rule_WCNF){0, -1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 1, .terms_count = 1, .rules_count = 3, .rules = rules}; +} + +// Complex grammar +// aaaabbbb or aaabbb +// +// Terms: [0 a] [1 b] +// Nonterms: [0 S] [n Sn] +// S -> S1 S2 [0 1 2 0] +// S -> S15 S16 [0 15 16 0] +// S1 -> S3 S4 [1 3 4 0] +// S2 -> S5 S6 [2 5 6 0] +// S3 -> S7 S8 [3 7 8 0] +// S4 -> S9 S10 [4 9 10 0] +// S5 -> S11 S12 [5 11 12 0] +// S6 -> S13 S14 [6 13 14 0] +// S16 -> S17 S18 [16 17 18 0] +// S17 -> S19 S20 [17 19 20 0] +// S18 -> S21 S22 [18 21 22 0] +// S22 -> S23 S24 [22 23 24 0] +// S7 -> a [7 0 -1 0] +// S8 -> a [8 0 -1 0] +// S9 -> a [9 0 -1 0] +// S10 -> a [10 0 -1 0] +// S11 -> b [11 1 -1 0] +// S12 -> b [12 1 -1 0] +// S13 -> b [13 1 -1 0] +// S14 -> b [14 1 -1 0] +// S15 -> a [15 0 -1 0] +// S19 -> a [19 0 -1 0] +// S20 -> a [20 0 -1 0] +// S21 -> b [21 1 -1 0] +// S23 -> b [23 1 -1 0] +// S24 -> b [24 1 -1 0] +void init_grammar_complex() +{ + LAGraph_rule_WCNF *rules = NULL; + LAGraph_Calloc((void **)&rules, 26, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 15, 16, 0}; + rules[2] = (LAGraph_rule_WCNF){1, 3, 4, 0}; + rules[3] = (LAGraph_rule_WCNF){2, 5, 6, 0}; + rules[4] = (LAGraph_rule_WCNF){3, 7, 8, 0}; + rules[5] = (LAGraph_rule_WCNF){4, 9, 10, 0}; + rules[6] = (LAGraph_rule_WCNF){5, 11, 12, 0}; + rules[7] = (LAGraph_rule_WCNF){6, 13, 14, 0}; + rules[8] = (LAGraph_rule_WCNF){16, 17, 18, 0}; + rules[9] = (LAGraph_rule_WCNF){17, 19, 20, 0}; + rules[10] = (LAGraph_rule_WCNF){18, 21, 22, 0}; + rules[11] = (LAGraph_rule_WCNF){22, 23, 24, 0}; + rules[12] = (LAGraph_rule_WCNF){7, 0, -1, 0}; + rules[13] = (LAGraph_rule_WCNF){8, 0, -1, 0}; + rules[14] = (LAGraph_rule_WCNF){9, 0, -1, 0}; + rules[15] = (LAGraph_rule_WCNF){10, 0, -1, 0}; + rules[16] = (LAGraph_rule_WCNF){11, 1, -1, 0}; + rules[17] = (LAGraph_rule_WCNF){12, 1, -1, 0}; + rules[18] = (LAGraph_rule_WCNF){13, 1, -1, 0}; + rules[19] = (LAGraph_rule_WCNF){14, 1, -1, 0}; + rules[20] = (LAGraph_rule_WCNF){15, 0, -1, 0}; + rules[21] = (LAGraph_rule_WCNF){19, 0, -1, 0}; + rules[22] = (LAGraph_rule_WCNF){20, 0, -1, 0}; + rules[23] = (LAGraph_rule_WCNF){21, 1, -1, 0}; + rules[24] = (LAGraph_rule_WCNF){23, 1, -1, 0}; + rules[25] = (LAGraph_rule_WCNF){24, 1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 25, .terms_count = 2, .rules_count = 26, .rules = rules}; +} + +//==================== +// Graphs +//==================== + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 0 +// 0 -b-> 3 +// 3 -b-> 0 +void init_graph_double_cycle() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 4, 4)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 4, 4)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 4, 4); + // } +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 0 +void init_graph_one_cycle() +{ + LAGraph_Calloc((void **)&adj_matrices, 1, sizeof(GrB_Matrix), msg); + n_adj_matrices = 1; + + GrB_Matrix adj_matrix_a; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); + + adj_matrices[0] = adj_matrix_a; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); + // } +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 3 +// 3 -a-> 4 +// 3 -b-> 5 +// 4 -b-> 3 +// 5 -b-> 6 +// 6 -b-> 7 +void init_graph_1() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 8, 8)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 8, 8)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 3)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 4)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 5)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 4, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 6)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 7)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 8, 8); + // } +} + +// Graph: +// +// 0 -a-> 2 +// 1 -a-> 2 +// 3 -a-> 5 +// 4 -a-> 5 +// 2 -a-> 6 +// 5 -a-> 6 +// 2 -b-> 0 +// 2 -b-> 1 +// 5 -b-> 3 +// 5 -b-> 4 +// 6 -b-> 2 +// 6 -b-> 5 +void init_graph_tree() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 7, 7)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 7, 7)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 5)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 4, 5)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 6)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 5, 6)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 1)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 4)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 2)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 5)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 7, 7); + // } +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -b-> 3 +// 3 -b-> 4 +void init_graph_line() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 5, 5); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 5, 5); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 4)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 5, 5); + // } +} + +// Graph: +// +// 0 -a-> 0 +// 0 -b-> 1 +// 1 -c-> 2 +void init_graph_2() +{ + LAGraph_Calloc((void **)&adj_matrices, 3, sizeof(GrB_Matrix), msg); + n_adj_matrices = 3; + + GrB_Matrix adj_matrix_a, adj_matrix_b, adj_matrix_c; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 3, 3); + GrB_Matrix_new(&adj_matrix_c, GrB_BOOL, 3, 3); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_c, true, 1, 2)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + adj_matrices[2] = adj_matrix_c; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); + // } +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 0 +// 0 -b-> 0 +void init_graph_3() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); + // } +} + +// Graph: +// +// 0 -b-> 1 +// 1 -b-> 0 +void init_graph_4() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 1, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); + // } +} + +//===================== +// Tests full result +//===================== + +void test_CFL_extract_single_path_two_cycle(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); + int expected_ret[16] = {GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_SUCCESS, GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_SUCCESS, GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_NO_VALUE, GrB_NO_VALUE}; + char *expected_path[16] = {"len: 12 path: 0->(0)->1 1->(0)->2 2->(0)->0 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 ", + "len: 0 path: ", + "len: 0 path: ", + "len: 6 path: 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 ", + "len: 4 path: 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 ", + "len: 0 path: ", + "len: 0 path: ", + "len: 10 path: 1->(0)->2 2->(0)->0 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 0->(1)->3 ", + "len: 8 path: 2->(0)->0 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 ", + "len: 0 path: ", + "len: 0 path: ", + "len: 2 path: 2->(0)->0 0->(1)->3 ", + "len: 0 path: ", + "len: 0 path: ", + "len: 0 path: ", + "len: 0 path: "}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 4; start++) + { + for (GrB_Index end = 0; end < 4; end++) + { + check_result1(expected_ret[start * 4 + end], expected_path[start * 4 + end]); + if (path.len > 0) + { + LAGraph_Free((void **)&path.path, msg); + } + } + } + free_workspace(); + teardown(); +#endif +} + +//========================================== +// Tests that the path exists in the graph +//========================================== + +void test_CFL_extract_single_path_cycle(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_outputs(); + init_graph_one_cycle(); + int expected[9] = {empty, non_empty, non_empty, + non_empty, empty, non_empty, + non_empty, non_empty, empty}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 3; start++) + { + for (GrB_Index end = 0; end < 3; end++) + { + check_result2(expected[start * 3 + end]); + if (path.len > 0) + { + LAGraph_Free((void **)&path.path, msg); + } + } + } + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_labels_more_than_nonterms(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_2(); + + int expected[9] = {non_exist, non_empty, non_exist, + non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 3; start++) + { + for (GrB_Index end = 0; end < 3; end++) + { + check_result2(expected[start * 3 + end]); + if (path.len > 0) + { + LAGraph_Free((void **)&path.path, msg); + } + } + } + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_complex_grammar(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_complex(); + init_outputs(); + init_graph_1(); + int expected[64] = {non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_empty, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_empty, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 8; start++) + { + for (GrB_Index end = 0; end < 8; end++) + { + check_result2(expected[start * 8 + end]); + if (path.len > 0) + { + LAGraph_Free((void **)&path.path, msg); + } + } + } + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_tree(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_tree(); + + int expected[49] = {non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, + non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, + non_exist, non_exist, non_empty, non_exist, non_exist, non_empty, non_exist, + non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, + non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, + non_exist, non_exist, non_empty, non_exist, non_exist, non_empty, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 7; start++) + { + for (GrB_Index end = 0; end < 7; end++) + { + check_result2(expected[start * 7 + end]); + if (path.len > 0) + { + LAGraph_Free((void **)&path.path, msg); + } + } + } + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_line(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_line(); + int expected[25] = {non_exist, non_exist, non_exist, non_exist, non_empty, + non_exist, non_exist, non_exist, non_empty, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist, + non_exist, non_exist, non_exist, non_exist, non_exist}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 5; start++) + { + for (GrB_Index end = 0; end < 5; end++) + { + check_result2(expected[start * 5 + end]); + if (path.len > 0) + { + LAGraph_Free((void **)&path.path, msg); + } + } + } + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_two_nodes_cycle(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_3(); + int expected[4] = {non_empty, non_exist, + non_empty, non_exist}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 2; start++) + { + for (GrB_Index end = 0; end < 2; end++) + { + check_result2(expected[start * 2 + end]); + if (path.len > 0) + { + LAGraph_Free((void **)&path.path, msg); + } + } + } + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_with_empty_adj_matrix(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_outputs(); + init_graph_4(); + + int expected[4] = {empty, non_exist, + non_exist, empty}; + OK(run_aux_algorithm()); + for (GrB_Index start = 0; start < 2; start++) + { + for (GrB_Index end = 0; end < 2; end++) + { + check_result2(expected[start * 2 + end]); + LAGraph_Free((void **)&path.path, msg); + } + } + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_inappropriate_grammar(void) +{ +#if LAGRAPH_SUITESPARSE + + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); + OK(run_aux_algorithm()); + // Random path + GrB_Index start = 0; + GrB_Index end = 0; + + LAGraph_Free((void **)&grammar.rules, msg); + grammar = (grammar_t){0, 0, 0, NULL}; + init_grammar_aS(); + check_result2(non_exist); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_inappropriate_graph(void) +{ +#if LAGRAPH_SUITESPARSE + + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); + OK(run_aux_algorithm()); + // Random path + GrB_Index start = 0; + GrB_Index end = 0; + + GrB_free(&adj_matrices[0]); + GrB_free(&adj_matrices[1]); + LAGraph_Free((void **)&adj_matrices, msg); + + init_graph_4(); + check_result2(non_exist); + + free_workspace(); + teardown(); +#endif +} + +//=========================== +// Tests with invalid result +//=========================== + +void test_CFL_extract_single_path_invalid_rules(void) +{ +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); + OK(run_aux_algorithm()); + // Random path + GrB_Index start = 1; + GrB_Index end = 2; + + // Rule [Variable -> _ B] + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = -1, .prod_B = 1, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [_ -> A B] + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = -1, .prod_A = 1, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [C -> A B], where C >= nonterms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 10, .prod_A = 1, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [S -> A B], where A >= nonterms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [C -> t], where t >= terms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = -1, .index = 0}; + check_error(GrB_INVALID_VALUE); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_null_pointers(void) +{ +#if LAGRAPH_SUITESPARSE + + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); + OK(run_aux_algorithm()); + // Random path + GrB_Index start = 1; + GrB_Index end = 2; + + // adj_matrices[0] = NULL; + // adj_matrices[1] = NULL; + GrB_free(&adj_matrices[0]); + GrB_free(&adj_matrices[1]); + + check_error(GrB_NULL_POINTER); + + // adj_matrices = NULL; + LAGraph_Free((void **)&adj_matrices, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); + + // outputs = NULL; + if (outputs != NULL) + { + for (size_t i = 0; i < grammar.nonterms_count; i++) + { + GrB_free(&outputs[i]); + } + } + LAGraph_Free((void **)&outputs, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); + + // grammar.rules = NULL; + LAGraph_Free((void **)&grammar.rules, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_extract_single_path_vertex_out_the_graph(void) +{ +#if LAGRAPH_SUITESPARSE + + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_outputs(); + init_graph_double_cycle(); // 4 * 4 + OK(run_aux_algorithm()); + + GrB_Index start = 4; // Vertex outside the graph + GrB_Index end = 2; + + check_error(GrB_INVALID_INDEX); + + free_workspace(); + teardown(); +#endif +} + +TEST_LIST = { + {"CFL_extract_single_path_two_cycle", test_CFL_extract_single_path_two_cycle}, + {"CFL_extract_single_path_cycle", test_CFL_extract_single_path_cycle}, + {"CFL_extract_single_path_labels_more_than_nonterms", test_CFL_extract_single_path_labels_more_than_nonterms}, + {"CFL_extract_single_path_complex_grammar", test_CFL_extract_single_path_complex_grammar}, + {"CFL_extract_single_path_tree", test_CFL_extract_single_path_tree}, + {"CFL_extract_single_path_line", test_CFL_extract_single_path_line}, + {"CFL_extract_single_path_two_nodes_cycle", test_CFL_extract_single_path_two_nodes_cycle}, + {"CFL_extract_single_path_with_empty_adj_matrix", test_CFL_extract_single_path_with_empty_adj_matrix}, + {"CFL_extract_single_path_inappropriate_grammar", test_CFL_extract_single_path_inappropriate_grammar}, + {"CFL_extract_single_path_inappropriate_graph", test_CFL_extract_single_path_inappropriate_graph}, + {"CFL_extract_single_path_invalid_rules", test_CFL_extract_single_path_invalid_rules}, + {"CFL_extract_single_path_null_pointers", test_CFL_extract_single_path_null_pointers}, + {"CFL_extract_single_path_vertex_out_the_graph", test_CFL_extract_single_path_vertex_out_the_graph}, + {NULL, NULL}}; diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_single_path.c similarity index 58% rename from experimental/test/test_CFL_AllPaths.c rename to experimental/test/test_CFL_single_path.c index d7d77c0041..4c0acb9690 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_single_path.c @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// LAGraph/experimental/test/LAGraph_CFL_reachability.c: test cases for Context-Free -// Language Reachability Matrix-Based Algorithm -//------------------------------------------------------------------------------ -// -// LAGraph, (c) 2019-2024 by The LAGraph Contributors, All Rights Reserved. -// SPDX-License-Identifier: BSD-2-Clause - -// Contributed by Ilhom Kombaev, Semyon Grigoriev, St. Petersburg State University. - -//------------------------------------------------------------------------------ - #include #include #include @@ -17,27 +5,28 @@ #include #include -#define run_algorithm() \ - LAGraph_CFL_reachability(outputs, adj_matrices, grammar.terms_count, \ - grammar.nonterms_count, grammar.rules, grammar.rules_count, \ - msg) +#define run_algorithm() \ + LAGraph_CFL_single_path(outputs, adj_matrices, grammar.terms_count, \ + grammar.nonterms_count, grammar.rules, grammar.rules_count, \ + msg) -#define check_error(error) \ - { \ - retval = run_algorithm(); \ - TEST_CHECK(retval == error); \ - TEST_MSG("retval = %d (%s)", retval, msg); \ +#define check_error(error) \ + { \ + retval = run_algorithm(); \ + TEST_CHECK(retval == error); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ } -#define check_result(result) \ - { \ - char *expected = output_to_str(0); \ - TEST_CHECK(strcmp(result, expected) == 0); \ - TEST_MSG("Wrong result. Actual: %s", expected); \ - LAGraph_Free ((void **) &expected, msg); \ +#define check_result(result) \ + { \ + char *expected = output_to_str(0); \ + TEST_CHECK(strcmp(result, expected) == 0); \ + TEST_MSG("Wrong result. Actual: %s", expected); \ + LAGraph_Free((void **)&expected, msg); \ } -typedef struct { +typedef struct +{ size_t nonterms_count; size_t terms_count; size_t rules_count; @@ -45,61 +34,70 @@ typedef struct { } grammar_t; GrB_Matrix *adj_matrices = NULL; -int n_adj_matrices = 0 ; +int n_adj_matrices = 0; GrB_Matrix *outputs = NULL; grammar_t grammar = {0, 0, 0, NULL}; char msg[LAGRAPH_MSG_LEN]; +// GrB_Type PI_type = NULL; -void setup() { LAGraph_Init(msg); } +void setup() +{ + LAGraph_Init(msg); + // GrB_Type_new(&PI_type, sizeof(PathIndex)); +} void teardown(void) { LAGraph_Finalize(msg); } void init_outputs() { - LAGraph_Calloc ((void **) &outputs, - grammar.nonterms_count, sizeof(GrB_Matrix), msg) ; + LAGraph_Calloc((void **)&outputs, + grammar.nonterms_count, sizeof(GrB_Matrix), msg); } -char *output_to_str(size_t nonterm) { +char *output_to_str(size_t nonterm) +{ GrB_Index nnz = 0; OK(GrB_Matrix_nvals(&nnz, outputs[nonterm])); - GrB_Index *row = NULL ; - GrB_Index *col = NULL ; - bool *val = NULL ; - LAGraph_Malloc ((void **) &row, nnz, sizeof (GrB_Index), msg) ; - LAGraph_Malloc ((void **) &col, nnz, sizeof (GrB_Index), msg) ; - LAGraph_Malloc ((void **) &val, nnz, sizeof (GrB_Index), msg) ; - - OK(GrB_Matrix_extractTuples(row, col, val, &nnz, outputs[nonterm])); - - // 11 - size of " (%ld, %ld)" - char *result_str = NULL ; - LAGraph_Malloc ((void **) &result_str, 11*nnz, sizeof (char), msg) ; - + GrB_Index *row = NULL; + GrB_Index *col = NULL; + PathIndex *val = NULL; + LAGraph_Malloc((void **)&row, nnz, sizeof(GrB_Index), msg); + LAGraph_Malloc((void **)&col, nnz, sizeof(GrB_Index), msg); + LAGraph_Malloc((void **)&val, nnz, sizeof(PathIndex), msg); + + OK(GrB_Matrix_extractTuples_UDT(row, col, val, &nnz, outputs[nonterm])); + + char *result_str = NULL; + // 33 - size of "(%ld, %ld): middle=%ld height-%d " + LAGraph_Malloc((void **)&result_str, nnz * 33, sizeof(char), msg); result_str[0] = '\0'; - for (size_t i = 0; i < nnz; i++) { - sprintf(result_str + strlen(result_str), i == 0 ? - "(%" PRIu64 ", %" PRIu64 ")" : " (%" PRIu64 ", %" PRIu64 ")", - row[i], col[i]); + + for (size_t i = 0; i < nnz; i++) + { + sprintf(result_str + strlen(result_str), "(%" PRIu64 ", %" PRIu64 "): middle=%" PRIu64 " height=%" PRId32 " ", + row[i], col[i], + val[i].middle, + val[i].height); } - LAGraph_Free ((void **) &row, msg); - LAGraph_Free ((void **) &col, msg); - LAGraph_Free ((void **) &val, msg); + LAGraph_Free((void **)&row, msg); + LAGraph_Free((void **)&col, msg); + LAGraph_Free((void **)&val, msg); return result_str; } -void free_workspace() { +void free_workspace() +{ if (adj_matrices != NULL) { - for (size_t i = 0; i < n_adj_matrices ; i++) + for (size_t i = 0; i < n_adj_matrices; i++) { GrB_free(&adj_matrices[i]); } } - LAGraph_Free ((void **) &adj_matrices, msg); + LAGraph_Free((void **)&adj_matrices, msg); if (outputs != NULL) { @@ -108,9 +106,9 @@ void free_workspace() { GrB_free(&outputs[i]); } } - LAGraph_Free ((void **) &outputs, msg); + LAGraph_Free((void **)&outputs, msg); - LAGraph_Free ((void **) &grammar.rules, msg); + LAGraph_Free((void **)&grammar.rules, msg); grammar = (grammar_t){0, 0, 0, NULL}; } @@ -127,9 +125,10 @@ void free_workspace() { // C -> SB [3 0 2 0] // A -> a [1 0 -1 0] // B -> b [2 1 -1 0] -void init_grammar_aSb() { - LAGraph_rule_WCNF *rules = NULL ; - LAGraph_Calloc ((void **) &rules, 5, sizeof(LAGraph_rule_WCNF), msg); +void init_grammar_aSb() +{ + LAGraph_rule_WCNF *rules = NULL; + LAGraph_Calloc((void **)&rules, 5, sizeof(LAGraph_rule_WCNF), msg); rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; rules[1] = (LAGraph_rule_WCNF){0, 1, 3, 0}; @@ -148,9 +147,10 @@ void init_grammar_aSb() { // S -> SS [0 0 0 0] // S -> a [0 0 -1 0] // S -> eps [0 -1 -1 0] -void init_grammar_aS() { - LAGraph_rule_WCNF *rules = NULL ; - LAGraph_Calloc ((void **) &rules, 3, sizeof(LAGraph_rule_WCNF), msg); +void init_grammar_aS() +{ + LAGraph_rule_WCNF *rules = NULL; + LAGraph_Calloc((void **)&rules, 3, sizeof(LAGraph_rule_WCNF), msg); rules[0] = (LAGraph_rule_WCNF){0, 0, 0, 0}; rules[1] = (LAGraph_rule_WCNF){0, 0, -1, 0}; @@ -191,9 +191,10 @@ void init_grammar_aS() { // S21 -> b [21 1 -1 0] // S23 -> b [23 1 -1 0] // S24 -> b [24 1 -1 0] -void init_grammar_complex() { - LAGraph_rule_WCNF *rules = NULL ; - LAGraph_Calloc ((void **) &rules, 26, sizeof(LAGraph_rule_WCNF), msg); +void init_grammar_complex() +{ + LAGraph_rule_WCNF *rules = NULL; + LAGraph_Calloc((void **)&rules, 26, sizeof(LAGraph_rule_WCNF), msg); rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; rules[1] = (LAGraph_rule_WCNF){0, 15, 16, 0}; @@ -237,9 +238,10 @@ void init_grammar_complex() { // 2 -a-> 0 // 0 -b-> 3 // 3 -b-> 0 -void init_graph_double_cycle() { - LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 2 ; +void init_graph_double_cycle() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; GrB_Matrix adj_matrix_a, adj_matrix_b; OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 4, 4)); @@ -254,6 +256,36 @@ void init_graph_double_cycle() { adj_matrices[0] = adj_matrix_a; adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 4, 4); + // } +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 0 +void init_graph_one_cycle() +{ + LAGraph_Calloc((void **)&adj_matrices, 1, sizeof(GrB_Matrix), msg); + n_adj_matrices = 1; + + GrB_Matrix adj_matrix_a; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); + + adj_matrices[0] = adj_matrix_a; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); + // } } // Graph: @@ -266,9 +298,10 @@ void init_graph_double_cycle() { // 4 -b-> 3 // 5 -b-> 6 // 6 -b-> 7 -void init_graph_1() { - LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 2 ; +void init_graph_1() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; GrB_Matrix adj_matrix_a, adj_matrix_b; OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 8, 8)); @@ -286,6 +319,11 @@ void init_graph_1() { adj_matrices[0] = adj_matrix_a; adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 8, 8); + // } } // Graph: @@ -302,9 +340,10 @@ void init_graph_1() { // 5 -b-> 4 // 6 -b-> 2 // 6 -b-> 5 -void init_graph_tree() { - LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 2 ; +void init_graph_tree() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; GrB_Matrix adj_matrix_a, adj_matrix_b; OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 7, 7)); @@ -326,36 +365,23 @@ void init_graph_tree() { adj_matrices[0] = adj_matrix_a; adj_matrices[1] = adj_matrix_b; -} -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -a-> 0 -void init_graph_one_cycle() { - LAGraph_Calloc ((void **) &adj_matrices, 1, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 1 ; - - GrB_Matrix adj_matrix_a; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); - - adj_matrices[0] = adj_matrix_a; + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 7, 7); + // } } // Graph: - +// // 0 -a-> 1 // 1 -a-> 2 // 2 -b-> 3 // 3 -b-> 4 -void init_graph_line() { - LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 2 ; +void init_graph_line() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; GrB_Matrix adj_matrix_a, adj_matrix_b; GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 5, 5); @@ -369,16 +395,22 @@ void init_graph_line() { adj_matrices[0] = adj_matrix_a; adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 5, 5); + // } } // Graph: - +// // 0 -a-> 0 // 0 -b-> 1 // 1 -c-> 2 -void init_graph_2() { - LAGraph_Calloc ((void **) &adj_matrices, 3, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 3 ; +void init_graph_2() +{ + LAGraph_Calloc((void **)&adj_matrices, 3, sizeof(GrB_Matrix), msg); + n_adj_matrices = 3; GrB_Matrix adj_matrix_a, adj_matrix_b, adj_matrix_c; GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); @@ -392,16 +424,22 @@ void init_graph_2() { adj_matrices[0] = adj_matrix_a; adj_matrices[1] = adj_matrix_b; adj_matrices[2] = adj_matrix_c; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); + // } } // Graph: - +// // 0 -a-> 1 // 1 -a-> 0 // 0 -b-> 0 -void init_graph_3() { - LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 2 ; +void init_graph_3() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; GrB_Matrix adj_matrix_a, adj_matrix_b; GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); @@ -413,15 +451,21 @@ void init_graph_3() { adj_matrices[0] = adj_matrix_a; adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); + // } } // Graph: - +// // 0 -b-> 1 // 1 -b-> 0 -void init_graph_4() { - LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; - n_adj_matrices = 2 ; +void init_graph_4() +{ + LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); + n_adj_matrices = 2; GrB_Matrix adj_matrix_a, adj_matrix_b; GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); @@ -432,143 +476,190 @@ void init_graph_4() { adj_matrices[0] = adj_matrix_a; adj_matrices[1] = adj_matrix_b; + + // for (int64_t i = 0; i < grammar.nonterms_count; i++) + // { + // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); + // } } //==================== // Tests with valid result //==================== -void test_CFL_reachability_cycle(void) { +void test_CFL_single_path_cycle(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aS(); + init_outputs(); init_graph_one_cycle(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2)"); + check_result("(0, 0): middle=0 height=1 " + "(0, 1): middle=0 height=1 " + "(0, 2): middle=1 height=2 " + "(1, 0): middle=2 height=2 " + "(1, 1): middle=1 height=1 " + "(1, 2): middle=1 height=1 " + "(2, 0): middle=2 height=1 " + "(2, 1): middle=0 height=2 " + "(2, 2): middle=2 height=1 "); free_workspace(); teardown(); #endif } -void test_CFL_reachability_two_cycle(void) { +void test_CFL_single_path_two_cycle(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aSb(); + init_outputs(); init_graph_double_cycle(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 0) (0, 3) (1, 0) (1, 3) (2, 0) (2, 3)"); + check_result("(0, 0): middle=1 height=12 " + "(0, 3): middle=1 height=6 " + "(1, 0): middle=2 height=4 " + "(1, 3): middle=2 height=10 " + "(2, 0): middle=0 height=8 " + "(2, 3): middle=0 height=2 "); free_workspace(); teardown(); #endif } -void test_CFL_reachability_labels_more_than_nonterms(void) { +void test_CFL_single_path_labels_more_than_nonterms(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aSb(); + init_outputs(); init_graph_2(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 1)"); + check_result("(0, 1): middle=0 height=2 "); free_workspace(); teardown(); #endif } -void test_CFL_reachability_complex_grammar(void) { +void test_CFL_single_path_complex_grammar(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_complex(); + init_outputs(); init_graph_1(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 7) (1, 6)"); + check_result("(0, 7): middle=4 height=4 " + "(1, 6): middle=2 height=5 "); free_workspace(); teardown(); #endif } -void test_CFL_reachability_tree(void) { +void test_CFL_single_path_tree(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aSb(); + init_outputs(); init_graph_tree(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 0) (0, 1) (0, 3) (0, 4) (1, 0) (1, 1) (1, 3) (1, 4) (2, 2) (2, 5) " - "(3, 0) (3, 1) (3, 3) (3, 4) (4, 0) (4, 1) (4, 3) (4, 4) (5, 2) (5, 5)"); - + check_result("(0, 0): middle=2 height=2 " + "(0, 1): middle=2 height=2 " + "(0, 3): middle=2 height=4 " + "(0, 4): middle=2 height=4 " + "(1, 0): middle=2 height=2 " + "(1, 1): middle=2 height=2 " + "(1, 3): middle=2 height=4 " + "(1, 4): middle=2 height=4 " + "(2, 2): middle=6 height=2 " + "(2, 5): middle=6 height=2 " + "(3, 0): middle=5 height=4 " + "(3, 1): middle=5 height=4 " + "(3, 3): middle=5 height=2 " + "(3, 4): middle=5 height=2 " + "(4, 0): middle=5 height=4 " + "(4, 1): middle=5 height=4 " + "(4, 3): middle=5 height=2 " + "(4, 4): middle=5 height=2 " + "(5, 2): middle=6 height=2 " + "(5, 5): middle=6 height=2 "); free_workspace(); teardown(); #endif } -void test_CFL_reachability_line(void) { +void test_CFL_single_path_line(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aSb(); + init_outputs(); init_graph_line(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 4) (1, 3)"); + check_result("(0, 4): middle=1 height=4 " + "(1, 3): middle=2 height=2 "); free_workspace(); teardown(); #endif } -void test_CFL_reachability_two_nodes_cycle(void) { +void test_CFL_single_path_two_nodes_cycle(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aSb(); + init_outputs(); init_graph_3(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 0) (1, 0)"); + check_result("(0, 0): middle=1 height=4 " + "(1, 0): middle=0 height=2 "); free_workspace(); teardown(); #endif } -void test_CFL_reachability_with_empty_adj_matrix(void) { +void test_CFL_single_path_with_empty_adj_matrix(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aS(); + init_outputs(); init_graph_4(); - init_outputs() ; OK(run_algorithm()); - check_result("(0, 0) (1, 1)"); + check_result("(0, 0): middle=0 height=1 " + "(1, 1): middle=1 height=1 "); free_workspace(); teardown(); @@ -579,14 +670,15 @@ void test_CFL_reachability_with_empty_adj_matrix(void) { // Tests with invalid result //==================== -void test_CFL_reachability_invalid_rules(void) { +void test_CFL_single_path_invalid_rules(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aSb(); + init_outputs(); init_graph_double_cycle(); - init_outputs() ; // Rule [Variable -> _ B] grammar.rules[0] = @@ -618,43 +710,53 @@ void test_CFL_reachability_invalid_rules(void) { #endif } -void test_CFL_reachability_null_pointers(void) { +void test_CFL_single_path_null_pointers(void) +{ #if LAGRAPH_SUITESPARSE setup(); GrB_Info retval; init_grammar_aSb(); + init_outputs(); init_graph_double_cycle(); - init_outputs() ; -// adj_matrices[0] = NULL; -// adj_matrices[1] = NULL; + // adj_matrices[0] = NULL; + // adj_matrices[1] = NULL; GrB_free(&adj_matrices[0]); GrB_free(&adj_matrices[1]); check_error(GrB_NULL_POINTER); -// adj_matrices = NULL; - LAGraph_Free ((void **) &adj_matrices, msg); + // adj_matrices = NULL; + LAGraph_Free((void **)&adj_matrices, msg); check_error(GrB_NULL_POINTER); free_workspace(); init_grammar_aSb(); + init_outputs(); init_graph_double_cycle(); - init_outputs() ; -// outputs = NULL; - LAGraph_Free ((void **) &outputs, msg); + // outputs = NULL; + if (outputs != NULL) + { + for (size_t i = 0; i < grammar.nonterms_count; i++) + { + GrB_free(&outputs[i]); + } + } + // check_error(GrB_NULL_POINTER); + + LAGraph_Free((void **)&outputs, msg); check_error(GrB_NULL_POINTER); free_workspace(); init_grammar_aSb(); + init_outputs(); init_graph_double_cycle(); - init_outputs() ; -// grammar.rules = NULL; - LAGraph_Free ((void **) &grammar.rules, msg); + // grammar.rules = NULL; + LAGraph_Free((void **)&grammar.rules, msg); check_error(GrB_NULL_POINTER); free_workspace(); @@ -662,18 +764,15 @@ void test_CFL_reachability_null_pointers(void) { #endif } -TEST_LIST = {{"CFL_reachability_complex_grammar", test_CFL_reachability_complex_grammar}, - {"CFL_reachability_cycle", test_CFL_reachability_cycle}, - {"CFL_reachability_two_cycle", test_CFL_reachability_two_cycle}, - {"CFL_reachability_labels_more_than_nonterms", - test_CFL_reachability_labels_more_than_nonterms}, - {"CFL_reachability_tree", test_CFL_reachability_tree}, - {"CFL_reachability_line", test_CFL_reachability_line}, - {"CFL_reachability_two_nodes_cycle", test_CFL_reachability_two_nodes_cycle}, - {"CFG_reach_basic_invalid_rules", test_CFL_reachability_invalid_rules}, - {"test_CFL_reachability_with_empty_adj_matrix", test_CFL_reachability_with_empty_adj_matrix}, - #if !defined ( GRAPHBLAS_HAS_CUDA ) - {"CFG_reachability_null_pointers", test_CFL_reachability_null_pointers}, - #endif - {NULL, NULL}}; - +TEST_LIST = { + {"CFL_reachability_cycle", test_CFL_single_path_cycle}, + {"CFL_path_two_cycle", test_CFL_single_path_two_cycle}, + {"CFL_single_path_labels_more_than_nonterms", test_CFL_single_path_labels_more_than_nonterms}, + {"CFL_single_path_complex_grammar", test_CFL_single_path_complex_grammar}, + {"CFL_single_path_tree", test_CFL_single_path_tree}, + {"CFL_single_path_line", test_CFL_single_path_line}, + {"CFL_single_path_two_nodes_cycle", test_CFL_single_path_two_nodes_cycle}, + {"CFL_single_path_with_empty_adj_matrix", test_CFL_single_path_with_empty_adj_matrix}, + {"CFL_single_path_invalid_rules", test_CFL_single_path_invalid_rules}, + {"CFL_single_path_null_pointers", test_CFL_single_path_null_pointers}, + {NULL, NULL}}; diff --git a/include/LAGraphX.h b/include/LAGraphX.h index 3355addb2e..11cc420876 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1096,6 +1096,145 @@ GrB_Info LAGraph_CFL_reachability char *msg // Message string for error reporting. ) ; +typedef struct +{ + GrB_Type type; + GrB_Semiring semiring; + GrB_BinaryOp add; + GrB_BinaryOp mult; + GrB_BinaryOp init_path; // Function for defining elements used to describe information about paths of length 0 and 1. + // Depends on the specific task, therefore it is included in this structure. + GrB_Scalar bottom_scalar; +} CFL_Semiring; + +// Edge of the graph from vertex start to vertex end with terminal label +typedef struct +{ + GrB_Index start; + int32_t label; + GrB_Index end; +} Edge; + +typedef struct +{ + Edge *path; + size_t len; +} Path; + +typedef struct +{ + Path *paths; + size_t count; + size_t capacity; +} PathArray; + +// Structure for storing single path information +typedef struct +{ + GrB_Index middle; // Auxiliary vertex for merging paths + int32_t height; // Minimum height of the derivation tree of the string formed by this path from a nonterminal +} PathIndex; + +GrB_Info LAGraph_CFPQ_core +( + // Output + GrB_Matrix *outputs, // Array of matrices containing results. + // The size of the array must be equal to nonterms_count. + // + // outputs[k]: (i, j) contains a corresponding semiring value if and only if there is a path + // from node i to node j whose edge labels form a word + // derivable from the non-terminal 'k' of the specified CFG. + // Input + const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. + // The length of this array is equal to the count of + // terminals (terms_count). + // + // adj_matrices[t]: (i, j) == 1 if and only if there + // is an edge between nodes i and j with the label of + // the terminal corresponding to index 't' (where t is + // in the range [0, terms_count - 1]). + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + const CFL_Semiring *semiring, // The algebraic structure that defines operations on matrices for a specific problem + char *msg // Message string for error reporting. +); + +GrB_Info LAGraph_CFL_single_path +( + // Output + GrB_Matrix *outputs, // Array of matrices containing results. + // The size of the array must be equal to nonterms_count. + // + // outputs[k]: (i, j) contains a PathIndex structure if and only if there is a path + // from node i to node j whose edge labels form a word + // derivable from the non-terminal 'k' of the specified CFG. + // Input + const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. + // The length of this array is equal to the count of + // terminals (terms_count). + // + // adj_matrices[t]: (i, j) == 1 if and only if there + // is an edge between nodes i and j with the label of + // the terminal corresponding to index 't' (where t is + // in the range [0, terms_count - 1]). + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +); + +GrB_Info LAGraph_CFL_extract_single_path( + // Output + PathArray *output, + // Input + GrB_Index *start, // Source vertex of a graph path. + // Pass NULL to get paths from all vertices of the graph. + GrB_Index *end, // Destination vertex of a graph path. + // Pass NULL to get paths to all vertices of the graph. + int32_t nonterm, + const GrB_Matrix *adj_matrices, + const GrB_Matrix *T, + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +); + +GrB_Info LAGraph_CFL_extract_single_path_internal +( + // Output + Path *output, // A path extracted from a graph that forms a word produced by the rules of the grammar. + // + // If the path is not found, it is equal to the empty path: len = 0, path = NULL and GrB_NO_VALUE is returned. + // If the path is empty, i.e., formed by the rule: nonterminal -> eps, then the empty path: len = 0, path = NULL and GrB_SUCCESS is returned. + // Input + GrB_Index start, // Source vertex of a graph path. + GrB_Index end, // Destination vertex of a graph path. + int32_t nonterm, + const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. + // The length of this array is equal to the count of + // terminals (terms_count). + // + // adj_matrices[t]: (i, j) == 1 if and only if there + // is an edge between nodes i and j with the label of + // the terminal corresponding to index 't' (where t is + // in the range [0, terms_count - 1]). + const GrB_Matrix *T, // Matrices containing information about existing paths for each non-terminal + // + // outputs[k]: (i, j) contains a PathIndex structure if and only if there is a path + // from node i to node j whose edge labels form a word + // derivable from the non-terminal 'k' of the specified CFG. + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +); + //------------------------------------------------------------------------------ // a simple example of an algorithm //------------------------------------------------------------------------------ From 4d4eb8d0189ca1affae6258b1c2a2732dd9a8e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Tue, 3 Mar 2026 00:21:08 +0400 Subject: [PATCH 03/34] demo demo version --- .../algorithm/LAGraph_CFL_all_paths.c | 289 ++++++++++++++++++ .../algorithm/LAGraph_CFL_single_path.c | 1 + experimental/algorithm/LAGraph_CFPQ_core.c | 2 +- include/LAGraphX.h | 8 + 4 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 experimental/algorithm/LAGraph_CFL_all_paths.c diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c new file mode 100644 index 0000000000..396de0c2d8 --- /dev/null +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -0,0 +1,289 @@ +#define LG_FREE_WORK \ + { \ + GrB_free(&bottom_scalar); \ + GrB_free(&IAllPaths_set); \ + GrB_free(&IAllPaths_mult); \ + GrB_free(&AllPaths_set); \ + GrB_free(&AllPaths_mult); \ + GrB_free(&AllPaths_add); \ + GrB_free(&AllPaths_semiring); \ + GrB_free(&Theta); \ + GrB_free(&AllPaths_monoid); \ + } + +#include "LG_internal.h" +#include + +GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Index na, const void* right, const GrB_Index nb){ + GrB_Index* a = (GrB_Index*) left; + GrB_Index* b = (GrB_Index*) right; + + LG_TRY(LAGraph_Malloc((void**)&tmp, na+nb, sizeof(GrB_Index), msg)); + + GrB_Index ia = 0, ib = 0, outn = 0; + while (ia < na && ib < nb) { + GrB_Index va = a[ia]; + GrB_Index vb = b[ib]; + if (va < vb) { + if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; + ia++; + } else if (vb < va) { + if (outn == 0 || tmp[outn-1] != vb) tmp[outn++] = vb; + ib++; + } else { + if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; + ia++; ib++; + } + } + while (ia < na) { + GrB_Index va = a[ia++]; + if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; + } + while (ib < nb) { + GrB_Index vb = b[ib++]; + if (outn == 0 || tmp[outn-1] != vb) tmp[outn++] = vb; + } + + LAGraph_Realloc((void**)&tmp, outn, na+nb, sizeof(GrB_Index), msg); + + *n = outn; + return tmp; +} + +void clear_all_paths_vex(AllPathsVex *z){ + if(z->middle) LG_TRY(LAGraph_Free((void**) z->middle, msg)); + z->n = 0; + z->exist = 0; +} + +void add_all_paths_index(AllPathsVex *z, const AllPathsVex *x, const AllPathsVex *y) +{ + AllPathsVex v_temp; + AllPathsVex* temp = &v_temp; + temp->exist = 0; + temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); + clear_all_paths_vex(z); + z->exist = 1; + z->middle = temp->middle; + z->n = temp->n; +} + +void add_all_paths_free_index(AllPathsVex *z, AllPathsVex *x, AllPathsVex *y) +{ + AllPathsVex v_temp; + AllPathsVex* temp = &v_temp; + temp->exist = 0; + temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); + clear_all_paths_vex(x); + clear_all_paths_vex(y); + clear_all_paths_vex(z); + z->exist = 1; + z->middle = temp->middle; + z->n = temp->n; +} + +void mult_all_paths_index(AllPathsVex *z, + const AllPathsVex *x, GrB_Index ix, GrB_Index jx, + const AllPathsVex *y, GrB_Index iy, GrB_Index jy, + const void *theta) +{ + clear_all_paths_vex(z); + if(x->exist && y->exist){ + LG_TRY(LAGraph_Malloc((void**) &z->middle, 1, size_of(GrB_Index), msg)); + z->middle[0] = jx; + z->n = 1; + z->exist = 1; + } +} + +void set_all_paths_index(AllPathsVex *z, + const AllPathsVex *x, GrB_Index ix, GrB_Index jx, + const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, + const void *theta) +{ + z->exist = *edge_exist; + z->middle = NULL; + z->n = 0; +} + +#define SET_PATH_INDEX_DEFN \ + "void set_all_paths_index( \n" \ + " AllPathsVex *z, \n" \ + " const AllPathsVex *x, GrB_Index ix, GrB_Index jx, \n" \ + " const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, \n" \ + " const void *theta) \n" \ + "{ \n" \ + " if (*edge_exist) \n" \ + " { \n" \ + " z->middle = ix; \n" \ + " z->height = 1; \n" \ + " } \n" \ + " else \n" \ + " { \n" \ + " z->middle = 0; \n" \ + " z->height = 0; \n" \ + " } \n" \ + "}" + +#define MULT_PATH_INDEX_DEFN \ + "void mult_all_paths_index( \n" \ + " AllPathsVex *z, \n" \ + " const AllPathsVex *x, GrB_Index ix, GrB_Index jx, \n" \ + " const AllPathsVex *y, GrB_Index iy, GrB_Index jy, \n" \ + " const void *theta) \n" \ + "{ \n" \ + " if ((x->height == 0 && x->middle == 0) || \n" \ + " (y->height == 0 && y->middle == 0)) \n" \ + " { \n" \ + " z->height = 0; \n" \ + " z->middle = 0; \n" \ + " } \n" \ + " else \n" \ + " { \n" \ + " z->middle = jx; \n" \ + " z->height = (x->height > y->height ? \n" \ + " x->height : y->height) + 1; \n" \ + " } \n" \ + "}" + +GrB_Info LAGraph_CFL_single_path( + // Output + GrB_Matrix *outputs, // Array of matrices containing results. + // The size of the array must be equal to nonterms_count. + // + // outputs[k]: (i, j) contains a AllPathsVex structure if and only if there is a path + // from node i to node j whose edge labels form a word + // derivable from the non-terminal 'k' of the specified CFG. + // Input + const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. + // The length of this array is equal to the count of + // terminals (terms_count). + // + // adj_matrices[t]: (i, j) == 1 if and only if there + // is an edge between nodes i and j with the label of + // the terminal corresponding to index 't' (where t is + // in the range [0, terms_count - 1]). + int64_t terms_count, // The total number of terminal symbols in the CFG. + int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. + const LAGraph_rule_WCNF *rules, // The rules of the CFG. + int64_t rules_count, // The total number of rules in the CFG. + char *msg // Message string for error reporting. +) +{ + // Semiring components + GrB_Type AllPaths_type = NULL; + GrB_BinaryOp AllPaths_add = NULL; + GrB_Monoid AllPaths_monoid = NULL; + GxB_IndexBinaryOp IAllPaths_mult = NULL; + GrB_BinaryOp AllPaths_mult = NULL; + GrB_Semiring AllPaths_semiring = NULL; + GxB_IndexBinaryOp IAllPaths_set = NULL; + GrB_BinaryOp AllPaths_set = NULL; + GrB_Scalar Theta = NULL; + GrB_Scalar bottom_scalar = NULL; + + GRB_TRY(GrB_Type_new(&AllPaths_type, sizeof(AllPathsVex))); // the memory is not being freed yet + + // Theta cannot be NULL + GRB_TRY(GrB_Scalar_new(&Theta, GrB_BOOL)); + GRB_TRY(GrB_Scalar_setElement_BOOL(Theta, false)); + + AllPathsVex bottom = {0, 0, NULL}; + GRB_TRY(GrB_Scalar_new(&bottom_scalar, AllPaths_type)); + GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); + + // Create semiring + GRB_TRY(GrB_BinaryOp_new( + &AllPaths_add, + (void *)add_all_paths_index, + AllPaths_type, + AllPaths_type, + AllPaths_type)); + +// free operands version + GRB_TRY(GrB_BinaryOp_new( + &AllPaths_add_free, + (void *)add_all_paths_free_index, + AllPaths_type, + AllPaths_type, + AllPaths_type)); + + GRB_TRY(GrB_Monoid_new( + &AllPaths_monoid, + AllPaths_add, + (void *)(&bottom))); // ⊥ - neutral element for the addition operation + + // free operands version + GRB_TRY(GrB_Monoid_new( + &AllPaths_free_monoid, + AllPaths_add_free, + (void *)(&bottom))); // ⊥ - neutral element for the addition operation + + GRB_TRY(GxB_IndexBinaryOp_new( + &IAllPaths_mult, + (void *)mult_all_paths_index, + AllPaths_type, + AllPaths_type, + AllPaths_type, + GrB_BOOL, + "mult_all_paths_index", + MULT_PATH_INDEX_DEFN)); + + GRB_TRY(GxB_BinaryOp_new_IndexOp( + &AllPaths_mult, + IAllPaths_mult, + Theta)); + + GRB_TRY(GrB_Semiring_new( + &AllPaths_semiring, + AllPaths_monoid, + AllPaths_mult)); + + // free operands version + GRB_TRY(GrB_Semiring_new( + &AllPaths_semiring_free, + AllPaths_monoid_free, + AllPaths_mult)); + + GRB_TRY(GxB_IndexBinaryOp_new( + &IAllPaths_set, + (void *)set_all_paths_index, + AllPaths_type, + AllPaths_type, + GrB_BOOL, + GrB_BOOL, + "set_all_paths_index", + SET_PATH_INDEX_DEFN)); + + GRB_TRY(GxB_BinaryOp_new_IndexOp( + &AllPaths_set, + IAllPaths_set, + Theta)); + +// CFL_Semiring semiring = {.type = AllPaths_type, +// .semiring = AllPaths_semiring, +// .add = AllPaths_add, +// .mult = AllPaths_mult, +// .init_path = AllPaths_set, +// .bottom_scalar = bottom_scalar}; +// // free operands version +// CFL_Semiring semiring_free = {.type = AllPaths_type, +// .semiring = AllPaths_semiring_free, +// .add = AllPaths_add_free, +// .mult = AllPaths_mult, +// .init_path = AllPaths_set, +// .bottom_scalar = bottom_scalar}; + + // free operands version or like this? + CFL_Semiring semiring = {.type = AllPaths_type, + .semiring = AllPaths_semiring_free, + .add = AllPaths_add_free, + .add_eps = AllPaths_add, + .mult = AllPaths_mult, + .init_path = AllPaths_set, + .bottom_scalar = bottom_scalar}; + + LG_TRY(LAGraph_CFPQ_core(outputs, adj_matrices, terms_count, nonterms_count, rules, rules_count, &semiring, msg)); + LG_FREE_WORK; + return GrB_SUCCESS; +} diff --git a/experimental/algorithm/LAGraph_CFL_single_path.c b/experimental/algorithm/LAGraph_CFL_single_path.c index bea45bcfa9..f6b10a2083 100644 --- a/experimental/algorithm/LAGraph_CFL_single_path.c +++ b/experimental/algorithm/LAGraph_CFL_single_path.c @@ -210,6 +210,7 @@ GrB_Info LAGraph_CFL_single_path( CFL_Semiring semiring = {.type = PI_type, .semiring = PI_semiring, .add = PI_add, + .add_eps = PI_add, .mult = PI_mult, .init_path = PI_set, .bottom_scalar = bottom_scalar}; diff --git a/experimental/algorithm/LAGraph_CFPQ_core.c b/experimental/algorithm/LAGraph_CFPQ_core.c index 17f10b02b4..fd6fc6db22 100644 --- a/experimental/algorithm/LAGraph_CFPQ_core.c +++ b/experimental/algorithm/LAGraph_CFPQ_core.c @@ -273,7 +273,7 @@ GrB_Info LAGraph_CFPQ_core( for (int64_t i = 0; i < eps_rules_count; i++) { LAGraph_rule_WCNF eps_rule = rules[eps_rules[i]]; - GrB_BinaryOp acc_op = t_empty_flags[eps_rule.nonterm] ? GrB_NULL : semiring->add; + GrB_BinaryOp acc_op = t_empty_flags[eps_rule.nonterm] ? GrB_NULL : semiring->add_eps; GxB_eWiseUnion( T[eps_rule.nonterm], GrB_NULL, acc_op, semiring->init_path, T[eps_rule.nonterm], semiring->bottom_scalar, identity_matrix, false_scalar, GrB_NULL); diff --git a/include/LAGraphX.h b/include/LAGraphX.h index 11cc420876..b48cc32cb1 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1101,6 +1101,7 @@ typedef struct GrB_Type type; GrB_Semiring semiring; GrB_BinaryOp add; + GrB_BinaryOp add_eps; GrB_BinaryOp mult; GrB_BinaryOp init_path; // Function for defining elements used to describe information about paths of length 0 and 1. // Depends on the specific task, therefore it is included in this structure. @@ -1128,6 +1129,13 @@ typedef struct size_t capacity; } PathArray; +typedef struct +{ + bool exist; + GrB_Index n; + GrB_Index* middle; +} AllPathsVex + // Structure for storing single path information typedef struct { From b5cf0c348e53514c7f1ca6db82a8eac05fc782d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Tue, 3 Mar 2026 03:35:44 +0400 Subject: [PATCH 04/34] change struct and remove LG_ function --- .../algorithm/LAGraph_CFL_all_paths.c | 94 +++++++++---------- include/LAGraphX.h | 1 - 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c index 396de0c2d8..6b32866b13 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -6,6 +6,7 @@ GrB_free(&AllPaths_set); \ GrB_free(&AllPaths_mult); \ GrB_free(&AllPaths_add); \ + GrB_free(&AllPaths_add_free); \ GrB_free(&AllPaths_semiring); \ GrB_free(&Theta); \ GrB_free(&AllPaths_monoid); \ @@ -14,56 +15,59 @@ #include "LG_internal.h" #include -GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Index na, const void* right, const GrB_Index nb){ - GrB_Index* a = (GrB_Index*) left; - GrB_Index* b = (GrB_Index*) right; - - LG_TRY(LAGraph_Malloc((void**)&tmp, na+nb, sizeof(GrB_Index), msg)); - - GrB_Index ia = 0, ib = 0, outn = 0; - while (ia < na && ib < nb) { - GrB_Index va = a[ia]; - GrB_Index vb = b[ib]; - if (va < vb) { - if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; - ia++; - } else if (vb < va) { - if (outn == 0 || tmp[outn-1] != vb) tmp[outn++] = vb; - ib++; - } else { - if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; - ia++; ib++; - } - } - while (ia < na) { - GrB_Index va = a[ia++]; - if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; - } - while (ib < nb) { - GrB_Index vb = b[ib++]; - if (outn == 0 || tmp[outn-1] != vb) tmp[outn++] = vb; +static GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Index na, const void* right, const GrB_Index nb){ + GrB_Index* a = (GrB_Index*) left; + GrB_Index* b = (GrB_Index*) right; + GrB_Index *tmp = malloc((na + nb) * sizeof(GrB_Index)); + // LG_TRY(LAGraph_Malloc((void**)&tmp, na+nb, sizeof(GrB_Index), msg)); + + GrB_Index ia = 0, ib = 0, outn = 0; + while (ia < na && ib < nb) { + GrB_Index va = a[ia]; + GrB_Index vb = b[ib]; + if (va < vb) { + if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; + ia++; + } else if (vb < va) { + if (outn == 0 || tmp[outn-1] != vb) tmp[outn++] = vb; + ib++; + } else { + if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; + ia++; ib++; } - - LAGraph_Realloc((void**)&tmp, outn, na+nb, sizeof(GrB_Index), msg); - - *n = outn; - return tmp; + } + while (ia < na) { + GrB_Index va = a[ia++]; + if (outn == 0 || tmp[outn-1] != va) tmp[outn++] = va; + } + while (ib < nb) { + GrB_Index vb = b[ib++]; + if (outn == 0 || tmp[outn-1] != vb) tmp[outn++] = vb; + } + + // LG_TRY(LAGraph_Realloc((void**)&tmp, outn, na+nb, sizeof(GrB_Index), msg)); + GrB_Index *sh = realloc(tmp, outn * sizeof(GrB_Index)); + if (sh) tmp = sh; + + *n = outn; + return tmp; } void clear_all_paths_vex(AllPathsVex *z){ - if(z->middle) LG_TRY(LAGraph_Free((void**) z->middle, msg)); +// if(z->middle) LG_TRY(LAGraph_Free((void**) z->middle, msg)); + if(z->middle){ + free(z->middle); + z->middle=NULL; + } z->n = 0; - z->exist = 0; } void add_all_paths_index(AllPathsVex *z, const AllPathsVex *x, const AllPathsVex *y) { AllPathsVex v_temp; AllPathsVex* temp = &v_temp; - temp->exist = 0; temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); clear_all_paths_vex(z); - z->exist = 1; z->middle = temp->middle; z->n = temp->n; } @@ -72,12 +76,10 @@ void add_all_paths_free_index(AllPathsVex *z, AllPathsVex *x, AllPathsVex *y) { AllPathsVex v_temp; AllPathsVex* temp = &v_temp; - temp->exist = 0; temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); clear_all_paths_vex(x); clear_all_paths_vex(y); clear_all_paths_vex(z); - z->exist = 1; z->middle = temp->middle; z->n = temp->n; } @@ -88,12 +90,10 @@ void mult_all_paths_index(AllPathsVex *z, const void *theta) { clear_all_paths_vex(z); - if(x->exist && y->exist){ - LG_TRY(LAGraph_Malloc((void**) &z->middle, 1, size_of(GrB_Index), msg)); - z->middle[0] = jx; - z->n = 1; - z->exist = 1; - } +// LG_TRY(LAGraph_Malloc((void**) &z->middle, 1, size_of(GrB_Index), msg)); + z->middle = malloc(sizeof(GrB_Index)); + z->middle[0] = jx; + z->n = 1; } void set_all_paths_index(AllPathsVex *z, @@ -101,7 +101,6 @@ void set_all_paths_index(AllPathsVex *z, const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, const void *theta) { - z->exist = *edge_exist; z->middle = NULL; z->n = 0; } @@ -188,7 +187,7 @@ GrB_Info LAGraph_CFL_single_path( GRB_TRY(GrB_Scalar_new(&Theta, GrB_BOOL)); GRB_TRY(GrB_Scalar_setElement_BOOL(Theta, false)); - AllPathsVex bottom = {0, 0, NULL}; + AllPathsVex bottom = {0, NULL}; GRB_TRY(GrB_Scalar_new(&bottom_scalar, AllPaths_type)); GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); @@ -274,7 +273,6 @@ GrB_Info LAGraph_CFL_single_path( // .init_path = AllPaths_set, // .bottom_scalar = bottom_scalar}; - // free operands version or like this? CFL_Semiring semiring = {.type = AllPaths_type, .semiring = AllPaths_semiring_free, .add = AllPaths_add_free, diff --git a/include/LAGraphX.h b/include/LAGraphX.h index b48cc32cb1..6b90d0d293 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1131,7 +1131,6 @@ typedef struct typedef struct { - bool exist; GrB_Index n; GrB_Index* middle; } AllPathsVex From ae86682871426fd49e0ab8931664d63f495344ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Tue, 3 Mar 2026 08:43:54 +0400 Subject: [PATCH 05/34] bottom scalar? --- experimental/algorithm/LAGraph_CFL_all_paths.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c index 6b32866b13..7112b67d6d 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -101,8 +101,9 @@ void set_all_paths_index(AllPathsVex *z, const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, const void *theta) { - z->middle = NULL; - z->n = 0; + z->middle = malloc(sizeof(GrB_Index)); + z->n = 1; + z->middle = GrB_INDEX_MAX; } #define SET_PATH_INDEX_DEFN \ From 9cf1ccc64d0541d1c849f75987da0a94e50749ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Tue, 3 Mar 2026 08:49:11 +0400 Subject: [PATCH 06/34] Update LAGraph_CFL_all_paths.c --- experimental/algorithm/LAGraph_CFL_all_paths.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c index 7112b67d6d..1c25c62ef0 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -101,9 +101,15 @@ void set_all_paths_index(AllPathsVex *z, const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, const void *theta) { - z->middle = malloc(sizeof(GrB_Index)); - z->n = 1; - z->middle = GrB_INDEX_MAX; + if(edge_exist){ + z->middle = malloc(sizeof(GrB_Index)); + z->n = 1; + z->middle = GrB_INDEX_MAX; + } + else{ + z->middle = NULL; + z->n = 0; + } } #define SET_PATH_INDEX_DEFN \ From 79bc5e276085e1c9b2857ea556383abb5ad8521c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Tue, 3 Mar 2026 08:49:29 +0400 Subject: [PATCH 07/34] Update LAGraph_CFL_all_paths.c --- experimental/algorithm/LAGraph_CFL_all_paths.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c index 1c25c62ef0..2357f69bf5 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -104,7 +104,7 @@ void set_all_paths_index(AllPathsVex *z, if(edge_exist){ z->middle = malloc(sizeof(GrB_Index)); z->n = 1; - z->middle = GrB_INDEX_MAX; + z->middle[0] = GrB_INDEX_MAX; } else{ z->middle = NULL; From b5e8036925529c852d483b94efdcee946cc0a78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Tue, 3 Mar 2026 19:11:40 +0400 Subject: [PATCH 08/34] Update LAGraph_CFL_all_paths.c --- .../algorithm/LAGraph_CFL_all_paths.c | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c index 2357f69bf5..6e71222309 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -1,5 +1,7 @@ #define LG_FREE_WORK \ { \ + GrB_free(&AllPaths_semiring_free); \ + GrB_free(&AllPaths_monoid_free); \ GrB_free(&bottom_scalar); \ GrB_free(&IAllPaths_set); \ GrB_free(&IAllPaths_mult); \ @@ -7,9 +9,7 @@ GrB_free(&AllPaths_mult); \ GrB_free(&AllPaths_add); \ GrB_free(&AllPaths_add_free); \ - GrB_free(&AllPaths_semiring); \ GrB_free(&Theta); \ - GrB_free(&AllPaths_monoid); \ } #include "LG_internal.h" @@ -188,9 +188,8 @@ GrB_Info LAGraph_CFL_single_path( GrB_Scalar Theta = NULL; GrB_Scalar bottom_scalar = NULL; - GRB_TRY(GrB_Type_new(&AllPaths_type, sizeof(AllPathsVex))); // the memory is not being freed yet + GRB_TRY(GrB_Type_new(&AllPaths_type, sizeof(AllPathsVex))); - // Theta cannot be NULL GRB_TRY(GrB_Scalar_new(&Theta, GrB_BOOL)); GRB_TRY(GrB_Scalar_setElement_BOOL(Theta, false)); @@ -198,7 +197,6 @@ GrB_Info LAGraph_CFL_single_path( GRB_TRY(GrB_Scalar_new(&bottom_scalar, AllPaths_type)); GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); - // Create semiring GRB_TRY(GrB_BinaryOp_new( &AllPaths_add, (void *)add_all_paths_index, @@ -206,24 +204,17 @@ GrB_Info LAGraph_CFL_single_path( AllPaths_type, AllPaths_type)); -// free operands version GRB_TRY(GrB_BinaryOp_new( &AllPaths_add_free, (void *)add_all_paths_free_index, AllPaths_type, AllPaths_type, AllPaths_type)); - - GRB_TRY(GrB_Monoid_new( - &AllPaths_monoid, - AllPaths_add, - (void *)(&bottom))); // ⊥ - neutral element for the addition operation - // free operands version GRB_TRY(GrB_Monoid_new( - &AllPaths_free_monoid, + &AllPaths_monoid_free, AllPaths_add_free, - (void *)(&bottom))); // ⊥ - neutral element for the addition operation + (void *)(&bottom))); GRB_TRY(GxB_IndexBinaryOp_new( &IAllPaths_mult, @@ -240,12 +231,6 @@ GrB_Info LAGraph_CFL_single_path( IAllPaths_mult, Theta)); - GRB_TRY(GrB_Semiring_new( - &AllPaths_semiring, - AllPaths_monoid, - AllPaths_mult)); - - // free operands version GRB_TRY(GrB_Semiring_new( &AllPaths_semiring_free, AllPaths_monoid_free, @@ -265,20 +250,6 @@ GrB_Info LAGraph_CFL_single_path( &AllPaths_set, IAllPaths_set, Theta)); - -// CFL_Semiring semiring = {.type = AllPaths_type, -// .semiring = AllPaths_semiring, -// .add = AllPaths_add, -// .mult = AllPaths_mult, -// .init_path = AllPaths_set, -// .bottom_scalar = bottom_scalar}; -// // free operands version -// CFL_Semiring semiring_free = {.type = AllPaths_type, -// .semiring = AllPaths_semiring_free, -// .add = AllPaths_add_free, -// .mult = AllPaths_mult, -// .init_path = AllPaths_set, -// .bottom_scalar = bottom_scalar}; CFL_Semiring semiring = {.type = AllPaths_type, .semiring = AllPaths_semiring_free, From 627c6c446c572482492c43e1c406169aed9e36d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 04:22:21 +0400 Subject: [PATCH 09/34] fix --- experimental/algorithm/LAGraph_CFL_all_paths.c | 2 +- include/LAGraphX.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c index 6e71222309..0dd0756b96 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -152,7 +152,7 @@ void set_all_paths_index(AllPathsVex *z, " } \n" \ "}" -GrB_Info LAGraph_CFL_single_path( +GrB_Info LAGraph_CFL_AllPaths( // Output GrB_Matrix *outputs, // Array of matrices containing results. // The size of the array must be equal to nonterms_count. diff --git a/include/LAGraphX.h b/include/LAGraphX.h index 6b90d0d293..acaa320495 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1133,7 +1133,7 @@ typedef struct { GrB_Index n; GrB_Index* middle; -} AllPathsVex +} AllPathsVex; // Structure for storing single path information typedef struct From bb7f6a610dd197a1a5b16f215b5a0a203af175ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 04:25:17 +0400 Subject: [PATCH 10/34] fix --- experimental/algorithm/LAGraph_CFL_all_paths.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_all_paths.c index 0dd0756b96..9f4e0a27cc 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_all_paths.c @@ -179,10 +179,11 @@ GrB_Info LAGraph_CFL_AllPaths( // Semiring components GrB_Type AllPaths_type = NULL; GrB_BinaryOp AllPaths_add = NULL; - GrB_Monoid AllPaths_monoid = NULL; + GrB_BinaryOp AllPaths_add_free = NULL; + GrB_Monoid AllPaths_monoid_free = NULL; GxB_IndexBinaryOp IAllPaths_mult = NULL; GrB_BinaryOp AllPaths_mult = NULL; - GrB_Semiring AllPaths_semiring = NULL; + GrB_Semiring AllPaths_semiring_free = NULL; GxB_IndexBinaryOp IAllPaths_set = NULL; GrB_BinaryOp AllPaths_set = NULL; GrB_Scalar Theta = NULL; From caefef09f42fc4cf66b9250b992b607b05c6c769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 04:52:22 +0400 Subject: [PATCH 11/34] fix --- ...CFL_all_paths.c => LAGraph_CFL_AllPaths.c} | 61 ++++++++----------- include/LAGraphX.h | 9 +++ 2 files changed, 34 insertions(+), 36 deletions(-) rename experimental/algorithm/{LAGraph_CFL_all_paths.c => LAGraph_CFL_AllPaths.c} (72%) diff --git a/experimental/algorithm/LAGraph_CFL_all_paths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c similarity index 72% rename from experimental/algorithm/LAGraph_CFL_all_paths.c rename to experimental/algorithm/LAGraph_CFL_AllPaths.c index 9f4e0a27cc..9a1670b7ec 100644 --- a/experimental/algorithm/LAGraph_CFL_all_paths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -113,44 +113,33 @@ void set_all_paths_index(AllPathsVex *z, } #define SET_PATH_INDEX_DEFN \ - "void set_all_paths_index( \n" \ - " AllPathsVex *z, \n" \ - " const AllPathsVex *x, GrB_Index ix, GrB_Index jx, \n" \ - " const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, \n" \ - " const void *theta) \n" \ - "{ \n" \ - " if (*edge_exist) \n" \ - " { \n" \ - " z->middle = ix; \n" \ - " z->height = 1; \n" \ - " } \n" \ - " else \n" \ - " { \n" \ - " z->middle = 0; \n" \ - " z->height = 0; \n" \ - " } \n" \ - "}" +"void set_all_paths_index(AllPathsVex *z, \n" \ +" const AllPathsVex *x, GrB_Index ix, GrB_Index jx,\n" \ +" const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, \n" \ +" const void *theta) \n" \ +"{ \n" \ +" if(edge_exist){ \n" \ +" z->middle = malloc(sizeof(GrB_Index)); \n" \ +" z->n = 1; \n" \ +" z->middle[0] = GrB_INDEX_MAX; \n" \ +" } \n" \ +" else{ \n" \ +" z->middle = NULL; \n" \ +" z->n = 0; \n" \ +" } \n" \ +"}" #define MULT_PATH_INDEX_DEFN \ - "void mult_all_paths_index( \n" \ - " AllPathsVex *z, \n" \ - " const AllPathsVex *x, GrB_Index ix, GrB_Index jx, \n" \ - " const AllPathsVex *y, GrB_Index iy, GrB_Index jy, \n" \ - " const void *theta) \n" \ - "{ \n" \ - " if ((x->height == 0 && x->middle == 0) || \n" \ - " (y->height == 0 && y->middle == 0)) \n" \ - " { \n" \ - " z->height = 0; \n" \ - " z->middle = 0; \n" \ - " } \n" \ - " else \n" \ - " { \n" \ - " z->middle = jx; \n" \ - " z->height = (x->height > y->height ? \n" \ - " x->height : y->height) + 1; \n" \ - " } \n" \ - "}" +"void mult_all_paths_index(AllPathsVex *z, \n" \ +" const AllPathsVex *x, GrB_Index ix, GrB_Index jx, \n" \ +" const AllPathsVex *y, GrB_Index iy, GrB_Index jy, \n" \ +" const void *theta) \n" \ +"{ \n" \ +" clear_all_paths_vex(z); \n" \ +" z->middle = malloc(sizeof(GrB_Index)); \n" \ +" z->middle[0] = jx; \n" \ +" z->n = 1; \n" \ +"}" GrB_Info LAGraph_CFL_AllPaths( // Output diff --git a/include/LAGraphX.h b/include/LAGraphX.h index acaa320495..5cc281c654 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1193,6 +1193,15 @@ GrB_Info LAGraph_CFL_single_path char *msg // Message string for error reporting. ); +GrB_Info LAGraph_CFL_AllPaths( + GrB_Matrix *outputs, + const GrB_Matrix *adj_matrices, + int64_t terms_count, + int64_t nonterms_count, + const LAGraph_rule_WCNF *rules, + int64_t rules_count, + char *msg); + GrB_Info LAGraph_CFL_extract_single_path( // Output PathArray *output, From 9d5c08dae083e82d4cdf736ec25accc440cc4d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 05:02:50 +0400 Subject: [PATCH 12/34] Update LAGraph_CFL_AllPaths.c --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 9a1670b7ec..5c7e916815 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -101,7 +101,7 @@ void set_all_paths_index(AllPathsVex *z, const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, const void *theta) { - if(edge_exist){ + if (edge_exist && *edge_exist){ z->middle = malloc(sizeof(GrB_Index)); z->n = 1; z->middle[0] = GrB_INDEX_MAX; @@ -118,7 +118,7 @@ void set_all_paths_index(AllPathsVex *z, " const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, \n" \ " const void *theta) \n" \ "{ \n" \ -" if(edge_exist){ \n" \ +" if (edge_exist && *edge_exist){ \n" \ " z->middle = malloc(sizeof(GrB_Index)); \n" \ " z->n = 1; \n" \ " z->middle[0] = GrB_INDEX_MAX; \n" \ From bf057d5c9c23576f0b97de2ebacf899d7f5bfb30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 05:25:08 +0400 Subject: [PATCH 13/34] Update LAGraph_CFL_AllPaths.c --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 5c7e916815..3a97067998 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -18,6 +18,12 @@ static GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Index na, const void* right, const GrB_Index nb){ GrB_Index* a = (GrB_Index*) left; GrB_Index* b = (GrB_Index*) right; + + if (na == 0 && nb == 0) { + *n = 0; + return NULL; + } + GrB_Index *tmp = malloc((na + nb) * sizeof(GrB_Index)); // LG_TRY(LAGraph_Malloc((void**)&tmp, na+nb, sizeof(GrB_Index), msg)); @@ -89,7 +95,6 @@ void mult_all_paths_index(AllPathsVex *z, const AllPathsVex *y, GrB_Index iy, GrB_Index jy, const void *theta) { - clear_all_paths_vex(z); // LG_TRY(LAGraph_Malloc((void**) &z->middle, 1, size_of(GrB_Index), msg)); z->middle = malloc(sizeof(GrB_Index)); z->middle[0] = jx; From fea16a917314c0b8ba00d09405b9d277d6351ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 05:33:17 +0400 Subject: [PATCH 14/34] clear --- .../LAGraph_CFL_extract_single_path.c | 227 ---- ...LAGraph_CFL_extract_single_path_internal.c | 227 ---- .../algorithm/LAGraph_CFL_single_path.c | 220 ---- .../test/test_CFL_extract_single_path.c | 997 ------------------ experimental/test/test_CFL_single_path.c | 778 -------------- include/LAGraphX.h | 77 -- 6 files changed, 2526 deletions(-) delete mode 100644 experimental/algorithm/LAGraph_CFL_extract_single_path.c delete mode 100644 experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c delete mode 100644 experimental/algorithm/LAGraph_CFL_single_path.c delete mode 100644 experimental/test/test_CFL_extract_single_path.c delete mode 100644 experimental/test/test_CFL_single_path.c diff --git a/experimental/algorithm/LAGraph_CFL_extract_single_path.c b/experimental/algorithm/LAGraph_CFL_extract_single_path.c deleted file mode 100644 index 0014758273..0000000000 --- a/experimental/algorithm/LAGraph_CFL_extract_single_path.c +++ /dev/null @@ -1,227 +0,0 @@ -#define LG_FREE_ALL \ - { \ - LAGraph_Free((void **)&output->paths, NULL); \ - output->count = 0; \ - output->capacity = 0; \ - } - -#include "LG_internal.h" -#include - -#define ADD_TO_MSG(...) \ - { \ - if (msg_len == 0) \ - { \ - msg_len += \ - snprintf(msg, LAGRAPH_MSG_LEN, \ - "LAGraph failure (file %s, line %d): ", \ - __FILE__, __LINE__); \ - } \ - if (msg_len < LAGRAPH_MSG_LEN) \ - { \ - msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ - __VA_ARGS__); \ - } \ - } - -#define ADD_INDEX_TO_ERROR_RULE(rule, i) \ - { \ - rule.len_indexes_str += snprintf( \ - rule.indexes_str + rule.len_indexes_str, \ - LAGRAPH_MSG_LEN - rule.len_indexes_str, \ - rule.count == 0 ? "%" PRId64 : ", %" PRId64, i); \ - rule.count++; \ - } - -GrB_Info LAGraph_CFL_extract_single_path( - // Output - PathArray *output, - // Input - GrB_Index *start, - GrB_Index *end, - int32_t nonterm, - const GrB_Matrix *adj_matrices, - const GrB_Matrix *T, - int64_t terms_count, // The total number of terminal symbols in the CFG. - int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. - const LAGraph_rule_WCNF *rules, // The rules of the CFG. - int64_t rules_count, // The total number of rules in the CFG. - char *msg // Message string for error reporting. -) -{ - LG_CLEAR_MSG; - size_t msg_len = 0; // For error formatting - GrB_Info info; - output->count = 0; - output->paths = NULL; - // Initial capacity 1, since most often looking for exactly 1 path with a fixed start and end - output->capacity = 1; - - LG_ASSERT_MSG(terms_count > 0, GrB_INVALID_VALUE, - "The number of terminals must be greater than zero."); - LG_ASSERT_MSG(nonterms_count > 0, GrB_INVALID_VALUE, - "The number of non-terminals must be greater than zero."); - LG_ASSERT_MSG(rules_count > 0, GrB_INVALID_VALUE, - "The number of rules must be greater than zero."); - LG_ASSERT_MSG(nonterm < nonterms_count, GrB_INVALID_VALUE, - "The start non-terminal must be no greater than the number of non-terminals."); - LG_ASSERT_MSG(T != NULL, GrB_NULL_POINTER, "The T array cannot be null."); - LG_ASSERT_MSG(rules != NULL, GrB_NULL_POINTER, "The rules array cannot be null."); - LG_ASSERT_MSG(adj_matrices != NULL, GrB_NULL_POINTER, - "The adjacency matrices array cannot be null."); - - // Find null adjacency matrices - bool found_null = false; - for (int64_t i = 0; i < terms_count; i++) - { - if (adj_matrices[i] != NULL) - continue; - - if (!found_null) - { - ADD_TO_MSG("Adjacency matrices with these indexes are null:"); - } - ADD_TO_MSG(" %" PRId64, i); - found_null = true; - } - - if (found_null) - { - LG_FREE_ALL; - return GrB_NULL_POINTER; - } - - // Find null T matrices - found_null = false; - for (int64_t i = 0; i < nonterms_count; i++) - { - if (T[i] != NULL) - continue; - - if (!found_null) - { - ADD_TO_MSG("T matrices with these indexes are null:"); - } - ADD_TO_MSG(" %" PRId64, i); - - found_null = true; - } - if (found_null) - { - LG_FREE_ALL; - return GrB_NULL_POINTER; - } - - // Check the rules - typedef struct - { - size_t count; - size_t len_indexes_str; - char indexes_str[LAGRAPH_MSG_LEN]; - } rule_error_s; - rule_error_s term_err = {0}; - rule_error_s nonterm_err = {0}; - rule_error_s invalid_err = {0}; - for (int64_t i = 0; i < rules_count; i++) - { - LAGraph_rule_WCNF rule = rules[i]; - - bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; - bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; - bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; - - // Check that all rules are well-formed - if (rule.nonterm < 0 || rule.nonterm >= nonterms_count) - { - ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); - } - - // [Variable -> term] - if (is_rule_term) - { - if (rule.prod_A < -1 || rule.prod_A >= terms_count) - { - ADD_INDEX_TO_ERROR_RULE(term_err, i); - } - continue; - } - - // [Variable -> A B] - if (is_rule_bin) - { - if (rule.prod_A < -1 || rule.prod_A >= nonterms_count || rule.prod_B < -1 || - rule.prod_B >= nonterms_count) - { - ADD_INDEX_TO_ERROR_RULE(nonterm_err, i); - } - continue; - } - - // [Variable -> _ B] - ADD_INDEX_TO_ERROR_RULE(invalid_err, i); - } - - if (term_err.count + nonterm_err.count + invalid_err.count > 0) - { - ADD_TO_MSG("Count of invalid rules: %" PRId64 ".\n", - (int64_t)(term_err.count + nonterm_err.count + invalid_err.count)); - - if (nonterm_err.count > 0) - { - ADD_TO_MSG("Non-terminals must be in range [0, nonterms_count). "); - ADD_TO_MSG("Indexes of invalid rules: %s\n", nonterm_err.indexes_str) - } - if (term_err.count > 0) - { - ADD_TO_MSG("Terminals must be in range [-1, nonterms_count). "); - ADD_TO_MSG("Indexes of invalid rules: %s\n", term_err.indexes_str) - } - if (invalid_err.count > 0) - { - ADD_TO_MSG("[Variable -> _ B] type of rule is not acceptable. "); - ADD_TO_MSG("Indexes of invalid rules: %.120s\n", invalid_err.indexes_str) - } - - LG_FREE_ALL; - return GrB_INVALID_VALUE; - } - - GrB_Index n; - GRB_TRY(GrB_Matrix_nrows(&n, adj_matrices[0])); - - GrB_Index start_begin = (start == NULL) ? 0 : *start; - GrB_Index start_end = (start == NULL) ? n - 1 : *start; - GrB_Index end_begin = (end == NULL) ? 0 : *end; - GrB_Index end_end = (end == NULL) ? n - 1 : *end; - - LG_TRY(LAGraph_Malloc((void **)&output->paths, output->capacity, sizeof(Path), msg)); - - for (GrB_Index st = start_begin; st <= start_end; st++) - { - for (GrB_Index en = end_begin; en <= end_end; en++) - { - Path path; - - // Function that extracts one path with fixed start and end - info = LAGraph_CFL_extract_single_path_internal(&path, st, en, nonterm, adj_matrices, T, terms_count, nonterms_count, rules, rules_count, msg); - if (info == GrB_SUCCESS) - { - if (output->count == output->capacity) - { - LG_TRY(LAGraph_Realloc((void **)&output->paths, output->capacity * 2, output->capacity, - sizeof(Path), msg)); - output->capacity *= 2; - } - output->paths[output->count++] = path; - } - else if (info != GrB_NO_VALUE) // GrB_NO_VALUE is the absence of a path, not an error - { - LG_FREE_ALL; - return info; - } - } - } - - LG_FREE_WORK; - return GrB_SUCCESS; -} diff --git a/experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c b/experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c deleted file mode 100644 index 766998730a..0000000000 --- a/experimental/algorithm/LAGraph_CFL_extract_single_path_internal.c +++ /dev/null @@ -1,227 +0,0 @@ -#define LG_FREE_WORK \ - { \ - LAGraph_Free((void **)&eps_rules, NULL); \ - LAGraph_Free((void **)&term_rules, NULL); \ - LAGraph_Free((void **)&bin_rules, NULL); \ - } - -#define LG_FREE_ALL \ - { \ - LAGraph_Free((void **)&output->path, NULL); \ - output->len = 0; \ - LG_FREE_WORK; \ - } - -#include "LG_internal.h" -#include - -#define ADD_TO_MSG(...) \ - { \ - if (msg_len == 0) \ - { \ - msg_len += \ - snprintf(msg, LAGRAPH_MSG_LEN, \ - "LAGraph failure (file %s, line %d): ", \ - __FILE__, __LINE__); \ - } \ - if (msg_len < LAGRAPH_MSG_LEN) \ - { \ - msg_len += snprintf(msg + msg_len, LAGRAPH_MSG_LEN - msg_len, \ - __VA_ARGS__); \ - } \ - } - -GrB_Info LAGraph_CFL_extract_single_path_internal( - // Output - Path *output, - // Input - GrB_Index start, - GrB_Index end, - int32_t nonterm, - const GrB_Matrix *adj_matrices, - const GrB_Matrix *T, - int64_t terms_count, // The total number of terminal symbols in the CFG. - int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. - const LAGraph_rule_WCNF *rules, // The rules of the CFG. - int64_t rules_count, // The total number of rules in the CFG. - char *msg // Message string for error reporting. -) -{ - LG_CLEAR_MSG; - size_t msg_len = 0; // For error formatting - output->len = 0; - output->path = NULL; - // Arrays for processing rules - size_t *eps_rules = NULL, eps_rules_count = 0; // [Variable -> eps] - size_t *term_rules = NULL, term_rules_count = 0; // [Variable -> term] - size_t *bin_rules = NULL, bin_rules_count = 0; // [Variable -> AB] - - LG_TRY(LAGraph_Calloc((void **)&eps_rules, rules_count, sizeof(size_t), msg)); - LG_TRY(LAGraph_Calloc((void **)&term_rules, rules_count, sizeof(size_t), msg)); - LG_TRY(LAGraph_Calloc((void **)&bin_rules, rules_count, sizeof(size_t), msg)); - - // Internal function: assumes all inputs have been validated by the public wrapper - - // Process rules - for (int64_t i = 0; i < rules_count; i++) - { - LAGraph_rule_WCNF rule = rules[i]; - - bool is_rule_eps = rule.prod_A == -1 && rule.prod_B == -1; - bool is_rule_term = rule.prod_A != -1 && rule.prod_B == -1; - bool is_rule_bin = rule.prod_A != -1 && rule.prod_B != -1; - - // [Variable -> eps] - if (is_rule_eps) - { - eps_rules[eps_rules_count++] = i; - continue; - } - - // [Variable -> term] - if (is_rule_term) - { - term_rules[term_rules_count++] = i; - continue; - } - - // [Variable -> A B] - if (is_rule_bin) - { - bin_rules[bin_rules_count++] = i; - continue; - } - } - - PathIndex index; - GrB_Info info = GrB_Matrix_extractElement_UDT(&index, T[nonterm], start, end); - if (info == GrB_SUCCESS) // Such a path exists - { - if (index.height == 1) - { - if (start == end) // Height = 1 and start = end is an empty eps-path - { - for (size_t i = 0; i < eps_rules_count; i++) - { - LAGraph_rule_WCNF term_rule = rules[eps_rules[i]]; - if (term_rule.nonterm == nonterm) - { - LG_FREE_WORK; - return GrB_SUCCESS; - } - } - } - // Height = 1 and different vertices is a term-path - for (int64_t i = 0; i < terms_count; i++) - { - bool edge; - if (GrB_Matrix_extractElement_BOOL(&edge, adj_matrices[i], start, end) == GrB_SUCCESS) - { - for (size_t j = 0; j < term_rules_count; j++) - { - LAGraph_rule_WCNF term_rule = rules[term_rules[j]]; - if (term_rule.nonterm == nonterm && term_rule.prod_A == i) - { - LG_TRY(LAGraph_Calloc((void **)&output->path, 1, sizeof(Edge), msg)); - output->len = 1; - output->path[0] = (Edge){start, i, end}; - LG_FREE_WORK; - return GrB_SUCCESS; - } - } - } - } - // If couldn't find rules for outputting an empty or terminal path, - // then the path were looking for doesn't match the rules - LG_FREE_WORK; - ADD_TO_MSG("The extracted path does not match the input grammar."); - return GrB_NO_VALUE; - } - // Rules of the form Nonterm -> Nonterm * Nonterm are traversed recursively and merged - for (size_t i = 0; i < bin_rules_count; i++) - { - LAGraph_rule_WCNF term_rule = rules[bin_rules[i]]; - if (term_rule.nonterm != nonterm) - { - continue; - } - PathIndex indexB, indexC; - if ((info = GrB_Matrix_extractElement_UDT(&indexB, T[term_rule.prod_A], start, index.middle)) != GrB_SUCCESS) - { - // If haven't found such a piece of the path, then continue. - if (info != GrB_NO_VALUE) - { - LG_FREE_WORK; - return info; - } - - continue; - } - if ((info = GrB_Matrix_extractElement_UDT(&indexC, T[term_rule.prod_B], index.middle, end)) != GrB_SUCCESS) - { - if (info != GrB_NO_VALUE) - { - LG_FREE_WORK; - return info; - } - continue; - } - - // Height compliance check - int32_t maxH = (indexB.height > indexC.height ? indexB.height : indexC.height); - if (index.height != maxH + 1) - { - continue; - } - - Path left, right; - // If didn't find the path, try the other rules. - if ((info = LAGraph_CFL_extract_single_path_internal(&left, start, index.middle, term_rule.prod_A, adj_matrices, T, terms_count, nonterms_count, rules, rules_count, msg)) != GrB_SUCCESS) - { - if (info == GrB_NO_VALUE) - { - continue; - } - LG_FREE_WORK; - return info; - } - if ((info = LAGraph_CFL_extract_single_path_internal(&right, index.middle, end, term_rule.prod_B, adj_matrices, T, terms_count, nonterms_count, rules, rules_count, msg)) != GrB_SUCCESS) - { - if (info == GrB_NO_VALUE) - { - LG_TRY(LAGraph_Free((void **)&left.path, msg)); - continue; - } - LG_TRY(LAGraph_Free((void **)&left.path, msg)); - LG_FREE_WORK; - return info; - } - - output->len = left.len + right.len; - - LG_TRY(LAGraph_Calloc((void **)&output->path, output->len, sizeof(Edge), msg)); - - memcpy(output->path, left.path, left.len * sizeof(Edge)); - memcpy(output->path + left.len, right.path, right.len * sizeof(Edge)); - LG_TRY(LAGraph_Free((void **)&left.path, msg)); - LG_TRY(LAGraph_Free((void **)&right.path, msg)); - LG_FREE_WORK; - return GrB_SUCCESS; - } - - // If couldn't find rules for outputting an path, - // then the path were looking for doesn't match the rules - LG_FREE_WORK; - ADD_TO_MSG("The extracted path does not match the input grammar."); - return GrB_NO_VALUE; - } - // Such a path doesn't exists - return an empty path and GrB_NO_VALUE - else if (info == GrB_NO_VALUE) - { - LG_FREE_WORK; - return GrB_NO_VALUE; - } - // Return some other error - LG_FREE_WORK; - return info; -} diff --git a/experimental/algorithm/LAGraph_CFL_single_path.c b/experimental/algorithm/LAGraph_CFL_single_path.c deleted file mode 100644 index f6b10a2083..0000000000 --- a/experimental/algorithm/LAGraph_CFL_single_path.c +++ /dev/null @@ -1,220 +0,0 @@ -#define LG_FREE_WORK \ - { \ - GrB_free(&bottom_scalar); \ - GrB_free(&IPI_set); \ - GrB_free(&IPI_mult); \ - GrB_free(&PI_set); \ - GrB_free(&PI_mult); \ - GrB_free(&PI_add); \ - GrB_free(&PI_semiring); \ - GrB_free(&Theta); \ - GrB_free(&PI_monoid); \ - } - -#include "LG_internal.h" -#include - -void add_path_index(PathIndex *z, const PathIndex *x, const PathIndex *y) -{ - // If one is ⊥, then we take another. - if (x->height == 0 && x->middle == 0) - { - *z = *y; - } - else if (y->height == 0 && y->middle == 0) - { - *z = *x; - } - // Take the path with the minimum height - else if (x->height < y->height || (x->height == y->height && x->middle <= y->middle)) - { - *z = *x; - } - else - { - *z = *y; - } -} - -void mult_path_index(PathIndex *z, - const PathIndex *x, GrB_Index ix, GrB_Index jx, - const PathIndex - *y, - GrB_Index iy, GrB_Index jy, - const void *theta) -{ - // Сoncatenation with ⊥ gives ⊥ - if ((x->height == 0 && x->middle == 0) || (y->height == 0 && y->middle == 0)) - { - z->height = 0; - z->middle = 0; - } - else - { - z->middle = jx; - z->height = (x->height > y->height ? x->height : y->height) + 1; - } -} - -void set_path_index(PathIndex *z, - const PathIndex *x, GrB_Index ix, GrB_Index jx, - const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, - const void *theta) -{ - if (*edge_exist) - { - z->middle = ix; - z->height = 1; - } - else - { - z->middle = 0; - z->height = 0; - } -} - -#define SET_PATH_INDEX_DEFN \ - "void set_path_index( \n" \ - " PathIndex *z, \n" \ - " const PathIndex *x, GrB_Index ix, GrB_Index jx, \n" \ - " const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, \n" \ - " const void *theta) \n" \ - "{ \n" \ - " if (*edge_exist) \n" \ - " { \n" \ - " z->middle = ix; \n" \ - " z->height = 1; \n" \ - " } \n" \ - " else \n" \ - " { \n" \ - " z->middle = 0; \n" \ - " z->height = 0; \n" \ - " } \n" \ - "}" - -#define MULT_PATH_INDEX_DEFN \ - "void mult_path_index( \n" \ - " PathIndex *z, \n" \ - " const PathIndex *x, GrB_Index ix, GrB_Index jx, \n" \ - " const PathIndex *y, GrB_Index iy, GrB_Index jy, \n" \ - " const void *theta) \n" \ - "{ \n" \ - " if ((x->height == 0 && x->middle == 0) || \n" \ - " (y->height == 0 && y->middle == 0)) \n" \ - " { \n" \ - " z->height = 0; \n" \ - " z->middle = 0; \n" \ - " } \n" \ - " else \n" \ - " { \n" \ - " z->middle = jx; \n" \ - " z->height = (x->height > y->height ? \n" \ - " x->height : y->height) + 1; \n" \ - " } \n" \ - "}" - -GrB_Info LAGraph_CFL_single_path( - // Output - GrB_Matrix *outputs, // Array of matrices containing results. - // The size of the array must be equal to nonterms_count. - // - // outputs[k]: (i, j) contains a PathIndex structure if and only if there is a path - // from node i to node j whose edge labels form a word - // derivable from the non-terminal 'k' of the specified CFG. - // Input - const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. - // The length of this array is equal to the count of - // terminals (terms_count). - // - // adj_matrices[t]: (i, j) == 1 if and only if there - // is an edge between nodes i and j with the label of - // the terminal corresponding to index 't' (where t is - // in the range [0, terms_count - 1]). - int64_t terms_count, // The total number of terminal symbols in the CFG. - int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. - const LAGraph_rule_WCNF *rules, // The rules of the CFG. - int64_t rules_count, // The total number of rules in the CFG. - char *msg // Message string for error reporting. -) -{ - // Semiring components - GrB_Type PI_type = NULL; // Type PathIndex - GrB_BinaryOp PI_add = NULL; - GrB_Monoid PI_monoid = NULL; - GxB_IndexBinaryOp IPI_mult = NULL; - GrB_BinaryOp PI_mult = NULL; - GrB_Semiring PI_semiring = NULL; - GxB_IndexBinaryOp IPI_set = NULL; - GrB_BinaryOp PI_set = NULL; - GrB_Scalar Theta = NULL; - GrB_Scalar bottom_scalar = NULL; - - GRB_TRY(GrB_Type_new(&PI_type, sizeof(PathIndex))); // the memory is not being freed yet - - // Theta cannot be NULL - GRB_TRY(GrB_Scalar_new(&Theta, GrB_BOOL)); - GRB_TRY(GrB_Scalar_setElement_BOOL(Theta, false)); - - PathIndex bottom = {0, 0}; - GRB_TRY(GrB_Scalar_new(&bottom_scalar, PI_type)); - GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); - - // Create semiring - GRB_TRY(GrB_BinaryOp_new( - &PI_add, - (void *)add_path_index, - PI_type, - PI_type, - PI_type)); - - GRB_TRY(GrB_Monoid_new( - &PI_monoid, - PI_add, - (void *)(&bottom))); // ⊥ - neutral element for the addition operation - - GRB_TRY(GxB_IndexBinaryOp_new( - &IPI_mult, - (void *)mult_path_index, - PI_type, - PI_type, - PI_type, - GrB_BOOL, - "mult_path_index", - MULT_PATH_INDEX_DEFN)); - - GRB_TRY(GxB_BinaryOp_new_IndexOp( - &PI_mult, - IPI_mult, - Theta)); - - GRB_TRY(GrB_Semiring_new( - &PI_semiring, - PI_monoid, - PI_mult)); - - GRB_TRY(GxB_IndexBinaryOp_new( - &IPI_set, - (void *)set_path_index, - PI_type, - PI_type, - GrB_BOOL, - GrB_BOOL, - "set_path_index", - SET_PATH_INDEX_DEFN)); - - GRB_TRY(GxB_BinaryOp_new_IndexOp( - &PI_set, - IPI_set, - Theta)); - - CFL_Semiring semiring = {.type = PI_type, - .semiring = PI_semiring, - .add = PI_add, - .add_eps = PI_add, - .mult = PI_mult, - .init_path = PI_set, - .bottom_scalar = bottom_scalar}; - LG_TRY(LAGraph_CFPQ_core(outputs, adj_matrices, terms_count, nonterms_count, rules, rules_count, &semiring, msg)); - LG_FREE_WORK; - return GrB_SUCCESS; -} diff --git a/experimental/test/test_CFL_extract_single_path.c b/experimental/test/test_CFL_extract_single_path.c deleted file mode 100644 index 2a3777841e..0000000000 --- a/experimental/test/test_CFL_extract_single_path.c +++ /dev/null @@ -1,997 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define run_aux_algorithm() \ - LAGraph_CFL_single_path(outputs, adj_matrices, grammar.terms_count, \ - grammar.nonterms_count, grammar.rules, grammar.rules_count, \ - msg) - -#define run_algorithm() \ - LAGraph_CFL_extract_single_path(&path, start, end, 0, adj_matrices, outputs, grammar.terms_count, \ - grammar.nonterms_count, grammar.rules, grammar.rules_count, \ - msg) - -#define check_error(error) \ - { \ - retval = run_algorithm(); \ - TEST_CHECK(retval == error); \ - TEST_MSG("retval = %d (%s)", retval, msg); \ - } - -// Check the path through the string -#define check_result1(expected_ret, result) \ - { \ - retval = run_algorithm(); \ - TEST_CHECK(retval == expected_ret); \ - TEST_MSG("retval = %d (%s)", retval, msg); \ - char *expected = path_to_str(); \ - TEST_CHECK(strcmp(result, expected) == 0); \ - TEST_MSG("Wrong result. Actual: %s", expected); \ - LAGraph_Free((void **)&expected, msg); \ - } - -// Check the path according to its type -#define check_result2(expected_path) \ - { \ - retval = run_algorithm(); \ - if (expected_path == non_exist) \ - { \ - TEST_CHECK(retval == GrB_NO_VALUE); \ - TEST_MSG("retval = %d (%s)", retval, msg); \ - TEST_CHECK(check_empty_path()); \ - TEST_MSG("Wrong result"); \ - } \ - else if (expected_path == empty) \ - { \ - TEST_CHECK(retval == GrB_SUCCESS); \ - TEST_MSG("retval = %d (%s)", retval, msg); \ - TEST_CHECK(check_empty_path()); \ - TEST_MSG("Wrong result"); \ - } \ - else \ - { \ - TEST_CHECK(retval == GrB_SUCCESS); \ - TEST_MSG("retval = %d (%s)", retval, msg); \ - TEST_CHECK(check_non_empty_path()); \ - TEST_MSG("Wrong result"); \ - } \ - } - -typedef struct -{ - size_t nonterms_count; - size_t terms_count; - size_t rules_count; - LAGraph_rule_WCNF *rules; -} grammar_t; - -GrB_Matrix *adj_matrices = NULL; -int n_adj_matrices = 0; -GrB_Matrix *outputs = NULL; -grammar_t grammar = {0, 0, 0, NULL}; -Path path; -char msg[LAGRAPH_MSG_LEN]; -// GrB_Type PI_type = NULL; - -typedef enum // Path type -{ - empty = 0, // Empty path - through a rule with eps - non_empty = 1, // Path with non-zero length - non_exist = 2, // Path does not exist -} Type_of_path; - -void setup() -{ - LAGraph_Init(msg); - // GrB_Type_new(&PI_type, sizeof(PathIndex)); -} - -void teardown(void) { LAGraph_Finalize(msg); } - -void init_outputs() -{ - LAGraph_Calloc((void **)&outputs, - grammar.nonterms_count, sizeof(GrB_Matrix), msg); -} - -bool check_empty_path() -{ - return path.len == 0 && path.path == NULL; -} - -bool check_non_empty_path() -{ - if (path.len == 0) - { - return false; - } - for (size_t i = 0; i < path.len; i++) - { - Edge cur_edge = path.path[i]; - bool edge_exist; - if (GrB_Matrix_extractElement_BOOL(&edge_exist, adj_matrices[cur_edge.label], cur_edge.start, cur_edge.end) != GrB_SUCCESS) - { - return false; - } - } - return true; -} - -char *path_to_str() -{ - char *result_str = NULL; - // 15 - size of "%ld->(%d)->%ld " - // 15 - size of "len: %zu path: " - // we need 16 + len * 15 - LAGraph_Malloc((void **)&result_str, 15 + path.len * 15, sizeof(char), msg); - result_str[0] = '\0'; - - sprintf(result_str + strlen(result_str), "len: %zu path: ", path.len); - - if (path.len > 0) - { - for (size_t i = 0; i < path.len; i++) - { - sprintf(result_str + strlen(result_str), "%" PRIu64 "->(%" PRId32 ")->%" PRIu64 " ", path.path[i].start, path.path[i].label, path.path[i].end); - } - } - - return result_str; -} - -void free_workspace() -{ - - if (adj_matrices != NULL) - { - for (size_t i = 0; i < n_adj_matrices; i++) - { - GrB_free(&adj_matrices[i]); - } - } - LAGraph_Free((void **)&adj_matrices, msg); - - if (outputs != NULL) - { - for (size_t i = 0; i < grammar.nonterms_count; i++) - { - GrB_free(&outputs[i]); - } - } - LAGraph_Free((void **)&outputs, msg); - - LAGraph_Free((void **)&grammar.rules, msg); - grammar = (grammar_t){0, 0, 0, NULL}; -} - -//==================== -// Grammars -//==================== - -// S -> aSb | ab in WCNF -// -// Terms: [0 a] [1 b] -// Nonterms: [0 S] [1 A] [2 B] [3 C] -// S -> AB [0 1 2 0] -// S -> AC [0 1 3 0] -// C -> SB [3 0 2 0] -// A -> a [1 0 -1 0] -// B -> b [2 1 -1 0] -void init_grammar_aSb() -{ - LAGraph_rule_WCNF *rules = NULL; - LAGraph_Calloc((void **)&rules, 5, sizeof(LAGraph_rule_WCNF), msg); - - rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; - rules[1] = (LAGraph_rule_WCNF){0, 1, 3, 0}; - rules[2] = (LAGraph_rule_WCNF){3, 0, 2, 0}; - rules[3] = (LAGraph_rule_WCNF){1, 0, -1, 0}; - rules[4] = (LAGraph_rule_WCNF){2, 1, -1, 0}; - - grammar = (grammar_t){ - .nonterms_count = 4, .terms_count = 2, .rules_count = 5, .rules = rules}; -} - -// S -> aS | a | eps in WCNF -// -// Terms: [0 a] -// Nonterms: [0 S] -// S -> SS [0 0 0 0] -// S -> a [0 0 -1 0] -// S -> eps [0 -1 -1 0] -void init_grammar_aS() -{ - LAGraph_rule_WCNF *rules = NULL; - LAGraph_Calloc((void **)&rules, 3, sizeof(LAGraph_rule_WCNF), msg); - - rules[0] = (LAGraph_rule_WCNF){0, 0, 0, 0}; - rules[1] = (LAGraph_rule_WCNF){0, 0, -1, 0}; - rules[2] = (LAGraph_rule_WCNF){0, -1, -1, 0}; - - grammar = (grammar_t){ - .nonterms_count = 1, .terms_count = 1, .rules_count = 3, .rules = rules}; -} - -// Complex grammar -// aaaabbbb or aaabbb -// -// Terms: [0 a] [1 b] -// Nonterms: [0 S] [n Sn] -// S -> S1 S2 [0 1 2 0] -// S -> S15 S16 [0 15 16 0] -// S1 -> S3 S4 [1 3 4 0] -// S2 -> S5 S6 [2 5 6 0] -// S3 -> S7 S8 [3 7 8 0] -// S4 -> S9 S10 [4 9 10 0] -// S5 -> S11 S12 [5 11 12 0] -// S6 -> S13 S14 [6 13 14 0] -// S16 -> S17 S18 [16 17 18 0] -// S17 -> S19 S20 [17 19 20 0] -// S18 -> S21 S22 [18 21 22 0] -// S22 -> S23 S24 [22 23 24 0] -// S7 -> a [7 0 -1 0] -// S8 -> a [8 0 -1 0] -// S9 -> a [9 0 -1 0] -// S10 -> a [10 0 -1 0] -// S11 -> b [11 1 -1 0] -// S12 -> b [12 1 -1 0] -// S13 -> b [13 1 -1 0] -// S14 -> b [14 1 -1 0] -// S15 -> a [15 0 -1 0] -// S19 -> a [19 0 -1 0] -// S20 -> a [20 0 -1 0] -// S21 -> b [21 1 -1 0] -// S23 -> b [23 1 -1 0] -// S24 -> b [24 1 -1 0] -void init_grammar_complex() -{ - LAGraph_rule_WCNF *rules = NULL; - LAGraph_Calloc((void **)&rules, 26, sizeof(LAGraph_rule_WCNF), msg); - - rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; - rules[1] = (LAGraph_rule_WCNF){0, 15, 16, 0}; - rules[2] = (LAGraph_rule_WCNF){1, 3, 4, 0}; - rules[3] = (LAGraph_rule_WCNF){2, 5, 6, 0}; - rules[4] = (LAGraph_rule_WCNF){3, 7, 8, 0}; - rules[5] = (LAGraph_rule_WCNF){4, 9, 10, 0}; - rules[6] = (LAGraph_rule_WCNF){5, 11, 12, 0}; - rules[7] = (LAGraph_rule_WCNF){6, 13, 14, 0}; - rules[8] = (LAGraph_rule_WCNF){16, 17, 18, 0}; - rules[9] = (LAGraph_rule_WCNF){17, 19, 20, 0}; - rules[10] = (LAGraph_rule_WCNF){18, 21, 22, 0}; - rules[11] = (LAGraph_rule_WCNF){22, 23, 24, 0}; - rules[12] = (LAGraph_rule_WCNF){7, 0, -1, 0}; - rules[13] = (LAGraph_rule_WCNF){8, 0, -1, 0}; - rules[14] = (LAGraph_rule_WCNF){9, 0, -1, 0}; - rules[15] = (LAGraph_rule_WCNF){10, 0, -1, 0}; - rules[16] = (LAGraph_rule_WCNF){11, 1, -1, 0}; - rules[17] = (LAGraph_rule_WCNF){12, 1, -1, 0}; - rules[18] = (LAGraph_rule_WCNF){13, 1, -1, 0}; - rules[19] = (LAGraph_rule_WCNF){14, 1, -1, 0}; - rules[20] = (LAGraph_rule_WCNF){15, 0, -1, 0}; - rules[21] = (LAGraph_rule_WCNF){19, 0, -1, 0}; - rules[22] = (LAGraph_rule_WCNF){20, 0, -1, 0}; - rules[23] = (LAGraph_rule_WCNF){21, 1, -1, 0}; - rules[24] = (LAGraph_rule_WCNF){23, 1, -1, 0}; - rules[25] = (LAGraph_rule_WCNF){24, 1, -1, 0}; - - grammar = (grammar_t){ - .nonterms_count = 25, .terms_count = 2, .rules_count = 26, .rules = rules}; -} - -//==================== -// Graphs -//==================== - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -a-> 0 -// 0 -b-> 3 -// 3 -b-> 0 -void init_graph_double_cycle() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 4, 4)); - OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 4, 4)); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 0)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 4, 4); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -a-> 0 -void init_graph_one_cycle() -{ - LAGraph_Calloc((void **)&adj_matrices, 1, sizeof(GrB_Matrix), msg); - n_adj_matrices = 1; - - GrB_Matrix adj_matrix_a; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); - - adj_matrices[0] = adj_matrix_a; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -a-> 3 -// 3 -a-> 4 -// 3 -b-> 5 -// 4 -b-> 3 -// 5 -b-> 6 -// 6 -b-> 7 -void init_graph_1() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 8, 8)); - OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 8, 8)); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 3)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 4)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 5)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 4, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 6)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 7)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 8, 8); - // } -} - -// Graph: -// -// 0 -a-> 2 -// 1 -a-> 2 -// 3 -a-> 5 -// 4 -a-> 5 -// 2 -a-> 6 -// 5 -a-> 6 -// 2 -b-> 0 -// 2 -b-> 1 -// 5 -b-> 3 -// 5 -b-> 4 -// 6 -b-> 2 -// 6 -b-> 5 -void init_graph_tree() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 7, 7)); - OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 7, 7)); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 5)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 4, 5)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 6)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 5, 6)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 0)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 1)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 4)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 2)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 5)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 7, 7); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -b-> 3 -// 3 -b-> 4 -void init_graph_line() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 5, 5); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 5, 5); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 4)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 5, 5); - // } -} - -// Graph: -// -// 0 -a-> 0 -// 0 -b-> 1 -// 1 -c-> 2 -void init_graph_2() -{ - LAGraph_Calloc((void **)&adj_matrices, 3, sizeof(GrB_Matrix), msg); - n_adj_matrices = 3; - - GrB_Matrix adj_matrix_a, adj_matrix_b, adj_matrix_c; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 3, 3); - GrB_Matrix_new(&adj_matrix_c, GrB_BOOL, 3, 3); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 0)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_c, true, 1, 2)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - adj_matrices[2] = adj_matrix_c; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 0 -// 0 -b-> 0 -void init_graph_3() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 0)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 0)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); - // } -} - -// Graph: -// -// 0 -b-> 1 -// 1 -b-> 0 -void init_graph_4() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 1, 0)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); - // } -} - -//===================== -// Tests full result -//===================== - -void test_CFL_extract_single_path_two_cycle(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - int expected_ret[16] = {GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_SUCCESS, GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_SUCCESS, GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_SUCCESS, GrB_NO_VALUE, GrB_NO_VALUE, GrB_NO_VALUE, GrB_NO_VALUE}; - char *expected_path[16] = {"len: 12 path: 0->(0)->1 1->(0)->2 2->(0)->0 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 ", - "len: 0 path: ", - "len: 0 path: ", - "len: 6 path: 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 ", - "len: 4 path: 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 ", - "len: 0 path: ", - "len: 0 path: ", - "len: 10 path: 1->(0)->2 2->(0)->0 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 0->(1)->3 ", - "len: 8 path: 2->(0)->0 0->(0)->1 1->(0)->2 2->(0)->0 0->(1)->3 3->(1)->0 0->(1)->3 3->(1)->0 ", - "len: 0 path: ", - "len: 0 path: ", - "len: 2 path: 2->(0)->0 0->(1)->3 ", - "len: 0 path: ", - "len: 0 path: ", - "len: 0 path: ", - "len: 0 path: "}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 4; start++) - { - for (GrB_Index end = 0; end < 4; end++) - { - check_result1(expected_ret[start * 4 + end], expected_path[start * 4 + end]); - if (path.len > 0) - { - LAGraph_Free((void **)&path.path, msg); - } - } - } - free_workspace(); - teardown(); -#endif -} - -//========================================== -// Tests that the path exists in the graph -//========================================== - -void test_CFL_extract_single_path_cycle(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aS(); - init_outputs(); - init_graph_one_cycle(); - int expected[9] = {empty, non_empty, non_empty, - non_empty, empty, non_empty, - non_empty, non_empty, empty}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 3; start++) - { - for (GrB_Index end = 0; end < 3; end++) - { - check_result2(expected[start * 3 + end]); - if (path.len > 0) - { - LAGraph_Free((void **)&path.path, msg); - } - } - } - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_labels_more_than_nonterms(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_2(); - - int expected[9] = {non_exist, non_empty, non_exist, - non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 3; start++) - { - for (GrB_Index end = 0; end < 3; end++) - { - check_result2(expected[start * 3 + end]); - if (path.len > 0) - { - LAGraph_Free((void **)&path.path, msg); - } - } - } - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_complex_grammar(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_complex(); - init_outputs(); - init_graph_1(); - int expected[64] = {non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_empty, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_empty, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 8; start++) - { - for (GrB_Index end = 0; end < 8; end++) - { - check_result2(expected[start * 8 + end]); - if (path.len > 0) - { - LAGraph_Free((void **)&path.path, msg); - } - } - } - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_tree(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_tree(); - - int expected[49] = {non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, - non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, - non_exist, non_exist, non_empty, non_exist, non_exist, non_empty, non_exist, - non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, - non_empty, non_empty, non_exist, non_empty, non_empty, non_exist, non_exist, - non_exist, non_exist, non_empty, non_exist, non_exist, non_empty, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, non_exist, non_exist}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 7; start++) - { - for (GrB_Index end = 0; end < 7; end++) - { - check_result2(expected[start * 7 + end]); - if (path.len > 0) - { - LAGraph_Free((void **)&path.path, msg); - } - } - } - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_line(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_line(); - int expected[25] = {non_exist, non_exist, non_exist, non_exist, non_empty, - non_exist, non_exist, non_exist, non_empty, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist, - non_exist, non_exist, non_exist, non_exist, non_exist}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 5; start++) - { - for (GrB_Index end = 0; end < 5; end++) - { - check_result2(expected[start * 5 + end]); - if (path.len > 0) - { - LAGraph_Free((void **)&path.path, msg); - } - } - } - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_two_nodes_cycle(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_3(); - int expected[4] = {non_empty, non_exist, - non_empty, non_exist}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 2; start++) - { - for (GrB_Index end = 0; end < 2; end++) - { - check_result2(expected[start * 2 + end]); - if (path.len > 0) - { - LAGraph_Free((void **)&path.path, msg); - } - } - } - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_with_empty_adj_matrix(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aS(); - init_outputs(); - init_graph_4(); - - int expected[4] = {empty, non_exist, - non_exist, empty}; - OK(run_aux_algorithm()); - for (GrB_Index start = 0; start < 2; start++) - { - for (GrB_Index end = 0; end < 2; end++) - { - check_result2(expected[start * 2 + end]); - LAGraph_Free((void **)&path.path, msg); - } - } - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_inappropriate_grammar(void) -{ -#if LAGRAPH_SUITESPARSE - - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - OK(run_aux_algorithm()); - // Random path - GrB_Index start = 0; - GrB_Index end = 0; - - LAGraph_Free((void **)&grammar.rules, msg); - grammar = (grammar_t){0, 0, 0, NULL}; - init_grammar_aS(); - check_result2(non_exist); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_inappropriate_graph(void) -{ -#if LAGRAPH_SUITESPARSE - - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - OK(run_aux_algorithm()); - // Random path - GrB_Index start = 0; - GrB_Index end = 0; - - GrB_free(&adj_matrices[0]); - GrB_free(&adj_matrices[1]); - LAGraph_Free((void **)&adj_matrices, msg); - - init_graph_4(); - check_result2(non_exist); - - free_workspace(); - teardown(); -#endif -} - -//=========================== -// Tests with invalid result -//=========================== - -void test_CFL_extract_single_path_invalid_rules(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - OK(run_aux_algorithm()); - // Random path - GrB_Index start = 1; - GrB_Index end = 2; - - // Rule [Variable -> _ B] - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = -1, .prod_B = 1, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [_ -> A B] - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = -1, .prod_A = 1, .prod_B = 2, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [C -> A B], where C >= nonterms_count - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 10, .prod_A = 1, .prod_B = 2, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [S -> A B], where A >= nonterms_count - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = 2, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [C -> t], where t >= terms_count - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = -1, .index = 0}; - check_error(GrB_INVALID_VALUE); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_null_pointers(void) -{ -#if LAGRAPH_SUITESPARSE - - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - OK(run_aux_algorithm()); - // Random path - GrB_Index start = 1; - GrB_Index end = 2; - - // adj_matrices[0] = NULL; - // adj_matrices[1] = NULL; - GrB_free(&adj_matrices[0]); - GrB_free(&adj_matrices[1]); - - check_error(GrB_NULL_POINTER); - - // adj_matrices = NULL; - LAGraph_Free((void **)&adj_matrices, msg); - check_error(GrB_NULL_POINTER); - - free_workspace(); - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - - // outputs = NULL; - if (outputs != NULL) - { - for (size_t i = 0; i < grammar.nonterms_count; i++) - { - GrB_free(&outputs[i]); - } - } - LAGraph_Free((void **)&outputs, msg); - check_error(GrB_NULL_POINTER); - - free_workspace(); - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - - // grammar.rules = NULL; - LAGraph_Free((void **)&grammar.rules, msg); - check_error(GrB_NULL_POINTER); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_extract_single_path_vertex_out_the_graph(void) -{ -#if LAGRAPH_SUITESPARSE - - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); // 4 * 4 - OK(run_aux_algorithm()); - - GrB_Index start = 4; // Vertex outside the graph - GrB_Index end = 2; - - check_error(GrB_INVALID_INDEX); - - free_workspace(); - teardown(); -#endif -} - -TEST_LIST = { - {"CFL_extract_single_path_two_cycle", test_CFL_extract_single_path_two_cycle}, - {"CFL_extract_single_path_cycle", test_CFL_extract_single_path_cycle}, - {"CFL_extract_single_path_labels_more_than_nonterms", test_CFL_extract_single_path_labels_more_than_nonterms}, - {"CFL_extract_single_path_complex_grammar", test_CFL_extract_single_path_complex_grammar}, - {"CFL_extract_single_path_tree", test_CFL_extract_single_path_tree}, - {"CFL_extract_single_path_line", test_CFL_extract_single_path_line}, - {"CFL_extract_single_path_two_nodes_cycle", test_CFL_extract_single_path_two_nodes_cycle}, - {"CFL_extract_single_path_with_empty_adj_matrix", test_CFL_extract_single_path_with_empty_adj_matrix}, - {"CFL_extract_single_path_inappropriate_grammar", test_CFL_extract_single_path_inappropriate_grammar}, - {"CFL_extract_single_path_inappropriate_graph", test_CFL_extract_single_path_inappropriate_graph}, - {"CFL_extract_single_path_invalid_rules", test_CFL_extract_single_path_invalid_rules}, - {"CFL_extract_single_path_null_pointers", test_CFL_extract_single_path_null_pointers}, - {"CFL_extract_single_path_vertex_out_the_graph", test_CFL_extract_single_path_vertex_out_the_graph}, - {NULL, NULL}}; diff --git a/experimental/test/test_CFL_single_path.c b/experimental/test/test_CFL_single_path.c deleted file mode 100644 index 4c0acb9690..0000000000 --- a/experimental/test/test_CFL_single_path.c +++ /dev/null @@ -1,778 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define run_algorithm() \ - LAGraph_CFL_single_path(outputs, adj_matrices, grammar.terms_count, \ - grammar.nonterms_count, grammar.rules, grammar.rules_count, \ - msg) - -#define check_error(error) \ - { \ - retval = run_algorithm(); \ - TEST_CHECK(retval == error); \ - TEST_MSG("retval = %d (%s)", retval, msg); \ - } - -#define check_result(result) \ - { \ - char *expected = output_to_str(0); \ - TEST_CHECK(strcmp(result, expected) == 0); \ - TEST_MSG("Wrong result. Actual: %s", expected); \ - LAGraph_Free((void **)&expected, msg); \ - } - -typedef struct -{ - size_t nonterms_count; - size_t terms_count; - size_t rules_count; - LAGraph_rule_WCNF *rules; -} grammar_t; - -GrB_Matrix *adj_matrices = NULL; -int n_adj_matrices = 0; -GrB_Matrix *outputs = NULL; -grammar_t grammar = {0, 0, 0, NULL}; -char msg[LAGRAPH_MSG_LEN]; -// GrB_Type PI_type = NULL; - -void setup() -{ - LAGraph_Init(msg); - // GrB_Type_new(&PI_type, sizeof(PathIndex)); -} - -void teardown(void) { LAGraph_Finalize(msg); } - -void init_outputs() -{ - LAGraph_Calloc((void **)&outputs, - grammar.nonterms_count, sizeof(GrB_Matrix), msg); -} - -char *output_to_str(size_t nonterm) -{ - GrB_Index nnz = 0; - OK(GrB_Matrix_nvals(&nnz, outputs[nonterm])); - GrB_Index *row = NULL; - GrB_Index *col = NULL; - PathIndex *val = NULL; - LAGraph_Malloc((void **)&row, nnz, sizeof(GrB_Index), msg); - LAGraph_Malloc((void **)&col, nnz, sizeof(GrB_Index), msg); - LAGraph_Malloc((void **)&val, nnz, sizeof(PathIndex), msg); - - OK(GrB_Matrix_extractTuples_UDT(row, col, val, &nnz, outputs[nonterm])); - - char *result_str = NULL; - // 33 - size of "(%ld, %ld): middle=%ld height-%d " - LAGraph_Malloc((void **)&result_str, nnz * 33, sizeof(char), msg); - result_str[0] = '\0'; - - for (size_t i = 0; i < nnz; i++) - { - sprintf(result_str + strlen(result_str), "(%" PRIu64 ", %" PRIu64 "): middle=%" PRIu64 " height=%" PRId32 " ", - row[i], col[i], - val[i].middle, - val[i].height); - } - - LAGraph_Free((void **)&row, msg); - LAGraph_Free((void **)&col, msg); - LAGraph_Free((void **)&val, msg); - - return result_str; -} - -void free_workspace() -{ - - if (adj_matrices != NULL) - { - for (size_t i = 0; i < n_adj_matrices; i++) - { - GrB_free(&adj_matrices[i]); - } - } - LAGraph_Free((void **)&adj_matrices, msg); - - if (outputs != NULL) - { - for (size_t i = 0; i < grammar.nonterms_count; i++) - { - GrB_free(&outputs[i]); - } - } - LAGraph_Free((void **)&outputs, msg); - - LAGraph_Free((void **)&grammar.rules, msg); - grammar = (grammar_t){0, 0, 0, NULL}; -} - -//==================== -// Grammars -//==================== - -// S -> aSb | ab in WCNF -// -// Terms: [0 a] [1 b] -// Nonterms: [0 S] [1 A] [2 B] [3 C] -// S -> AB [0 1 2 0] -// S -> AC [0 1 3 0] -// C -> SB [3 0 2 0] -// A -> a [1 0 -1 0] -// B -> b [2 1 -1 0] -void init_grammar_aSb() -{ - LAGraph_rule_WCNF *rules = NULL; - LAGraph_Calloc((void **)&rules, 5, sizeof(LAGraph_rule_WCNF), msg); - - rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; - rules[1] = (LAGraph_rule_WCNF){0, 1, 3, 0}; - rules[2] = (LAGraph_rule_WCNF){3, 0, 2, 0}; - rules[3] = (LAGraph_rule_WCNF){1, 0, -1, 0}; - rules[4] = (LAGraph_rule_WCNF){2, 1, -1, 0}; - - grammar = (grammar_t){ - .nonterms_count = 4, .terms_count = 2, .rules_count = 5, .rules = rules}; -} - -// S -> aS | a | eps in WCNF -// -// Terms: [0 a] -// Nonterms: [0 S] -// S -> SS [0 0 0 0] -// S -> a [0 0 -1 0] -// S -> eps [0 -1 -1 0] -void init_grammar_aS() -{ - LAGraph_rule_WCNF *rules = NULL; - LAGraph_Calloc((void **)&rules, 3, sizeof(LAGraph_rule_WCNF), msg); - - rules[0] = (LAGraph_rule_WCNF){0, 0, 0, 0}; - rules[1] = (LAGraph_rule_WCNF){0, 0, -1, 0}; - rules[2] = (LAGraph_rule_WCNF){0, -1, -1, 0}; - - grammar = (grammar_t){ - .nonterms_count = 1, .terms_count = 1, .rules_count = 3, .rules = rules}; -} - -// Complex grammar -// aaaabbbb or aaabbb -// -// Terms: [0 a] [1 b] -// Nonterms: [0 S] [n Sn] -// S -> S1 S2 [0 1 2 0] -// S -> S15 S16 [0 15 16 0] -// S1 -> S3 S4 [1 3 4 0] -// S2 -> S5 S6 [2 5 6 0] -// S3 -> S7 S8 [3 7 8 0] -// S4 -> S9 S10 [4 9 10 0] -// S5 -> S11 S12 [5 11 12 0] -// S6 -> S13 S14 [6 13 14 0] -// S16 -> S17 S18 [16 17 18 0] -// S17 -> S19 S20 [17 19 20 0] -// S18 -> S21 S22 [18 21 22 0] -// S22 -> S23 S24 [22 23 24 0] -// S7 -> a [7 0 -1 0] -// S8 -> a [8 0 -1 0] -// S9 -> a [9 0 -1 0] -// S10 -> a [10 0 -1 0] -// S11 -> b [11 1 -1 0] -// S12 -> b [12 1 -1 0] -// S13 -> b [13 1 -1 0] -// S14 -> b [14 1 -1 0] -// S15 -> a [15 0 -1 0] -// S19 -> a [19 0 -1 0] -// S20 -> a [20 0 -1 0] -// S21 -> b [21 1 -1 0] -// S23 -> b [23 1 -1 0] -// S24 -> b [24 1 -1 0] -void init_grammar_complex() -{ - LAGraph_rule_WCNF *rules = NULL; - LAGraph_Calloc((void **)&rules, 26, sizeof(LAGraph_rule_WCNF), msg); - - rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; - rules[1] = (LAGraph_rule_WCNF){0, 15, 16, 0}; - rules[2] = (LAGraph_rule_WCNF){1, 3, 4, 0}; - rules[3] = (LAGraph_rule_WCNF){2, 5, 6, 0}; - rules[4] = (LAGraph_rule_WCNF){3, 7, 8, 0}; - rules[5] = (LAGraph_rule_WCNF){4, 9, 10, 0}; - rules[6] = (LAGraph_rule_WCNF){5, 11, 12, 0}; - rules[7] = (LAGraph_rule_WCNF){6, 13, 14, 0}; - rules[8] = (LAGraph_rule_WCNF){16, 17, 18, 0}; - rules[9] = (LAGraph_rule_WCNF){17, 19, 20, 0}; - rules[10] = (LAGraph_rule_WCNF){18, 21, 22, 0}; - rules[11] = (LAGraph_rule_WCNF){22, 23, 24, 0}; - rules[12] = (LAGraph_rule_WCNF){7, 0, -1, 0}; - rules[13] = (LAGraph_rule_WCNF){8, 0, -1, 0}; - rules[14] = (LAGraph_rule_WCNF){9, 0, -1, 0}; - rules[15] = (LAGraph_rule_WCNF){10, 0, -1, 0}; - rules[16] = (LAGraph_rule_WCNF){11, 1, -1, 0}; - rules[17] = (LAGraph_rule_WCNF){12, 1, -1, 0}; - rules[18] = (LAGraph_rule_WCNF){13, 1, -1, 0}; - rules[19] = (LAGraph_rule_WCNF){14, 1, -1, 0}; - rules[20] = (LAGraph_rule_WCNF){15, 0, -1, 0}; - rules[21] = (LAGraph_rule_WCNF){19, 0, -1, 0}; - rules[22] = (LAGraph_rule_WCNF){20, 0, -1, 0}; - rules[23] = (LAGraph_rule_WCNF){21, 1, -1, 0}; - rules[24] = (LAGraph_rule_WCNF){23, 1, -1, 0}; - rules[25] = (LAGraph_rule_WCNF){24, 1, -1, 0}; - - grammar = (grammar_t){ - .nonterms_count = 25, .terms_count = 2, .rules_count = 26, .rules = rules}; -} - -//==================== -// Graphs -//==================== - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -a-> 0 -// 0 -b-> 3 -// 3 -b-> 0 -void init_graph_double_cycle() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 4, 4)); - OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 4, 4)); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 0)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 4, 4); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -a-> 0 -void init_graph_one_cycle() -{ - LAGraph_Calloc((void **)&adj_matrices, 1, sizeof(GrB_Matrix), msg); - n_adj_matrices = 1; - - GrB_Matrix adj_matrix_a; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); - - adj_matrices[0] = adj_matrix_a; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -a-> 3 -// 3 -a-> 4 -// 3 -b-> 5 -// 4 -b-> 3 -// 5 -b-> 6 -// 6 -b-> 7 -void init_graph_1() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 8, 8)); - OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 8, 8)); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 3)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 4)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 5)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 4, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 6)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 7)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 8, 8); - // } -} - -// Graph: -// -// 0 -a-> 2 -// 1 -a-> 2 -// 3 -a-> 5 -// 4 -a-> 5 -// 2 -a-> 6 -// 5 -a-> 6 -// 2 -b-> 0 -// 2 -b-> 1 -// 5 -b-> 3 -// 5 -b-> 4 -// 6 -b-> 2 -// 6 -b-> 5 -void init_graph_tree() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 7, 7)); - OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 7, 7)); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 5)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 4, 5)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 6)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 5, 6)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 0)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 1)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 4)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 2)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 5)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 7, 7); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 2 -// 2 -b-> 3 -// 3 -b-> 4 -void init_graph_line() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 5, 5); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 5, 5); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 3)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 4)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 5, 5); - // } -} - -// Graph: -// -// 0 -a-> 0 -// 0 -b-> 1 -// 1 -c-> 2 -void init_graph_2() -{ - LAGraph_Calloc((void **)&adj_matrices, 3, sizeof(GrB_Matrix), msg); - n_adj_matrices = 3; - - GrB_Matrix adj_matrix_a, adj_matrix_b, adj_matrix_c; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 3, 3); - GrB_Matrix_new(&adj_matrix_c, GrB_BOOL, 3, 3); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 0)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_c, true, 1, 2)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - adj_matrices[2] = adj_matrix_c; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 3, 3); - // } -} - -// Graph: -// -// 0 -a-> 1 -// 1 -a-> 0 -// 0 -b-> 0 -void init_graph_3() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); - - OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 0)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 0)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); - // } -} - -// Graph: -// -// 0 -b-> 1 -// 1 -b-> 0 -void init_graph_4() -{ - LAGraph_Calloc((void **)&adj_matrices, 2, sizeof(GrB_Matrix), msg); - n_adj_matrices = 2; - - GrB_Matrix adj_matrix_a, adj_matrix_b; - GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); - GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); - - OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); - OK(GrB_Matrix_setElement(adj_matrix_b, true, 1, 0)); - - adj_matrices[0] = adj_matrix_a; - adj_matrices[1] = adj_matrix_b; - - // for (int64_t i = 0; i < grammar.nonterms_count; i++) - // { - // GrB_Matrix_new(&outputs[i], PI_type, 2, 2); - // } -} - -//==================== -// Tests with valid result -//==================== - -void test_CFL_single_path_cycle(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aS(); - init_outputs(); - init_graph_one_cycle(); - - OK(run_algorithm()); - check_result("(0, 0): middle=0 height=1 " - "(0, 1): middle=0 height=1 " - "(0, 2): middle=1 height=2 " - "(1, 0): middle=2 height=2 " - "(1, 1): middle=1 height=1 " - "(1, 2): middle=1 height=1 " - "(2, 0): middle=2 height=1 " - "(2, 1): middle=0 height=2 " - "(2, 2): middle=2 height=1 "); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_two_cycle(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - - OK(run_algorithm()); - check_result("(0, 0): middle=1 height=12 " - "(0, 3): middle=1 height=6 " - "(1, 0): middle=2 height=4 " - "(1, 3): middle=2 height=10 " - "(2, 0): middle=0 height=8 " - "(2, 3): middle=0 height=2 "); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_labels_more_than_nonterms(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_2(); - - OK(run_algorithm()); - check_result("(0, 1): middle=0 height=2 "); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_complex_grammar(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_complex(); - init_outputs(); - init_graph_1(); - - OK(run_algorithm()); - check_result("(0, 7): middle=4 height=4 " - "(1, 6): middle=2 height=5 "); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_tree(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_tree(); - - OK(run_algorithm()); - check_result("(0, 0): middle=2 height=2 " - "(0, 1): middle=2 height=2 " - "(0, 3): middle=2 height=4 " - "(0, 4): middle=2 height=4 " - "(1, 0): middle=2 height=2 " - "(1, 1): middle=2 height=2 " - "(1, 3): middle=2 height=4 " - "(1, 4): middle=2 height=4 " - "(2, 2): middle=6 height=2 " - "(2, 5): middle=6 height=2 " - "(3, 0): middle=5 height=4 " - "(3, 1): middle=5 height=4 " - "(3, 3): middle=5 height=2 " - "(3, 4): middle=5 height=2 " - "(4, 0): middle=5 height=4 " - "(4, 1): middle=5 height=4 " - "(4, 3): middle=5 height=2 " - "(4, 4): middle=5 height=2 " - "(5, 2): middle=6 height=2 " - "(5, 5): middle=6 height=2 "); - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_line(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_line(); - - OK(run_algorithm()); - check_result("(0, 4): middle=1 height=4 " - "(1, 3): middle=2 height=2 "); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_two_nodes_cycle(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_3(); - - OK(run_algorithm()); - check_result("(0, 0): middle=1 height=4 " - "(1, 0): middle=0 height=2 "); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_with_empty_adj_matrix(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aS(); - init_outputs(); - init_graph_4(); - - OK(run_algorithm()); - check_result("(0, 0): middle=0 height=1 " - "(1, 1): middle=1 height=1 "); - - free_workspace(); - teardown(); -#endif -} - -//==================== -// Tests with invalid result -//==================== - -void test_CFL_single_path_invalid_rules(void) -{ -#if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - - // Rule [Variable -> _ B] - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = -1, .prod_B = 1, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [_ -> A B] - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = -1, .prod_A = 1, .prod_B = 2, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [C -> A B], where C >= nonterms_count - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 10, .prod_A = 1, .prod_B = 2, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [S -> A B], where A >= nonterms_count - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = 2, .index = 0}; - check_error(GrB_INVALID_VALUE); - - // Rule [C -> t], where t >= terms_count - grammar.rules[0] = - (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = -1, .index = 0}; - check_error(GrB_INVALID_VALUE); - - free_workspace(); - teardown(); -#endif -} - -void test_CFL_single_path_null_pointers(void) -{ -#if LAGRAPH_SUITESPARSE - - setup(); - GrB_Info retval; - - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - - // adj_matrices[0] = NULL; - // adj_matrices[1] = NULL; - GrB_free(&adj_matrices[0]); - GrB_free(&adj_matrices[1]); - - check_error(GrB_NULL_POINTER); - - // adj_matrices = NULL; - LAGraph_Free((void **)&adj_matrices, msg); - check_error(GrB_NULL_POINTER); - - free_workspace(); - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - - // outputs = NULL; - if (outputs != NULL) - { - for (size_t i = 0; i < grammar.nonterms_count; i++) - { - GrB_free(&outputs[i]); - } - } - // check_error(GrB_NULL_POINTER); - - LAGraph_Free((void **)&outputs, msg); - check_error(GrB_NULL_POINTER); - - free_workspace(); - init_grammar_aSb(); - init_outputs(); - init_graph_double_cycle(); - - // grammar.rules = NULL; - LAGraph_Free((void **)&grammar.rules, msg); - check_error(GrB_NULL_POINTER); - - free_workspace(); - teardown(); -#endif -} - -TEST_LIST = { - {"CFL_reachability_cycle", test_CFL_single_path_cycle}, - {"CFL_path_two_cycle", test_CFL_single_path_two_cycle}, - {"CFL_single_path_labels_more_than_nonterms", test_CFL_single_path_labels_more_than_nonterms}, - {"CFL_single_path_complex_grammar", test_CFL_single_path_complex_grammar}, - {"CFL_single_path_tree", test_CFL_single_path_tree}, - {"CFL_single_path_line", test_CFL_single_path_line}, - {"CFL_single_path_two_nodes_cycle", test_CFL_single_path_two_nodes_cycle}, - {"CFL_single_path_with_empty_adj_matrix", test_CFL_single_path_with_empty_adj_matrix}, - {"CFL_single_path_invalid_rules", test_CFL_single_path_invalid_rules}, - {"CFL_single_path_null_pointers", test_CFL_single_path_null_pointers}, - {NULL, NULL}}; diff --git a/include/LAGraphX.h b/include/LAGraphX.h index 5cc281c654..c30b84beae 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1108,40 +1108,12 @@ typedef struct GrB_Scalar bottom_scalar; } CFL_Semiring; -// Edge of the graph from vertex start to vertex end with terminal label -typedef struct -{ - GrB_Index start; - int32_t label; - GrB_Index end; -} Edge; - -typedef struct -{ - Edge *path; - size_t len; -} Path; - -typedef struct -{ - Path *paths; - size_t count; - size_t capacity; -} PathArray; - typedef struct { GrB_Index n; GrB_Index* middle; } AllPathsVex; -// Structure for storing single path information -typedef struct -{ - GrB_Index middle; // Auxiliary vertex for merging paths - int32_t height; // Minimum height of the derivation tree of the string formed by this path from a nonterminal -} PathIndex; - GrB_Info LAGraph_CFPQ_core ( // Output @@ -1202,55 +1174,6 @@ GrB_Info LAGraph_CFL_AllPaths( int64_t rules_count, char *msg); -GrB_Info LAGraph_CFL_extract_single_path( - // Output - PathArray *output, - // Input - GrB_Index *start, // Source vertex of a graph path. - // Pass NULL to get paths from all vertices of the graph. - GrB_Index *end, // Destination vertex of a graph path. - // Pass NULL to get paths to all vertices of the graph. - int32_t nonterm, - const GrB_Matrix *adj_matrices, - const GrB_Matrix *T, - int64_t terms_count, // The total number of terminal symbols in the CFG. - int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. - const LAGraph_rule_WCNF *rules, // The rules of the CFG. - int64_t rules_count, // The total number of rules in the CFG. - char *msg // Message string for error reporting. -); - -GrB_Info LAGraph_CFL_extract_single_path_internal -( - // Output - Path *output, // A path extracted from a graph that forms a word produced by the rules of the grammar. - // - // If the path is not found, it is equal to the empty path: len = 0, path = NULL and GrB_NO_VALUE is returned. - // If the path is empty, i.e., formed by the rule: nonterminal -> eps, then the empty path: len = 0, path = NULL and GrB_SUCCESS is returned. - // Input - GrB_Index start, // Source vertex of a graph path. - GrB_Index end, // Destination vertex of a graph path. - int32_t nonterm, - const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. - // The length of this array is equal to the count of - // terminals (terms_count). - // - // adj_matrices[t]: (i, j) == 1 if and only if there - // is an edge between nodes i and j with the label of - // the terminal corresponding to index 't' (where t is - // in the range [0, terms_count - 1]). - const GrB_Matrix *T, // Matrices containing information about existing paths for each non-terminal - // - // outputs[k]: (i, j) contains a PathIndex structure if and only if there is a path - // from node i to node j whose edge labels form a word - // derivable from the non-terminal 'k' of the specified CFG. - int64_t terms_count, // The total number of terminal symbols in the CFG. - int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. - const LAGraph_rule_WCNF *rules, // The rules of the CFG. - int64_t rules_count, // The total number of rules in the CFG. - char *msg // Message string for error reporting. -); - //------------------------------------------------------------------------------ // a simple example of an algorithm //------------------------------------------------------------------------------ From cf6b1315bbe307bcc823f39d8c8a2a0045f4c380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 05:36:53 +0400 Subject: [PATCH 15/34] Update LAGraph_CFL_reachability.c --- experimental/algorithm/LAGraph_CFL_reachability.c | 1 + 1 file changed, 1 insertion(+) diff --git a/experimental/algorithm/LAGraph_CFL_reachability.c b/experimental/algorithm/LAGraph_CFL_reachability.c index 4b1dbce46e..8e4f02b6f5 100644 --- a/experimental/algorithm/LAGraph_CFL_reachability.c +++ b/experimental/algorithm/LAGraph_CFL_reachability.c @@ -95,6 +95,7 @@ GrB_Info LAGraph_CFL_reachability( CFL_Semiring semiring = {.type = GrB_BOOL, .semiring = GxB_ANY_PAIR_BOOL, .add = GxB_ANY_BOOL, + .add_eps = GxB_ANY_BOOL, .mult = GxB_PAIR_BOOL, .init_path = GxB_PAIR_BOOL, .bottom_scalar = false_scalar}; From ab63c5726acfb59c4b84f87ff4dd46f0ae376868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 05:38:10 +0400 Subject: [PATCH 16/34] Update LAGraphX.h --- include/LAGraphX.h | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/include/LAGraphX.h b/include/LAGraphX.h index c30b84beae..da490091a8 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1140,31 +1140,6 @@ GrB_Info LAGraph_CFPQ_core char *msg // Message string for error reporting. ); -GrB_Info LAGraph_CFL_single_path -( - // Output - GrB_Matrix *outputs, // Array of matrices containing results. - // The size of the array must be equal to nonterms_count. - // - // outputs[k]: (i, j) contains a PathIndex structure if and only if there is a path - // from node i to node j whose edge labels form a word - // derivable from the non-terminal 'k' of the specified CFG. - // Input - const GrB_Matrix *adj_matrices, // Array of adjacency matrices representing the graph. - // The length of this array is equal to the count of - // terminals (terms_count). - // - // adj_matrices[t]: (i, j) == 1 if and only if there - // is an edge between nodes i and j with the label of - // the terminal corresponding to index 't' (where t is - // in the range [0, terms_count - 1]). - int64_t terms_count, // The total number of terminal symbols in the CFG. - int64_t nonterms_count, // The total number of non-terminal symbols in the CFG. - const LAGraph_rule_WCNF *rules, // The rules of the CFG. - int64_t rules_count, // The total number of rules in the CFG. - char *msg // Message string for error reporting. -); - GrB_Info LAGraph_CFL_AllPaths( GrB_Matrix *outputs, const GrB_Matrix *adj_matrices, From d366dbd361f76c284d54d5ba2f60ae9d8528fb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 16:31:38 +0400 Subject: [PATCH 17/34] fix --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 4 +- experimental/test/test_CFL_AllPaths.c | 533 ++++++++++++++++++ 2 files changed, 535 insertions(+), 2 deletions(-) create mode 100644 experimental/test/test_CFL_AllPaths.c diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 3a97067998..d564e38d80 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -73,7 +73,7 @@ void add_all_paths_index(AllPathsVex *z, const AllPathsVex *x, const AllPathsVex AllPathsVex v_temp; AllPathsVex* temp = &v_temp; temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); - clear_all_paths_vex(z); +// clear_all_paths_vex(z); z->middle = temp->middle; z->n = temp->n; } @@ -85,7 +85,7 @@ void add_all_paths_free_index(AllPathsVex *z, AllPathsVex *x, AllPathsVex *y) temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); clear_all_paths_vex(x); clear_all_paths_vex(y); - clear_all_paths_vex(z); +// clear_all_paths_vex(z); z->middle = temp->middle; z->n = temp->n; } diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c new file mode 100644 index 0000000000..084727a853 --- /dev/null +++ b/experimental/test/test_CFL_AllPaths.c @@ -0,0 +1,533 @@ +//------------------------------------------------------------------------------ +// LAGraph/experimental/test/LAGraph_CFL_reachability.c: test cases for Context-Free +// Language Reachability Matrix-Based Algorithm +//------------------------------------------------------------------------------ +// +// LAGraph, (c) 2019-2024 by The LAGraph Contributors, All Rights Reserved. +// SPDX-License-Identifier: BSD-2-Clause + +// Contributed by Ilhom Kombaev, Semyon Grigoriev, St. Petersburg State University. + +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define run_algorithm() \ + LAGraph_CFL_AllPaths(outputs, adj_matrices, grammar.terms_count, \ + grammar.nonterms_count, grammar.rules, grammar.rules_count, \ + msg) + +#define check_error(error) \ + { \ + retval = run_algorithm(); \ + TEST_CHECK(retval == error); \ + TEST_MSG("retval = %d (%s)", retval, msg); \ + } + +#define check_result(result) \ + { \ + char *expected = output_to_str(0); \ + TEST_CHECK(strcmp(result, expected) == 0); \ + TEST_MSG("Wrong result. Actual: %s", expected); \ + LAGraph_Free ((void **) &expected, msg); \ + } + +typedef struct { + size_t nonterms_count; + size_t terms_count; + size_t rules_count; + LAGraph_rule_WCNF *rules; +} grammar_t; + +GrB_Matrix *adj_matrices = NULL; +int n_adj_matrices = 0 ; +GrB_Matrix *outputs = NULL; +grammar_t grammar = {0, 0, 0, NULL}; +char msg[LAGRAPH_MSG_LEN]; + +void setup() { LAGraph_Init(msg); } + +void teardown(void) { LAGraph_Finalize(msg); } + +void init_outputs() +{ + LAGraph_Calloc ((void **) &outputs, + grammar.nonterms_count, sizeof(GrB_Matrix), msg) ; +} + +char *output_to_str(size_t nonterm) +{ + GrB_Index nnz = 0; + OK(GrB_Matrix_nvals(&nnz, outputs[nonterm])); + + if (nnz == 0) + { + char *empty = NULL; + LAGraph_Malloc ((void **) &empty, 1, sizeof(char), msg); + empty[0] = '\0'; + return empty; + } + + GrB_Index *row = NULL; + GrB_Index *col = NULL; + void *val_void = NULL; + + LAGraph_Malloc ((void **) &row, nnz, sizeof(GrB_Index), msg); + LAGraph_Malloc ((void **) &col, nnz, sizeof(GrB_Index), msg); + LAGraph_Malloc ((void **) &val_void, nnz, sizeof(AllPathsVex), msg); + + OK(GrB_Matrix_extractTuples(row, col, val_void, &nnz, outputs[nonterm])); + + AllPathsVex *val = (AllPathsVex *) val_void; + + size_t cap = 256; + size_t len = 0; + char *result_str = NULL; + LAGraph_Malloc ((void **) &result_str, cap, sizeof(char), msg); + result_str[0] = '\0'; + + for (size_t i = 0; i < nnz; i++) + { + size_t needed = 64 + (val[i].n) * 20; + if (len + needed + 1 > cap) + { + while (len + needed + 1 > cap) cap *= 2; + result_str = (char *) realloc(result_str, cap); + if (result_str == NULL) + { + LAGraph_Free ((void **) &row, msg); + LAGraph_Free ((void **) &col, msg); + LAGraph_Free ((void **) &val, msg); + LAGraph_Free ((void **) &result_str, msg); + OK(GrB_OUT_OF_MEMORY); + } + } + + int wrote = 0; + if (val[i].n == 0 || val[i].middle == NULL) + { + wrote = sprintf(result_str + len, + (i == 0 ? "(%" PRIu64 ", %" PRIu64 "):[]" : " (%" PRIu64 ", %" PRIu64 "):[]"), + (uint64_t) row[i], (uint64_t) col[i]); + len += (size_t) wrote; + } + else + { + wrote = sprintf(result_str + len, + (i == 0 ? "(%" PRIu64 ", %" PRIu64 "):[" : " (%" PRIu64 ", %" PRIu64 "):["), + (uint64_t) row[i], (uint64_t) col[i]); + len += (size_t) wrote; + + for (GrB_Index k = 0; k < val[i].n; k++) + { + if (k == 0) + { + wrote = sprintf(result_str + len, "%" PRIu64, (uint64_t) val[i].middle[k]); + } + else + { + wrote = sprintf(result_str + len, ",%" PRIu64, (uint64_t) val[i].middle[k]); + } + len += (size_t) wrote; + } + + result_str[len++] = ']'; + result_str[len] = '\0'; + } + } + + LAGraph_Free ((void **) &row, msg); + LAGraph_Free ((void **) &col, msg); + LAGraph_Free ((void **) &val, msg); + + return result_str; +} + +void print_outputs(void) +{ + for (size_t i = 0; i < grammar.nonterms_count; i++) + { + char *s = output_to_str(i); + printf("Output[%zu]: %s\n", i, s[0] ? s : "(empty)"); + LAGraph_Free ((void **) &s, msg); + } +} + +void free_workspace() { + + if (adj_matrices != NULL) + { + for (size_t i = 0; i < n_adj_matrices ; i++) + { + GrB_free(&adj_matrices[i]); + } + } + LAGraph_Free ((void **) &adj_matrices, msg); + + if (outputs != NULL) + { + for (size_t i = 0; i < grammar.nonterms_count; i++) + { + GrB_free(&outputs[i]); + } + } + LAGraph_Free ((void **) &outputs, msg); + + LAGraph_Free ((void **) &grammar.rules, msg); + grammar = (grammar_t){0, 0, 0, NULL}; +} + +//==================== +// Grammars +//==================== + +// S -> aSb | ab in WCNF +// +// Terms: [0 a] [1 b] +// Nonterms: [0 S] [1 A] [2 B] [3 C] +// S -> AB [0 1 2 0] +// S -> AC [0 1 3 0] +// C -> SB [3 0 2 0] +// A -> a [1 0 -1 0] +// B -> b [2 1 -1 0] +void init_grammar_aSb() { + LAGraph_rule_WCNF *rules = NULL ; + LAGraph_Calloc ((void **) &rules, 5, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 1, 3, 0}; + rules[2] = (LAGraph_rule_WCNF){3, 0, 2, 0}; + rules[3] = (LAGraph_rule_WCNF){1, 0, -1, 0}; + rules[4] = (LAGraph_rule_WCNF){2, 1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 4, .terms_count = 2, .rules_count = 5, .rules = rules}; +} + +// S -> aS | a | eps in WCNF +// +// Terms: [0 a] +// Nonterms: [0 S] +// S -> SS [0 0 0 0] +// S -> a [0 0 -1 0] +// S -> eps [0 -1 -1 0] +void init_grammar_aS() { + LAGraph_rule_WCNF *rules = NULL ; + LAGraph_Calloc ((void **) &rules, 3, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 0, 0, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 0, -1, 0}; + rules[2] = (LAGraph_rule_WCNF){0, -1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 1, .terms_count = 1, .rules_count = 3, .rules = rules}; +} + +// Complex grammar +// aaaabbbb or aaabbb +// +// Terms: [0 a] [1 b] +// Nonterms: [0 S] [n Sn] +// S -> S1 S2 [0 1 2 0] +// S -> S15 S16 [0 15 16 0] +// S1 -> S3 S4 [1 3 4 0] +// S2 -> S5 S6 [2 5 6 0] +// S3 -> S7 S8 [3 7 8 0] +// S4 -> S9 S10 [4 9 10 0] +// S5 -> S11 S12 [5 11 12 0] +// S6 -> S13 S14 [6 13 14 0] +// S16 -> S17 S18 [16 17 18 0] +// S17 -> S19 S20 [17 19 20 0] +// S18 -> S21 S22 [18 21 22 0] +// S22 -> S23 S24 [22 23 24 0] +// S7 -> a [7 0 -1 0] +// S8 -> a [8 0 -1 0] +// S9 -> a [9 0 -1 0] +// S10 -> a [10 0 -1 0] +// S11 -> b [11 1 -1 0] +// S12 -> b [12 1 -1 0] +// S13 -> b [13 1 -1 0] +// S14 -> b [14 1 -1 0] +// S15 -> a [15 0 -1 0] +// S19 -> a [19 0 -1 0] +// S20 -> a [20 0 -1 0] +// S21 -> b [21 1 -1 0] +// S23 -> b [23 1 -1 0] +// S24 -> b [24 1 -1 0] +void init_grammar_complex() { + LAGraph_rule_WCNF *rules = NULL ; + LAGraph_Calloc ((void **) &rules, 26, sizeof(LAGraph_rule_WCNF), msg); + + rules[0] = (LAGraph_rule_WCNF){0, 1, 2, 0}; + rules[1] = (LAGraph_rule_WCNF){0, 15, 16, 0}; + rules[2] = (LAGraph_rule_WCNF){1, 3, 4, 0}; + rules[3] = (LAGraph_rule_WCNF){2, 5, 6, 0}; + rules[4] = (LAGraph_rule_WCNF){3, 7, 8, 0}; + rules[5] = (LAGraph_rule_WCNF){4, 9, 10, 0}; + rules[6] = (LAGraph_rule_WCNF){5, 11, 12, 0}; + rules[7] = (LAGraph_rule_WCNF){6, 13, 14, 0}; + rules[8] = (LAGraph_rule_WCNF){16, 17, 18, 0}; + rules[9] = (LAGraph_rule_WCNF){17, 19, 20, 0}; + rules[10] = (LAGraph_rule_WCNF){18, 21, 22, 0}; + rules[11] = (LAGraph_rule_WCNF){22, 23, 24, 0}; + rules[12] = (LAGraph_rule_WCNF){7, 0, -1, 0}; + rules[13] = (LAGraph_rule_WCNF){8, 0, -1, 0}; + rules[14] = (LAGraph_rule_WCNF){9, 0, -1, 0}; + rules[15] = (LAGraph_rule_WCNF){10, 0, -1, 0}; + rules[16] = (LAGraph_rule_WCNF){11, 1, -1, 0}; + rules[17] = (LAGraph_rule_WCNF){12, 1, -1, 0}; + rules[18] = (LAGraph_rule_WCNF){13, 1, -1, 0}; + rules[19] = (LAGraph_rule_WCNF){14, 1, -1, 0}; + rules[20] = (LAGraph_rule_WCNF){15, 0, -1, 0}; + rules[21] = (LAGraph_rule_WCNF){19, 0, -1, 0}; + rules[22] = (LAGraph_rule_WCNF){20, 0, -1, 0}; + rules[23] = (LAGraph_rule_WCNF){21, 1, -1, 0}; + rules[24] = (LAGraph_rule_WCNF){23, 1, -1, 0}; + rules[25] = (LAGraph_rule_WCNF){24, 1, -1, 0}; + + grammar = (grammar_t){ + .nonterms_count = 25, .terms_count = 2, .rules_count = 26, .rules = rules}; +} + +//==================== +// Graphs +//==================== + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 0 +// 0 -b-> 3 +// 3 -b-> 0 +void init_graph_double_cycle() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 4, 4)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 4, 4)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 3 +// 3 -a-> 4 +// 3 -b-> 5 +// 4 -b-> 3 +// 5 -b-> 6 +// 6 -b-> 7 +void init_graph_1() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 8, 8)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 8, 8)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 3)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 4)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 5)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 4, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 6)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 7)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: +// +// 0 -a-> 2 +// 1 -a-> 2 +// 3 -a-> 5 +// 4 -a-> 5 +// 2 -a-> 6 +// 5 -a-> 6 +// 2 -b-> 0 +// 2 -b-> 1 +// 5 -b-> 3 +// 5 -b-> 4 +// 6 -b-> 2 +// 6 -b-> 5 +void init_graph_tree() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + OK(GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 7, 7)); + OK(GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 7, 7)); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 3, 5)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 4, 5)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 6)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 5, 6)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 1)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 5, 4)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 2)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 6, 5)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: +// +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -a-> 0 +void init_graph_one_cycle() { + LAGraph_Calloc ((void **) &adj_matrices, 1, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 1 ; + + GrB_Matrix adj_matrix_a; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 2, 0)); + + adj_matrices[0] = adj_matrix_a; +} + +// Graph: + +// 0 -a-> 1 +// 1 -a-> 2 +// 2 -b-> 3 +// 3 -b-> 4 +void init_graph_line() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 5, 5); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 5, 5); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 2)); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 2, 3)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 3, 4)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: + +// 0 -a-> 0 +// 0 -b-> 1 +// 1 -c-> 2 +void init_graph_2() { + LAGraph_Calloc ((void **) &adj_matrices, 3, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 3 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b, adj_matrix_c; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 3, 3); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 3, 3); + GrB_Matrix_new(&adj_matrix_c, GrB_BOOL, 3, 3); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_c, true, 1, 2)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; + adj_matrices[2] = adj_matrix_c; +} + +// Graph: + +// 0 -a-> 1 +// 1 -a-> 0 +// 0 -b-> 0 +void init_graph_3() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); + + OK(GrB_Matrix_setElement(adj_matrix_a, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_a, true, 1, 0)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +// Graph: + +// 0 -b-> 1 +// 1 -b-> 0 +void init_graph_4() { + LAGraph_Calloc ((void **) &adj_matrices, 2, sizeof (GrB_Matrix), msg) ; + n_adj_matrices = 2 ; + + GrB_Matrix adj_matrix_a, adj_matrix_b; + GrB_Matrix_new(&adj_matrix_a, GrB_BOOL, 2, 2); + GrB_Matrix_new(&adj_matrix_b, GrB_BOOL, 2, 2); + + OK(GrB_Matrix_setElement(adj_matrix_b, true, 0, 1)); + OK(GrB_Matrix_setElement(adj_matrix_b, true, 1, 0)); + + adj_matrices[0] = adj_matrix_a; + adj_matrices[1] = adj_matrix_b; +} + +//==================== +// Tests with valid result +//==================== + +void test_CFL_AllPaths_cycle(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_graph_one_cycle(); + init_outputs() ; + + OK(run_algorithm()); + printf("\nMatrices:\n"); + print_outputs(); +// check_result("(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2)"); + +// free_workspace(); + teardown(); +#endif +} + +TEST_LIST = { + {"CFL_AllPaths_cycle", test_CFL_AllPaths_cycle}, + {NULL, NULL}}; + From 72e0b44ce452a787b3ce9a79f44a493b2b4666e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 16:38:43 +0400 Subject: [PATCH 18/34] tests to check that something is running --- experimental/test/test_CFL_AllPaths.c | 47 ++++++++++++++++++--------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index 084727a853..b91e3bd293 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -504,30 +504,47 @@ void init_graph_4() { adj_matrices[1] = adj_matrix_b; } -//==================== -// Tests with valid result -//==================== void test_CFL_AllPaths_cycle(void) { #if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; - - init_grammar_aS(); - init_graph_one_cycle(); - init_outputs() ; - - OK(run_algorithm()); + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_graph_one_cycle(); + init_outputs() ; + + OK(run_algorithm()); printf("\nMatrices:\n"); print_outputs(); -// check_result("(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2)"); - -// free_workspace(); - teardown(); + // check_result("(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2)"); + + // free_workspace(); + teardown(); #endif } +void test_CFL_AllPaths_two_nodes_cycle(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_3(); + init_outputs() ; + + OK(run_algorithm()); + printf("\nMatrices:\n"); + print_outputs(); + // check_result("(0, 0) (1, 0)"); + +// free_workspace(); + teardown(); +#endif +} + TEST_LIST = { + {"CFL_AllPaths_two_nodes_cycle", test_CFL_AllPaths_two_nodes_cycle}, {"CFL_AllPaths_cycle", test_CFL_AllPaths_cycle}, {NULL, NULL}}; From 28fdd2b2710f711de91c96a86f116ea16391d43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 17:16:01 +0400 Subject: [PATCH 19/34] Update LAGraph_CFL_AllPaths.c --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index d564e38d80..65d08346b0 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -70,12 +70,7 @@ void clear_all_paths_vex(AllPathsVex *z){ void add_all_paths_index(AllPathsVex *z, const AllPathsVex *x, const AllPathsVex *y) { - AllPathsVex v_temp; - AllPathsVex* temp = &v_temp; - temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); -// clear_all_paths_vex(z); - z->middle = temp->middle; - z->n = temp->n; + z->middle = merge_all_paths(&z->n, x->middle, x->n, y->middle, y->n); } void add_all_paths_free_index(AllPathsVex *z, AllPathsVex *x, AllPathsVex *y) @@ -140,7 +135,7 @@ void set_all_paths_index(AllPathsVex *z, " const AllPathsVex *y, GrB_Index iy, GrB_Index jy, \n" \ " const void *theta) \n" \ "{ \n" \ -" clear_all_paths_vex(z); \n" \ +" //clear_all_paths_vex(z); \n" \ " z->middle = malloc(sizeof(GrB_Index)); \n" \ " z->middle[0] = jx; \n" \ " z->n = 1; \n" \ From 571bf7bf06bff7a6aed79822f3ceebc1003dae47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 18:08:10 +0400 Subject: [PATCH 20/34] index op fix --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 83 +++++++++---------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 65d08346b0..81fa301c4e 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -3,7 +3,6 @@ GrB_free(&AllPaths_semiring_free); \ GrB_free(&AllPaths_monoid_free); \ GrB_free(&bottom_scalar); \ - GrB_free(&IAllPaths_set); \ GrB_free(&IAllPaths_mult); \ GrB_free(&AllPaths_set); \ GrB_free(&AllPaths_mult); \ @@ -68,12 +67,12 @@ void clear_all_paths_vex(AllPathsVex *z){ z->n = 0; } -void add_all_paths_index(AllPathsVex *z, const AllPathsVex *x, const AllPathsVex *y) +void add_all_paths(AllPathsVex *z, const AllPathsVex *x, const AllPathsVex *y) { z->middle = merge_all_paths(&z->n, x->middle, x->n, y->middle, y->n); } -void add_all_paths_free_index(AllPathsVex *z, AllPathsVex *x, AllPathsVex *y) +void add_all_paths_free(AllPathsVex *z, AllPathsVex *x, AllPathsVex *y) { AllPathsVex v_temp; AllPathsVex* temp = &v_temp; @@ -96,39 +95,24 @@ void mult_all_paths_index(AllPathsVex *z, z->n = 1; } -void set_all_paths_index(AllPathsVex *z, - const AllPathsVex *x, GrB_Index ix, GrB_Index jx, - const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, - const void *theta) +void set_all_paths(AllPathsVex *z, const AllPathsVex *x, const bool *edge_exist, const void *theta) { + AllPathsVex temp; + temp.middle = NULL; + temp.n = 0; if (edge_exist && *edge_exist){ - z->middle = malloc(sizeof(GrB_Index)); - z->n = 1; - z->middle[0] = GrB_INDEX_MAX; + temp.middle = malloc(sizeof(GrB_Index)); + temp.n = 1; + temp.middle[0] = GrB_INDEX_MAX; } else{ - z->middle = NULL; - z->n = 0; + temp.middle = NULL; + temp.n = 0; } + z->middle = merge_all_paths(&z->n, x->middle, x->n, temp.middle, temp.n); + clear_all_paths_vex(&temp); } -#define SET_PATH_INDEX_DEFN \ -"void set_all_paths_index(AllPathsVex *z, \n" \ -" const AllPathsVex *x, GrB_Index ix, GrB_Index jx,\n" \ -" const bool *edge_exist, GrB_Index i_edge, GrB_Index j_edge, \n" \ -" const void *theta) \n" \ -"{ \n" \ -" if (edge_exist && *edge_exist){ \n" \ -" z->middle = malloc(sizeof(GrB_Index)); \n" \ -" z->n = 1; \n" \ -" z->middle[0] = GrB_INDEX_MAX; \n" \ -" } \n" \ -" else{ \n" \ -" z->middle = NULL; \n" \ -" z->n = 0; \n" \ -" } \n" \ -"}" - #define MULT_PATH_INDEX_DEFN \ "void mult_all_paths_index(AllPathsVex *z, \n" \ " const AllPathsVex *x, GrB_Index ix, GrB_Index jx, \n" \ @@ -173,7 +157,7 @@ GrB_Info LAGraph_CFL_AllPaths( GxB_IndexBinaryOp IAllPaths_mult = NULL; GrB_BinaryOp AllPaths_mult = NULL; GrB_Semiring AllPaths_semiring_free = NULL; - GxB_IndexBinaryOp IAllPaths_set = NULL; +// GxB_IndexBinaryOp IAllPaths_set = NULL; GrB_BinaryOp AllPaths_set = NULL; GrB_Scalar Theta = NULL; GrB_Scalar bottom_scalar = NULL; @@ -189,14 +173,14 @@ GrB_Info LAGraph_CFL_AllPaths( GRB_TRY(GrB_BinaryOp_new( &AllPaths_add, - (void *)add_all_paths_index, + (void *)add_all_paths, AllPaths_type, AllPaths_type, AllPaths_type)); GRB_TRY(GrB_BinaryOp_new( &AllPaths_add_free, - (void *)add_all_paths_free_index, + (void *)add_all_paths_free, AllPaths_type, AllPaths_type, AllPaths_type)); @@ -226,20 +210,27 @@ GrB_Info LAGraph_CFL_AllPaths( AllPaths_monoid_free, AllPaths_mult)); - GRB_TRY(GxB_IndexBinaryOp_new( - &IAllPaths_set, - (void *)set_all_paths_index, - AllPaths_type, - AllPaths_type, - GrB_BOOL, - GrB_BOOL, - "set_all_paths_index", - SET_PATH_INDEX_DEFN)); - - GRB_TRY(GxB_BinaryOp_new_IndexOp( - &AllPaths_set, - IAllPaths_set, - Theta)); + GRB_TRY(GrB_BinaryOp_new( + &AllPaths_set, + (void *)set_all_paths, + AllPaths_type, + AllPaths_type, + GrB_BOOL)); + +// GRB_TRY(GxB_IndexBinaryOp_new( +// &IAllPaths_set, +// (void *)set_all_paths_index, +// AllPaths_type, +// AllPaths_type, +// GrB_BOOL, +// GrB_BOOL, +// "set_all_paths_index", +// SET_PATH_INDEX_DEFN)); + +// GRB_TRY(GxB_BinaryOp_new_IndexOp( +// &AllPaths_set, +// IAllPaths_set, +// Theta)); CFL_Semiring semiring = {.type = AllPaths_type, .semiring = AllPaths_semiring_free, From c612d927c2ea9393870ba68b8c01a5bb5f670503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 18:15:09 +0400 Subject: [PATCH 21/34] Update LAGraph_CFL_AllPaths.c --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 81fa301c4e..ebdf395b55 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -95,7 +95,7 @@ void mult_all_paths_index(AllPathsVex *z, z->n = 1; } -void set_all_paths(AllPathsVex *z, const AllPathsVex *x, const bool *edge_exist, const void *theta) +void set_all_paths(AllPathsVex *z, const AllPathsVex *x, const bool *edge_exist) { AllPathsVex temp; temp.middle = NULL; From 3e6add08062737e040ad9e75de9596c4ab85db61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 21:18:16 +0400 Subject: [PATCH 22/34] replace GrB_Matrix_nvals in CFPQ_core --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 45 ++++++++++++++++--- .../algorithm/LAGraph_CFL_reachability.c | 15 ++++--- experimental/algorithm/LAGraph_CFPQ_core.c | 2 +- include/LAGraphX.h | 3 ++ 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index ebdf395b55..b21ae11f2e 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -125,6 +125,38 @@ void set_all_paths(AllPathsVex *z, const AllPathsVex *x, const bool *edge_exist) " z->n = 1; \n" \ "}" +//A function that replaces GrB_Matrix_nvals for counting non-zero elements +GrB_Info all_paths_get_nvals(GrB_Index *nvals, const GrB_Matrix A){ + GrB_Index nnz = 0; + GrB_Index accum = 0; + GrB_Matrix_nvals(&nnz, A); + *nvals = 0; + void *val_void = NULL; + + if(nnz==0){ + *nvals = 0; + return GrB_SUCCESS; + } + + val_void = malloc(nnz * sizeof(AllPathsVex)); + + GrB_Matrix_extractTuples(NULL, NULL, val_void, &nnz, A); + + AllPathsVex *val = (AllPathsVex *) val_void; + + size_t end = nnz; + + for (size_t start = 0; start < end; ++start){ + accum+=val[start].n; + } + + *nvals = accum; + + free(val_void); + + return GrB_SUCCESS; +} + GrB_Info LAGraph_CFL_AllPaths( // Output GrB_Matrix *outputs, // Array of matrices containing results. @@ -233,12 +265,13 @@ GrB_Info LAGraph_CFL_AllPaths( // Theta)); CFL_Semiring semiring = {.type = AllPaths_type, - .semiring = AllPaths_semiring_free, - .add = AllPaths_add_free, - .add_eps = AllPaths_add, - .mult = AllPaths_mult, - .init_path = AllPaths_set, - .bottom_scalar = bottom_scalar}; + .semiring = AllPaths_semiring_free, + .add = AllPaths_add_free, + .add_eps = AllPaths_add, + .mult = AllPaths_mult, + .init_path = AllPaths_set, + .bottom_scalar = bottom_scalar, + .get_nvals = all_paths_get_nvals}; LG_TRY(LAGraph_CFPQ_core(outputs, adj_matrices, terms_count, nonterms_count, rules, rules_count, &semiring, msg)); LG_FREE_WORK; diff --git a/experimental/algorithm/LAGraph_CFL_reachability.c b/experimental/algorithm/LAGraph_CFL_reachability.c index 8e4f02b6f5..4091fd3ca1 100644 --- a/experimental/algorithm/LAGraph_CFL_reachability.c +++ b/experimental/algorithm/LAGraph_CFL_reachability.c @@ -92,13 +92,14 @@ GrB_Info LAGraph_CFL_reachability( GrB_Scalar false_scalar; GRB_TRY(GrB_Scalar_new(&false_scalar, GrB_BOOL)); GRB_TRY(GrB_Scalar_setElement_BOOL(false_scalar, false)); - CFL_Semiring semiring = {.type = GrB_BOOL, - .semiring = GxB_ANY_PAIR_BOOL, - .add = GxB_ANY_BOOL, - .add_eps = GxB_ANY_BOOL, - .mult = GxB_PAIR_BOOL, - .init_path = GxB_PAIR_BOOL, - .bottom_scalar = false_scalar}; + CFL_Semiring semiring = {.type = GrB_BOOL, + .semiring = GxB_ANY_PAIR_BOOL, + .add = GxB_ANY_BOOL, + .add_eps = GxB_ANY_BOOL, + .mult = GxB_PAIR_BOOL, + .init_path = GxB_PAIR_BOOL, + .bottom_scalar = false_scalar, + .get_nvals = GrB_Matrix_nvals}; LG_TRY(LAGraph_CFPQ_core(outputs, adj_matrices, terms_count, nonterms_count, rules, rules_count, &semiring, msg)); LG_FREE_WORK; return GrB_SUCCESS; diff --git a/experimental/algorithm/LAGraph_CFPQ_core.c b/experimental/algorithm/LAGraph_CFPQ_core.c index fd6fc6db22..7c532860e2 100644 --- a/experimental/algorithm/LAGraph_CFPQ_core.c +++ b/experimental/algorithm/LAGraph_CFPQ_core.c @@ -303,7 +303,7 @@ GrB_Info LAGraph_CFPQ_core( GrB_NULL)) GrB_Index new_nnz; - GRB_TRY(GrB_Matrix_nvals(&new_nnz, T[bin_rule.nonterm])); + GRB_TRY(semiring->get_nvals(&new_nnz, T[bin_rule.nonterm])); if (new_nnz != 0) t_empty_flags[bin_rule.nonterm] = false; diff --git a/include/LAGraphX.h b/include/LAGraphX.h index da490091a8..244f8b1f92 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1096,6 +1096,8 @@ GrB_Info LAGraph_CFL_reachability char *msg // Message string for error reporting. ) ; +typedef GrB_Info (*CFPQ_get_nvals)(GrB_Index *nvals, const GrB_Matrix A); + typedef struct { GrB_Type type; @@ -1105,6 +1107,7 @@ typedef struct GrB_BinaryOp mult; GrB_BinaryOp init_path; // Function for defining elements used to describe information about paths of length 0 and 1. // Depends on the specific task, therefore it is included in this structure. + CFPQ_get_nvals get_nvals; GrB_Scalar bottom_scalar; } CFL_Semiring; From 5ca41bbb72018a7798c7a411a504fc4eef92b3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 4 Mar 2026 22:29:36 +0400 Subject: [PATCH 23/34] cosmetic --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index b21ae11f2e..29f78c8150 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -100,15 +100,13 @@ void set_all_paths(AllPathsVex *z, const AllPathsVex *x, const bool *edge_exist) AllPathsVex temp; temp.middle = NULL; temp.n = 0; + if (edge_exist && *edge_exist){ temp.middle = malloc(sizeof(GrB_Index)); temp.n = 1; temp.middle[0] = GrB_INDEX_MAX; } - else{ - temp.middle = NULL; - temp.n = 0; - } + z->middle = merge_all_paths(&z->n, x->middle, x->n, temp.middle, temp.n); clear_all_paths_vex(&temp); } @@ -139,17 +137,13 @@ GrB_Info all_paths_get_nvals(GrB_Index *nvals, const GrB_Matrix A){ } val_void = malloc(nnz * sizeof(AllPathsVex)); - GrB_Matrix_extractTuples(NULL, NULL, val_void, &nnz, A); - AllPathsVex *val = (AllPathsVex *) val_void; size_t end = nnz; - for (size_t start = 0; start < end; ++start){ accum+=val[start].n; } - *nvals = accum; free(val_void); @@ -189,7 +183,6 @@ GrB_Info LAGraph_CFL_AllPaths( GxB_IndexBinaryOp IAllPaths_mult = NULL; GrB_BinaryOp AllPaths_mult = NULL; GrB_Semiring AllPaths_semiring_free = NULL; -// GxB_IndexBinaryOp IAllPaths_set = NULL; GrB_BinaryOp AllPaths_set = NULL; GrB_Scalar Theta = NULL; GrB_Scalar bottom_scalar = NULL; @@ -249,21 +242,6 @@ GrB_Info LAGraph_CFL_AllPaths( AllPaths_type, GrB_BOOL)); -// GRB_TRY(GxB_IndexBinaryOp_new( -// &IAllPaths_set, -// (void *)set_all_paths_index, -// AllPaths_type, -// AllPaths_type, -// GrB_BOOL, -// GrB_BOOL, -// "set_all_paths_index", -// SET_PATH_INDEX_DEFN)); - -// GRB_TRY(GxB_BinaryOp_new_IndexOp( -// &AllPaths_set, -// IAllPaths_set, -// Theta)); - CFL_Semiring semiring = {.type = AllPaths_type, .semiring = AllPaths_semiring_free, .add = AllPaths_add_free, From ad8bedcbf4aa2d6aef0c0adce34f9ad54a495b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Sun, 8 Mar 2026 17:51:47 +0400 Subject: [PATCH 24/34] fix naming, comment, and replace get_nvals --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 113 ++++++++++-------- experimental/test/test_CFL_AllPaths.c | 4 +- include/LAGraphX.h | 2 +- 3 files changed, 66 insertions(+), 53 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 29f78c8150..5f703f1388 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -2,12 +2,14 @@ { \ GrB_free(&AllPaths_semiring_free); \ GrB_free(&AllPaths_monoid_free); \ + GrB_free(&AllPaths_monoid_get_nvals); \ GrB_free(&bottom_scalar); \ GrB_free(&IAllPaths_mult); \ GrB_free(&AllPaths_set); \ GrB_free(&AllPaths_mult); \ GrB_free(&AllPaths_add); \ GrB_free(&AllPaths_add_free); \ + GrB_free(&AllPaths_add_get_nvals); \ GrB_free(&Theta); \ } @@ -24,7 +26,6 @@ static GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Inde } GrB_Index *tmp = malloc((na + nb) * sizeof(GrB_Index)); - // LG_TRY(LAGraph_Malloc((void**)&tmp, na+nb, sizeof(GrB_Index), msg)); GrB_Index ia = 0, ib = 0, outn = 0; while (ia < na && ib < nb) { @@ -50,7 +51,6 @@ static GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Inde if (outn == 0 || tmp[outn-1] != vb) tmp[outn++] = vb; } - // LG_TRY(LAGraph_Realloc((void**)&tmp, outn, na+nb, sizeof(GrB_Index), msg)); GrB_Index *sh = realloc(tmp, outn * sizeof(GrB_Index)); if (sh) tmp = sh; @@ -58,8 +58,7 @@ static GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Inde return tmp; } -void clear_all_paths_vex(AllPathsVex *z){ -// if(z->middle) LG_TRY(LAGraph_Free((void**) z->middle, msg)); +void clear_elem_all_paths(AllPathsElem *z){ if(z->middle){ free(z->middle); z->middle=NULL; @@ -67,37 +66,35 @@ void clear_all_paths_vex(AllPathsVex *z){ z->n = 0; } -void add_all_paths(AllPathsVex *z, const AllPathsVex *x, const AllPathsVex *y) +void add_all_paths(AllPathsElem *z, const AllPathsElem *x, const AllPathsElem *y) { z->middle = merge_all_paths(&z->n, x->middle, x->n, y->middle, y->n); } -void add_all_paths_free(AllPathsVex *z, AllPathsVex *x, AllPathsVex *y) +void add_free_all_paths(AllPathsElem *z, AllPathsElem *x, AllPathsElem *y) { - AllPathsVex v_temp; - AllPathsVex* temp = &v_temp; + AllPathsElem v_temp; + AllPathsElem* temp = &v_temp; temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); - clear_all_paths_vex(x); - clear_all_paths_vex(y); -// clear_all_paths_vex(z); + clear_elem_all_paths(x); + clear_elem_all_paths(y); z->middle = temp->middle; z->n = temp->n; } -void mult_all_paths_index(AllPathsVex *z, - const AllPathsVex *x, GrB_Index ix, GrB_Index jx, - const AllPathsVex *y, GrB_Index iy, GrB_Index jy, +void mult_all_paths(AllPathsElem *z, + const AllPathsElem *x, GrB_Index ix, GrB_Index jx, + const AllPathsElem *y, GrB_Index iy, GrB_Index jy, const void *theta) { -// LG_TRY(LAGraph_Malloc((void**) &z->middle, 1, size_of(GrB_Index), msg)); z->middle = malloc(sizeof(GrB_Index)); z->middle[0] = jx; z->n = 1; } -void set_all_paths(AllPathsVex *z, const AllPathsVex *x, const bool *edge_exist) +void set_all_paths(AllPathsElem *z, const AllPathsElem *x, const bool *edge_exist) { - AllPathsVex temp; + AllPathsElem temp; temp.middle = NULL; temp.n = 0; @@ -108,46 +105,45 @@ void set_all_paths(AllPathsVex *z, const AllPathsVex *x, const bool *edge_exist) } z->middle = merge_all_paths(&z->n, x->middle, x->n, temp.middle, temp.n); - clear_all_paths_vex(&temp); + clear_elem_all_paths(&temp); } #define MULT_PATH_INDEX_DEFN \ -"void mult_all_paths_index(AllPathsVex *z, \n" \ -" const AllPathsVex *x, GrB_Index ix, GrB_Index jx, \n" \ -" const AllPathsVex *y, GrB_Index iy, GrB_Index jy, \n" \ +"void mult_all_paths(AllPathsElem *z, \n" \ +" const AllPathsElem *x, GrB_Index ix, GrB_Index jx, \n" \ +" const AllPathsElem *y, GrB_Index iy, GrB_Index jy, \n" \ " const void *theta) \n" \ "{ \n" \ -" //clear_all_paths_vex(z); \n" \ " z->middle = malloc(sizeof(GrB_Index)); \n" \ " z->middle[0] = jx; \n" \ " z->n = 1; \n" \ "}" +void add_get_nvals_all_paths(AllPathsElem *z, const AllPathsElem *x, const AllPathsElem *y) +{ + z->n = x->n + y->n; + z->middle = NULL; +} + +static GrB_Type* AllPaths_type_get_nvals = NULL; +static GrB_Monoid AllPaths_monoid_get_nvals = NULL; + //A function that replaces GrB_Matrix_nvals for counting non-zero elements -GrB_Info all_paths_get_nvals(GrB_Index *nvals, const GrB_Matrix A){ - GrB_Index nnz = 0; - GrB_Index accum = 0; - GrB_Matrix_nvals(&nnz, A); - *nvals = 0; - void *val_void = NULL; +GrB_Info get_nvals_all_paths(GrB_Index *nvals, const GrB_Matrix A){ + GrB_Scalar s = NULL; + GrB_Scalar_new(&s, *AllPaths_type_get_nvals); + GrB_Info info = GrB_reduce(s, NULL, AllPaths_monoid_get_nvals, A, NULL); - if(nnz==0){ - *nvals = 0; - return GrB_SUCCESS; + if (info != GrB_SUCCESS) + { + GrB_free(&s); + return info; } - val_void = malloc(nnz * sizeof(AllPathsVex)); - GrB_Matrix_extractTuples(NULL, NULL, val_void, &nnz, A); - AllPathsVex *val = (AllPathsVex *) val_void; - - size_t end = nnz; - for (size_t start = 0; start < end; ++start){ - accum+=val[start].n; - } - *nvals = accum; - - free(val_void); - + AllPathsElem result; + GrB_Scalar_extractElement_UDT(&result, s); + *nvals = result.n; + GrB_free(&s); return GrB_SUCCESS; } @@ -156,7 +152,7 @@ GrB_Info LAGraph_CFL_AllPaths( GrB_Matrix *outputs, // Array of matrices containing results. // The size of the array must be equal to nonterms_count. // - // outputs[k]: (i, j) contains a AllPathsVex structure if and only if there is a path + // outputs[k]: (i, j) contains a AllPathsElem structure if and only if there is a path // from node i to node j whose edge labels form a word // derivable from the non-terminal 'k' of the specified CFG. // Input @@ -179,6 +175,7 @@ GrB_Info LAGraph_CFL_AllPaths( GrB_Type AllPaths_type = NULL; GrB_BinaryOp AllPaths_add = NULL; GrB_BinaryOp AllPaths_add_free = NULL; + GrB_BinaryOp AllPaths_add_get_nvals = NULL; GrB_Monoid AllPaths_monoid_free = NULL; GxB_IndexBinaryOp IAllPaths_mult = NULL; GrB_BinaryOp AllPaths_mult = NULL; @@ -187,12 +184,14 @@ GrB_Info LAGraph_CFL_AllPaths( GrB_Scalar Theta = NULL; GrB_Scalar bottom_scalar = NULL; - GRB_TRY(GrB_Type_new(&AllPaths_type, sizeof(AllPathsVex))); + GRB_TRY(GrB_Type_new(&AllPaths_type, sizeof(AllPathsElem))); + AllPaths_type_get_nvals = &AllPaths_type; + GRB_TRY(GrB_Scalar_new(&Theta, GrB_BOOL)); GRB_TRY(GrB_Scalar_setElement_BOOL(Theta, false)); - AllPathsVex bottom = {0, NULL}; + AllPathsElem bottom = {0, NULL}; GRB_TRY(GrB_Scalar_new(&bottom_scalar, AllPaths_type)); GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); @@ -205,7 +204,7 @@ GrB_Info LAGraph_CFL_AllPaths( GRB_TRY(GrB_BinaryOp_new( &AllPaths_add_free, - (void *)add_all_paths_free, + (void *)add_free_all_paths, AllPaths_type, AllPaths_type, AllPaths_type)); @@ -217,12 +216,12 @@ GrB_Info LAGraph_CFL_AllPaths( GRB_TRY(GxB_IndexBinaryOp_new( &IAllPaths_mult, - (void *)mult_all_paths_index, + (void *)mult_all_paths, AllPaths_type, AllPaths_type, AllPaths_type, GrB_BOOL, - "mult_all_paths_index", + "mult_all_paths", MULT_PATH_INDEX_DEFN)); GRB_TRY(GxB_BinaryOp_new_IndexOp( @@ -242,6 +241,18 @@ GrB_Info LAGraph_CFL_AllPaths( AllPaths_type, GrB_BOOL)); + GRB_TRY(GrB_BinaryOp_new( + &AllPaths_add_get_nvals, + (void *)add_get_nvals_all_paths, + AllPaths_type, + AllPaths_type, + AllPaths_type)); + + GRB_TRY(GrB_Monoid_new( + &AllPaths_monoid_get_nvals, + AllPaths_add_get_nvals, + (void *)(&bottom))); + CFL_Semiring semiring = {.type = AllPaths_type, .semiring = AllPaths_semiring_free, .add = AllPaths_add_free, @@ -249,9 +260,11 @@ GrB_Info LAGraph_CFL_AllPaths( .mult = AllPaths_mult, .init_path = AllPaths_set, .bottom_scalar = bottom_scalar, - .get_nvals = all_paths_get_nvals}; + .get_nvals = get_nvals_all_paths}; LG_TRY(LAGraph_CFPQ_core(outputs, adj_matrices, terms_count, nonterms_count, rules, rules_count, &semiring, msg)); + + AllPaths_type_get_nvals = NULL; LG_FREE_WORK; return GrB_SUCCESS; } diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index b91e3bd293..d6339de0c3 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -81,11 +81,11 @@ char *output_to_str(size_t nonterm) LAGraph_Malloc ((void **) &row, nnz, sizeof(GrB_Index), msg); LAGraph_Malloc ((void **) &col, nnz, sizeof(GrB_Index), msg); - LAGraph_Malloc ((void **) &val_void, nnz, sizeof(AllPathsVex), msg); + LAGraph_Malloc ((void **) &val_void, nnz, sizeof(AllPathsElem), msg); OK(GrB_Matrix_extractTuples(row, col, val_void, &nnz, outputs[nonterm])); - AllPathsVex *val = (AllPathsVex *) val_void; + AllPathsElem *val = (AllPathsElem *) val_void; size_t cap = 256; size_t len = 0; diff --git a/include/LAGraphX.h b/include/LAGraphX.h index 244f8b1f92..3c87a5d53e 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1115,7 +1115,7 @@ typedef struct { GrB_Index n; GrB_Index* middle; -} AllPathsVex; +} AllPathsElem; GrB_Info LAGraph_CFPQ_core ( From 2252e74561b85a6494dc0be0def807a861c924ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Sun, 8 Mar 2026 20:08:24 +0400 Subject: [PATCH 25/34] Sanitizer fix --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 3 ++- experimental/test/test_CFL_AllPaths.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 5f703f1388..d738176a7b 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -11,6 +11,7 @@ GrB_free(&AllPaths_add_free); \ GrB_free(&AllPaths_add_get_nvals); \ GrB_free(&Theta); \ + GrB_free(&AllPaths_type); \ } #include "LG_internal.h" @@ -256,7 +257,7 @@ GrB_Info LAGraph_CFL_AllPaths( CFL_Semiring semiring = {.type = AllPaths_type, .semiring = AllPaths_semiring_free, .add = AllPaths_add_free, - .add_eps = AllPaths_add, + .add_eps = AllPaths_add_free, .mult = AllPaths_mult, .init_path = AllPaths_set, .bottom_scalar = bottom_scalar, diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index d6339de0c3..6c3b57231c 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -175,6 +175,14 @@ void free_workspace() { { for (size_t i = 0; i < grammar.nonterms_count; i++) { + void* val_void = NULL; + GrB_Index nnz = 0; + GrB_Matrix_nvals(&nnz, outputs[i]); + LAGraph_Malloc ((void **) &val_void, nnz, sizeof (AllPathsElem), msg) ; + GrB_Matrix_extractTuples(NULL, NULL, val_void, &nnz, outputs[i]); + AllPathsElem* val = (AllPathsElem*) val_void; + for(size_t j = 0; j Date: Sun, 8 Mar 2026 20:21:46 +0400 Subject: [PATCH 26/34] delete add without free mem --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 15 --------------- experimental/algorithm/LAGraph_CFL_reachability.c | 1 - experimental/algorithm/LAGraph_CFPQ_core.c | 2 +- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index d738176a7b..4b1d9100db 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -7,7 +7,6 @@ GrB_free(&IAllPaths_mult); \ GrB_free(&AllPaths_set); \ GrB_free(&AllPaths_mult); \ - GrB_free(&AllPaths_add); \ GrB_free(&AllPaths_add_free); \ GrB_free(&AllPaths_add_get_nvals); \ GrB_free(&Theta); \ @@ -67,11 +66,6 @@ void clear_elem_all_paths(AllPathsElem *z){ z->n = 0; } -void add_all_paths(AllPathsElem *z, const AllPathsElem *x, const AllPathsElem *y) -{ - z->middle = merge_all_paths(&z->n, x->middle, x->n, y->middle, y->n); -} - void add_free_all_paths(AllPathsElem *z, AllPathsElem *x, AllPathsElem *y) { AllPathsElem v_temp; @@ -174,7 +168,6 @@ GrB_Info LAGraph_CFL_AllPaths( { // Semiring components GrB_Type AllPaths_type = NULL; - GrB_BinaryOp AllPaths_add = NULL; GrB_BinaryOp AllPaths_add_free = NULL; GrB_BinaryOp AllPaths_add_get_nvals = NULL; GrB_Monoid AllPaths_monoid_free = NULL; @@ -195,13 +188,6 @@ GrB_Info LAGraph_CFL_AllPaths( AllPathsElem bottom = {0, NULL}; GRB_TRY(GrB_Scalar_new(&bottom_scalar, AllPaths_type)); GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); - - GRB_TRY(GrB_BinaryOp_new( - &AllPaths_add, - (void *)add_all_paths, - AllPaths_type, - AllPaths_type, - AllPaths_type)); GRB_TRY(GrB_BinaryOp_new( &AllPaths_add_free, @@ -257,7 +243,6 @@ GrB_Info LAGraph_CFL_AllPaths( CFL_Semiring semiring = {.type = AllPaths_type, .semiring = AllPaths_semiring_free, .add = AllPaths_add_free, - .add_eps = AllPaths_add_free, .mult = AllPaths_mult, .init_path = AllPaths_set, .bottom_scalar = bottom_scalar, diff --git a/experimental/algorithm/LAGraph_CFL_reachability.c b/experimental/algorithm/LAGraph_CFL_reachability.c index 4091fd3ca1..b09881924c 100644 --- a/experimental/algorithm/LAGraph_CFL_reachability.c +++ b/experimental/algorithm/LAGraph_CFL_reachability.c @@ -95,7 +95,6 @@ GrB_Info LAGraph_CFL_reachability( CFL_Semiring semiring = {.type = GrB_BOOL, .semiring = GxB_ANY_PAIR_BOOL, .add = GxB_ANY_BOOL, - .add_eps = GxB_ANY_BOOL, .mult = GxB_PAIR_BOOL, .init_path = GxB_PAIR_BOOL, .bottom_scalar = false_scalar, diff --git a/experimental/algorithm/LAGraph_CFPQ_core.c b/experimental/algorithm/LAGraph_CFPQ_core.c index 7c532860e2..c4aecae1cf 100644 --- a/experimental/algorithm/LAGraph_CFPQ_core.c +++ b/experimental/algorithm/LAGraph_CFPQ_core.c @@ -273,7 +273,7 @@ GrB_Info LAGraph_CFPQ_core( for (int64_t i = 0; i < eps_rules_count; i++) { LAGraph_rule_WCNF eps_rule = rules[eps_rules[i]]; - GrB_BinaryOp acc_op = t_empty_flags[eps_rule.nonterm] ? GrB_NULL : semiring->add_eps; + GrB_BinaryOp acc_op = t_empty_flags[eps_rule.nonterm] ? GrB_NULL : semiring->add; GxB_eWiseUnion( T[eps_rule.nonterm], GrB_NULL, acc_op, semiring->init_path, T[eps_rule.nonterm], semiring->bottom_scalar, identity_matrix, false_scalar, GrB_NULL); From dba1f05070a088491a1d556d4d3b8cb6ad0d756d Mon Sep 17 00:00:00 2001 From: Litvyakov Daniil <140255125+DanyaLitva@users.noreply.github.com> Date: Sun, 8 Mar 2026 20:23:30 +0300 Subject: [PATCH 27/34] delete add_eps from LAGraphX.h --- include/LAGraphX.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/LAGraphX.h b/include/LAGraphX.h index 3c87a5d53e..8b340d7dca 100644 --- a/include/LAGraphX.h +++ b/include/LAGraphX.h @@ -1103,7 +1103,6 @@ typedef struct GrB_Type type; GrB_Semiring semiring; GrB_BinaryOp add; - GrB_BinaryOp add_eps; GrB_BinaryOp mult; GrB_BinaryOp init_path; // Function for defining elements used to describe information about paths of length 0 and 1. // Depends on the specific task, therefore it is included in this structure. From 239f8223a30e6364b0ee07410b7a90fcf3fdf0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Sun, 8 Mar 2026 21:59:47 +0400 Subject: [PATCH 28/34] cosmetic --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 4b1d9100db..6a1e030829 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -68,13 +68,12 @@ void clear_elem_all_paths(AllPathsElem *z){ void add_free_all_paths(AllPathsElem *z, AllPathsElem *x, AllPathsElem *y) { - AllPathsElem v_temp; - AllPathsElem* temp = &v_temp; - temp->middle = merge_all_paths(&temp->n, x->middle, x->n, y->middle, y->n); + AllPathsElem temp; + temp.middle = merge_all_paths(&temp.n, x->middle, x->n, y->middle, y->n); clear_elem_all_paths(x); clear_elem_all_paths(y); - z->middle = temp->middle; - z->n = temp->n; + z->middle = temp.middle; + z->n = temp.n; } void mult_all_paths(AllPathsElem *z, From ab746fb68750d2bee69ad9599ae36ba36a6e4726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 11 Mar 2026 00:46:02 +0400 Subject: [PATCH 29/34] shared tests across different algorithms --- experimental/test/test_CFL_AllPaths.c | 291 ++++++++++++++++++++++---- 1 file changed, 250 insertions(+), 41 deletions(-) diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index 6c3b57231c..0fad6372cb 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// LAGraph/experimental/test/LAGraph_CFL_reachability.c: test cases for Context-Free -// Language Reachability Matrix-Based Algorithm -//------------------------------------------------------------------------------ -// -// LAGraph, (c) 2019-2024 by The LAGraph Contributors, All Rights Reserved. -// SPDX-License-Identifier: BSD-2-Clause - -// Contributed by Ilhom Kombaev, Semyon Grigoriev, St. Petersburg State University. - -//------------------------------------------------------------------------------ - #include #include #include @@ -31,9 +19,9 @@ TEST_MSG("retval = %d (%s)", retval, msg); \ } -#define check_result(result) \ +#define check_result(nonterm, result) \ { \ - char *expected = output_to_str(0); \ + char *expected = output_to_str(nonterm); \ TEST_CHECK(strcmp(result, expected) == 0); \ TEST_MSG("Wrong result. Actual: %s", expected); \ LAGraph_Free ((void **) &expected, msg); \ @@ -129,11 +117,19 @@ char *output_to_str(size_t nonterm) { if (k == 0) { - wrote = sprintf(result_str + len, "%" PRIu64, (uint64_t) val[i].middle[k]); + if (val[i].middle[k] == GrB_INDEX_MAX) { + wrote = sprintf(result_str + len, "INDEX_MAX"); + } else { + wrote = sprintf(result_str + len, "%" PRIu64, (uint64_t)val[i].middle[k]); + } } else { - wrote = sprintf(result_str + len, ",%" PRIu64, (uint64_t) val[i].middle[k]); + if (val[i].middle[k] == GrB_INDEX_MAX) { + wrote = sprintf(result_str + len, ",INDEX_MAX"); + } else { + wrote = sprintf(result_str + len, ",%" PRIu64, (uint64_t)val[i].middle[k]); + } } len += (size_t) wrote; } @@ -512,47 +508,260 @@ void init_graph_4() { adj_matrices[1] = adj_matrix_b; } +//==================== +// Tests with valid result +//==================== void test_CFL_AllPaths_cycle(void) { #if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_graph_one_cycle(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0, "(0, 0):[0,1,2,INDEX_MAX] (0, 1):[0,1,2,INDEX_MAX] (0, 2):[0,1,2] (1, 0):[0,1,2] (1, 1):[0,1,2,INDEX_MAX] (1, 2):[0,1,2,INDEX_MAX] (2, 0):[0,1,2,INDEX_MAX] (2, 1):[0,1,2] (2, 2):[0,1,2,INDEX_MAX]"); - init_grammar_aS(); - init_graph_one_cycle(); - init_outputs() ; + free_workspace(); + teardown(); +#endif +} + +void test_CFL_AllPaths_two_cycle(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0, "(0, 0):[1] (0, 3):[1] (1, 0):[2] (1, 3):[2] (2, 0):[0] (2, 3):[0]"); + check_result(1,"(0, 1):[INDEX_MAX] (1, 2):[INDEX_MAX] (2, 0):[INDEX_MAX]"); - OK(run_algorithm()); - printf("\nMatrices:\n"); - print_outputs(); - // check_result("(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2)"); + free_workspace(); + teardown(); +#endif +} + +void test_CFL_AllPaths_labels_more_than_nonterms(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_2(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0, "(0, 1):[0]"); + check_result(1,"(0, 0):[INDEX_MAX]"); + check_result(2,"(0, 1):[INDEX_MAX]"); + check_result(3,""); - free_workspace(); - teardown(); + free_workspace(); + teardown(); #endif } -void test_CFL_AllPaths_two_nodes_cycle(void) { +void test_CFL_AllPaths_complex_grammar(void) { #if LAGRAPH_SUITESPARSE - setup(); - GrB_Info retval; + setup(); + GrB_Info retval; + + init_grammar_complex(); + init_graph_1(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0, "(0, 7):[1,4] (1, 6):[2]"); + check_result(1, "(0, 4):[2]"); + check_result(6,"(3, 6):[5] (4, 5):[3] (5, 7):[6]"); + check_result(7,"(0, 1):[INDEX_MAX] (1, 2):[INDEX_MAX] (2, 3):[INDEX_MAX] (3, 4):[INDEX_MAX]"); + - init_grammar_aSb(); - init_graph_3(); - init_outputs() ; + free_workspace(); + teardown(); +#endif +} + +void test_CFL_AllPaths_tree(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_tree(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0,"(0, 0):[2] (0, 1):[2] (0, 3):[2] (0, 4):[2] (1, 0):[2] (1, 1):[2] (1, 3):[2] (1, 4):[2] (2, 2):[6] (2, 5):[6] (3, 0):[5] (3, 1):[5] (3, 3):[5] (3, 4):[5] (4, 0):[5] (4, 1):[5] (4, 3):[5] (4, 4):[5] (5, 2):[6] (5, 5):[6]"); + check_result(1, "(0, 2):[INDEX_MAX] (1, 2):[INDEX_MAX] (2, 6):[INDEX_MAX] (3, 5):[INDEX_MAX] (4, 5):[INDEX_MAX] (5, 6):[INDEX_MAX]"); - OK(run_algorithm()); - printf("\nMatrices:\n"); - print_outputs(); - // check_result("(0, 0) (1, 0)"); + free_workspace(); + teardown(); +#endif +} + +void test_CFL_AllPaths_line(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_line(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0,"(0, 4):[1] (1, 3):[2]"); + check_result(1,"(0, 1):[INDEX_MAX] (1, 2):[INDEX_MAX]"); + check_result(2,"(2, 3):[INDEX_MAX] (3, 4):[INDEX_MAX]"); + check_result(3,"(1, 4):[3]"); - free_workspace(); - teardown(); + free_workspace(); + teardown(); #endif } + +void test_CFL_AllPaths_two_nodes_cycle(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_3(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0,"(0, 0):[1] (1, 0):[0]"); + check_result(1,"(0, 1):[INDEX_MAX] (1, 0):[INDEX_MAX]"); + check_result(2, "(0, 0):[INDEX_MAX]"); + check_result(3, "(0, 0):[0] (1, 0):[0]"); -TEST_LIST = { - {"CFL_AllPaths_two_nodes_cycle", test_CFL_AllPaths_two_nodes_cycle}, + free_workspace(); + teardown(); +#endif +} + +void test_CFL_AllPaths_with_empty_adj_matrix(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aS(); + init_graph_4(); + init_outputs() ; + + OK(run_algorithm()); + check_result(0, "(0, 0):[0,INDEX_MAX] (1, 1):[1,INDEX_MAX]"); + + free_workspace(); + teardown(); +#endif +} + +//==================== +// Tests with invalid result +//==================== + +void test_CFL_AllPaths_invalid_rules(void) { +#if LAGRAPH_SUITESPARSE + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + + // Rule [Variable -> _ B] + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = -1, .prod_B = 1, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [_ -> A B] + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = -1, .prod_A = 1, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [C -> A B], where C >= nonterms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 10, .prod_A = 1, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [S -> A B], where A >= nonterms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = 2, .index = 0}; + check_error(GrB_INVALID_VALUE); + + // Rule [C -> t], where t >= terms_count + grammar.rules[0] = + (LAGraph_rule_WCNF){.nonterm = 0, .prod_A = 10, .prod_B = -1, .index = 0}; + check_error(GrB_INVALID_VALUE); + + free_workspace(); + teardown(); +#endif +} + +void test_CFL_AllPaths_null_pointers(void) { +#if LAGRAPH_SUITESPARSE + + setup(); + GrB_Info retval; + + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + +// adj_matrices[0] = NULL; +// adj_matrices[1] = NULL; + GrB_free(&adj_matrices[0]); + GrB_free(&adj_matrices[1]); + + check_error(GrB_NULL_POINTER); + +// adj_matrices = NULL; + LAGraph_Free ((void **) &adj_matrices, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + +// outputs = NULL; + LAGraph_Free ((void **) &outputs, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + init_grammar_aSb(); + init_graph_double_cycle(); + init_outputs() ; + +// grammar.rules = NULL; + LAGraph_Free ((void **) &grammar.rules, msg); + check_error(GrB_NULL_POINTER); + + free_workspace(); + teardown(); +#endif +} + +TEST_LIST = {{"CFL_AllPaths_complex_grammar", test_CFL_AllPaths_complex_grammar}, {"CFL_AllPaths_cycle", test_CFL_AllPaths_cycle}, + {"CFL_AllPaths_two_cycle", test_CFL_AllPaths_two_cycle}, + {"CFL_AllPaths_labels_more_than_nonterms", + test_CFL_AllPaths_labels_more_than_nonterms}, + {"CFL_AllPaths_tree", test_CFL_AllPaths_tree}, + {"CFL_AllPaths_line", test_CFL_AllPaths_line}, + {"CFL_AllPaths_two_nodes_cycle", test_CFL_AllPaths_two_nodes_cycle}, + {"CFG_reach_basic_invalid_rules", test_CFL_AllPaths_invalid_rules}, + {"test_CFL_AllPaths_with_empty_adj_matrix", test_CFL_AllPaths_with_empty_adj_matrix}, + #if !defined ( GRAPHBLAS_HAS_CUDA ) + {"CFL_AllPaths_null_pointers", test_CFL_AllPaths_null_pointers}, + #endif {NULL, NULL}}; From 14d69d910ea5738c2e2668411678a37538694ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 11 Mar 2026 00:54:19 +0400 Subject: [PATCH 30/34] fix naming and remove print from tests --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 30 +++++++++---------- experimental/test/test_CFL_AllPaths.c | 10 ------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 6a1e030829..01a99e196b 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -1,13 +1,13 @@ #define LG_FREE_WORK \ { \ - GrB_free(&AllPaths_semiring_free); \ - GrB_free(&AllPaths_monoid_free); \ + GrB_free(&AllPaths_semiring); \ + GrB_free(&AllPaths_monoid); \ GrB_free(&AllPaths_monoid_get_nvals); \ GrB_free(&bottom_scalar); \ GrB_free(&IAllPaths_mult); \ GrB_free(&AllPaths_set); \ GrB_free(&AllPaths_mult); \ - GrB_free(&AllPaths_add_free); \ + GrB_free(&AllPaths_add); \ GrB_free(&AllPaths_add_get_nvals); \ GrB_free(&Theta); \ GrB_free(&AllPaths_type); \ @@ -66,7 +66,7 @@ void clear_elem_all_paths(AllPathsElem *z){ z->n = 0; } -void add_free_all_paths(AllPathsElem *z, AllPathsElem *x, AllPathsElem *y) +void add_all_paths(AllPathsElem *z, AllPathsElem *x, AllPathsElem *y) { AllPathsElem temp; temp.middle = merge_all_paths(&temp.n, x->middle, x->n, y->middle, y->n); @@ -167,12 +167,12 @@ GrB_Info LAGraph_CFL_AllPaths( { // Semiring components GrB_Type AllPaths_type = NULL; - GrB_BinaryOp AllPaths_add_free = NULL; + GrB_BinaryOp AllPaths_add = NULL; GrB_BinaryOp AllPaths_add_get_nvals = NULL; - GrB_Monoid AllPaths_monoid_free = NULL; + GrB_Monoid AllPaths_monoid = NULL; GxB_IndexBinaryOp IAllPaths_mult = NULL; GrB_BinaryOp AllPaths_mult = NULL; - GrB_Semiring AllPaths_semiring_free = NULL; + GrB_Semiring AllPaths_semiring = NULL; GrB_BinaryOp AllPaths_set = NULL; GrB_Scalar Theta = NULL; GrB_Scalar bottom_scalar = NULL; @@ -189,15 +189,15 @@ GrB_Info LAGraph_CFL_AllPaths( GRB_TRY(GrB_Scalar_setElement_UDT(bottom_scalar, (void *)(&bottom))); GRB_TRY(GrB_BinaryOp_new( - &AllPaths_add_free, - (void *)add_free_all_paths, + &AllPaths_add, + (void *)add_all_paths, AllPaths_type, AllPaths_type, AllPaths_type)); GRB_TRY(GrB_Monoid_new( - &AllPaths_monoid_free, - AllPaths_add_free, + &AllPaths_monoid, + AllPaths_add, (void *)(&bottom))); GRB_TRY(GxB_IndexBinaryOp_new( @@ -216,8 +216,8 @@ GrB_Info LAGraph_CFL_AllPaths( Theta)); GRB_TRY(GrB_Semiring_new( - &AllPaths_semiring_free, - AllPaths_monoid_free, + &AllPaths_semiring, + AllPaths_monoid, AllPaths_mult)); GRB_TRY(GrB_BinaryOp_new( @@ -240,8 +240,8 @@ GrB_Info LAGraph_CFL_AllPaths( (void *)(&bottom))); CFL_Semiring semiring = {.type = AllPaths_type, - .semiring = AllPaths_semiring_free, - .add = AllPaths_add_free, + .semiring = AllPaths_semiring, + .add = AllPaths_add, .mult = AllPaths_mult, .init_path = AllPaths_set, .bottom_scalar = bottom_scalar, diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index 0fad6372cb..129dbe6310 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -146,16 +146,6 @@ char *output_to_str(size_t nonterm) return result_str; } -void print_outputs(void) -{ - for (size_t i = 0; i < grammar.nonterms_count; i++) - { - char *s = output_to_str(i); - printf("Output[%zu]: %s\n", i, s[0] ? s : "(empty)"); - LAGraph_Free ((void **) &s, msg); - } -} - void free_workspace() { if (adj_matrices != NULL) From 719250eb01f9a3e8088a2b904325eaf7e045cd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 11 Mar 2026 01:11:13 +0400 Subject: [PATCH 31/34] change test name --- experimental/test/test_CFL_AllPaths.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index 129dbe6310..6217038274 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -748,8 +748,8 @@ TEST_LIST = {{"CFL_AllPaths_complex_grammar", test_CFL_AllPaths_complex_grammar} {"CFL_AllPaths_tree", test_CFL_AllPaths_tree}, {"CFL_AllPaths_line", test_CFL_AllPaths_line}, {"CFL_AllPaths_two_nodes_cycle", test_CFL_AllPaths_two_nodes_cycle}, - {"CFG_reach_basic_invalid_rules", test_CFL_AllPaths_invalid_rules}, - {"test_CFL_AllPaths_with_empty_adj_matrix", test_CFL_AllPaths_with_empty_adj_matrix}, + {"CFL_AllPaths_basic_invalid_rules", test_CFL_AllPaths_invalid_rules}, + {"CFL_AllPaths_with_empty_adj_matrix", test_CFL_AllPaths_with_empty_adj_matrix}, #if !defined ( GRAPHBLAS_HAS_CUDA ) {"CFL_AllPaths_null_pointers", test_CFL_AllPaths_null_pointers}, #endif From fb813519040f4a2ff29bb6602dc666995093aed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Wed, 11 Mar 2026 21:16:14 +0400 Subject: [PATCH 32/34] free output matrix with iterator and return print for debug --- experimental/test/test_CFL_AllPaths.c | 35 ++++++++++++++++++++------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index 6217038274..1d50398064 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -146,6 +146,16 @@ char *output_to_str(size_t nonterm) return result_str; } +void print_outputs(void) +{ + for (size_t i = 0; i < grammar.nonterms_count; i++) + { + char *s = output_to_str(i); + printf("Output[%zu]: %s\n", i, s[0] ? s : "(empty)"); + LAGraph_Free ((void **) &s, msg); + } +} + void free_workspace() { if (adj_matrices != NULL) @@ -161,15 +171,22 @@ void free_workspace() { { for (size_t i = 0; i < grammar.nonterms_count; i++) { - void* val_void = NULL; - GrB_Index nnz = 0; - GrB_Matrix_nvals(&nnz, outputs[i]); - LAGraph_Malloc ((void **) &val_void, nnz, sizeof (AllPathsElem), msg) ; - GrB_Matrix_extractTuples(NULL, NULL, val_void, &nnz, outputs[i]); - AllPathsElem* val = (AllPathsElem*) val_void; - for(size_t j = 0; j Date: Wed, 11 Mar 2026 21:23:27 +0400 Subject: [PATCH 33/34] to the previous commit --- experimental/test/test_CFL_AllPaths.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index 1d50398064..80554db2f4 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -175,13 +175,11 @@ void free_workspace() { GxB_Iterator_new(&iterator); GrB_Info info = GxB_Matrix_Iterator_attach(iterator, outputs[i], NULL); info = GxB_Matrix_Iterator_seek(iterator, 0); + AllPathsElem val; while (info != GxB_EXHAUSTED) { - AllPathsElem val; GxB_Iterator_get_UDT(iterator, (void*) &val); - if (val.middle!=NULL){ - free(val.middle); - } + if (val.middle) free(val.middle); info = GxB_Matrix_Iterator_next(iterator); } From f9f7047d602287fa4bb55ef87a5974110743612c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D1=8F?= Date: Thu, 12 Mar 2026 03:56:36 +0400 Subject: [PATCH 34/34] get_nvals v1.1 and first comment --- experimental/algorithm/LAGraph_CFL_AllPaths.c | 30 ++++++++++++++-- experimental/test/test_CFL_AllPaths.c | 34 +++++++++++-------- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/experimental/algorithm/LAGraph_CFL_AllPaths.c b/experimental/algorithm/LAGraph_CFL_AllPaths.c index 01a99e196b..5dad453604 100644 --- a/experimental/algorithm/LAGraph_CFL_AllPaths.c +++ b/experimental/algorithm/LAGraph_CFL_AllPaths.c @@ -16,6 +16,7 @@ #include "LG_internal.h" #include +//Merging two ordered arrays of internal vertices in the add function static GrB_Index* merge_all_paths(GrB_Index* n, const void* left, const GrB_Index na, const void* right, const GrB_Index nb){ GrB_Index* a = (GrB_Index*) left; GrB_Index* b = (GrB_Index*) right; @@ -68,6 +69,7 @@ void clear_elem_all_paths(AllPathsElem *z){ void add_all_paths(AllPathsElem *z, AllPathsElem *x, AllPathsElem *y) { + //temp is needed to avoid freeing the memory of z in case z == x or z == y AllPathsElem temp; temp.middle = merge_all_paths(&temp.n, x->middle, x->n, y->middle, y->n); clear_elem_all_paths(x); @@ -95,7 +97,7 @@ void set_all_paths(AllPathsElem *z, const AllPathsElem *x, const bool *edge_exis if (edge_exist && *edge_exist){ temp.middle = malloc(sizeof(GrB_Index)); temp.n = 1; - temp.middle[0] = GrB_INDEX_MAX; + temp.middle[0] = GrB_INDEX_MAX; //GrB_INDEX_MAX - marker of A->eps and A->t } z->middle = merge_all_paths(&z->n, x->middle, x->n, temp.middle, temp.n); @@ -113,16 +115,18 @@ void set_all_paths(AllPathsElem *z, const AllPathsElem *x, const bool *edge_exis " z->n = 1; \n" \ "}" +//Adding the count of all internal vertices in a reduction void add_get_nvals_all_paths(AllPathsElem *z, const AllPathsElem *x, const AllPathsElem *y) { z->n = x->n + y->n; z->middle = NULL; } +//Made global so that the get_nvals_all_paths matches the GrB_Matrix_nvals signature static GrB_Type* AllPaths_type_get_nvals = NULL; static GrB_Monoid AllPaths_monoid_get_nvals = NULL; -//A function that replaces GrB_Matrix_nvals for counting non-zero elements +//A function that replaces GrB_Matrix_nvals in Reachability to check if new vertices have been added to the matrix. GrB_Info get_nvals_all_paths(GrB_Index *nvals, const GrB_Matrix A){ GrB_Scalar s = NULL; GrB_Scalar_new(&s, *AllPaths_type_get_nvals); @@ -141,6 +145,28 @@ GrB_Info get_nvals_all_paths(GrB_Index *nvals, const GrB_Matrix A){ return GrB_SUCCESS; } +//To test the non-reduction approach in the future +GrB_Info get_nvals_all_paths2(GrB_Index *nvals, const GrB_Matrix A){ + GrB_Index accum = 0; + GxB_Iterator iterator; + GxB_Iterator_new(&iterator); + GrB_Info info = GxB_Matrix_Iterator_attach(iterator, A, NULL); + info = GxB_Matrix_Iterator_seek(iterator, 0); + AllPathsElem val; + + while (info != GxB_EXHAUSTED) + { + GxB_Iterator_get_UDT(iterator, (void*) &val); + accum+=val.n; + info = GxB_Matrix_Iterator_next(iterator); + } + + GrB_free(&iterator); + *nvals = accum; + + return GrB_SUCCESS; +} + GrB_Info LAGraph_CFL_AllPaths( // Output GrB_Matrix *outputs, // Array of matrices containing results. diff --git a/experimental/test/test_CFL_AllPaths.c b/experimental/test/test_CFL_AllPaths.c index 80554db2f4..f7be983edf 100644 --- a/experimental/test/test_CFL_AllPaths.c +++ b/experimental/test/test_CFL_AllPaths.c @@ -156,6 +156,25 @@ void print_outputs(void) } } +//Cleaning of internal elements before free matrix +void free_AllPaths_matrix(GrB_Matrix* ptr_output){ + GrB_Matrix output = *ptr_output; + GxB_Iterator iterator; + GxB_Iterator_new(&iterator); + GrB_Info info = GxB_Matrix_Iterator_attach(iterator, output, NULL); + info = GxB_Matrix_Iterator_seek(iterator, 0); + AllPathsElem val; + while (info != GxB_EXHAUSTED) + { + GxB_Iterator_get_UDT(iterator, (void*) &val); + if (val.middle) free(val.middle); + info = GxB_Matrix_Iterator_next(iterator); + } + + GrB_free(&iterator); + GrB_free(ptr_output); +} + void free_workspace() { if (adj_matrices != NULL) @@ -171,20 +190,7 @@ void free_workspace() { { for (size_t i = 0; i < grammar.nonterms_count; i++) { - GxB_Iterator iterator; - GxB_Iterator_new(&iterator); - GrB_Info info = GxB_Matrix_Iterator_attach(iterator, outputs[i], NULL); - info = GxB_Matrix_Iterator_seek(iterator, 0); - AllPathsElem val; - while (info != GxB_EXHAUSTED) - { - GxB_Iterator_get_UDT(iterator, (void*) &val); - if (val.middle) free(val.middle); - info = GxB_Matrix_Iterator_next(iterator); - } - - GrB_free(&iterator); - GrB_free(&outputs[i]); + free_AllPaths_matrix(&outputs[i]); } } LAGraph_Free ((void **) &outputs, msg);