|
pulsatrix
|
y = a S b for a selected t-conorm S, over two independent fuzzy-truth-valued operand tensors (values intended in [0,1]; out-of-range values are not rejected – see conjunction_module.hpp's identical note).
More...
#include <disjunction_module.hpp>


Public Types | |
| enum class | TConorm { Product , Lukasiewicz , Godel } |
| Which t-conorm this instance computes. Product is the campaign's primary case. More... | |
Public Member Functions | |
| DisjunctionModule (DeviceBackend *backend, TConorm t_conorm=TConorm::Product) | |
| Constructs a disjunction module. | |
| Tensor | forward (const Tensor &a, const Tensor &b) |
| Convenience two-operand entry point – see ConjunctionModule::forward(a, b)'s identical convention. | |
| Tensor | backward (const Tensor &grad_output) override |
| Gradient w.r.t. this module's (stacked) input. | |
| OpType | op_type () const override |
| Elementwise per this module's own op_type() convention. | |
| Tensor | propagate_relevance (const Tensor &relevance_out, const LRPRuleConfig &config) override |
| LRP relevance propagation for the selected t-conorm – genuinely novel, no prior art (research_2026_neuro_symbolic_ai.md §3); hand-derived and conservation-tested before this implementation existed. | |
| std::optional< DeviceType > | compute_device () const override |
| Where this layer computes, so forward() rejects an input on another device (FND-8). | |
| Tensor | forward (const Tensor &input) |
| Runs this module's forward computation. | |
Public Member Functions inherited from pulsatrix::Module | |
| virtual | ~Module ()=default |
| Tensor | forward (const Tensor &input) |
| Runs this module's forward computation. | |
| std::pair< Tensor, NodeId > | forward_traced (const Tensor &input, NodeId input_node, ComputationGraph &graph, Autograd &autograd) |
| Runs forward() while also registering a ComputationGraph node (tagged with this module's op_type(), parented to input_node) and wiring an Autograd backward function that reuses this module's own backward() – the opt-in traced/explainable path, per Phase 2 Mission 0. | |
| virtual bool | supports_lrp_rule (LRPRule rule) const |
Whether propagate_relevance() implements rule (no silent fallback: callers such as ExplainerContext::relevance_pass() throw rather than run a module on a rule it does not implement). | |
| virtual std::vector< NamedParamRef > | named_parameters () |
| This module's trainable parameters, each with its hierarchical name – the one place a module declares its parameters (roadmap FND-1). | |
| virtual std::vector< ParamRef > | parameters () |
| This module's trainable parameters and their gradients, for an optimizer to update uniformly across module types. | |
| void | set_requires_grad (bool requires_grad, const std::string &prefix="") |
Freezes (false) or unfreezes (true) parameters by name (roadmap FND-2). | |
| virtual void | set_training (bool training) |
| Sets this module's training/eval mode. Defaults to training (matches every mainstream framework's Module default). | |
| bool | is_training () const |
| Whether this module is currently in training mode. | |
Static Public Member Functions | |
| static Tensor | stack_operands (const Tensor &a, const Tensor &b, DeviceBackend *backend) |
| See ConjunctionModule::stack_operands()'s identical convention. | |
Protected Member Functions | |
| Tensor | forward_impl (const Tensor &input) override |
| Splits input (leading dim 2) into the two operands and computes the selected t-conorm elementwise. | |
y = a S b for a selected t-conorm S, over two independent fuzzy-truth-valued operand tensors (values intended in [0,1]; out-of-range values are not rejected – see conjunction_module.hpp's identical note).
|
strong |
|
explicit |
Constructs a disjunction module.
| backend | Backend to allocate/compute through. Not owned; must outlive this module. |
| t_conorm | Which t-conorm to compute. Defaults to Product. |
Gradient w.r.t. this module's (stacked) input.
| std::logic_error | if called before any forward(). |
| std::invalid_argument | if grad_output's shape differs from the cached output shape. |
Implements pulsatrix::Module.
|
inlineoverridevirtual |
Where this layer computes, so forward() rejects an input on another device (FND-8).
Reimplemented from pulsatrix::Module.
Convenience two-operand entry point – see ConjunctionModule::forward(a, b)'s identical convention.
Runs this module's forward computation.
| input | Input tensor. Must be non-empty. |
| std::invalid_argument | if input is empty – external boundary (campaign_exai_dl_library_adversarial_hardening.md, Mission 2, finding 15 systemic sweep): the single most external-facing check in the whole system, since every Module::forward() call – including from Phase 5's Python bindings – passes through this NVI wrapper first. Escalated from PULSATRIX_ASSERT-only. |
Splits input (leading dim 2) into the two operands and computes the selected t-conorm elementwise.
| std::invalid_argument | if input's rank is 0 or its leading dimension isn't 2. |
Implements pulsatrix::Module.
|
inlineoverridevirtual |
Elementwise per this module's own op_type() convention.
Implements pulsatrix::Module.
|
overridevirtual |
LRP relevance propagation for the selected t-conorm – genuinely novel, no prior art (research_2026_neuro_symbolic_ai.md §3); hand-derived and conservation-tested before this implementation existed.
y = a + b - a*b): unlike conjunction's pure bilinear product, this has a mixed additive+bilinear structure – neither a plain weighted sum nor a plain bilinear form, so neither LinearModule's nor AttnLRP Eq. 15's rule applies directly. This is this operator's own genuinely new derivation: y decomposes exactly two ways as a two-term "weighted sum" (the shape every other rule in this codebase already knows how to split) by picking which operand plays the fixed-weight-1 role: y = a*1 + b*(1-a) (decomposition 1: weight_a=1, weight_b=(1-a)) y = b*1 + a*(1-b) (decomposition 2: weight_b=1, weight_a=(1-b)) Both are exact (a*1+b*(1-a) = a+b-ab = y, symmetric for decomposition 2) but each is individually asymmetric in a/b – decomposition 1 privileges a, decomposition 2 privileges b. Averaging the two term-by-term restores the symmetry the operator itself has (disjunction doesn't distinguish its operands): z_a = (a + a*(1-b)) / 2 = a*(2-b)/2, z_b = (b + b*(1-a)) / 2 = b*(2-a)/2 z_a + z_b = (a*(2-b) + b*(2-a)) / 2 = (2a - ab + 2b - ab) / 2 = a + b - ab = y exactly, for every a, b – not just near-exact. The averaged terms are then run through this codebase's standard epsilon-rule split against y (LinearModule-shaped): R_a = z_a/(y+eps*sign(y)) * R_out, R_b = z_b/(y+eps*sign(y)) * R_out, giving near-exact conservation (R_a+R_b = y/(y+eps) * R_out), the same near-exactness class as every other epsilon-stabilized rule in this codebase.y = min(1, a+b)): same active/inactive split as ConjunctionModule's Lukasiewicz rule, mirrored – active (a+b < 1): bias-free epsilon split on z = a+b (R_a = a/(z+eps)*R_out, R_b = b/(z+eps)*R_out); saturated (a+b >= 1, y = 1 locally constant): both operands' derivative is 0, both receive 0, consistent with backward().y = max(a, b)): winning (larger, tie -> a) operand receives all of R_out exactly; the other receives 0 – exact conservation, mirroring ConjunctionModule's Godel rule. Implements pulsatrix::Module.
|
static |
See ConjunctionModule::stack_operands()'s identical convention.