Basic Boolean Gates & T-Norm Semantics¶
This tutorial demonstrates how JLNN handles classical Boolean operations (AND, OR, NOT, NAND, NOR, XOR) using interval logic. You will see how crisp inputs (0/1) translate to certain intervals, how the system manages uncertainty, and how different logical semantics affect propagation.
Note
The interactive notebooks are hosted externally to ensure the best viewing experience and to allow immediate execution in the cloud.
Grid comparing the Classic and Multi-Semantics JLNN version 0.1.3¶
Basic Version: Learn the core syntax, manual crisp inputs, and the transfer of basic interval uncertainty without training.
Browse the classic notebook source code and outputs directly in the GitHub viewer.
Comprehensive Version: Compare how uncertainty propagates across multiple logic setups (lukasiewicz, godel, product) and entropic Physical Fuzzy Logic.
Browse the multi-semantics notebook source code and advanced matrix visualizations on GitHub.
Theoretical Overview of Semantics¶
When moving beyond basic logical evaluations, JLNN provides seamless switching between different fuzzy t-norms and space-curving fields:
Łukasiewicz Logic: Nilpotent linear approach (T(A, B) = max(0, A + B - 1)). Accumulates uncertainty linearly and provides rock-solid gradient properties.
Gödel Logic: Strict lattice mapping based on minimums (T(A, B) = min(A, B)). Conserves boundary extremes completely without compounding uncertainty.
Product Logic: Smooth multiplicative interpretation (T(A, B) = A … B). Generates smooth continuous polynomial propagation landscapes.
Physical Fuzzy Logic (PFL): Warps the underlying logical grid coordinate space based on local Shannon entropy, shielding deep rule-graphs from cascading uncertainty noise.
import os
import sys
'''
def setup_jlnn_custom_environment(backend: str = "gpu"):
import os
backend = backend.lower().strip()
valid_backends = ["cpu", "gpu", "tpu"]
if backend not in valid_backends:
raise ValueError(f"Invalid backend '{backend}'. Choose one of: {valid_backends}")
print(f"🧹 Cleaning the environment and installing JLNN for backend: {backend.upper()}...")
# We will only uninstall any old versions of Jax from Colab to avoid driver conflicts
!pip uninstall -y jax jaxlib --quiet
# Installation using our newly prepared production packages
if backend == "gpu":
print("🔥 Installing JAX & JLNN with official CUDA 12+ GPU support...")
# Uses jax[cuda12] and the export pipeline without pulling the entire TF
!pip install "jax-lnn[gpu,export]" --quiet
elif backend == "tpu":
print("🚀 Installing JAX & JLNN with TPU support...")
!pip install "jax-lnn[tpu,export]" --quiet
else:
print("💻 Installing JAX & JLNN for pure CPU calculations...")
!pip install "jax-lnn[cpu,export]" --quiet
# We will only install specific add-ons for this particular nanoGPT laptop
print("📊 Installing additional data and NLP tools for nanoGPT...")
!pip install pandas polars matplotlib seaborn --quiet
print("\n🔄 RESTARTING COLAB SESSION to apply hardware changes to Python...")
print("💡 Please note: If your browser disconnects, just wait 5 seconds and click 'Reconnect'.")
# Graceful restart via UI in Colab is recommended, but if automatic repair is running,
# os.kill is required to load new JAX C++ libraries into memory.
os.kill(os.getpid(), 9)
'''
REQUIRED_BACKEND = "cpu"
try:
import jax
import jlnn
import os
if REQUIRED_BACKEND == "tpu" and 'TPU_NAME' in os.environ:
import jax.tools.colab_tpu
jax.tools.colab_tpu.setup_tpu()
current_platform = jax.devices()[0].platform.lower()
if REQUIRED_BACKEND == "gpu" and current_platform == "cpu":
raise RuntimeError("JAX only sees the CPU, even if you demand full GPU power with CUDA 12!")
if REQUIRED_BACKEND == "tpu" and current_platform == "cpu":
raise RuntimeError("JAX fell into CPU mode, failed to map TPU correctly!")
print(f"✅ The jax-lnn environment is successfully configured.")
print(f"✅ Active hardware backend: {current_platform.upper()}")
print(f"✅ Confirmed detected devices: {jax.devices()}")
except (ImportError, RuntimeError) as e:
print(f"⚠️ Environment does not meet specification ({str(e)}). Running automatic repair...")
setup_jlnn_custom_environment(backend=REQUIRED_BACKEND)
'''
!pip show jax-lnn
!rm -rf /content/sample_data
'''
# Imports
import jlnn
import jax.numpy as jnp
from flax import nnx
import jax
import matplotlib.pyplot as plt
# JLNN core imports (Updated for v013 backend verification)
from jlnn.nn.gates import WeightedAnd, WeightedOr, WeightedNot, WeightedNand, WeightedNor, WeightedXor
from jlnn.nn.gates import PhysicalAnd, PhysicalOr, PhysicalNot, PhysicalNand, PhysicalNor
from jlnn.nn.predicates import FixedPredicate
from jlnn.symbolic.compiler import LNNFormula
print("JLNN loaded with advanced T-norm and PFL support.")
# 2. Common inputs for experiments (batch size 1)
crisp_inputs = {
"A": jnp.array([[1.0, 1.0]]), # A = True
"B": jnp.array([[0.0, 0.0]]) # B = False
}
fuzzy_inputs = {
"A": jnp.array([[0.95, 1.0]]), # A almost True
"B": jnp.array([[0.05, 0.1]]) # B almost False
}
# Dynamic Gate Runner with Method Switching
def run_gate_with_method(rule, inputs, method='lukasiewicz'):
"""
Runs a formula graph and dynamically injects either traditional parametric
or physical fuzzy logic components using safe Flax NNX traversal.
"""
model = LNNFormula(rule, nnx.Rngs(42))
# 1. Override predicates to Fixed (identity) identity transformations
for name in inputs:
if name in model.predicates:
model.predicates[name].predicate = FixedPredicate()
# 2. Dynamic module transmutation depending on target semantics
# We use nnx.iter_modules() to fix the DeprecationWarning
for path, module in nnx.iter_modules(model):
# Identify Node references that wrap a gate component
if hasattr(module, 'gate'):
current_gate = module.gate
if method.startswith('physical_'):
# Transmute traditional parametric gates to Physical (PFL) equivalents
if isinstance(current_gate, WeightedAnd):
module.gate = PhysicalAnd(method=method)
elif isinstance(current_gate, WeightedOr):
module.gate = PhysicalOr(method=method)
elif isinstance(current_gate, WeightedNot):
module.gate = PhysicalNot()
elif isinstance(current_gate, WeightedNand):
module.gate = PhysicalNand(method=method)
elif isinstance(current_gate, WeightedNor):
module.gate = PhysicalNor(method=method)
elif isinstance(current_gate, WeightedXor):
# For complex compounds like XOR under PFL, we configure fallback semantic execution
if hasattr(current_gate, 'method'):
current_gate.method = 'lukasiewicz' # Parametric routing fallback for XOR
else:
# Standard parametric setup
if hasattr(current_gate, 'method'):
current_gate.method = method
# 3. Secure forward evaluation
output = model(inputs)
# 4. Extract metrics
output = jnp.sort(output, axis=-1)
L = output[0, 0].item()
U = output[0, 1].item()
width = U - L
return L, U, width
# 3. AND Semantics Comparison
methods_to_test = ['lukasiewicz', 'godel', 'product', 'physical_kleene_dienes']
print("=== AND Semantics ===")
for m in methods_to_test:
_, _, w_c = run_gate_with_method("A & B", crisp_inputs, method=m)
_, _, w_f = run_gate_with_method("A & B", fuzzy_inputs, method=m)
print(f"Method: {m:<25} -> Crisp Width: {w_c:.4f}, Fuzzy Width: {w_f:.4f}")
# 4. OR Semantics Comparison
print("\n=== OR Semantics ===")
for m in methods_to_test:
_, _, w_c = run_gate_with_method("A | B", crisp_inputs, method=m)
_, _, w_f = run_gate_with_method("A | B", fuzzy_inputs, method=m)
print(f"Method: {m:<25} -> Crisp Width: {w_c:.4f}, Fuzzy Width: {w_f:.4f}")
# 5. XOR Uncertainty Singularity
print("\n=== XOR Semantics ===")
for m in methods_to_test:
_, _, w_c = run_gate_with_method("(A & ~B) | (~A & B)", crisp_inputs, method=m)
_, _, w_f = run_gate_with_method("(A & ~B) | (~A & B)", fuzzy_inputs, method=m)
print(f"Method: {m:<25} -> Crisp Width: {w_c:.4f}, Fuzzy Width: {w_f:.4f}")
# 6. Visualizing the uncertainty matrix with multiple semantics
gates = ["AND", "OR", "XOR"]
methods = ['lukasiewicz', 'godel', 'product', 'physical_kleene_dienes']
results = {m: [] for m in methods}
for gate in gates:
if gate == "AND":
rule = "A & B"
elif gate == "OR":
rule = "A | B"
elif gate == "XOR":
rule = "(A & ~B) | (~A & B)"
for m in methods:
_, _, w_fuzzy = run_gate_with_method(rule, fuzzy_inputs, method=m)
results[m].append(w_fuzzy)
# 7. Plotting the multi-bar chart
x = jnp.arange(len(gates))
width = 0.2
fig, ax = plt.subplots(figsize=(10, 6))
for i, m in enumerate(methods):
ax.bar(x + (i * width) - (len(methods)*width/2) + width/2,
results[m], width, label=m)
ax.set_xticks(x)
ax.set_xticklabels(gates)
ax.set_ylabel('Output Uncertainty Width (U - L)')
ax.set_title('v013 Update: Uncertainty Propagation Across Different Logical Semantics')
ax.legend()
plt.grid(axis='y', linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()
Download¶
You can also download the raw notebook file for local use:
JLNN_basic_boolean_gates2.ipynb
Tip
To run the notebook locally, make sure you have installed the package using pip install -e .[test].