Files
tono/tests/node_tests/logistic_classification.py
2026-04-04 00:25:53 -07:00

112 lines
3.5 KiB
Python

import numpy as np
from backend.execution_context import active_node, execution_callbacks
from tests.node_tests._shared import make_field
def test_output_shapes():
from backend.nodes.logistic_classification import LogisticClassification
node = LogisticClassification()
data = np.random.default_rng(0).standard_normal((64, 64))
field = make_field(data=data)
previews = []
with execution_callbacks(preview=lambda nid, uri: previews.append(uri)), active_node("test"):
mask, prob = node.process(
field,
use_gaussians=True,
n_gaussians=4,
use_sobel=True,
use_laplacian=True,
regularization=1.0,
max_iter=500,
seed=42,
)
assert mask.shape == field.data.shape
assert prob.data.shape == field.data.shape
def test_mask_binary():
from backend.nodes.logistic_classification import LogisticClassification
node = LogisticClassification()
data = np.zeros((32, 32))
data[:, 16:] = 1.0
field = make_field(data=data)
with execution_callbacks(preview=lambda nid, uri: None), active_node("test"):
mask, _ = node.process(
field,
use_gaussians=True,
n_gaussians=2,
use_sobel=True,
use_laplacian=True,
regularization=1.0,
max_iter=500,
seed=42,
)
unique = set(np.unique(mask))
assert unique <= {0, 255}, f"Mask contains non-binary values: {unique}"
def test_probability_range():
from backend.nodes.logistic_classification import LogisticClassification
node = LogisticClassification()
data = np.random.default_rng(7).standard_normal((48, 48))
field = make_field(data=data)
with execution_callbacks(preview=lambda nid, uri: None), active_node("test"):
_, prob = node.process(
field,
use_gaussians=True,
n_gaussians=3,
use_sobel=True,
use_laplacian=True,
regularization=1.0,
max_iter=500,
seed=42,
)
assert prob.data.min() >= 0.0, f"Probability min {prob.data.min()} < 0"
assert prob.data.max() <= 1.0, f"Probability max {prob.data.max()} > 1"
def test_with_training():
from backend.nodes.logistic_classification import LogisticClassification
node = LogisticClassification()
# Create a field with two distinct regions
data = np.zeros((64, 64))
data[:, 32:] = 2.0
data += np.random.default_rng(1).standard_normal((64, 64)) * 0.1
field = make_field(data=data)
# Create a training mask marking the right half as positive
training_mask = np.zeros((64, 64), dtype=np.uint8)
training_mask[:, 32:] = 255
with execution_callbacks(preview=lambda nid, uri: None), active_node("test"):
mask, prob = node.process(
field,
use_gaussians=True,
n_gaussians=3,
use_sobel=True,
use_laplacian=True,
regularization=1.0,
max_iter=500,
seed=42,
training_mask=training_mask,
)
assert mask.dtype == np.uint8
assert mask.shape == field.data.shape
# The classifier should learn that the right half is positive
right_positive = np.count_nonzero(mask[:, 32:] == 255)
left_positive = np.count_nonzero(mask[:, :32] == 255)
assert right_positive > left_positive, (
f"Expected more positives on right ({right_positive}) than left ({left_positive})"
)