@@ -630,6 +630,163 @@ <h3 id="ray-csv">CSV I/O</h3>
630630 </ div >
631631 </ div >
632632
633+ <!-- ============================================================ -->
634+ < h2 id ="datalog "> Datalog API</ h2 >
635+ < p > Build, stratify, and evaluate Datalog programs over Rayforce tables. Include < code > "datalog/datalog.h"</ code > for all Datalog types and functions.</ p >
636+
637+ < h3 id ="dl-program-new "> dl_program_new / dl_program_free</ h3 >
638+ < div class ="fn-card ">
639+ < div class ="fn-card-header ">
640+ < span class ="fn-name "> dl_program_new / dl_program_free</ span >
641+ </ div >
642+ < div class ="fn-desc "> Create a new empty Datalog program, or free one and release all owned tables.</ div >
643+ < div class ="fn-example ">
644+ < span class ="hl-type "> dl_program_t</ span > * < span class ="hl-fn "> dl_program_new</ span > (< span class ="hl-type "> void</ span > );
645+ < span class ="hl-type "> void</ span > < span class ="hl-fn "> dl_program_free</ span > (< span class ="hl-type "> dl_program_t</ span > * prog);
646+ </ div >
647+ </ div >
648+
649+ < h3 id ="dl-add-edb "> dl_add_edb</ h3 >
650+ < div class ="fn-card ">
651+ < div class ="fn-card-header ">
652+ < span class ="fn-name "> dl_add_edb</ span >
653+ </ div >
654+ < div class ="fn-desc "> Register an extensional (base fact) relation backed by an existing columnar table. Column names are auto-generated as < code > "c0"</ code > , < code > "c1"</ code > , … unless the table already has named columns. Returns the relation index.</ div >
655+ < div class ="fn-example ">
656+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> dl_add_edb</ span > (< span class ="hl-type "> dl_program_t</ span > * prog, < span class ="hl-kw "> const</ span > < span class ="hl-type "> char</ span > * name,
657+ < span class ="hl-type "> ray_t</ span > * table, < span class ="hl-type "> int</ span > arity);
658+ </ div >
659+ </ div >
660+
661+ < h3 id ="dl-add-rule "> dl_add_rule</ h3 >
662+ < div class ="fn-card ">
663+ < div class ="fn-card-header ">
664+ < span class ="fn-name "> dl_add_rule</ span >
665+ </ div >
666+ < div class ="fn-desc "> Add a rule to the program. The rule struct is copied. Returns the rule index. Use the rule builder helpers (< code > dl_rule_init</ code > , < code > dl_rule_add_atom</ code > , etc.) to construct rules.</ div >
667+ < div class ="fn-example ">
668+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> dl_add_rule</ span > (< span class ="hl-type "> dl_program_t</ span > * prog, < span class ="hl-kw "> const</ span > < span class ="hl-type "> dl_rule_t</ span > * rule);
669+ </ div >
670+ </ div >
671+
672+ < h3 id ="dl-stratify "> dl_stratify</ h3 >
673+ < div class ="fn-card ">
674+ < div class ="fn-card-header ">
675+ < span class ="fn-name "> dl_stratify</ span >
676+ </ div >
677+ < div class ="fn-desc "> Compute stratification (topological sort of the negation dependency graph). Returns 0 on success, −1 if the program has an unstratifiable negation cycle.</ div >
678+ < div class ="fn-example ">
679+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> dl_stratify</ span > (< span class ="hl-type "> dl_program_t</ span > * prog);
680+ </ div >
681+ </ div >
682+
683+ < h3 id ="dl-eval "> dl_eval</ h3 >
684+ < div class ="fn-card ">
685+ < div class ="fn-card-header ">
686+ < span class ="fn-name "> dl_eval</ span >
687+ </ div >
688+ < div class ="fn-desc "> Evaluate the program to fixpoint using semi-naive evaluation. Returns 0 on success, −1 on error. After evaluation, derived relations are populated and queryable.</ div >
689+ < div class ="fn-example ">
690+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> dl_eval</ span > (< span class ="hl-type "> dl_program_t</ span > * prog);
691+ </ div >
692+ </ div >
693+
694+ < h3 id ="dl-query "> dl_query</ h3 >
695+ < div class ="fn-card ">
696+ < div class ="fn-card-header ">
697+ < span class ="fn-name "> dl_query</ span >
698+ </ div >
699+ < div class ="fn-desc "> Query the result of a derived relation after evaluation. Returns the backing < code > ray_t*</ code > table. The caller does < strong > not</ strong > own the result — do not release it.</ div >
700+ < div class ="fn-example ">
701+ < span class ="hl-type "> ray_t</ span > * < span class ="hl-fn "> dl_query</ span > (< span class ="hl-type "> dl_program_t</ span > * prog, < span class ="hl-kw "> const</ span > < span class ="hl-type "> char</ span > * pred_name);
702+ </ div >
703+ </ div >
704+
705+ < h3 id ="dl-rule-builder "> Rule Builder Helpers</ h3 >
706+ < div class ="fn-card ">
707+ < div class ="fn-card-header ">
708+ < span class ="fn-name "> dl_rule_init / dl_rule_add_atom / dl_body_set_var / dl_body_set_const</ span >
709+ </ div >
710+ < div class ="fn-desc "> Build rules programmatically: initialize a rule with a head predicate, add positive body atoms, and bind arguments to variables or constants.</ div >
711+ < div class ="fn-example ">
712+ < span class ="hl-type "> void</ span > < span class ="hl-fn "> dl_rule_init</ span > (< span class ="hl-type "> dl_rule_t</ span > * rule, < span class ="hl-kw "> const</ span > < span class ="hl-type "> char</ span > * head_pred, < span class ="hl-type "> int</ span > head_arity);
713+ < span class ="hl-type "> void</ span > < span class ="hl-fn "> dl_rule_head_var</ span > (< span class ="hl-type "> dl_rule_t</ span > * rule, < span class ="hl-type "> int</ span > pos, < span class ="hl-type "> int</ span > var_idx);
714+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> dl_rule_add_atom</ span > (< span class ="hl-type "> dl_rule_t</ span > * rule, < span class ="hl-kw "> const</ span > < span class ="hl-type "> char</ span > * pred, < span class ="hl-type "> int</ span > arity);
715+ < span class ="hl-type "> void</ span > < span class ="hl-fn "> dl_body_set_var</ span > (< span class ="hl-type "> dl_rule_t</ span > * rule, < span class ="hl-type "> int</ span > body_idx, < span class ="hl-type "> int</ span > pos, < span class ="hl-type "> int</ span > var_idx);
716+ < span class ="hl-type "> void</ span > < span class ="hl-fn "> dl_body_set_const</ span > (< span class ="hl-type "> dl_rule_t</ span > * rule, < span class ="hl-type "> int</ span > body_idx, < span class ="hl-type "> int</ span > pos, < span class ="hl-type "> int64_t</ span > val);
717+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> dl_rule_add_neg</ span > (< span class ="hl-type "> dl_rule_t</ span > * rule, < span class ="hl-kw "> const</ span > < span class ="hl-type "> char</ span > * pred, < span class ="hl-type "> int</ span > arity);
718+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> dl_rule_add_cmp_const</ span > (< span class ="hl-type "> dl_rule_t</ span > * rule, < span class ="hl-type "> int</ span > cmp_op,
719+ < span class ="hl-type "> int</ span > lhs_var, < span class ="hl-type "> int64_t</ span > rhs_val);
720+ </ div >
721+ </ div >
722+
723+ < h4 > Datalog example: transitive closure</ h4 >
724+ < pre > < code > < span class ="hl-kw "> #include</ span > < span class ="hl-str "> <rayforce.h></ span >
725+ < span class ="hl-kw "> #include</ span > < span class ="hl-str "> "ops/ops.h"</ span >
726+ < span class ="hl-kw "> #include</ span > < span class ="hl-str "> "mem/heap.h"</ span >
727+ < span class ="hl-kw "> #include</ span > < span class ="hl-str "> "datalog/datalog.h"</ span >
728+
729+ < span class ="hl-type "> int</ span > < span class ="hl-fn "> main</ span > (< span class ="hl-type "> void</ span > ) {
730+ < span class ="hl-fn "> ray_heap_init</ span > ();
731+ < span class ="hl-fn "> ray_sym_init</ span > ();
732+
733+ < span class ="hl-comment "> /* Build edge table: 0->1, 1->2, 2->3 */</ span >
734+ < span class ="hl-type "> ray_t</ span > * c0 = < span class ="hl-fn "> ray_vec_from_raw</ span > (< span class ="hl-num "> RAY_I64</ span > , (< span class ="hl-type "> int64_t</ span > []){< span class ="hl-num "> 0</ span > ,< span class ="hl-num "> 1</ span > ,< span class ="hl-num "> 2</ span > }, < span class ="hl-num "> 3</ span > );
735+ < span class ="hl-type "> ray_t</ span > * c1 = < span class ="hl-fn "> ray_vec_from_raw</ span > (< span class ="hl-num "> RAY_I64</ span > , (< span class ="hl-type "> int64_t</ span > []){< span class ="hl-num "> 1</ span > ,< span class ="hl-num "> 2</ span > ,< span class ="hl-num "> 3</ span > }, < span class ="hl-num "> 3</ span > );
736+ < span class ="hl-type "> ray_t</ span > * edges = < span class ="hl-fn "> ray_table_new</ span > (< span class ="hl-num "> 2</ span > );
737+ edges = < span class ="hl-fn "> ray_table_add_col</ span > (edges, < span class ="hl-fn "> ray_sym_intern</ span > (< span class ="hl-str "> "c0"</ span > , < span class ="hl-num "> 2</ span > ), c0);
738+ edges = < span class ="hl-fn "> ray_table_add_col</ span > (edges, < span class ="hl-fn "> ray_sym_intern</ span > (< span class ="hl-str "> "c1"</ span > , < span class ="hl-num "> 2</ span > ), c1);
739+
740+ < span class ="hl-comment "> /* Create Datalog program */</ span >
741+ < span class ="hl-type "> dl_program_t</ span > * prog = < span class ="hl-fn "> dl_program_new</ span > ();
742+ < span class ="hl-fn "> dl_add_edb</ span > (prog, < span class ="hl-str "> "edge"</ span > , edges, < span class ="hl-num "> 2</ span > );
743+
744+ < span class ="hl-comment "> /* Rule 1: reach(X, Y) :- edge(X, Y). */</ span >
745+ < span class ="hl-type "> dl_rule_t</ span > r1;
746+ < span class ="hl-fn "> dl_rule_init</ span > (&r1, < span class ="hl-str "> "reach"</ span > , < span class ="hl-num "> 2</ span > );
747+ < span class ="hl-fn "> dl_rule_head_var</ span > (&r1, < span class ="hl-num "> 0</ span > , < span class ="hl-num "> 0</ span > ); < span class ="hl-comment "> /* X */</ span >
748+ < span class ="hl-fn "> dl_rule_head_var</ span > (&r1, < span class ="hl-num "> 1</ span > , < span class ="hl-num "> 1</ span > ); < span class ="hl-comment "> /* Y */</ span >
749+ < span class ="hl-type "> int</ span > b = < span class ="hl-fn "> dl_rule_add_atom</ span > (&r1, < span class ="hl-str "> "edge"</ span > , < span class ="hl-num "> 2</ span > );
750+ < span class ="hl-fn "> dl_body_set_var</ span > (&r1, b, < span class ="hl-num "> 0</ span > , < span class ="hl-num "> 0</ span > );
751+ < span class ="hl-fn "> dl_body_set_var</ span > (&r1, b, < span class ="hl-num "> 1</ span > , < span class ="hl-num "> 1</ span > );
752+ r1.n_vars = < span class ="hl-num "> 2</ span > ;
753+ < span class ="hl-fn "> dl_add_rule</ span > (prog, &r1);
754+
755+ < span class ="hl-comment "> /* Rule 2: reach(X, Z) :- reach(X, Y), edge(Y, Z). */</ span >
756+ < span class ="hl-type "> dl_rule_t</ span > r2;
757+ < span class ="hl-fn "> dl_rule_init</ span > (&r2, < span class ="hl-str "> "reach"</ span > , < span class ="hl-num "> 2</ span > );
758+ < span class ="hl-fn "> dl_rule_head_var</ span > (&r2, < span class ="hl-num "> 0</ span > , < span class ="hl-num "> 0</ span > ); < span class ="hl-comment "> /* X */</ span >
759+ < span class ="hl-fn "> dl_rule_head_var</ span > (&r2, < span class ="hl-num "> 1</ span > , < span class ="hl-num "> 2</ span > ); < span class ="hl-comment "> /* Z */</ span >
760+ < span class ="hl-type "> int</ span > b1 = < span class ="hl-fn "> dl_rule_add_atom</ span > (&r2, < span class ="hl-str "> "reach"</ span > , < span class ="hl-num "> 2</ span > );
761+ < span class ="hl-fn "> dl_body_set_var</ span > (&r2, b1, < span class ="hl-num "> 0</ span > , < span class ="hl-num "> 0</ span > );
762+ < span class ="hl-fn "> dl_body_set_var</ span > (&r2, b1, < span class ="hl-num "> 1</ span > , < span class ="hl-num "> 1</ span > );
763+ < span class ="hl-type "> int</ span > b2 = < span class ="hl-fn "> dl_rule_add_atom</ span > (&r2, < span class ="hl-str "> "edge"</ span > , < span class ="hl-num "> 2</ span > );
764+ < span class ="hl-fn "> dl_body_set_var</ span > (&r2, b2, < span class ="hl-num "> 0</ span > , < span class ="hl-num "> 1</ span > );
765+ < span class ="hl-fn "> dl_body_set_var</ span > (&r2, b2, < span class ="hl-num "> 1</ span > , < span class ="hl-num "> 2</ span > );
766+ r2.n_vars = < span class ="hl-num "> 3</ span > ;
767+ < span class ="hl-fn "> dl_add_rule</ span > (prog, &r2);
768+
769+ < span class ="hl-comment "> /* Stratify, evaluate, query */</ span >
770+ < span class ="hl-fn "> dl_stratify</ span > (prog);
771+ < span class ="hl-fn "> dl_eval</ span > (prog);
772+ < span class ="hl-type "> ray_t</ span > * result = < span class ="hl-fn "> dl_query</ span > (prog, < span class ="hl-str "> "reach"</ span > );
773+ < span class ="hl-comment "> /* result is a table with columns c0, c1:
774+ * 0 | 1
775+ * 1 | 2
776+ * 2 | 3
777+ * 0 | 2
778+ * 1 | 3
779+ * 0 | 3 */</ span >
780+
781+ < span class ="hl-fn "> dl_program_free</ span > (prog);
782+ < span class ="hl-fn "> ray_release</ span > (c0);
783+ < span class ="hl-fn "> ray_release</ span > (c1);
784+ < span class ="hl-fn "> ray_release</ span > (edges);
785+ < span class ="hl-fn "> ray_sym_destroy</ span > ();
786+ < span class ="hl-fn "> ray_heap_destroy</ span > ();
787+ < span class ="hl-kw "> return</ span > < span class ="hl-num "> 0</ span > ;
788+ }</ code > </ pre >
789+
633790 <!-- ============================================================ -->
634791 < h2 id ="examples "> Complete Examples</ h2 >
635792
0 commit comments