Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
ignores: [
(message) => /^Update\b/.test(message)
(message) => /^Update\b/.test(message),
(message) => message.startsWith('📝')
],
rules: {
'header-max-length': [2, 'always', 72]
Expand Down
8 changes: 6 additions & 2 deletions scripts/ci/guard-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ DST="$2" # base ref
die() { echo "::error::$*"; exit 1; }

case "$SRC" in
coderabbitai/*)
[[ "$DST" == "main" ]] \
|| die "coderabbitai/* branches must target main."
;;
feat/minimal-dpoi-qca-loop)
# Temporary allowance while PR #70 lands Phase 1 directly onto main.
[[ "$DST" == "main" ]] \
Expand All @@ -31,6 +35,6 @@ case "$SRC" in
esac

if [[ "$DST" == "main" && ! "$SRC" =~ ^(release|fix)/ ]]; then
[[ "$SRC" == "feat/minimal-dpoi-qca-loop" ]] \
|| die "Only release/* or fix/* may target main."
[[ "$SRC" == "feat/minimal-dpoi-qca-loop" || "$SRC" =~ ^coderabbitai/ ]] \
|| die "Only release/*, fix/*, coderabbitai/*, or feat/minimal-dpoi-qca-loop may target main."
fi
123 changes: 123 additions & 0 deletions src/dpoi.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ static metagraph_result_t metagraph_match_set_grow(mg_match_set_t *set,
uint32_t min_capacity);
static metagraph_result_t metagraph_prepare_match_buffer(mg_match_set_t *set);

/**
* Ensure a match set has capacity for at least `min_capacity` entries.
*
* If the set's capacity is smaller than `min_capacity`, the underlying storage
* is resized and `set->data` and `set->capacity` are updated accordingly.
*
* @param set Match set to ensure capacity for.
* @param min_capacity Minimum required capacity (number of mg_match_t entries).
* @returns METAGRAPH_SUCCESS on success, or an error result on failure (for
* example, out-of-memory if allocation fails).
*/
static metagraph_result_t metagraph_match_set_grow(mg_match_set_t *set,
uint32_t min_capacity) {
if (set->capacity >= min_capacity) {
Expand All @@ -46,6 +57,19 @@ static metagraph_result_t metagraph_match_set_grow(mg_match_set_t *set,
return METAGRAPH_SUCCESS;
}

/**
* Append a computed match for `rule` using the provided `image` into `set`,
* computing its ordering key and recording touched nodes.
*
* @param rule Source rule whose `rule_id` is recorded in the emitted match.
* @param image Array of graph node identifiers forming the match (length
* `count`).
* @param count Number of node identifiers in `image`.
* @param set Match buffer to which the new match will be appended; must be
* prepared prior to calling.
* @returns METAGRAPH_SUCCESS on success, or an error code on failure (for
* example, when memory allocation fails).
*/
static metagraph_result_t metagraph_emit_match(const mg_rule_t *rule,
const mg_node_id_t *image,
uint8_t count,
Expand Down Expand Up @@ -76,6 +100,20 @@ static metagraph_result_t metagraph_emit_match(const mg_rule_t *rule,
return METAGRAPH_SUCCESS;
}

/**
* Find and emit matches for a rule that consists of a single node.
*
* For each graph node whose type equals the rule's required node type, appends
* a one-node mapping to the provided match set.
*
* @param graph Graph to search for matching nodes.
* @param rule Rule whose single-node pattern (rule->L.node_type[0]) is used.
* @param set Match set to append found matches into; must be prepared by the
* caller.
*
* @returns METAGRAPH_SUCCESS on success, or an error code propagated from match
* emission (e.g., out-of-memory).
*/
static metagraph_result_t metagraph_match_single_node(const mg_graph_t *graph,
const mg_rule_t *rule,
mg_match_set_t *set) {
Expand All @@ -91,6 +129,21 @@ static metagraph_result_t metagraph_match_single_node(const mg_graph_t *graph,
return METAGRAPH_SUCCESS;
}

/**
* Find and emit matches for a two-node (one-edge) rule pattern in the given
* graph.
*
* Scans graph edges for ordered pairs where the source node has the rule's
* left-hand type and the target node has the rule's right-hand type, and
* appends a two-node mapping for each valid pair to the provided match set.
*
* @param graph Graph to search for matching node pairs.
* @param rule Rule whose two-node LHS/RHS types are used to filter matches.
* @param set Output match set to which discovered two-node matches are
* appended.
* @returns METAGRAPH_SUCCESS on success, or an error code propagated from match
* emission on failure.
*/
static metagraph_result_t metagraph_match_two_nodes(const mg_graph_t *graph,
const mg_rule_t *rule,
mg_match_set_t *set) {
Expand Down Expand Up @@ -122,6 +175,24 @@ static metagraph_result_t metagraph_match_two_nodes(const mg_graph_t *graph,
return METAGRAPH_SUCCESS;
}

/**
* Generate candidate matches for a rule by scanning a rule-meta-graph's
* skeleton.
*
* Prepares the output match buffer and emits matches for simple rule arities:
* - single-node rules: emits one-node matches for graph nodes whose type
* matches the rule.
* - two-node one-edge rules: emits ordered node-pair matches for adjacent nodes
* matching lhs/rhs types.
*
* @param rmg Rule-meta-graph containing a skeleton graph to search; must not be
* NULL.
* @param rule Rule definition whose pattern will be matched; must not be NULL.
* @param arena Arena allocator (unused by this implementation; may be NULL).
* @param out_matches Match buffer to populate; must not be NULL.
* @returns METAGRAPH_SUCCESS on success. Propagates error codes from buffer
* preparation or allocation failures (e.g., out-of-memory) on failure.
*/
metagraph_result_t mg_dpoi_match_rmg(const mg_rmg_t *rmg, const mg_rule_t *rule,
mg_arena_t *arena,
mg_match_set_t *out_matches) {
Expand All @@ -147,6 +218,15 @@ metagraph_result_t mg_dpoi_match_rmg(const mg_rmg_t *rmg, const mg_rule_t *rule,
return METAGRAPH_SUCCESS;
}

/**
* Compare two match records by their composite key: first `key_hi`, then
* `key_lo`.
*
* @param lhs Pointer to the first `mg_match_t`.
* @param rhs Pointer to the second `mg_match_t`.
* @returns `-1` if `lhs` is less than `rhs`, `1` if `lhs` is greater than
* `rhs`, `0` if they are equal.
*/
static int metagraph_match_compare(const void *lhs, const void *rhs) {
const mg_match_t *left = (const mg_match_t *)lhs;
const mg_match_t *right = (const mg_match_t *)rhs;
Expand All @@ -165,6 +245,18 @@ static int metagraph_match_compare(const void *lhs, const void *rhs) {
return 0;
}

/**
* Ensure the match set has an initialized match buffer and resets its count.
*
* If the set already has an allocated data buffer, the function resets
* set->count to 0. Otherwise it allocates an initial array of
* METAGRAPH_INITIAL_MATCH_CAPACITY elements, sets set->capacity and
* set->count accordingly.
*
* @param set Match set whose buffer and counters will be initialized or reset.
* @returns METAGRAPH_SUCCESS on success, otherwise an out-of-memory error
* result.
*/
static metagraph_result_t metagraph_prepare_match_buffer(mg_match_set_t *set) {
if (set->data != NULL) {
set->count = 0U;
Expand All @@ -181,6 +273,16 @@ static metagraph_result_t metagraph_prepare_match_buffer(mg_match_set_t *set) {
return METAGRAPH_SUCCESS;
}

/**
* Determine whether two matches share any touched node identifiers.
*
* @param lhs First match to compare; its touched_nodes array and tn count are
* used.
* @param rhs Second match to compare; its touched_nodes array and tn count are
* used.
* @returns `true` if the matches share at least one touched node identifier,
* `false` otherwise.
*/
static bool metagraph_matches_overlap(const mg_match_t *lhs,
const mg_match_t *rhs) {
for (uint16_t lhs_index = 0; lhs_index < lhs->tn; ++lhs_index) {
Expand All @@ -194,6 +296,18 @@ static bool metagraph_matches_overlap(const mg_match_t *lhs,
return false;
}

/**
* Selects a maximal subset of non-overlapping matches from the provided match
* set.
*
* Sorts the matches by their composite key and then retains, in-place, the
* first set of matches that do not overlap on any touched node. The function
* updates matches->count to reflect the number of matches kept.
*
* @param matches Match set to reduce; modified in-place. If `matches` is NULL
* or contains zero or one entry, the function returns without
* making changes.
*/
void mg_dpoi_schedule_maximal(mg_match_set_t *matches) {
if (!matches || matches->count <= 1U) {
return;
Expand Down Expand Up @@ -222,6 +336,15 @@ void mg_dpoi_schedule_maximal(mg_match_set_t *matches) {
matches->count = kept;
}

/**
* Commit a set of scheduled matches to the graph.
*
* @param graph Target graph to apply the scheduled matches to.
* @param rules Array of rules corresponding to the matches in `schedule`.
* @param rule_count Number of rules in `rules`.
* @param schedule Set of matches to commit to `graph`.
* @returns METAGRAPH_SUCCESS on success, or an error code on failure.
*/
metagraph_result_t mg_dpo_commit(mg_graph_t *graph, const mg_rule_t *rules,
uint32_t rule_count,
const mg_match_set_t *schedule) {
Expand Down
35 changes: 35 additions & 0 deletions src/hilbert.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@
#include "metagraph/base.h"
#include "metagraph/result.h"

/**
* Initialize a Hilbert register handle with storage for a given number of
* nodes.
*
* Allocates and zero-initializes the internal node_bits array and sets
* node_count.
*
* @param hilbert Pointer to the hilbert handle to initialize; must not be NULL.
* @param count Number of node entries to allocate (zero is allowed and
* allocates a minimal buffer).
* @returns `METAGRAPH_OK()` on success;
* `METAGRAPH_ERR(METAGRAPH_ERROR_NULL_POINTER, ...)` if `hilbert` is NULL;
* `METAGRAPH_ERR(METAGRAPH_ERROR_OUT_OF_MEMORY, ...)` if allocation
* fails.
*/
metagraph_result_t mg_hilbert_init(mg_hilbert_t *hilbert, size_t count) {
if (!hilbert) {
return METAGRAPH_ERR(METAGRAPH_ERROR_NULL_POINTER,
Expand All @@ -20,6 +35,14 @@ metagraph_result_t mg_hilbert_init(mg_hilbert_t *hilbert, size_t count) {
return METAGRAPH_OK();
}

/**
* Release resources held by a Hilbert handle.
*
* Frees the internal `node_bits` buffer and resets `node_count` to 0.
* Safe to call with a NULL `hilbert` pointer (no action will be taken).
*
* @param hilbert Pointer to the hilbert handle whose resources will be freed.
*/
void mg_hilbert_free(mg_hilbert_t *hilbert) {
if (!hilbert) {
return;
Expand All @@ -29,6 +52,18 @@ void mg_hilbert_free(mg_hilbert_t *hilbert) {
hilbert->node_count = 0;
}

/**
* Resize a Hilbert register to hold a specified number of nodes.
*
* Preserves existing node state up to the smaller of the old and new counts.
*
* @param hilbert Pointer to the Hilbert handle to resize; must not be NULL.
* @param new_count Desired number of nodes (may be zero).
* @returns `METAGRAPH_OK()` on success.
* Returns `METAGRAPH_ERR(METAGRAPH_ERROR_NULL_POINTER, ...)` if
* `hilbert` is NULL. Returns `METAGRAPH_ERR(METAGRAPH_ERROR_OUT_OF_MEMORY,
* ...)` if memory allocation fails.
*/
metagraph_result_t mg_hilbert_resize(mg_hilbert_t *hilbert, size_t new_count) {
if (!hilbert) {
return METAGRAPH_ERR(METAGRAPH_ERROR_NULL_POINTER,
Expand Down
55 changes: 55 additions & 0 deletions src/match.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@
#include <stdint.h>
#include <stdlib.h>

/**
* Initialize an mg_match_set_t structure and optionally allocate its internal
* storage.
*
* The function zeroes the provided structure and, if capacity > 0, allocates an
* array of mg_match_t of the requested size and sets the structure's capacity
* accordingly.
*
* @param set Pointer to the mg_match_set_t to initialize (must not be NULL).
* @param capacity Initial number of elements to allocate; if zero no allocation
* is performed.
* @returns `true` if the structure was initialized and any requested allocation
* succeeded, `false` otherwise.
*/
bool mg_match_set_init(mg_match_set_t *set, uint32_t capacity) {
if (!set) {
return false;
Expand All @@ -19,6 +33,20 @@ bool mg_match_set_init(mg_match_set_t *set, uint32_t capacity) {
return true;
}

/**
* Ensure the match set can hold at least the specified number of elements.
*
* Allocates or grows the internal storage so that the set can contain at least
* min_capacity elements and updates set->capacity on success; no allocation is
* performed if the current capacity already meets or exceeds min_capacity.
*
* @param set Pointer to the match set to modify; if NULL the function does
* nothing and returns `false`.
* @param min_capacity Minimum required number of elements the set must be able
* to hold.
* @returns `true` if the set has at least min_capacity capacity after the call,
* `false` on allocation failure or when `set` is NULL.
*/
bool mg_match_set_reserve(mg_match_set_t *set, uint32_t min_capacity) {
if (!set) {
return false;
Expand Down Expand Up @@ -48,6 +76,16 @@ bool mg_match_set_reserve(mg_match_set_t *set, uint32_t min_capacity) {
return true;
}

/**
* Append a match to the end of a match set, growing the set if necessary.
*
* Ensures the set has space for one more element and copies the provided
* match into the next slot. Does nothing if `set` or `match` is NULL.
*
* @param set Destination match set to append to.
* @param match Match to append (copied into the set).
* @returns `true` if the match was appended successfully, `false` on failure.
*/
bool mg_match_set_push(mg_match_set_t *set, const mg_match_t *match) {
if (!set || !match) {
return false;
Expand All @@ -63,13 +101,30 @@ bool mg_match_set_push(mg_match_set_t *set, const mg_match_t *match) {
return true;
}

/**
* Reset the set to be empty without releasing its allocated storage.
*
* Clears the element count so the set contains no matches while preserving
* any previously allocated internal buffer for reuse. If `set` is NULL,
* no action is taken.
*
* @param set Match set to clear.
*/
void mg_match_set_clear(mg_match_set_t *set) {
if (!set) {
return;
}
set->count = 0;
}

/**
* Release resources held by a match set and reset it to an empty state.
*
* Frees the internal data buffer if present and sets `data` to NULL, `count`
* to 0, and `capacity` to 0. Safe to call with a NULL pointer.
*
* @param set Pointer to the mg_match_set_t to free; may be NULL.
*/
void mg_match_set_free(mg_match_set_t *set) {
if (!set) {
return;
Expand Down
Loading
Loading