در بازبینی یک ماژول کاهش بُعد، مقایسهٔ مستقیمِ بردارهای خروجی ممکن است آزمونِ سالم را شکست بدهد: یک روش بردار v و دیگری منفیِ همان بردار را برمی‌گرداند، درحالی‌که خطِ انتخاب‌شده یکسان است. این درس با چهار مشاهدهٔ دوکانالهٔ تألیفی، شیء مناسبِ مقایسه را می‌سازد. هیچ حسگر واقعی، دسته‌بند یا آزمایش حذف نویز نداریم؛ مسئله، بررسیِ یک تصویر هندسی و مرزِ انتخابِ جهت در PCA است.

۱. دو کانال هم‌واحد و انتخاب یک جهت

مرکز مشترکِ نمونه‌ها با مقیاس حفظ‌شده
تصویر مفهومی: مرکز مشترکِ نمونه‌ها با مقیاس حفظ‌شده؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

چهار ردیف داده، به ترتیب زوج‌های دو و چهار، چهار و دو، منفی چهار و منفی دو، سپس منفی دو و منفی چهارند. هر دو ستون را مقدارِ ساختگی با واحد ولت می‌گیریم. میانگین هر ستون صفر است. ردیف، یک مشاهدهٔ دوکاناله است؛ ستون، یک ویژگی ثابت. عوض‌کردنِ این قرارداد، ماتریس دیگری می‌سازد و فقط تغییرِ شکلِ نمایشی نیست.

در این درس ابتدا میانگینِ هر ستون کم می‌شود و سپس کوواریانسِ نمونه را با مخرجِ تعداد منهای یک می‌سازیم. مرجع cov در NumPy 2.4 جهتِ rowvar و نقشِ ddof را جدا توضیح می‌دهد؛ کد صریحاً rowvar=False و ddof=1 دارد. ماتریس دو در دوِ مثال، قطرهای چهل‌سوم و دو خانهٔ دیگرِ سی‌ودوسوم، همگی با واحد ولتِ مربع دارد.

مرجع PCA، مرکزکردن را از مقیاس‌گذاریِ جداگانهٔ ویژگی‌ها متمایز می‌کند. اینجا تقسیم بر انحراف معیار یا whitening نداریم. حتی هم‌واحد بودن، اهمیتِ عملیِ برابرِ کانال‌ها را تضمین نمی‌کند؛ انتخاب مقیاس باید از مسئله بیاید. اگر فقط یک کانال را دو برابر کنیم، هندسه و جهتِ انتخاب‌شده می‌تواند تغییر کند؛ این حالت را صریحاً آزموده‌ایم.

۲. پروژکتور را به جای علامت بردار بسنجیم

یک ریل هندسی با دو جهت‌گیری معادل
تصویر مفهومی: یک ریل هندسی با دو جهت‌گیری معادل؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

قرارداد eigh مقدارهای ویژه را صعودی و بردارهای متناظر را در ستون‌ها می‌دهد. برای یک مؤلفه، ستون آخر را می‌گیریم؛ ستون اول جهتِ کوچک‌ترین واریانس است. در حسابِ دقیقِ مثال، مقدارهای ویژه هشت‌سوم و بیست‌وچهار ولتِ مربع‌اند. جهتِ اصلی، بردار یک و یک پس از تقسیم بر ریشهٔ دو است.

هر دو جهت v و منفی v یک خط را توصیف می‌کنند. به جای علامت، ماتریس P برابر حاصل‌ضربِ v در ترانهادهٔ آن را می‌سنجیم؛ در مثال، هر چهار خانهٔ P نیم‌اند. عوض‌کردنِ هم‌زمانِ علامت دو عامل، P را تغییر نمی‌دهد. این نتیجهٔ جبریِ خودِ تعریف است؛ مقایسهٔ عددی همچنان خطای گردکردن و تلورانس دارد و مساوی‌بودنِ بیتیِ خروجی‌ها را درخواست نمی‌کنیم.

پروژکتور متقارن است و اعمالِ دوبارهٔ آن، در مدلِ دقیق همان اعمالِ نخست است. برای ردیفِ x، بازسازی را میانگین به‌علاوهٔ حاصل‌ضربِ x منهای میانگین در P می‌گیریم. خروجی هنوز دو مختصات دارد، ولی روی خطِ یک‌بُعدی می‌افتد. ذخیره‌کردنِ ماتریس دو در دو و این مختصات، ادعای فشرده‌سازی فایل یا کاهشِ واقعیِ بایت‌های حافظه نیست.

در دادهٔ همسانگرد، دو مقدار ویژه برابرند و یک خطِ اصلیِ یکتا نداریم. انتخاب یک بردار توسط حل‌گر، این ابهامِ مسئله را حذف نمی‌کند. برای مقدارهای نزدیک نیز آزمونِ جهت به اغتشاش حساس می‌شود. به جای توقعِ تکرارِ یک علامت یا جهت اتفاقی، مرزِ انتخاب ماژول را با یک سیاستِ صریحِ امتناع محدود می‌کنیم؛ این سیاست، معیار آماریِ اطمینان نیست.

۳. کد کامل و مرز فاصلهٔ مقدارهای ویژه

انتخاب جهت فقط با جداییِ روشنِ دو مؤلفه
تصویر مفهومی: انتخاب جهت فقط با جداییِ روشنِ دو مؤلفه؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

آموزش فقط tuple واقعی با دو تا ۶۴ ردیفِ دو عضوی می‌پذیرد. هر عضو int یا float دقیقِ پایتون، متناهی و در بازهٔ بستهٔ منفی شانزده تا شانزده ولت است. bool، رشته، مختلط، اسکالر NumPy و تبدیلِ خودکار پذیرفته نیستند. آرایهٔ داخلی float64 است. این کران‌ها دامنهٔ نرم‌افزاریِ تمرین‌اند و مجوز اتصالِ چنین ولتاژی به دستگاهی واقعی نیستند.

"""Two-channel centered PCA: sign-free projector and bounded audit fixtures."""
from dataclasses import dataclass, FrozenInstanceError
import json
import math
import platform
import numpy as np

ABS = 1e-10
REL = 1e-10
MIN_TOP_VARIANCE = 1e-12  # V**2, a chosen refusal policy.
MIN_RELATIVE_GAP = 1e-8


@dataclass(frozen=True)
class Projection:
    mean: tuple
    projector: tuple
    eigenvalues: tuple  # ascending, V**2
    training_rows: int


def row_array(rows, minimum):
    if type(rows) is not tuple:
        raise TypeError("actual tuple of rows required")
    if not minimum <= len(rows) <= 64:
        raise ValueError("row count outside contract")
    for row in rows:
        if type(row) is not tuple or len(row) != 2:
            raise ValueError("each row must be an actual two-member tuple")
        for value in row:
            if type(value) not in (int, float):
                raise TypeError("exact finite int/float, no bool or scalar coercion")
            if not -16 <= value <= 16 or not math.isfinite(value):
                raise ValueError("channel value must be within +/-16 V")
    return np.array(rows, dtype=np.float64)


def fit_projector(training_rows):
    x = row_array(training_rows, 2)
    mean = x.mean(axis=0)
    covariance = np.cov(x, rowvar=False, ddof=1, dtype=np.float64)
    values, vectors = np.linalg.eigh(covariance)
    top = float(values[-1])
    gap = (top - float(values[0])) / top if top > 0 else 0.0
    if top <= MIN_TOP_VARIANCE or gap <= MIN_RELATIVE_GAP:
        raise ValueError("variance too small or top direction insufficiently separated")
    direction = vectors[:, -1]
    projector = np.outer(direction, direction)
    return Projection(tuple(map(float, mean)),
                      tuple(tuple(map(float, row)) for row in projector),
                      tuple(map(float, values)), len(training_rows))


def validate_state(state):
    if type(state) is not Projection:
        raise TypeError("Projection state required")
    if type(state.mean) is not tuple or len(state.mean) != 2:
        raise ValueError("two mean values required")
    if type(state.projector) is not tuple or len(state.projector) != 2:
        raise ValueError("two-by-two projector required")
    if any(type(row) is not tuple or len(row) != 2 for row in state.projector):
        raise ValueError("two-by-two projector rows required")
    if type(state.eigenvalues) is not tuple or len(state.eigenvalues) != 2:
        raise ValueError("two eigenvalues required")
    if type(state.training_rows) is not int or not 2 <= state.training_rows <= 64:
        raise ValueError("training count invalid")
    numbers = state.mean + state.eigenvalues + sum(state.projector, ())
    if any(type(value) is not float or not math.isfinite(value) for value in numbers):
        raise ValueError("state contains non-finite or non-float values")
    low, top = state.eigenvalues
    if (any(abs(v) > 16 for v in state.mean) or low < -ABS or top > 2048
            or top <= MIN_TOP_VARIANCE or (top - low) / top <= MIN_RELATIVE_GAP):
        raise ValueError("state variance/mean/gap invalid")
    p = np.array(state.projector)
    if not (np.allclose(p, p.T, atol=ABS, rtol=0)
            and np.allclose(p @ p, p, atol=ABS, rtol=0)
            and math.isclose(float(np.trace(p)), 1, abs_tol=ABS, rel_tol=0)):
        raise ValueError("state must contain a symmetric rank-one projector")
    return np.array(state.mean), p


def reconstruct(state, rows):
    mean, p = validate_state(state)
    x = row_array(rows, 1)
    result = (x - mean) @ p + mean
    return tuple(tuple(map(float, row)) for row in result)


def svd_reference(rows):
    # Different data path: explicit fsum centering, rectangular SVD, no covariance/eigh.
    count = len(rows)
    mean = [math.fsum(row[j] for row in rows) / count for j in range(2)]
    centered = np.array([[row[j] - mean[j] for j in range(2)] for row in rows])
    _, singular, vh = np.linalg.svd(centered, full_matrices=False, hermitian=False)
    return np.outer(vh[0], vh[0]), singular[::-1] ** 2 / (count - 1)


def close(a, b):
    return bool(np.allclose(a, b, atol=ABS, rtol=REL))


def rejected(function, *args):
    try:
        function(*args)
    except (TypeError, ValueError):
        return True
    return False


def run_checks():
    rows = ((2, 4), (4, 2), (-4, -2), (-2, -4))
    state = fit_projector(rows)
    p = np.array(state.projector)
    result = np.array(reconstruct(state, rows))
    covariance = np.cov(np.array(rows), rowvar=False, ddof=1)
    residual_sum = float(np.sum((np.array(rows) - result) ** 2))
    checks = {}
    checks["literal_hand_mean_covariance_and_eigenvalues"] = (
        close(state.mean, (0, 0)) and close(covariance, ((40 / 3, 32 / 3), (32 / 3, 40 / 3)))
        and close(state.eigenvalues, (8 / 3, 24))
    )
    direction = np.array((1 / math.sqrt(2), 1 / math.sqrt(2)))
    checks["literal_half_projector_and_sign_invariance"] = (
        close(p, ((0.5, 0.5), (0.5, 0.5)))
        and close(np.outer(direction, direction), np.outer(-direction, -direction))
        and close(p @ p, p) and close(p.T, p)
    )
    checks["literal_reconstruction_and_omitted_sample_variance_identity"] = (
        close(result, ((3, 3), (3, 3), (-3, -3), (-3, -3)))
        and math.isclose(residual_sum, 8, abs_tol=ABS)
        and math.isclose(residual_sum, (len(rows) - 1) * state.eigenvalues[0], abs_tol=ABS)
    )
    max_projector_error = max_variance_error = 0.0
    oracle_cases = 0
    for angle in (0.0, 0.2, 0.7, 1.2):
        c, s = math.cos(angle), math.sin(angle)
        rotation = np.array(((c, -s), (s, c)))
        for scale in (0.5, 1.0, 2.0):
            for offset in ((0, 0), (3, -2), (1, 1)):
                data = np.array(rows) @ rotation.T * scale + offset
                fixture = tuple(tuple(map(float, row)) for row in data)
                fitted = fit_projector(fixture)
                reference_p, reference_v = svd_reference(fixture)
                max_projector_error = max(max_projector_error, float(np.max(np.abs(np.array(fitted.projector) - reference_p))))
                max_variance_error = max(max_variance_error, float(np.max(np.abs(np.array(fitted.eigenvalues) - reference_v))))
                assert close(fitted.projector, reference_p) and close(fitted.eigenvalues, reference_v)
                oracle_cases += 1
    checks["independent_rectangular_svd_36_fixtures_one_group"] = oracle_cases == 36
    checks["paired_observation_permutation_preserves_projector"] = close(
        fit_projector(tuple(rows[i] for i in (3, 1, 0, 2))).projector, p
    )
    shifted = tuple((a + 5, b - 2) for a, b in rows)
    shifted_state = fit_projector(shifted)
    checks["common_offset_changes_mean_not_projector"] = (
        close(shifted_state.mean, (5, -2)) and close(shifted_state.projector, p)
        and close(np.array(reconstruct(shifted_state, shifted)), result + (5, -2))
    )
    scaled_state = fit_projector(tuple((a * 2, b * 2) for a, b in rows))
    checks["uniform_gain_preserves_projector_scales_variance_square"] = (
        close(scaled_state.projector, p)
        and close(scaled_state.eigenvalues, np.array(state.eigenvalues) * 4)
    )
    anisotropic = fit_projector(tuple((a * 2, b) for a, b in rows))
    checks["one_channel_rescaling_can_change_selected_direction"] = not close(anisotropic.projector, p)
    isotropic = ((1, 0), (-1, 0), (0, 1), (0, -1))
    near_equal = ((1, 0), (-1, 0), (0, math.sqrt(1 + 1e-10)), (0, -math.sqrt(1 + 1e-10)))
    separated = ((1, 0), (-1, 0), (0, math.sqrt(1 + 1e-6)), (0, -math.sqrt(1 + 1e-6)))
    checks["equal_and_near_equal_eigenvalues_refused_but_separated_axis_accepted"] = (
        rejected(fit_projector, isotropic) and rejected(fit_projector, near_equal)
        and close(fit_projector(separated).projector, ((0, 0), (0, 1)))
    )
    checks["chosen_absolute_variance_floor_refuses_constant_and_tiny_data"] = (
        rejected(fit_projector, ((1, 1), (1, 1)))
        and rejected(fit_projector, ((1e-8, 0), (-1e-8, 0)))
    )
    checks["two_rows_64_rows_and_inclusive_input_bound"] = (
        close(fit_projector(((16, 0), (-16, 0))).projector, ((1, 0), (0, 0)))
        and close(fit_projector(rows * 16).projector, p)
    )
    checks["container_length_and_shape_contract_errors"] = all(
        rejected(fit_projector, value)
        for value in (list(rows), (), ((1, 2),), rows * 17, ((1,), (2,)),
                      ([1, 2], (3, 4)), ((1, 2, 3), (4, 5, 6)))
    )
    checks["numeric_type_finiteness_and_magnitude_contract_errors"] = all(
        rejected(fit_projector, ((0, 1), (value, 2)))
        for value in (True, "1", 1j, np.float64(1), math.nan, math.inf, -math.inf, 17, 10 ** 500)
    )
    checks["later_rows_use_stored_mean_projector_without_refitting"] = (
        close(reconstruct(state, ((3, 1),)), ((2, 2),))
        and close(state.mean, (0, 0)) and state.training_rows == 4
    )
    bad_states = (
        Projection((0.0,), state.projector, state.eigenvalues, 4),
        Projection(state.mean, ((1.0, 0.0), (0.0, 1.0)), state.eigenvalues, 4),
        Projection(state.mean, ((1.0, 0.5), (0.0, 0.0)), state.eigenvalues, 4),
        Projection(state.mean, state.projector, (1.0, 1.0), 4),
        Projection(state.mean, state.projector, state.eigenvalues, True),
    )
    checks["malformed_state_and_reconstruction_rows_rejected"] = (
        all(rejected(reconstruct, bad, ((0, 0),)) for bad in bad_states)
        and rejected(reconstruct, state, ()) and rejected(reconstruct, state, ((17, 0),))
        and rejected(reconstruct, {}, ((0, 0),))
    )
    try:
        state.mean = (0.0, 1.0)
        assignment_refused = False
    except FrozenInstanceError:
        assignment_refused = True
    checks["ordinary_state_assignment_refused_and_input_tuples_preserved"] = (
        assignment_refused and rows == ((2, 4), (4, 2), (-4, -2), (-2, -4))
    )
    assert all(type(value) is bool and value for value in checks.values())
    return {
        "python": platform.python_version(), "numpy": np.__version__,
        "sklearn_executed": False, "scipy_executed": False, "units": "both channels V; covariance and squared residual V**2; projector and ratio dimensionless",
        "absolute_comparison_tolerance": ABS, "relative_comparison_tolerance": REL,
        "minimum_top_variance_V_squared": MIN_TOP_VARIANCE,
        "minimum_relative_eigenvalue_gap_exclusive": MIN_RELATIVE_GAP,
        "hand_input_rows_V": rows, "hand_mean_V": state.mean,
        "hand_sample_covariance_V_squared": covariance.tolist(),
        "hand_ascending_eigenvalues_V_squared": state.eigenvalues,
        "hand_projector": state.projector,
        "hand_reconstruction_V": result.tolist(), "hand_squared_residual_sum_V_squared": residual_sum,
        "hand_retained_variance_ratio": state.eigenvalues[-1] / sum(state.eigenvalues),
        "anisotropic_gain_projector": anisotropic.projector,
        "independent_svd_fixtures_inside_one_group": oracle_cases,
        "maximum_svd_projector_absolute_difference": max_projector_error,
        "maximum_svd_variance_absolute_difference_V_squared": max_variance_error,
        "checks": checks, "unique_check_groups": len(checks), "all_passed": True,
        "limits": ["Centered two-channel unweighted PCA only; not a full sklearn API",
                   "Eigengap and variance floors are chosen numerical refusal rules, not certified statistical confidence",
                   "Both numerical paths use NumPy/LAPACK; different algorithms plus literal hand geometry do not prove universal correctness",
                   "No model training, denoising quality, causal separation, uncertainty or deployment-performance claim"],
    }


if __name__ == "__main__":
    print(json.dumps(run_checks(), ensure_ascii=False, indent=2))

بزرگ‌ترین مقدار ویژه باید از ده به توان منفی دوازده ولتِ مربع بیشتر باشد. همچنین فاصلهٔ دو مقدار ویژه، تقسیم بر بزرگ‌ترین، باید از ده به توان منفی هشت بیشتر باشد؛ مرز مساوی رد می‌شود. نخستین شرط مقیاسِ مطلق دارد و دومی بی‌بُعد است. این عددها انتخاب آموزشیِ ما هستند، نه تضمینِ دقت یا سیاستِ همهٔ پیاده‌سازی‌های PCA.

حالتِ برازش، میانگین، P، دو مقدار ویژه و تعداد ردیفِ آموزش را نگه می‌دارد. reconstruct با همین حالت و یک تا ۶۴ ردیفِ بعدی کار می‌کند؛ میانگین یا جهت را دوباره برازش نمی‌کند. اعتبارسنجی، شکل، اعداد متناهی، تقارن، تکرارپذیریِ اعمال و ردّ ماتریسِ برابر یک را با تلورانس کنترل می‌کند. فیلدهای frozen انتساب معمول را رد می‌کنند؛ منشأ درستِ حالت یا مرزِ امنیتی را ثابت نمی‌کنند.

کدِ کامل با Python 3.14.5 و NumPy 2.4.6 اجرا شده است؛ scikit-learn و SciPy اجرا نشده‌اند. در حالتِ ساخته‌شده، منفیِ بسیار کوچکِ مقدار ویژه تا تلورانس مطلق برای گردکردن تحمل می‌شود و مقدارِ گزارش‌شده پنهان یا به صفر بریده نمی‌شود. این تحمل را با واریانسِ منفیِ فیزیکی یا گواهیِ سراسریِ خطا یکی نمی‌گیریم. آرایه یا دادهٔ فراخواننده بازنویسی نمی‌شود.

۴. مرجع SVD، جواب دستی و بازسازی

دو مسیر مستقل و یک قاب بازسازی مفهومی
تصویر مفهومی: دو مسیر مستقل و یک قاب بازسازی مفهومی؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

مرجعِ عددیِ جدا، میانگین را با جمع fsum می‌گیرد و مستقیماً روی ماتریسِ مستطیلیِ مرکز‌شده SVD اجرا می‌کند؛ cov و eigh یا تابعِ اصلی را فراخوانی نمی‌کند. طبق تعریف SVD در NumPy، سطر نخستِ Vh جهتِ مقدار منفردِ بزرگ‌تر است. مربعِ مقدارهای منفرد تقسیم بر تعداد منهای یک، مقدارهای ویژهٔ کوواریانس را می‌دهد؛ ترتیب‌ها را هنگام مقایسه هماهنگ می‌کنیم.

دو مسیرِ عددی الگوریتم متفاوت دارند، ولی هر دو از NumPy و LAPACK استفاده می‌کنند. بنابراین خطای مشترکِ پلتفرم را مستقل و ناممکن اعلام نمی‌کنیم. شاهدِ دستی هم داریم: P باید ماتریسِ نیم‌ها باشد و زوج دو و چهار را به سه و سه ببرد. دو زوجِ مثبت به سه و سه و دو زوج منفی به منفی سه و منفی سه بازسازی می‌شوند.

در هر ردیف، مجموع مربعِ خطای دو کانال دو ولتِ مربع است؛ برای چهار ردیف، جمع هشت می‌شود. همین مقدار از سه ضرب‌در هشت‌سوم، یعنی تعداد منهای یک ضرب‌در واریانسِ حذف‌شده به دست می‌آید. میانگینِ این جمع بر تعداد مشاهده‌ها دو است؛ اگر بر هشت مختصات تقسیم کنیم، معیارِ دیگری برابر یک داریم. مخرج و واحد را همراهِ نامِ خطا می‌نویسیم.

{
  "python": "3.14.5",
  "numpy": "2.4.6",
  "sklearn_executed": false,
  "scipy_executed": false,
  "units": "both channels V; covariance and squared residual V**2; projector and ratio dimensionless",
  "absolute_comparison_tolerance": 1e-10,
  "relative_comparison_tolerance": 1e-10,
  "minimum_top_variance_V_squared": 1e-12,
  "minimum_relative_eigenvalue_gap_exclusive": 1e-08,
  "hand_input_rows_V": [
    [
      2,
      4
    ],
    [
      4,
      2
    ],
    [
      -4,
      -2
    ],
    [
      -2,
      -4
    ]
  ],
  "hand_mean_V": [
    0.0,
    0.0
  ],
  "hand_sample_covariance_V_squared": [
    [
      13.333333333333332,
      10.666666666666666
    ],
    [
      10.666666666666666,
      13.333333333333332
    ]
  ],
  "hand_ascending_eigenvalues_V_squared": [
    2.666666666666665,
    24.0
  ],
  "hand_projector": [
    [
      0.4999999999999999,
      0.4999999999999999
    ],
    [
      0.4999999999999999,
      0.4999999999999999
    ]
  ],
  "hand_reconstruction_V": [
    [
      2.999999999999999,
      2.999999999999999
    ],
    [
      2.999999999999999,
      2.999999999999999
    ],
    [
      -2.999999999999999,
      -2.999999999999999
    ],
    [
      -2.999999999999999,
      -2.999999999999999
    ]
  ],
  "hand_squared_residual_sum_V_squared": 8.0,
  "hand_retained_variance_ratio": 0.9000000000000001,
  "anisotropic_gain_projector": [
    [
      0.8419705644406649,
      0.3647686020700426
    ],
    [
      0.3647686020700426,
      0.15802943555933513
    ]
  ],
  "independent_svd_fixtures_inside_one_group": 36,
  "maximum_svd_projector_absolute_difference": 2.220446049250313e-16,
  "maximum_svd_variance_absolute_difference_V_squared": 2.842170943040401e-14,
  "checks": {
    "literal_hand_mean_covariance_and_eigenvalues": true,
    "literal_half_projector_and_sign_invariance": true,
    "literal_reconstruction_and_omitted_sample_variance_identity": true,
    "independent_rectangular_svd_36_fixtures_one_group": true,
    "paired_observation_permutation_preserves_projector": true,
    "common_offset_changes_mean_not_projector": true,
    "uniform_gain_preserves_projector_scales_variance_square": true,
    "one_channel_rescaling_can_change_selected_direction": true,
    "equal_and_near_equal_eigenvalues_refused_but_separated_axis_accepted": true,
    "chosen_absolute_variance_floor_refuses_constant_and_tiny_data": true,
    "two_rows_64_rows_and_inclusive_input_bound": true,
    "container_length_and_shape_contract_errors": true,
    "numeric_type_finiteness_and_magnitude_contract_errors": true,
    "later_rows_use_stored_mean_projector_without_refitting": true,
    "malformed_state_and_reconstruction_rows_rejected": true,
    "ordinary_state_assignment_refused_and_input_tuples_preserved": true
  },
  "unique_check_groups": 16,
  "all_passed": true,
  "limits": [
    "Centered two-channel unweighted PCA only; not a full sklearn API",
    "Eigengap and variance floors are chosen numerical refusal rules, not certified statistical confidence",
    "Both numerical paths use NumPy/LAPACK; different algorithms plus literal hand geometry do not prove universal correctness",
    "No model training, denoising quality, causal separation, uncertainty or deployment-performance claim"
  ]
}

شانزده گروه نام‌دار ثبت شده‌اند. سی‌وشش fixture حاصلِ چرخش، بهرهٔ مشترک و جابه‌جاییِ مشخص، فقط یک گروهِ مرجع SVD هستند. بیشینهٔ اختلافِ خانهٔ پروژکتور حدود ۲٫۲۲ ضرب‌در ده به توان منفی شانزده و اختلافِ واریانس حدود ۲٫۸۴ ضرب‌در ده به توان منفی چهارده ولتِ مربع بود. تلورانس نسبی و مطلقِ مقایسه ده به توان منفی ده است؛ واحدِ مطلق تابعِ کمیتِ مورد مقایسه است.

دادهٔ ثابت، واریانس کوچک، دو مقدار ویژهٔ برابر یا نزدیک، شکل و نوع نامعتبر، کران‌های تعداد، تغییر مقیاس یک کانال و حالتِ خراب جدا بررسی شده‌اند. همانند آزمون حساسیت دستگاه خطی، مناسب‌بودنِ خودِ مسئله را از اجرای موفقِ یک محاسبه جدا می‌کنیم. اینجا مسئله، انتخاب زیرفضاست؛ دستگاهِ مربعی برای مجهول‌های ولتاژ حل نمی‌شود.

۵. چهار پرسش و محدودیت تفسیر PCA

کاهش بُعد جای شاهد کیفیت سامانه را نمی‌گیرد
تصویر مفهومی: کاهش بُعد جای شاهد کیفیت سامانه را نمی‌گیرد؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

پرسش اول: اگر مرجع بردارِ منفی را بدهد آزمون باید رد شود؟ پاسخ: خیر؛ حاصل‌ضربِ بیرونیِ هر دو جهت یکسان است. در مثال هر چهار خانهٔ P نیم‌اند و بازسازی ثابت می‌ماند. اگر علامتِ یک مختصه عوض شود ولی دیگری ثابت بماند، لزوماً همان خط نیست؛ آزمون به شباهتِ نامِ بردار اکتفا نمی‌کند.

پرسش دوم: برای زوج سه و یک با حالتِ مثال چه بازسازی داریم؟ پاسخ: میانگین صفر است و P میانگینِ دو مختصه را در هر دو جایگاه می‌گذارد؛ جواب دو و دو ولت است. این ردیفِ بعدی جهت را تغییر نمی‌دهد. مرز برازش و تبدیلِ دادهٔ آموزش، مسئلهٔ منشأ حالت را جدا توضیح می‌دهد؛ نمونهٔ ما اعتبارِ تقسیم واقعیِ داده را اثبات نمی‌کند.

پرسش سوم: در دو مقدار ویژهٔ برابر، چرا یک بردار دلخواه کافی نیست؟ پاسخ: تابعِ مطلوبِ ما جهتِ اصلیِ جداشده می‌خواهد و داده چنین جهت یکتایی ندارد. بنابراین قرارداد حاضر امتناع می‌کند. این تصمیم با محاسبهٔ یک پایهٔ کامل برای زیرفضای دوبعدی فرق دارد؛ انتخاب تعداد مؤلفه یا تحملِ ابهام به نیازِ مصرف‌کننده وابسته است.

پرسش چهارم: نسبتِ حفظ واریانسِ ۰٫۹ چه چیزی را ثابت می‌کند؟ پاسخ: فقط سهمِ همان مؤلفه از واریانسِ این دادهٔ تألیفی را؛ اجرای شناور ۰٫۹۰۰۰۰۰۰۰۰۰۰۰۰۰۰۱ ثبت کرده است. این عدد کیفیت حذف نویز، تفکیک منبع، حفظ عیبِ مهم یا بهبود دسته‌بندی را ثابت نمی‌کند. منابع در ۲۶ مهر ۱۴۰۵ با نسخه‌های مستندات NumPy 2.4 و scikit-learn 1.9.1 خوانده شدند؛ تاریخ اصلیِ صفحات معلوم نیست و مطلب خبر پژوهشی تازه نیست.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۶ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.