Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new “Chain” inference engine to the deductive system, intended to match all premises of a rule within a single execute cycle, and exposes it consistently across the C++ core, Python bindings, and WASM/TypeScript wrapper—with corresponding multi-language tests.
Changes:
- Added
ds::chain_timplementation and public header (include/ds/chain.hh,src/chain.cc). - Exposed
Chainvia pybind11 (apyds/ds.cc,apyds/_ds.pyi,apyds/chain_t.py, export wiring) and embind/TypeScript wrapper (atsds/ds.cc,atsds/index.mts). - Added new tests validating Chain behavior in C++/gtest, Python/pytest, and JS/jest.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_chain.py | Adds pytest coverage for the new apyds.Chain wrapper behavior. |
| tests/test_chain.mjs | Adds jest coverage for the new Chain TS wrapper over WASM bindings. |
| tests/test_chain.cc | Adds gtest coverage for ds::chain_t. |
| src/chain.cc | Implements the new chain-based inference algorithm. |
| include/ds/chain.hh | Declares the ds::chain_t public API and internal state. |
| atsds/index.mts | Adds a TS Chain wrapper consistent with existing Search wrapper. |
| atsds/ds.cc | Adds embind bindings for ds::chain_t (Chain) and its methods. |
| apyds/ds.py | Exposes the bound Chain type from the extension module. |
| apyds/ds.cc | Adds pybind11 bindings for ds::chain_t (Chain). |
| apyds/chain_t.py | Adds a Python ergonomic wrapper Chain mirroring Search. |
| apyds/_ds.pyi | Adds typing stubs for the bound Chain class. |
| apyds/init.py | Exports the Python wrapper Chain at the package top-level. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,167 @@ | |||
| #include <cstring> | |||
| #include <set> | |||
There was a problem hiding this comment.
chain_t::execute() uses std::vector but this translation unit doesn’t include <vector>, which will fail to compile on toolchains that don’t pull it in transitively. Add the missing header include (and avoid relying on indirect includes).
| #include <set> | |
| #include <set> | |
| #include <vector> |
| if (rules_cycle <= done_cycle && facts_cycle <= done_cycle) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
The cycle-filtering logic for multi-premise chaining is incorrect: initial chain generation skips pairs where both rules_cycle and the first-matched facts_cycle are <= done_cycle. This causes missed inferences when an old rule’s first premise is satisfied by an old fact, but a later premise is satisfied by a newly-added fact (e.g., rule a b -> c, old fact a, new fact b won’t infer c). You’ll need to restructure the algorithm to allow old facts for earlier premises while still ensuring each emitted result depends on at least one ‘new’ rule/fact (e.g., propagate a has_new_input flag alongside each partial chain and gate results on it, rather than gating only on the first premise match).
| if (rules_cycle <= done_cycle && facts_cycle <= done_cycle) { | |
| continue; | |
| } |
| auto target = ds::text_to_rule("q", limit_size); | ||
| bool success = false; | ||
| auto count = search->execute([&success, &target](ds::rule_t* rule) { | ||
| if (memcmp(rule, target.get(), rule->data_size()) == 0) { | ||
| success = true; | ||
| return true; |
There was a problem hiding this comment.
The callback comparison uses memcmp(rule, target.get(), rule->data_size()) without first checking that target->data_size() is at least rule->data_size(). If a candidate rule differs in size, this can read past the end of target (UB) and make the test flaky. Compare sizes first (or compare via a safe helper that checks size equality before memcmp).
| auto target = ds::text_to_rule("r", limit_size); | ||
| bool success = false; | ||
| auto count = search->execute([&success, &target](ds::rule_t* rule) { | ||
| if (memcmp(rule, target.get(), rule->data_size()) == 0) { | ||
| success = true; | ||
| return true; |
There was a problem hiding this comment.
Same potential out-of-bounds memcmp issue as above: the length passed is rule->data_size() but target may be smaller when the candidate doesn’t match, leading to UB. Check data_size() equality before calling memcmp (or use a safe equality helper).
| auto target = ds::text_to_rule("s", limit_size); | ||
| bool success = false; | ||
| auto count = search->execute([&success, &target](ds::rule_t* rule) { | ||
| if (memcmp(rule, target.get(), rule->data_size()) == 0) { | ||
| success = true; | ||
| return true; |
There was a problem hiding this comment.
Same potential out-of-bounds memcmp issue as above: the length passed is rule->data_size() but target may be smaller when the candidate doesn’t match, leading to UB. Check data_size() equality before calling memcmp (or use a safe equality helper).
- Add chain_t class (include/ds/chain.hh, src/chain.cc) - Add tests for chain_t (tests/test_chain.cc) - chain_t matches all premises of a rule in a single cycle vs search_t which only matches one premise per cycle
Add documentation for the Chain/chain_t class to README and all API documentation files: - README.md: Add Chain to features and API overview sections - docs/en/api/typescript.md: Add Chain class API reference - docs/en/api/python.md: Add Chain class API reference - docs/en/api/cpp.md: Add chain_t class API reference - docs/zh/api/typescript.md: Add Chinese Chain class API reference - docs/zh/api/python.md: Add Chinese Chain class API reference - docs/zh/api/cpp.md: Add Chinese chain_t class API reference
- Refactor chain_t::execute to use recursive matching for premises - Add max_depth parameter to control maximum recursion depth - Add set_max_depth() method to chain_t class - Reject rules with premises count exceeding max_depth in add() - Remove existing rules exceeding new max_depth when set_max_depth is called - Update cycle tracking: use last_fact_cycle instead of per-fact cycles - Add bindings for Python (apyds) and TypeScript (atsds) - Add tests for set_max_depth functionality - Update documentation for C++, Python, and TypeScript APIs
eeb67fc to
b883953
Compare
Summary
This PR introduces the chain_t class for multi-premise rule matching in a single cycle, along with Python and TypeScript bindings and comprehensive documentation.
Changes
Core Implementation
Language Bindings
Documentation
Tests
Key Features