pulsatrix
Loading...
Searching...
No Matches
pulsatrix::DisjunctionModule Class Reference

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>

Inheritance diagram for pulsatrix::DisjunctionModule:
Collaboration diagram for pulsatrix::DisjunctionModule:

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.
 

Detailed Description

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).

Note
Same Stack-based binary-input design as ConjunctionModule (mission_0_tnorm_operators.md Stage 3) – see that header's design-decision note; not re-derived here.

Member Enumeration Documentation

◆ TConorm

Which t-conorm this instance computes. Product is the campaign's primary case.

Enumerator
Product 

a + b - a*b

Lukasiewicz 

min(1, a + b)

Godel 

max(a, b)

Constructor & Destructor Documentation

◆ DisjunctionModule()

pulsatrix::DisjunctionModule::DisjunctionModule ( DeviceBackend *  backend,
TConorm  t_conorm = TConorm::Product 
)
explicit

Constructs a disjunction module.

Parameters
backendBackend to allocate/compute through. Not owned; must outlive this module.
t_conormWhich t-conorm to compute. Defaults to Product.

Member Function Documentation

◆ backward()

Tensor pulsatrix::DisjunctionModule::backward ( const Tensor &  grad_output)
overridevirtual

Gradient w.r.t. this module's (stacked) input.

Exceptions
std::logic_errorif called before any forward().
std::invalid_argumentif grad_output's shape differs from the cached output shape.
Note
Same Godel/Lukasiewicz tie-breaking convention as ConjunctionModule: ties go to the first operand (a).

Implements pulsatrix::Module.

◆ compute_device()

std::optional< DeviceType > pulsatrix::DisjunctionModule::compute_device ( ) const
inlineoverridevirtual

Where this layer computes, so forward() rejects an input on another device (FND-8).

Reimplemented from pulsatrix::Module.

◆ forward() [1/2]

Tensor pulsatrix::DisjunctionModule::forward ( const Tensor &  a,
const Tensor &  b 
)

Convenience two-operand entry point – see ConjunctionModule::forward(a, b)'s identical convention.

◆ forward() [2/2]

Tensor pulsatrix::Module::forward ( const Tensor &  input)
inline

Runs this module's forward computation.

Parameters
inputInput tensor. Must be non-empty.
Returns
The module's output.
Exceptions
std::invalid_argumentif 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.

◆ forward_impl()

Tensor pulsatrix::DisjunctionModule::forward_impl ( const Tensor &  input)
overrideprotectedvirtual

Splits input (leading dim 2) into the two operands and computes the selected t-conorm elementwise.

Exceptions
std::invalid_argumentif input's rank is 0 or its leading dimension isn't 2.

Implements pulsatrix::Module.

◆ op_type()

OpType pulsatrix::DisjunctionModule::op_type ( ) const
inlineoverridevirtual

Elementwise per this module's own op_type() convention.

Implements pulsatrix::Module.

◆ propagate_relevance()

Tensor pulsatrix::DisjunctionModule::propagate_relevance ( const Tensor &  relevance_out,
const LRPRuleConfig &  config 
)
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.

  • Product (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.
  • Lukasiewicz (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().
  • Godel (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.

◆ stack_operands()

static Tensor pulsatrix::DisjunctionModule::stack_operands ( const Tensor &  a,
const Tensor &  b,
DeviceBackend *  backend 
)
static

See ConjunctionModule::stack_operands()'s identical convention.


The documentation for this class was generated from the following file: