Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions examples/half_adder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Half Adder example."""

from p_kit.psl.gates import HalfAdder
from p_kit.solver.csd_solver import CaSuDaSolver
from p_kit.visualization import histplot
import numpy as np

adder = HalfAdder()
# Clamp inputs to 1+1 to demonstrate carry output
adder.h[0] = 10 # input1 = 1
adder.h[1] = 10 # input2 = 1

solver = CaSuDaSolver(Nt=10000, dt=0.1667, i0=0.9)
_, output, _ = solver.solve(adder)

print(f"Sum output (bit 2): {np.mean(output[:, 2]):.2f}")
print(f"Carry output (bit 3): {np.mean(output[:, 3]):.2f}")

histplot(output)
12 changes: 12 additions & 0 deletions examples/xor_gate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""XOR gate example."""

from p_kit.psl.gates import XORGate
from p_kit.solver.csd_solver import CaSuDaSolver
from p_kit.visualization import histplot

gate = XORGate()

solver = CaSuDaSolver(Nt=10000, dt=0.1667, i0=0.8)
input_data, output, energy = solver.solve(gate)

histplot(output)
88 changes: 88 additions & 0 deletions p_kit/psl/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,91 @@ class FullAdder:
)

h = np.array([[-1], [-1], [-1], [1], [2]])


@pcircuit(n_pbits=4)
class XORGate:
"""
Probabilistic implementation of an XOR gate using auxiliary bit

Order: [input1, input2, output, aux]
where aux = input1 AND input2

Attributes:
input1 (Port): First input port
input2 (Port): Second input port
output (Port): XOR output port
aux (Port): Auxiliary bit (internal AND)
"""

input1 = Port("input1")
input2 = Port("input2")
output = Port("output")
aux = Port("aux")

J = np.array([
[0, -3, 2, 6],
[-3, 0, 2, 6],
[2, 2, 0, -4],
[6, 6, -4, 0],
])
h = np.array([[3], [3], [-2], [-6]])


@pcircuit(n_pbits=4)
class XNORGate:
"""
Probabilistic implementation of an XNOR gate using auxiliary bit
XNOR = NOT(XOR), outputs 1 when inputs match

Order: [input1, input2, output, aux]
where aux = input1 AND input2

Attributes:
input1 (Port): First input port
input2 (Port): Second input port
output (Port): XNOR output port
aux (Port): Auxiliary bit (internal AND)
"""

input1 = Port("input1")
input2 = Port("input2")
output = Port("output")
aux = Port("aux")

J = np.array([
[0, -3, -2, 6],
[-3, 0, -2, 6],
[-2, -2, 0, 4],
[6, 6, 4, 0],
])
h = np.array([[3], [3], [2], [-6]])


@pcircuit(n_pbits=4)
class HalfAdder:
"""
Probabilistic implementation of a Half Adder
Sum = A XOR B, Carry = A AND B

Order: [input1, input2, sumout, carryout]

Attributes:
input1 (Port): First input port
input2 (Port): Second input port
sumout (Port): Sum output port (XOR)
carryout (Port): Carry output port (AND)
"""

input1 = Port("input1")
input2 = Port("input2")
sumout = Port("sumout")
carryout = Port("carryout")

J = np.array([
[0, -3, 2, 6],
[-3, 0, 2, 6],
[2, 2, 0, -4],
[6, 6, -4, 0],
])
h = np.array([[3], [3], [-2], [-6]])
2 changes: 1 addition & 1 deletion p_kit/solver/csd_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def solve(self, c: PCircuit, annealing_func=constant, n_shots=1):
n_pbits = c.n_pbits

J = xp.asarray(c.J)
h = xp.asarray(c.h)
h = xp.asarray(c.h).flatten() # Ensure h is 1D for proper broadcasting
threshold = float(np.arctanh(self.expected_mean))

# m is (n_shots, n_pbits) — works for n_shots=1 too
Expand Down
132 changes: 132 additions & 0 deletions tests/test_psl.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,135 @@ def __init__(self):
J_from_sparse[i, j] = w

assert np.allclose(J_dense, J_from_sparse)



# ── XOR Gate Tests ────────────────────────────────────────────────────────────

def test_xor_gate_structure():
"""Test XOR gate has correct structure."""
from p_kit.psl.gates import XORGate

gate = XORGate()
assert gate.input1.width == 1
assert gate.input2.width == 1
assert gate.output.width == 1
assert gate.aux.width == 1
assert gate.J.shape == (4, 4)
assert gate.h.shape == (4, 1)


def test_xor_gate_truth_table():
"""Test XOR gate produces correct truth table with high i0."""
from p_kit.psl.gates import XORGate
from p_kit.solver.csd_solver import CaSuDaSolver

gate = XORGate()
solver = CaSuDaSolver(Nt=5000, dt=0.1667, i0=0.95, seed=42)

test_cases = [
([-1, -1], -1), # 0 XOR 0 = 0
([-1, 1], 1), # 0 XOR 1 = 1
([1, -1], 1), # 1 XOR 0 = 1
([1, 1], -1), # 1 XOR 1 = 0
]

for inputs, expected_output in test_cases:
gate.h[0] = inputs[0] * 10
gate.h[1] = inputs[1] * 10
_, output, _ = solver.solve(gate)

# Output is at index 2 (order: input1, input2, output, aux)
output_states = output[:, 2]
most_common = 1 if np.mean(output_states) > 0 else -1
assert most_common == expected_output, \
f"XOR({inputs[0]}, {inputs[1]}) expected {expected_output}, got {most_common}"


# ── XNOR Gate Tests ───────────────────────────────────────────────────────────

def test_xnor_gate_structure():
"""Test XNOR gate has correct structure."""
from p_kit.psl.gates import XNORGate

gate = XNORGate()
assert gate.input1.width == 1
assert gate.input2.width == 1
assert gate.output.width == 1
assert gate.aux.width == 1
assert gate.J.shape == (4, 4)
assert gate.h.shape == (4, 1)


def test_xnor_gate_truth_table():
"""Test XNOR gate produces correct truth table with high i0."""
from p_kit.psl.gates import XNORGate
from p_kit.solver.csd_solver import CaSuDaSolver

gate = XNORGate()
solver = CaSuDaSolver(Nt=5000, dt=0.1667, i0=0.95, seed=42)

test_cases = [
([-1, -1], 1), # 0 XNOR 0 = 1
([-1, 1], -1), # 0 XNOR 1 = 0
([1, -1], -1), # 1 XNOR 0 = 0
([1, 1], 1), # 1 XNOR 1 = 1
]

for inputs, expected_output in test_cases:
gate.h[0] = inputs[0] * 10
gate.h[1] = inputs[1] * 10
_, output, _ = solver.solve(gate)

# Output is at index 2 (order: input1, input2, output, aux)
output_states = output[:, 2]
most_common = 1 if np.mean(output_states) > 0 else -1
assert most_common == expected_output, \
f"XNOR({inputs[0]}, {inputs[1]}) expected {expected_output}, got {most_common}"


# ── Half Adder Tests ──────────────────────────────────────────────────────────

def test_half_adder_structure():
"""Test Half Adder has correct structure."""
from p_kit.psl.gates import HalfAdder

gate = HalfAdder()
assert gate.input1.width == 1
assert gate.input2.width == 1
assert gate.sumout.width == 1
assert gate.carryout.width == 1
assert gate.J.shape == (4, 4)
assert gate.h.shape == (4, 1)


def test_half_adder_truth_table():
"""Test Half Adder produces correct sum and carry outputs."""
from p_kit.psl.gates import HalfAdder
from p_kit.solver.csd_solver import CaSuDaSolver

gate = HalfAdder()
solver = CaSuDaSolver(Nt=5000, dt=0.1667, i0=0.95, seed=42)

test_cases = [
([-1, -1], -1, -1),
([-1, 1], 1, -1),
([1, -1], 1, -1),
([1, 1], -1, 1),
]

for inputs, expected_sum, expected_carry in test_cases:
gate.h[0] = inputs[0] * 10
gate.h[1] = inputs[1] * 10
_, output, _ = solver.solve(gate)

sum_states = output[:, 2]
carry_states = output[:, 3]

sum_result = 1 if np.mean(sum_states) > 0 else -1
carry_result = 1 if np.mean(carry_states) > 0 else -1

assert sum_result == expected_sum
assert carry_result == expected_carry


Loading