A small, hand-traceable knowledge base: two neural predicates A(x)/B(x) (each a sigmoid-squashed LinearModule(1,1), producing a fuzzy truth degree in (0,1)), one logical rule A(x) -> not(B(x)), expressed via De Morgan (not(A(x)) or not(B(x)), Product t-conorm/t-negation) using only NegationModule/DisjunctionModule (Mission 0) – no dedicated Implication Module – aggregated across synthetic groundings into one scalar via SatisfactionLoss (Mission 1).
More...
#include <neuro_symbolic_toy_kb.hpp>
|
| | ToyKnowledgeBase (DeviceBackend *backend, float p=2.0f) |
| | Constructs the toy KB with fixed, small, sign-varied initial predicate weights (mission file Stage 3's own hand-picked values: W_A=0.6, b_A=0.0, W_B=-0.4, b_B=0.0), mirroring XorNetwork's documented non-zero-init rationale.
|
| |
| float | forward (const Tensor &x) |
| | Runs the full forward pass: A(x)/B(x) (Linear+sigmoid) -> not(A)/not(B) (NegationModule) -> rule = not(A) or not(B) (DisjunctionModule, Product) -> loss = 1 - agg_p(rule) (SatisfactionLoss). Caches every intermediate backward() needs.
|
| |
| void | backward () |
| | Backpropagates the loss through SatisfactionLoss -> DisjunctionModule -> NegationModule(x2) -> sigmoid(x2) -> LinearModule(x2), accumulating each predicate's weight/bias gradient (LinearModule::accumulate() convention).
|
| |
| float | train_step (const Tensor &x, SGDOptimizer &optimizer) |
| | One SGD step: zero_grad both predicates, forward(), backward(), optimizer step on both predicates.
|
| |
| const Tensor & | last_a () const |
| | Cached predicate A output from the most recent forward(), shape (N, 1).
|
| |
| const Tensor & | last_b () const |
| | Cached predicate B output from the most recent forward(), shape (N, 1).
|
| |
| const Tensor & | last_rule () const |
| | Cached rule truth degrees from the most recent forward(), shape (N,).
|
| |
| LinearModule & | predicate_a () |
| | Predicate A's own LinearModule – test/inspection accessor.
|
| |
| LinearModule & | predicate_b () |
| | Predicate B's own LinearModule – test/inspection accessor.
|
| |
| NegationModule & | negation_a () |
| | Predicate A's own NegationModule (not(A)) – test/inspection accessor, added for Phase 2 Mission 0's end-to-end conservation test (needs to chain propagate_relevance across the actual composed pipeline, not a reimplementation of it).
|
| |
| NegationModule & | negation_b () |
| | Predicate B's own NegationModule (not(B)) – test/inspection accessor, same rationale as negation_a().
|
| |
| DisjunctionModule & | disjunction () |
| | The rule's own DisjunctionModule (not(A) or not(B), Product) – test/inspection accessor, same rationale as negation_a().
|
| |
A small, hand-traceable knowledge base: two neural predicates A(x)/B(x) (each a sigmoid-squashed LinearModule(1,1), producing a fuzzy truth degree in (0,1)), one logical rule A(x) -> not(B(x)), expressed via De Morgan (not(A(x)) or not(B(x)), Product t-conorm/t-negation) using only NegationModule/DisjunctionModule (Mission 0) – no dedicated Implication Module – aggregated across synthetic groundings into one scalar via SatisfactionLoss (Mission 1).
- Note
- Not a
Module subclass – directly analogous to XorNetwork's own classification (mission_training_loop.md): a composing class that owns and chains real Modules/SatisfactionLoss but is not itself relevance-bearing in the propagate_relevance sense. See mission_2_toy_kb_training_demo.md's Stage 3 "Interface/class design" note.
-
De Morgan is exact, not approximate, for this rule under the Product family:
A -> not(B) == not(A) or not(B) is the ordinary propositional-logic identity (P -> Q == not(P) or Q, here Q = not(B)); with Product t-conorm/t-norm, the composed rule's truth degree collapses algebraically to the closed form rule_i = 1 - a_i*b_i (disjunction_Product(not_a, not_b) = not_a+not_b-not_a*not_b = 1-a*b after substituting not_a=1-a, not_b=1-b) – verified as a dedicated test comparing the composed Negation->Negation->Disjunction pipeline against this closed form elementwise, not merely asserted. See mission file Stage 3, Decision 1.
-
Sigmoid squashing is file-local glue, not a new library
Module – pulsatrix has no standalone Sigmoid Module (checked); adding one for a single toy demo would be scope creep the mission file explicitly cautions against (by direct extension of its Implication-Module caution). sigmoid/sigmoid_backward are small anonymous-namespace free functions in neuro_symbolic_toy_kb.cpp, the same "small, per-file helper" convention ConjunctionModule/DisjunctionModule's own split_operands/combine_operands already established (mission_0's Stage 3).
-
No direct (label) supervision on
A/B – the only training signal is the rule's own satisfaction loss, per the phase gate's literal "trainable end-to-end...
on the toy knowledge base" wording. The honest degenerate optimum (push a_i*b_i toward 0 per grounding) is exactly what "satisfaction increases over epochs" is expected to show – not disguised as a more semantically rich result than it is.
◆ ToyKnowledgeBase()
| pulsatrix::ToyKnowledgeBase::ToyKnowledgeBase |
( |
DeviceBackend * |
backend, |
|
|
float |
p = 2.0f |
|
) |
| |
|
explicit |
Constructs the toy KB with fixed, small, sign-varied initial predicate weights (mission file Stage 3's own hand-picked values: W_A=0.6, b_A=0.0, W_B=-0.4, b_B=0.0), mirroring XorNetwork's documented non-zero-init rationale.
- Parameters
-
| backend | Backend to compute through. Not owned; must outlive this object. |
| p | Aggregator exponent, forwarded to the owned SatisfactionLoss. Defaults to 2.0 (RMS), matching SatisfactionLoss's own default. |
◆ backward()
| void pulsatrix::ToyKnowledgeBase::backward |
( |
| ) |
|
Backpropagates the loss through SatisfactionLoss -> DisjunctionModule -> NegationModule(x2) -> sigmoid(x2) -> LinearModule(x2), accumulating each predicate's weight/bias gradient (LinearModule::accumulate() convention).
- Exceptions
-
◆ disjunction()
◆ forward()
| float pulsatrix::ToyKnowledgeBase::forward |
( |
const Tensor & |
x | ) |
|
Runs the full forward pass: A(x)/B(x) (Linear+sigmoid) -> not(A)/not(B) (NegationModule) -> rule = not(A) or not(B) (DisjunctionModule, Product) -> loss = 1 - agg_p(rule) (SatisfactionLoss). Caches every intermediate backward() needs.
- Parameters
-
| x | Shape (N, 1) – N synthetic groundings' shared feature value. |
- Returns
- The scalar satisfaction loss (to minimize via gradient descent).
- Exceptions
-
| std::invalid_argument | if x is not rank 2 with a trailing dimension of 1, or if N == 0 – external boundary, this class's own documented shape contract (not delegated silently to whichever downstream module would happen to reject it first). |
◆ last_a()
| const Tensor & pulsatrix::ToyKnowledgeBase::last_a |
( |
| ) |
const |
|
inline |
Cached predicate A output from the most recent forward(), shape (N, 1).
◆ last_b()
| const Tensor & pulsatrix::ToyKnowledgeBase::last_b |
( |
| ) |
const |
|
inline |
Cached predicate B output from the most recent forward(), shape (N, 1).
◆ last_rule()
| const Tensor & pulsatrix::ToyKnowledgeBase::last_rule |
( |
| ) |
const |
|
inline |
Cached rule truth degrees from the most recent forward(), shape (N,).
◆ negation_a()
Predicate A's own NegationModule (not(A)) – test/inspection accessor, added for Phase 2 Mission 0's end-to-end conservation test (needs to chain propagate_relevance across the actual composed pipeline, not a reimplementation of it).
◆ negation_b()
◆ predicate_a()
◆ predicate_b()
◆ train_step()
| float pulsatrix::ToyKnowledgeBase::train_step |
( |
const Tensor & |
x, |
|
|
SGDOptimizer & |
optimizer |
|
) |
| |
One SGD step: zero_grad both predicates, forward(), backward(), optimizer step on both predicates.
- Parameters
-
| x | Shape (N, 1), as forward()'s own parameter. |
| optimizer | Optimizer applied to both predicate LinearModules. |
- Returns
- The loss value for this step, before the update (matches XorNetwork::train_step's own "before the update" convention).
The documentation for this class was generated from the following file: