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
4 changes: 2 additions & 2 deletions .vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default defineConfig({
items: [
{ text: "Terms", link: "/en/concepts/terms" },
{ text: "Rules", link: "/en/concepts/rules" },
{ text: "Search Engine", link: "/en/concepts/search" },
{ text: "Engine", link: "/en/concepts/engine" },
],
},
{
Expand Down Expand Up @@ -94,7 +94,7 @@ export default defineConfig({
items: [
{ text: "Term", link: "/zh/concepts/terms" },
{ text: "Rule", link: "/zh/concepts/rules" },
{ text: "搜索引擎", link: "/zh/concepts/search" },
{ text: "引擎", link: "/zh/concepts/engine" },
],
},
{
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ A deductive system for logical inference, implemented in C++. The library provid
- **Rule-Based Inference**: Flexible framework for defining rules and facts to perform complex logical deduction.
- **Unification Engine**: Powerful built-in mechanisms for term unification and rule matching.
- **Automated Search**: Built-in search engine for iterative inference.
- **Chain Inference**: Chain engine that matches all premises of a rule in a single cycle.

## Installation

Expand Down Expand Up @@ -266,6 +267,7 @@ console.log(mp.match(pq).toString()); // "(! (! `x))\n----------\n`x\n"
- `Term`: General term class (variable, item, or list)
- `Rule`: Logical rule class
- `Search`: Search engine for inference
- `Chain`: Chain engine for inference (matches all premises in a single cycle)

### Python

Expand All @@ -278,6 +280,7 @@ console.log(mp.match(pq).toString()); // "(! (! `x))\n----------\n`x\n"
- `Term`: General term class
- `Rule`: Logical rule class
- `Search`: Search engine for inference
- `Chain`: Chain engine for inference (matches all premises in a single cycle)

### C++ (Core)

Expand All @@ -290,6 +293,7 @@ All classes are in the `ds` namespace:
- `term_t`: General terms
- `rule_t`: Logical rules
- `search_t`: Search engine (in `<ds/search.hh>`)
- `chain_t`: Chain engine (in `<ds/chain.hh>`)

See header files in `include/ds/` for detailed API documentation.

Expand Down
2 changes: 2 additions & 0 deletions apyds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"Term",
"Rule",
"Search",
"Chain",
]

from .buffer_size import buffer_size, scoped_buffer_size
Expand All @@ -23,3 +24,4 @@
from .term_t import Term
from .rule_t import Rule
from .search_t import Search
from .chain_t import Chain
54 changes: 54 additions & 0 deletions apyds/_ds.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -674,3 +674,57 @@ class Search:
The number of rules processed.
"""
...

class Chain:
"""C++ binding for ds::chain_t."""

def __init__(self, limit_size: int, buffer_size: int) -> None:
"""Create a new chain engine instance.

Args:
limit_size: Size of the buffer for storing final objects.
buffer_size: Size of the buffer for internal operations.
"""
...

def set_limit_size(self, limit_size: int) -> None:
"""Set the size of the buffer for storing final objects.

Args:
limit_size: The new limit size.
"""
...

def set_buffer_size(self, buffer_size: int) -> None:
"""Set the buffer size for internal operations.

Args:
buffer_size: The new buffer size.
"""
...

def reset(self) -> None:
"""Reset the chain engine, clearing all rules and facts."""
...

def add(self, text: str) -> bool:
"""Add a rule or fact to the knowledge base.

Args:
text: The rule or fact as a string.

Returns:
True if successfully added, False otherwise.
"""
...

def execute(self, callback: Callable[[Rule], bool]) -> int:
"""Execute the chain engine with a callback for each inferred rule.

Args:
callback: Function called for each candidate rule.
Return False to continue, True to stop.

Returns:
The number of rules processed.
"""
80 changes: 80 additions & 0 deletions apyds/chain_t.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Chain engine for the deductive system."""

__all__ = [
"Chain",
]

import typing
from . import ds
from .rule_t import Rule


class Chain:
"""Chain engine for the deductive system.

Similar to Search, but matches all premises of a rule in a single cycle.

Example:
>>> chain = Chain()
>>> chain.add("p q r")
>>> chain.add("p")
>>> chain.add("q")
>>> def callback(rule):
... print(rule)
... return False # Return False to continue, True to stop
>>> chain.execute(callback) # Will find r in a single cycle
"""

def __init__(self, limit_size: int = 1000, buffer_size: int = 10000):
"""Creates a new chain engine instance.

Args:
limit_size: Size of the buffer for storing the final objects (rules/facts)
in the knowledge base (default: 1000).
buffer_size: Size of the buffer for internal operations like conversions
and transformations (default: 10000).
"""
self._chain: ds.Chain = ds.Chain(limit_size, buffer_size)

def set_limit_size(self, limit_size: int) -> None:
"""Set the size of the buffer for storing final objects.

Args:
limit_size: The new limit size for storing rules/facts.
"""
self._chain.set_limit_size(limit_size)

def set_buffer_size(self, buffer_size: int) -> None:
"""Set the buffer size for internal operations.

Args:
buffer_size: The new buffer size.
"""
self._chain.set_buffer_size(buffer_size)

def reset(self) -> None:
"""Reset the chain engine, clearing all rules and facts."""
self._chain.reset()

def add(self, text: str) -> bool:
"""Add a rule or fact to the knowledge base.

Args:
text: The rule or fact as a string.

Returns:
True if successfully added, False otherwise.
"""
return self._chain.add(text)

def execute(self, callback: typing.Callable[[Rule], bool]) -> int:
"""Execute the chain engine with a callback for each inferred rule.

Args:
callback: Function called for each candidate rule. Return False to continue,
True to stop.

Returns:
The number of rules processed.
"""
return self._chain.execute(lambda candidate: callback(Rule(candidate.clone())))
9 changes: 9 additions & 0 deletions apyds/ds.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <ds/chain.hh>
#include <ds/ds.hh>
#include <ds/search.hh>
#include <pybind11/functional.h>
Expand Down Expand Up @@ -168,4 +169,12 @@ PYBIND11_MODULE(_ds, m, py::mod_gil_not_used()) {
search_t.def("reset", &ds::search_t::reset);
search_t.def("add", &ds::search_t::add);
search_t.def("execute", &ds::search_t::execute);

auto chain_t = py::class_<ds::chain_t>(m, "Chain");
chain_t.def(py::init<ds::length_t, ds::length_t>());
chain_t.def("set_limit_size", &ds::chain_t::set_limit_size);
chain_t.def("set_buffer_size", &ds::chain_t::set_buffer_size);
chain_t.def("reset", &ds::chain_t::reset);
chain_t.def("add", &ds::chain_t::add);
chain_t.def("execute", &ds::chain_t::execute);
}
3 changes: 2 additions & 1 deletion apyds/ds.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"Term",
"Rule",
"Search",
"Chain",
]

from ._ds import String, Variable, Item, List, Term, Rule, Search
from ._ds import String, Variable, Item, List, Term, Rule, Search, Chain
18 changes: 18 additions & 0 deletions atsds/ds.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <ds/chain.hh>
#include <ds/ds.hh>
#include <ds/search.hh>
#include <emscripten/bind.h>
Expand Down Expand Up @@ -133,6 +134,14 @@ auto search_execute(ds::search_t* search, const em::val& callback) -> ds::length
return search->execute([&callback](ds::rule_t* candidate) -> bool { return callback(candidate, em::allow_raw_pointers()).as<bool>(); });
}

auto chain_add(ds::chain_t* chain, const std::string& text) -> bool {
return chain->add(text);
}

auto chain_execute(ds::chain_t* chain, const em::val& callback) -> ds::length_t {
return chain->execute([&callback](ds::rule_t* candidate) -> bool { return callback(candidate, em::allow_raw_pointers()).as<bool>(); });
}

EMSCRIPTEN_BINDINGS(ds) {
em::register_vector<std::uint8_t>("Buffer");

Expand Down Expand Up @@ -186,4 +195,13 @@ EMSCRIPTEN_BINDINGS(ds) {
// 因为embind的限制,这里无法使用string_view和function。
search_t.function("add", &search_add, em::allow_raw_pointers());
search_t.function("execute", &search_execute, em::allow_raw_pointers());

auto chain_t = em::class_<ds::chain_t>("Chain");
chain_t.constructor<ds::length_t, ds::length_t>();
chain_t.function("set_limit_size", &ds::chain_t::set_limit_size);
chain_t.function("set_buffer_size", &ds::chain_t::set_buffer_size);
chain_t.function("reset", &ds::chain_t::reset);
// 因为 embind 的限制,这里无法使用 string_view 和 function。
chain_t.function("add", &chain_add, em::allow_raw_pointers());
chain_t.function("execute", &chain_execute, em::allow_raw_pointers());
}
77 changes: 77 additions & 0 deletions atsds/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -621,3 +621,80 @@ export class Search {
});
}
}

/**
* Chain engine for the deductive system.
* Similar to Search, but matches all premises of a rule in a single cycle.
*
* @example
* ```typescript
* const chain = new Chain();
* chain.add("p q r");
* chain.add("p");
* chain.add("q");
* chain.execute((rule) => {
* console.log(rule.toString()); // Will find r in a single cycle
* return false; // Return false to continue, true to stop
* });
* ```
*/
export class Chain {
_chain: dst.Chain;

/**
* Creates a new chain engine instance.
*
* @param limit_size - Size of the buffer for storing the final objects (rules/facts) in the knowledge base (default: 1000).
* @param buffer_size - Size of the buffer for internal operations like conversions and transformations (default: 10000).
*/
constructor(limit_size: number = 1000, buffer_size: number = 10000) {
this._chain = new ds.Chain(limit_size, buffer_size);
}

/**
* Set the size of the buffer for storing final objects.
*
* @param limit_size - The new limit size for storing rules/facts.
*/
set_limit_size(limit_size: number): void {
this._chain.set_limit_size(limit_size);
}

/**
* Set the buffer size for internal operations.
*
* @param buffer_size - The new buffer size.
*/
set_buffer_size(buffer_size: number): void {
this._chain.set_buffer_size(buffer_size);
}

/**
* Reset the chain engine, clearing all rules and facts.
*/
reset(): void {
this._chain.reset();
}

/**
* Add a rule or fact to the knowledge base.
*
* @param text - The rule or fact as a string.
* @returns True if successfully added, false otherwise.
*/
add(text: string): boolean {
return this._chain.add(text);
}

/**
* Execute the chain engine with a callback for each inferred rule.
*
* @param callback - Function called for each candidate rule. Return false to continue, true to stop.
* @returns The number of rules processed.
*/
execute(callback: (candidate: Rule) => boolean): number {
return this._chain.execute((candidate: dst.Rule): boolean => {
return callback(new Rule(candidate).copy());
});
}
}
Loading
Loading