Basic Boolean Gates

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 and how the system manages uncertainty.

Note

The interactive notebook is hosted externally to ensure the best viewing experience and to allow immediate execution in the cloud.

Run in Google Colab

Execute the code directly in your browser without any local setup.

https://colab.research.google.com/github/RadimKozl/JLNN/blob/main/examples/JLNN_basic_boolean_gates.ipynb
View on GitHub

Browse the source code and outputs in the GitHub notebook viewer.

https://github.com/RadimKozl/JLNN/blob/main/examples/JLNN_basic_boolean_gates.ipynb

Content Overview

This tutorial explores the intersection of classical logic and neuro-symbolic reasoning. You will learn how to:

  • Define Boolean Gates as Logical Formulas: Using JLNN’s symbolic syntax to represent AND, OR, NOT, NAND, NOR, and XOR operations.

  • Bridge Crisp and Fuzzy Inputs: Observe how the system handles perfect certainty (0/1) versus real-world uncertainty (e.g., “almost true” intervals like [0.95, 1.0]).

  • Monitor Interval Integrity: Verify that the system maintains consistent boundaries (L ≤ U) even through complex nested operations like XOR.

  • Analyze Uncertainty Widths: Visualize how “ignorance” propagates through different logical gates using JAX-backed computations.

'''
try:
    import jlnn
    from flax import nnx
    import jax.numpy as jnp
    print("✅ JLNN and JAX are ready.")
except ImportError:
    print("🚀 Installing JLNN from GitHub and fixing JAX for Colab...")
    # Instalace frameworku
    !pip install jax-lnn --quiet
    #!pip install git+https://github.com/RadimKozl/JLNN.git --quiet
    # Fix JAX/CUDA compatibility for 2026 in Colab
    !pip install --upgrade "jax[cuda12_pip]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html

    import os
    print("\n🔄 RESTARTING ENVIRONMENT... Please wait a second and then run the cell again.")
    os.kill(os.getpid(), 9)
    os.kill(os.getpid(), 9) # After this line, the cell stops and the environment restarts
'''
import os
os.environ["JAX_PLATFORMS"] = "cpu"

import jlnn
import jax.numpy as jnp
from flax import nnx
import jax
import matplotlib.pyplot as plt

# JLNN core imports
from jlnn.nn.gates import WeightedAnd, WeightedOr, WeightedNot, WeightedNand, WeightedNor, WeightedXor
from jlnn.nn.predicates import FixedPredicate
from jlnn.symbolic.compiler import LNNFormula, PredicateNode, UnaryGateNode, BinaryGateNode

print("JLNN loaded.")

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
}

def run_gate(rule, inputs):
    model = LNNFormula(rule, nnx.Rngs(42))

    # Key override: replace LearnedPredicate with Fixed (identity)
    for name in inputs:
        if name in model.predicates:
            model.predicates[name].predicate = FixedPredicate()

    output = model(inputs)

    # Fuse L <= U (although it should already be OK)
    output = jnp.sort(output, axis=-1)

    L = output[0, 0].item()
    U = output[0, 1].item()
    width = U - L

    print(f"Rule: {rule}")
    print(f"  Output: [{L:.4f}, {U:.4f}] (width {width:.4f})")

    return L, U, width


print("=== AND ===")
run_gate("A & B", crisp_inputs)   # we expect [0,0]
run_gate("A & B", fuzzy_inputs)   # we expect low values ​​+ small width

print("=== OR ===")
run_gate("A | B", crisp_inputs)   # we expect [1,1]
run_gate("A | B", fuzzy_inputs)   # we expect high values

print("=== NOT ===")
run_gate("~A", {"A": crisp_inputs["A"]})     # we expect [0,0]
run_gate("~A", {"A": fuzzy_inputs["A"]})     # we expect low values

print("=== NOT ===")
run_gate("~B", {"B": crisp_inputs["B"]})
run_gate("~B", {"B": fuzzy_inputs["B"]})

print("=== NAND ===")
run_gate("~(A & B)", crisp_inputs)

print("=== NOR ===")
run_gate("~(A | B)", crisp_inputs)

print("=== XOR ===")
run_gate("(A & ~B) | (~A & B)", crisp_inputs)
run_gate("(A & ~B) | (~A & B)", fuzzy_inputs)   # interesting – XOR is sensitive to uncertainty

gates = ["AND", "OR", "NOT A", "NAND", "NOR", "XOR"]
crisp_widths = []
fuzzy_widths = []

for gate in gates:
    # Crisp
    if gate == "NOT A":
        rule = "~A"
        inp = {"A": crisp_inputs["A"]}
    elif gate == "XOR":
        rule = "(A & ~B) | (~A & B)"
        inp = crisp_inputs
    else:
        rule = f"A {'&' if 'AND' in gate or 'NAND' in gate else '|'} B"
        if 'NAND' in gate or 'NOR' in gate:
            rule = f"~({rule})"
        inp = crisp_inputs

    _, _, w_crisp = run_gate(rule, inp)
    crisp_widths.append(w_crisp)

    # Fuzzy – we correctly pass the keys from inp
    inp_f = {key: fuzzy_inputs[key] for key in inp}
    _, _, w_fuzzy = run_gate(rule, inp_f)
    fuzzy_widths.append(w_fuzzy)

# Check lengths (debug)
print(f"Gates: {len(gates)}")
print(f"Crisp widths: {len(crisp_widths)}")
print(f"Fuzzy widths: {len(fuzzy_widths)}")

# If duplicates – take only the last unique values ​​(if the loop ran twice)
if len(crisp_widths) > len(gates):
    crisp_widths = crisp_widths[-len(gates):]
    fuzzy_widths = fuzzy_widths[-len(gates):]

x = range(len(gates))
bar_width = 0.35

# Fuzzy – full columns
plt.bar([i + bar_width/2 for i in x], fuzzy_widths, width=bar_width, label='Fuzzy inputs', color='orange')

# Crisp – thin columns or just dots at zero
for i, w in enumerate(crisp_widths):
    if w > 0:
        plt.bar(i - bar_width/2, w, width=bar_width, color='skyblue', label='Crisp inputs' if i == 0 else "")
    else:
        plt.plot(i - bar_width/2, 0, 'o', color='skyblue', markersize=10, label='Crisp inputs (width=0)' if i == 0 else "")

plt.xticks(x, gates, rotation=45)
plt.ylabel('Uncertainty width (U - L)')
plt.title('Output uncertainty for various Boolean operations')
plt.legend()
plt.axhline(y=0, color='gray', linestyle='--', linewidth=0.8, alpha=0.7)
plt.tight_layout()
plt.figtext(0.5, -0.15,
            "Note: Crisp inputs (0/1) always have width 0 → blue columns are invisible (exact logic).",
            ha='center', fontsize=10, color='gray')
plt.show()

Download

You can also download the raw notebook file for local use: JLNN_basic_boolean_gates.ipynb

Tip

To run the notebook locally, make sure you have installed the package using pip install -e .[test].