Carry only the code, tests, and user-facing docs that matter to end users; drop the modernization scaffolding accumulated while building it. - Remove the bench harness, results, and environment captures (bench/). - Remove internal docs and research spikes (docs/). - Remove the Makefile; tests run via uv / pytest directly. - Strip the bench harness and quantization-matrix steps from the Tier 2 workflow. The golden-image test drives conversion through the Core ML Converter node at runtime, so no separate convert step is needed. - Replace phase/handoff annotations across code, tests, and config with neutral docstrings and comments.
123 lines
3.4 KiB
Python
123 lines
3.4 KiB
Python
"""Smoke tests for the pure batch-chunking helpers in coreml_suite.core.
|
|
|
|
Uses torch.device('cpu') instead of comfy.model_management.get_torch_device
|
|
so Tier 0 runs without ComfyUI.
|
|
"""
|
|
import pytest
|
|
import torch
|
|
|
|
from coreml_suite.core.controlnet import chunk_control
|
|
from coreml_suite.core.inputs import CoreMLInputs
|
|
from coreml_suite.core.latents import chunk_batch, merge_chunks
|
|
|
|
|
|
CPU = torch.device("cpu")
|
|
|
|
|
|
@pytest.fixture
|
|
def expected_inputs():
|
|
return {
|
|
"sample": {"shape": (2, 4, 64, 64)},
|
|
"timestep": {"shape": (2,)},
|
|
"timestep_cond": {"shape": (2, 256)},
|
|
"encoder_hidden_states": {"shape": (2, 768, 1, 77)},
|
|
"additional_residual_0": {"shape": (2, 320, 64, 64)},
|
|
"additional_residual_1": {"shape": (2, 640, 32, 32)},
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize("batch_size", [1, 2, 4, 5, 9])
|
|
def test_batch_chunking(batch_size):
|
|
latent_image = torch.randn(batch_size, 4, 64, 64).to(CPU)
|
|
target_shape = (4, 4, 64, 64)
|
|
|
|
chunked = chunk_batch(latent_image, target_shape)
|
|
|
|
for chunk in chunked:
|
|
assert chunk.shape == target_shape
|
|
|
|
if batch_size % target_shape[0] != 0:
|
|
assert chunked[-1][batch_size % target_shape[0] :].sum() == 0
|
|
|
|
|
|
@pytest.mark.parametrize("batch_size", [1, 2, 4, 5, 9])
|
|
def test_merge_chunks(batch_size):
|
|
input_tensor = torch.randn(batch_size, 4, 64, 64).to(CPU)
|
|
target_shape = (4, 4, 64, 64)
|
|
chunked = chunk_batch(input_tensor, target_shape)
|
|
|
|
merged = merge_chunks(chunked, input_tensor.shape)
|
|
|
|
assert merged.shape == input_tensor.shape
|
|
assert torch.equal(input_tensor, merged)
|
|
|
|
|
|
@pytest.fixture
|
|
def inputs():
|
|
x = torch.randn(1, 4, 64, 64).to(CPU)
|
|
t = torch.randn([1]).to(CPU)
|
|
c_crossattn = torch.randn(1, 77, 768).to(CPU)
|
|
control = {
|
|
"output": [
|
|
torch.randn(1, 320, 64, 64).to(CPU),
|
|
torch.randn(1, 640, 32, 32).to(CPU),
|
|
],
|
|
}
|
|
timestep_cond = torch.randn(1, 256).to(CPU)
|
|
|
|
return CoreMLInputs(x, t, c_crossattn, control, timestep_cond=timestep_cond)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"b, target_size, num_chunks",
|
|
[
|
|
(1, 2, 1),
|
|
(1, 1, 1),
|
|
(2, 2, 1),
|
|
(3, 2, 2),
|
|
(4, 2, 2),
|
|
(5, 3, 2),
|
|
(9, 4, 3),
|
|
],
|
|
)
|
|
def test_chunking_controlnet(b, target_size, num_chunks):
|
|
cn = {
|
|
"output": [
|
|
torch.randn(b, 320, 64, 64).to(CPU),
|
|
torch.randn(b, 640, 32, 32).to(CPU),
|
|
],
|
|
"middle": [
|
|
torch.randn(b, 1280, 8, 8).to(CPU),
|
|
],
|
|
}
|
|
|
|
chunked = chunk_control(cn, target_size)
|
|
|
|
assert len(chunked) == num_chunks
|
|
for chunk in chunked:
|
|
assert chunk["output"][0].shape == (target_size, 320, 64, 64)
|
|
assert chunk["output"][1].shape == (target_size, 640, 32, 32)
|
|
assert chunk["middle"][0].shape == (target_size, 1280, 8, 8)
|
|
|
|
|
|
def test_chunking_no_control():
|
|
cn = None
|
|
target_size = 2
|
|
|
|
chunked = chunk_control(cn, target_size)
|
|
|
|
assert chunked == [None, None]
|
|
|
|
|
|
def test_chunking_inputs(expected_inputs, inputs):
|
|
chunked = inputs.chunks(expected_inputs)
|
|
|
|
assert len(chunked) == 1
|
|
|
|
assert chunked[0].x.shape == (2, 4, 64, 64)
|
|
assert chunked[0].t.shape == (2,)
|
|
assert chunked[0].context.shape == (2, 77, 768)
|
|
assert chunked[0].control["output"][0].shape == (2, 320, 64, 64)
|
|
assert chunked[0].control["output"][1].shape == (2, 640, 32, 32)
|
|
assert chunked[0].ts_cond.shape == (2, 256)
|