SabaPivot commited on
Commit
ede7320
·
verified ·
1 Parent(s): 4be561c

Remove obsolete duplicate Claim 5 page

Browse files
pages/claim-5-modular-style-transfer/page.md DELETED
@@ -1,675 +0,0 @@
1
- # claim-5-modular-style-transfer
2
-
3
-
4
- ---
5
- <!-- trackio-cell
6
- {"type": "code", "id": "cell_bfc1be2c467e", "created_at": "2026-07-16T07:28:04+00:00", "title": "Direct core-matrix interpolation: explicit A_alpha blend on the trained four-style checkpoint", "command": ["/home/ubuntu/samuel/repro/.venv-torch/bin/python", "claim5_direct_core_blend.py", "--device", "cuda", "--output-dir", "outputs/claim5_direct_core_blend_logged"], "exit_code": 0, "duration_s": 60.134}
7
- -->
8
- ````bash
9
- $ /home/ubuntu/samuel/repro/.venv-torch/bin/python claim5_direct_core_blend.py --device cuda --output-dir outputs/claim5_direct_core_blend_logged
10
- ````
11
-
12
- exit 0 · 60.1s
13
-
14
-
15
- ````python title=claim5_direct_core_blend.py
16
- #!/usr/bin/env python3
17
- """Direct matrix interpolation audit for the Linearizer style model.
18
-
19
- The released ``style_intrepolations.py`` interpolates the scalar conditioning
20
- index passed to a nonlinear hypernetwork. This script tests the stronger
21
- matrix-algebra claim explicitly:
22
-
23
- A_alpha = (1 - alpha) A_style_1 + alpha A_style_2
24
- y_alpha = g_y^{-1}(A_alpha g_x(x)).
25
-
26
- ``g_x(x)`` is computed exactly once, ``g_x`` and ``g_y`` remain fixed, and the
27
- direct core chord is compared with the repository's conditioned-index path.
28
- The output JSON contains the actual 3x4x4 endpoint/direct/conditioned kernels,
29
- per-alpha comparisons, endpoint checks, and path-smoothness diagnostics.
30
- """
31
-
32
- from __future__ import annotations
33
-
34
- import argparse
35
- import hashlib
36
- import json
37
- import math
38
- import os
39
- import platform
40
- import random
41
- import sys
42
- import time
43
- from pathlib import Path
44
- from typing import Any, Iterable
45
-
46
- os.environ.setdefault("CUBLAS_WORKSPACE_CONFIG", ":4096:8")
47
-
48
- import numpy as np
49
- import torch
50
- from PIL import Image
51
- from torch import Tensor
52
- from torchvision.transforms import ToTensor
53
-
54
- WORKSPACE = Path(__file__).resolve().parent
55
- LINEARIZER_ROOT = WORKSPACE / "Linearizer"
56
- STYLE_ROOT = LINEARIZER_ROOT / "style_transfer"
57
-
58
- # The full-object checkpoint refers to both ``style_transfer.*`` and modules
59
- # imported from the Linearizer repository root. Register both roots before
60
- # torch.load unpickles it.
61
- for import_root in (str(STYLE_ROOT), str(LINEARIZER_ROOT)):
62
- if import_root not in sys.path:
63
- sys.path.insert(0, import_root)
64
-
65
- from style_transfer.modules.linear_network import batch_depthwise_conv2d # noqa: E402
66
-
67
- STYLE_TO_INDEX = {
68
- "mosaic": 0,
69
- "candy": 1,
70
- "rain_princess": 2,
71
- "udnie": 3,
72
- }
73
- DEFAULT_PAIRS = ("candy:rain_princess", "mosaic:udnie")
74
- DEFAULT_ALPHAS = tuple(index / 8 for index in range(9))
75
-
76
-
77
- def parse_args(argv: Iterable[str] | None = None) -> argparse.Namespace:
78
- parser = argparse.ArgumentParser(
79
- description="Compare direct Linearizer core blending with conditioned-index interpolation."
80
- )
81
- parser.add_argument(
82
- "--model-path",
83
- type=Path,
84
- default=STYLE_ROOT / "outputs/07_16_12_26_15/model.pth",
85
- )
86
- parser.add_argument(
87
- "--image-path",
88
- type=Path,
89
- default=STYLE_ROOT / "david_256x256/original.png",
90
- )
91
- parser.add_argument(
92
- "--output-dir",
93
- type=Path,
94
- default=WORKSPACE / "outputs/claim5_direct_core_blend",
95
- )
96
- parser.add_argument(
97
- "--pairs",
98
- nargs="+",
99
- default=list(DEFAULT_PAIRS),
100
- metavar="STYLE1:STYLE2",
101
- )
102
- parser.add_argument(
103
- "--alphas",
104
- nargs="+",
105
- type=float,
106
- default=list(DEFAULT_ALPHAS),
107
- help="ordered blend coefficients; must include 0 and 1",
108
- )
109
- parser.add_argument("--device", default="auto", help="auto, cpu, cuda, or cuda:N")
110
- parser.add_argument("--seed", type=int, default=25191)
111
- parser.add_argument("--dpi", type=int, default=170)
112
- args = parser.parse_args(argv)
113
-
114
- parsed_pairs: list[tuple[str, str]] = []
115
- for pair in args.pairs:
116
- pieces = pair.split(":")
117
- if len(pieces) != 2 or any(piece not in STYLE_TO_INDEX for piece in pieces):
118
- parser.error(
119
- f"invalid pair {pair!r}; styles are {', '.join(STYLE_TO_INDEX)}"
120
- )
121
- if pieces[0] == pieces[1]:
122
- parser.error(f"style pair must contain two distinct endpoints: {pair!r}")
123
- parsed_pairs.append((pieces[0], pieces[1]))
124
- args.pairs = parsed_pairs
125
-
126
- if len(args.alphas) < 3:
127
- parser.error("provide at least three alpha values")
128
- if any(alpha < 0.0 or alpha > 1.0 for alpha in args.alphas):
129
- parser.error("all alpha values must lie in [0,1]")
130
- if args.alphas != sorted(args.alphas) or len(set(args.alphas)) != len(args.alphas):
131
- parser.error("alpha values must be unique and increasing")
132
- if not math.isclose(args.alphas[0], 0.0) or not math.isclose(args.alphas[-1], 1.0):
133
- parser.error("alpha values must begin at 0 and end at 1")
134
- if args.dpi <= 0:
135
- parser.error("--dpi must be positive")
136
- return args
137
-
138
-
139
- def choose_device(requested: str) -> torch.device:
140
- if requested == "auto":
141
- return torch.device("cuda" if torch.cuda.is_available() else "cpu")
142
- device = torch.device(requested)
143
- if device.type == "cuda" and not torch.cuda.is_available():
144
- raise RuntimeError("CUDA was requested but is unavailable")
145
- return device
146
-
147
-
148
- def seed_everything(seed: int) -> None:
149
- random.seed(seed)
150
- np.random.seed(seed)
151
- torch.manual_seed(seed)
152
- if torch.cuda.is_available():
153
- torch.cuda.manual_seed_all(seed)
154
- torch.use_deterministic_algorithms(True, warn_only=True)
155
- if hasattr(torch.backends, "cudnn"):
156
- torch.backends.cudnn.benchmark = False
157
- torch.backends.cudnn.deterministic = True
158
- if hasattr(torch.backends, "cuda") and hasattr(torch.backends.cuda, "matmul"):
159
- torch.backends.cuda.matmul.allow_tf32 = False
160
-
161
-
162
- def file_sha256(path: Path) -> str:
163
- digest = hashlib.sha256()
164
- with path.open("rb") as handle:
165
- for chunk in iter(lambda: handle.read(1024 * 1024), b""):
166
- digest.update(chunk)
167
- return digest.hexdigest()
168
-
169
-
170
- def tensor_sha256(tensor: Tensor) -> str:
171
- values = tensor.detach().cpu().contiguous().numpy()
172
- return hashlib.sha256(values.tobytes()).hexdigest()
173
-
174
-
175
- def load_rgb(path: Path) -> Tensor:
176
- with Image.open(path) as image:
177
- return ToTensor()(image.convert("RGB"))
178
-
179
-
180
- def error_metrics(first: Tensor, second: Tensor) -> dict[str, float]:
181
- difference = (first - second).double()
182
- mse = float(difference.square().mean())
183
- return {
184
- "mse": mse,
185
- "rms": math.sqrt(mse),
186
- "mean_abs": float(difference.abs().mean()),
187
- "max_abs": float(difference.abs().max()),
188
- "psnr_db_assuming_unit_range": -10.0 * math.log10(max(mse, 1e-15)),
189
- }
190
-
191
-
192
- def output_summary(output: Tensor) -> dict[str, float | str]:
193
- return {
194
- "min": float(output.min()),
195
- "max": float(output.max()),
196
- "mean": float(output.double().mean()),
197
- "std": float(output.double().std()),
198
- "sha256_float32": tensor_sha256(output.float()),
199
- "sha256_display_clipped_float32": tensor_sha256(output.clamp(0, 1).float()),
200
- }
201
-
202
-
203
- def path_smoothness(images: list[Tensor], alphas: list[float]) -> dict[str, Any]:
204
- if len(images) != len(alphas):
205
- raise ValueError("images and alpha schedules must have the same length")
206
- successive_rms = [
207
- error_metrics(images[index + 1], images[index])["rms"]
208
- for index in range(len(images) - 1)
209
- ]
210
- successive_per_alpha = [
211
- step / (alphas[index + 1] - alphas[index])
212
- for index, step in enumerate(successive_rms)
213
- ]
214
- second_difference_rms = []
215
- if len(images) >= 3:
216
- # This diagnostic is directly comparable across the default uniform
217
- # schedule. For a nonuniform custom schedule, also report the
218
- # first-derivative changes below.
219
- for index in range(1, len(images) - 1):
220
- second = images[index + 1] - 2.0 * images[index] + images[index - 1]
221
- second_difference_rms.append(
222
- float(torch.sqrt(second.double().square().mean()))
223
- )
224
- velocity_changes = [
225
- abs(successive_per_alpha[index + 1] - successive_per_alpha[index])
226
- for index in range(len(successive_per_alpha) - 1)
227
- ]
228
- chord_rms = error_metrics(images[-1], images[0])["rms"]
229
- path_length = float(sum(successive_rms))
230
- mean_step = float(np.mean(successive_rms))
231
- std_step = float(np.std(successive_rms))
232
- return {
233
- "successive_image_rms": successive_rms,
234
- "successive_rms_per_alpha": successive_per_alpha,
235
- "second_difference_rms": second_difference_rms,
236
- "mean_abs_velocity_change": (
237
- float(np.mean(velocity_changes)) if velocity_changes else 0.0
238
- ),
239
- "max_abs_velocity_change": max(velocity_changes, default=0.0),
240
- "mean_step_rms": mean_step,
241
- "step_rms_coefficient_of_variation": std_step / mean_step if mean_step else 0.0,
242
- "endpoint_chord_rms": chord_rms,
243
- "path_length_sum_rms": path_length,
244
- "path_to_chord_ratio": path_length / chord_rms if chord_rms else 1.0,
245
- }
246
-
247
-
248
- def core_path_smoothness(cores: list[Tensor]) -> dict[str, Any]:
249
- successive = [
250
- error_metrics(cores[index + 1], cores[index])["rms"]
251
- for index in range(len(cores) - 1)
252
- ]
253
- second_differences = [
254
- float(
255
- torch.sqrt(
256
- (cores[index + 1] - 2.0 * cores[index] + cores[index - 1])
257
- .double()
258
- .square()
259
- .mean()
260
- )
261
- )
262
- for index in range(1, len(cores) - 1)
263
- ]
264
- mean_step = float(np.mean(successive))
265
- return {
266
- "successive_core_rms": successive,
267
- "max_second_difference_rms": max(second_differences, default=0.0),
268
- "mean_second_difference_rms": (
269
- float(np.mean(second_differences)) if second_differences else 0.0
270
- ),
271
- "step_rms_coefficient_of_variation": (
272
- float(np.std(successive)) / mean_step if mean_step else 0.0
273
- ),
274
- }
275
-
276
-
277
- def endpoint_target_metrics(
278
- output: Tensor, reference_path: Path
279
- ) -> dict[str, Any] | None:
280
- if not reference_path.exists():
281
- return None
282
- reference = load_rgb(reference_path).unsqueeze(0)
283
- if output.shape != reference.shape:
284
- raise ValueError(
285
- f"reference {reference_path} has shape {tuple(reference.shape)}, "
286
- f"expected {tuple(output.shape)}"
287
- )
288
- return {
289
- "reference_path": str(reference_path.resolve()),
290
- "reference_sha256": file_sha256(reference_path),
291
- "raw_output_vs_reference": error_metrics(output.cpu(), reference),
292
- "clipped_output_vs_reference": error_metrics(
293
- output.cpu().clamp(0, 1), reference
294
- ),
295
- }
296
-
297
-
298
- @torch.inference_mode()
299
- def evaluate_pair(
300
- linearizer: torch.nn.Module,
301
- encoded_content: Tensor,
302
- style1: str,
303
- style2: str,
304
- alphas: list[float],
305
- device: torch.device,
306
- reference_dir: Path,
307
- ) -> tuple[dict[str, Any], list[Tensor], list[Tensor], list[Tensor]]:
308
- index1 = STYLE_TO_INDEX[style1]
309
- index2 = STYLE_TO_INDEX[style2]
310
- core_module = linearizer.linear_network
311
- if not hasattr(core_module, "get_matrix"):
312
- raise TypeError("the loaded core module does not expose get_matrix(t)")
313
-
314
- t1 = torch.tensor([float(index1)], device=device)
315
- t2 = torch.tensor([float(index2)], device=device)
316
- core1 = core_module.get_matrix(t1)
317
- core2 = core_module.get_matrix(t2)
318
- if core1.shape != core2.shape or core1.ndim != 4:
319
- raise ValueError(
320
- f"unexpected endpoint core shapes: {tuple(core1.shape)} and {tuple(core2.shape)}"
321
- )
322
-
323
- direct_cores: list[Tensor] = []
324
- conditioned_cores: list[Tensor] = []
325
- direct_images: list[Tensor] = []
326
- conditioned_images: list[Tensor] = []
327
- abs_difference_images: list[Tensor] = []
328
- alpha_rows: list[dict[str, Any]] = []
329
-
330
- endpoint_latent1 = batch_depthwise_conv2d(encoded_content, core1)
331
- endpoint_latent2 = batch_depthwise_conv2d(encoded_content, core2)
332
-
333
- for alpha in alphas:
334
- direct_core = (1.0 - alpha) * core1 + alpha * core2
335
- conditioned_t = (1.0 - alpha) * index1 + alpha * index2
336
- conditioned_core = core_module.get_matrix(
337
- torch.tensor([conditioned_t], device=device)
338
- )
339
-
340
- direct_latent = batch_depthwise_conv2d(encoded_content, direct_core)
341
- conditioned_latent = batch_depthwise_conv2d(encoded_content, conditioned_core)
342
- direct_output = linearizer.gy_inverse(direct_latent)
343
- conditioned_output = linearizer.gy_inverse(conditioned_latent)
344
-
345
- expected_direct_latent = (
346
- 1.0 - alpha
347
- ) * endpoint_latent1 + alpha * endpoint_latent2
348
- latent_linearity = error_metrics(direct_latent, expected_direct_latent)
349
- direct_vs_conditioned_raw = error_metrics(direct_output, conditioned_output)
350
- direct_vs_conditioned_clipped = error_metrics(
351
- direct_output.clamp(0, 1), conditioned_output.clamp(0, 1)
352
- )
353
-
354
- direct_cores.append(direct_core.detach().cpu())
355
- conditioned_cores.append(conditioned_core.detach().cpu())
356
- direct_images.append(direct_output.detach().cpu())
357
- conditioned_images.append(conditioned_output.detach().cpu())
358
- abs_difference_images.append(
359
- (direct_output - conditioned_output).abs().detach().cpu()
360
- )
361
- alpha_rows.append(
362
- {
363
- "alpha": alpha,
364
- "conditioned_style_index": conditioned_t,
365
- "direct_core_values": direct_core.detach().cpu().flatten().tolist(),
366
- "conditioned_core_values": conditioned_core.detach()
367
- .cpu()
368
- .flatten()
369
- .tolist(),
370
- "direct_core_vs_conditioned_core": error_metrics(
371
- direct_core, conditioned_core
372
- ),
373
- "direct_latent_linearity_check": latent_linearity,
374
- "direct_vs_conditioned_raw": direct_vs_conditioned_raw,
375
- "direct_vs_conditioned_display_clipped": direct_vs_conditioned_clipped,
376
- "direct_output": output_summary(direct_output),
377
- "conditioned_output": output_summary(conditioned_output),
378
- }
379
- )
380
-
381
- endpoint_checks = {
382
- "alpha_0_direct_vs_conditioned": alpha_rows[0]["direct_vs_conditioned_raw"],
383
- "alpha_1_direct_vs_conditioned": alpha_rows[-1]["direct_vs_conditioned_raw"],
384
- style1: endpoint_target_metrics(
385
- direct_images[0], reference_dir / f"{style1}.png"
386
- ),
387
- style2: endpoint_target_metrics(
388
- direct_images[-1], reference_dir / f"{style2}.png"
389
- ),
390
- }
391
-
392
- internal_rows = alpha_rows[1:-1]
393
- pair_metrics: dict[str, Any] = {
394
- "style_1": {"name": style1, "index": index1},
395
- "style_2": {"name": style2, "index": index2},
396
- "endpoint_core_shape": list(core1.shape),
397
- "endpoint_core_1_values": core1.detach().cpu().flatten().tolist(),
398
- "endpoint_core_2_values": core2.detach().cpu().flatten().tolist(),
399
- "endpoint_core_1_sha256": tensor_sha256(core1.float()),
400
- "endpoint_core_2_sha256": tensor_sha256(core2.float()),
401
- "endpoint_checks": endpoint_checks,
402
- "per_alpha": alpha_rows,
403
- "direct_path_smoothness_raw": path_smoothness(direct_images, alphas),
404
- "direct_path_smoothness_display_clipped": path_smoothness(
405
- [image.clamp(0, 1) for image in direct_images], alphas
406
- ),
407
- "conditioned_path_smoothness_raw": path_smoothness(conditioned_images, alphas),
408
- "conditioned_path_smoothness_display_clipped": path_smoothness(
409
- [image.clamp(0, 1) for image in conditioned_images], alphas
410
- ),
411
- "direct_core_path_smoothness": core_path_smoothness(direct_cores),
412
- "conditioned_core_path_smoothness": core_path_smoothness(conditioned_cores),
413
- "internal_direct_vs_conditioned": {
414
- "mean_raw_rms": float(
415
- np.mean(
416
- [row["direct_vs_conditioned_raw"]["rms"] for row in internal_rows]
417
- )
418
- ),
419
- "max_raw_rms": max(
420
- (row["direct_vs_conditioned_raw"]["rms"] for row in internal_rows),
421
- default=0.0,
422
- ),
423
- "max_raw_max_abs": max(
424
- (row["direct_vs_conditioned_raw"]["max_abs"] for row in internal_rows),
425
- default=0.0,
426
- ),
427
- "mean_core_rms": float(
428
- np.mean(
429
- [
430
- row["direct_core_vs_conditioned_core"]["rms"]
431
- for row in internal_rows
432
- ]
433
- )
434
- ),
435
- "max_core_rms": max(
436
- (
437
- row["direct_core_vs_conditioned_core"]["rms"]
438
- for row in internal_rows
439
- ),
440
- default=0.0,
441
- ),
442
- },
443
- "maximum_direct_latent_linearity_error": max(
444
- row["direct_latent_linearity_check"]["max_abs"] for row in alpha_rows
445
- ),
446
- }
447
- return pair_metrics, direct_images, conditioned_images, abs_difference_images
448
-
449
-
450
- def save_figure(
451
- path: Path,
452
- pair_outputs: list[
453
- tuple[tuple[str, str], list[Tensor], list[Tensor], list[Tensor]]
454
- ],
455
- alphas: list[float],
456
- dpi: int,
457
- ) -> None:
458
- import matplotlib
459
-
460
- matplotlib.use("Agg")
461
- import matplotlib.pyplot as plt
462
-
463
- rows_per_pair = 3
464
- figure, axes = plt.subplots(
465
- rows_per_pair * len(pair_outputs),
466
- len(alphas),
467
- figsize=(2.05 * len(alphas), 2.0 * rows_per_pair * len(pair_outputs)),
468
- squeeze=False,
469
- )
470
-
471
- for pair_index, (pair, direct, conditioned, differences) in enumerate(pair_outputs):
472
- style1, style2 = pair
473
- base_row = pair_index * rows_per_pair
474
- for column, alpha in enumerate(alphas):
475
- panels = (
476
- (direct[column], "direct core chord", "gray"),
477
- (conditioned[column], "conditioned index", "gray"),
478
- (differences[column] * 4.0, "4x |direct-conditioned|", "magma"),
479
- )
480
- for offset, (tensor, row_label, color_map) in enumerate(panels):
481
- axis = axes[base_row + offset, column]
482
- image = tensor[0].permute(1, 2, 0).clamp(0, 1).numpy()
483
- if color_map == "magma":
484
- image = image.mean(axis=2)
485
- axis.imshow(image, cmap=color_map, vmin=0, vmax=1)
486
- else:
487
- axis.imshow(image)
488
- axis.set_xticks([])
489
- axis.set_yticks([])
490
- if offset == 0:
491
- endpoint = ""
492
- if column == 0:
493
- endpoint = f"\n{style1}"
494
- elif column == len(alphas) - 1:
495
- endpoint = f"\n{style2}"
496
- axis.set_title(f"alpha={alpha:g}{endpoint}", fontsize=9)
497
- if column == 0:
498
- axis.set_ylabel(f"{style1} -> {style2}\n{row_label}", fontsize=9)
499
-
500
- figure.suptitle(
501
- "Direct Linearizer core interpolation versus nonlinear conditioned-index path\n"
502
- r"$A_\alpha=(1-\alpha)A_1+\alpha A_2$ with one shared $g_x(x)$ and fixed $g_y^{-1}$",
503
- fontsize=15,
504
- )
505
- figure.tight_layout(rect=(0, 0, 1, 0.965), h_pad=0.8, w_pad=0.25)
506
- figure.savefig(path, dpi=dpi, bbox_inches="tight")
507
- plt.close(figure)
508
-
509
-
510
- def environment_metadata(device: torch.device) -> dict[str, Any]:
511
- return {
512
- "python": sys.version.split()[0],
513
- "platform": platform.platform(),
514
- "torch": torch.__version__,
515
- "numpy": np.__version__,
516
- "device": str(device),
517
- "cuda_available": torch.cuda.is_available(),
518
- "cuda_version": torch.version.cuda,
519
- "cuda_device_name": (
520
- torch.cuda.get_device_name(device) if device.type == "cuda" else None
521
- ),
522
- "deterministic_algorithms": torch.are_deterministic_algorithms_enabled(),
523
- }
524
-
525
-
526
- def main(argv: Iterable[str] | None = None) -> int:
527
- args = parse_args(argv)
528
- seed_everything(args.seed)
529
- device = choose_device(args.device)
530
- args.output_dir.mkdir(parents=True, exist_ok=True)
531
- if not args.model_path.exists():
532
- raise FileNotFoundError(args.model_path)
533
- if not args.image_path.exists():
534
- raise FileNotFoundError(args.image_path)
535
-
536
- started = time.perf_counter()
537
- print(f"device={device} seed={args.seed}", flush=True)
538
- linearizer = torch.load(
539
- args.model_path, map_location=device, weights_only=False
540
- ).to(device)
541
- linearizer.eval()
542
- parameter_count = sum(parameter.numel() for parameter in linearizer.parameters())
543
- content = load_rgb(args.image_path).unsqueeze(0).to(device)
544
-
545
- with torch.inference_mode():
546
- encoded_content = linearizer.gx(content)
547
- print(
548
- f"loaded={args.model_path} parameters={parameter_count} "
549
- f"content_shape={tuple(content.shape)} encoded_gx_once=true",
550
- flush=True,
551
- )
552
-
553
- all_pair_metrics: dict[str, Any] = {}
554
- pair_outputs = []
555
- for style1, style2 in args.pairs:
556
- print(f"pair={style1}:{style2}", flush=True)
557
- metrics, direct, conditioned, differences = evaluate_pair(
558
- linearizer,
559
- encoded_content,
560
- style1,
561
- style2,
562
- args.alphas,
563
- device,
564
- args.image_path.parent,
565
- )
566
- key = f"{style1}_to_{style2}"
567
- all_pair_metrics[key] = metrics
568
- pair_outputs.append(((style1, style2), direct, conditioned, differences))
569
- comparison = metrics["internal_direct_vs_conditioned"]
570
- smoothness = metrics["direct_path_smoothness_display_clipped"]
571
- print(
572
- f"pair={key} internal_direct_vs_conditioned_mean_rms="
573
- f"{comparison['mean_raw_rms']:.6e} max_rms={comparison['max_raw_rms']:.6e} "
574
- f"direct_path_to_chord={smoothness['path_to_chord_ratio']:.6f} "
575
- f"latent_linearity_max={metrics['maximum_direct_latent_linearity_error']:.3e}",
576
- flush=True,
577
- )
578
-
579
- figure_path = args.output_dir / "direct_core_blend_vs_conditioned.png"
580
- save_figure(figure_path, pair_outputs, args.alphas, args.dpi)
581
-
582
- elapsed = time.perf_counter() - started
583
- endpoint_max = max(
584
- max(
585
- pair["endpoint_checks"]["alpha_0_direct_vs_conditioned"]["max_abs"],
586
- pair["endpoint_checks"]["alpha_1_direct_vs_conditioned"]["max_abs"],
587
- )
588
- for pair in all_pair_metrics.values()
589
- )
590
- direct_latent_max = max(
591
- pair["maximum_direct_latent_linearity_error"]
592
- for pair in all_pair_metrics.values()
593
- )
594
- results: dict[str, Any] = {
595
- "schema_version": 1,
596
- "claim": (
597
- "With fixed shared encoders, Linearizer style cores can be directly "
598
- "interpolated as A_alpha=(1-alpha)A1+alpha*A2."
599
- ),
600
- "distinction_under_test": (
601
- "Direct matrix interpolation is compared against the released script's "
602
- "nonlinear hypernetwork path obtained by interpolating scalar style index t."
603
- ),
604
- "protocol": {
605
- "seed": args.seed,
606
- "alphas": args.alphas,
607
- "pairs": [list(pair) for pair in args.pairs],
608
- "gx_evaluations": 1,
609
- "shared_gx_and_gy_fixed": True,
610
- "direct_core_formula": "A_alpha=(1-alpha)*A_style1+alpha*A_style2",
611
- "conditioned_index_formula": "t_alpha=(1-alpha)*t_style1+alpha*t_style2",
612
- "output_formula": "y=g_y_inverse(depthwise_conv(g_x(x), A))",
613
- },
614
- "model": {
615
- "path": str(args.model_path.resolve()),
616
- "sha256": file_sha256(args.model_path),
617
- "parameter_count": parameter_count,
618
- "core_module": type(linearizer.linear_network).__name__,
619
- },
620
- "content": {
621
- "path": str(args.image_path.resolve()),
622
- "sha256": file_sha256(args.image_path),
623
- "shape": list(content.shape),
624
- "encoded_shape": list(encoded_content.shape),
625
- "encoded_sha256_float32": tensor_sha256(encoded_content.float()),
626
- },
627
- "pairs": all_pair_metrics,
628
- "summary": {
629
- "endpoint_direct_vs_conditioned_worst_max_abs": endpoint_max,
630
- "direct_latent_linearity_worst_max_abs": direct_latent_max,
631
- "elapsed_seconds": elapsed,
632
- },
633
- "environment": environment_metadata(device),
634
- "artifacts": {
635
- "figure": figure_path.name,
636
- "figure_sha256": file_sha256(figure_path),
637
- "metrics": "metrics.json",
638
- },
639
- }
640
- metrics_path = args.output_dir / "metrics.json"
641
- with metrics_path.open("w", encoding="utf-8") as handle:
642
- json.dump(results, handle, indent=2, sort_keys=True)
643
- handle.write("\n")
644
-
645
- print(
646
- f"RESULT endpoint_worst_max={endpoint_max:.3e} "
647
- f"latent_linearity_worst_max={direct_latent_max:.3e} "
648
- f"elapsed_seconds={elapsed:.2f}",
649
- flush=True,
650
- )
651
- print(f"FIGURE={figure_path.resolve()}", flush=True)
652
- print(f"METRICS_JSON={metrics_path.resolve()}", flush=True)
653
- return 0
654
-
655
-
656
- if __name__ == "__main__":
657
- raise SystemExit(main())
658
-
659
- ````
660
-
661
-
662
- ````output
663
- device=cuda seed=25191
664
- loaded=/home/ubuntu/samuel/linearizer-repro/Linearizer/style_transfer/outputs/07_16_12_26_15/model.pth parameters=3698544 content_shape=(1, 3, 256, 256) encoded_gx_once=true
665
- pair=candy:rain_princess
666
- /home/ubuntu/samuel/linearizer-repro/Linearizer/style_transfer/modules/linear_network.py:31: UserWarning: Using padding='same' with even kernel lengths and odd dilation may require a zero-padded copy of the input be created (Triggered internally at ../aten/src/ATen/native/Convolution.cpp:1036.)
667
- y = F.conv2d(x_, w_, bias=None, stride=stride, padding=padding,
668
- pair=candy_to_rain_princess internal_direct_vs_conditioned_mean_rms=1.272126e-02 max_rms=2.245972e-02 direct_path_to_chord=1.203732 latent_linearity_max=5.960e-07
669
- pair=mosaic:udnie
670
- pair=mosaic_to_udnie internal_direct_vs_conditioned_mean_rms=1.365280e-01 max_rms=1.885035e-01 direct_path_to_chord=1.424578 latent_linearity_max=5.960e-07
671
- RESULT endpoint_worst_max=0.000e+00 latent_linearity_worst_max=5.960e-07 elapsed_seconds=55.15
672
- FIGURE=/home/ubuntu/samuel/linearizer-repro/outputs/claim5_direct_core_blend_logged/direct_core_blend_vs_conditioned.png
673
- METRICS_JSON=/home/ubuntu/samuel/linearizer-repro/outputs/claim5_direct_core_blend_logged/metrics.json
674
-
675
- ````