۱. تکرارِ منو را با مدلِ احتمالیِ محدود تعریف کنیم

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

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

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

در اسلایدهای ۴ تا ۹ درسِ مارکوفِ MIT، توزیعِ حالتِ بعد فقط به حالتِ کنونی وابسته است و احتمال‌های گذار در طولِ گام‌ها ثابت‌اند. این‌ها فرض‌های مدل‌اند. اگر احتمالِ بازگشت به تعدادِ تلاش‌های گذشته وابسته باشد، یک حالتِ مشترک برای همهٔ تلاش‌ها کافی نیست؛ باید آن اطلاعات را واردِ حالت کنیم. ظاهرِ چرخه در نمودار، به‌تنهایی فرضِ مارکوف را برقرار نمی‌کند.

۲. وزنِ گذار، حالتِ پایانی و شمارِ گام را روشن کنیم

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

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

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

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

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

۳. جرمِ احتمال را با کسرهای دقیق پیش ببریم

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

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

کلاسِ Fraction در مستنداتِ Python 3.14.8 امکانِ ساختِ نسبتِ دقیقِ دو عدد صحیح را می‌دهد. کد، وزن و جمعِ سطر را مستقیماً به سازنده می‌دهد؛ از اعشارِ شناور برای ساختنِ احتمال استفاده نمی‌کند. دقتِ حساب دربارهٔ پارامترهای فرضی است، نه دقتِ برآوردِ رفتارِ انسان. کسرِ دقیق، فرضِ اشتباه را به دادهٔ معتبر تبدیل نمی‌کند.

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

فایلِ کامل زیر فقط از کتابخانهٔ استاندارد استفاده می‌کند. نسخهٔ واقعیِ اجرا در JSON ثبت می‌شود و خروجی با UTF-8 و پایان‌خطِ LF نوشته می‌شود. بردارهای ردگیری تاپل‌اند و تغییرِ یک گزارش، فراخوانیِ بعدی را تغییر نمی‌دهد. نمونهٔ دوم دو حالتِ گذرا دارد؛ تا دو گذار، سهمِ پایانِ مطلوب پنج‌هشتم و انصراف سه‌هشتم است و سهمِ حل‌نشده صفر می‌شود.

"""Bounded synthetic IVR Markov outcomes; no calls, audio, clocks or simulation."""
from fractions import Fraction as F
from itertools import product
import json
import platform
import sys

STATE_CAP = 5
STEP_CAP = 6
WEIGHT_CAP = 8
ROW_TOTAL_CAP = 16


def _validated_probabilities(weights, start, success, abandon, horizon):
    if type(weights) is not tuple:
        raise TypeError('weights must be an exact tuple')
    n = len(weights)
    if not 2 <= n <= STATE_CAP:
        raise ValueError('own state count must be 2..5')
    for row in weights:
        if type(row) is not tuple:
            raise TypeError('each row must be an exact tuple')
        if len(row) != n:
            raise ValueError('square rows required')
        if any(type(value) is not int for value in row):
            raise TypeError('weights must be exact ints, excluding bool')
        if any(not 0 <= value <= WEIGHT_CAP for value in row):
            raise ValueError('own weights must be 0..8')
        if not 1 <= sum(row) <= ROW_TOTAL_CAP:
            raise ValueError('own row total must be 1..16')
    if any(type(index) is not int for index in (start, success, abandon, horizon)):
        raise TypeError('state indices and horizon must be exact ints')
    if any(not 0 <= index < n for index in (start, success, abandon)):
        raise ValueError('state index outside matrix')
    if success == abandon:
        raise ValueError('two distinct terminal states required')
    if not 0 <= horizon <= STEP_CAP:
        raise ValueError('own horizon must be 0..6 abstract transitions')
    for terminal in (success, abandon):
        row = weights[terminal]
        if row[terminal] == 0 or any(row[j] for j in range(n) if j != terminal):
            raise ValueError('terminal rows must already be absorbing')
    return tuple(tuple(F(value, sum(row)) for value in row) for row in weights)


def bounded_outcomes(weights, start, success, abandon, horizon):
    """Exact state mass after K transitions; terminal mass equals hit-by-K mass.

    The two terminal rows are supplied and validated, never silently replaced.
    All inputs are validated even for K0 or an initially terminal state.
    """
    probabilities = _validated_probabilities(weights, start, success, abandon, horizon)
    n = len(weights)
    current = tuple(F(int(i == start)) for i in range(n))
    trace = [current]
    for _ in range(horizon):
        following = [F(0) for _ in range(n)]
        for source in range(n):
            for target in range(n):
                following[target] += current[source] * probabilities[source][target]
        current = tuple(following)
        trace.append(current)
    unresolved = sum((current[i] for i in range(n) if i not in (success, abandon)), F(0))
    return {'success': current[success], 'abandon': current[abandon],
            'unresolved': unresolved, 'state_mass': current, 'trace': tuple(trace)}


def stopped_path_oracle(weights, start, success, abandon, horizon):
    """Test-only trusted-fixture path enumeration; no main/validation/DP calls.

    A path ends at its first terminal or at the horizon. Its probability is
    the direct product of raw edge-weight / raw row-total factors.
    """
    endpoints = [F(0) for _ in weights]
    leaves = 0

    def visit(state, depth, probability):
        nonlocal leaves
        if state in (success, abandon) or depth == horizon:
            endpoints[state] += probability
            leaves += 1
            return
        row = weights[state]
        denominator = sum(row)
        for target, weight in enumerate(row):
            if weight:
                visit(target, depth + 1, probability * F(weight, denominator))

    visit(start, 0, F(1))
    unresolved = F(0)
    for state, probability in enumerate(endpoints):
        if state != success and state != abandon:
            unresolved += probability
    return {'success': endpoints[success], 'abandon': endpoints[abandon],
            'unresolved': unresolved, 'state_mass': tuple(endpoints), 'stopped_leaves': leaves}


def raises(error, call):
    try:
        call()
    except error:
        return True
    return False


def as_json(value):
    if type(value) is F:
        return str(value)
    if isinstance(value, dict):
        return {key: as_json(child) for key, child in value.items()}
    if isinstance(value, (tuple, list)):
        return [as_json(child) for child in value]
    return value


def self_check():
    retry = ((1, 2, 1), (0, 1, 0), (0, 0, 1))
    example = bounded_outcomes(retry, 0, 1, 2, 3)
    checks = {}
    checks['hand_retry_K3_exact_outcomes'] = (
        example['success'] == F(21, 32) and example['abandon'] == F(21, 64)
        and example['unresolved'] == F(1, 64)
        and example['trace'] == ((F(1), F(0), F(0)),
                                 (F(1, 4), F(1, 2), F(1, 4)),
                                 (F(1, 16), F(5, 8), F(5, 16)),
                                 (F(1, 64), F(21, 32), F(21, 64))))
    zero = bounded_outcomes(retry, 0, 1, 2, 0)
    checks['zero_horizon_does_not_take_a_transition'] = (
        zero['success'] == zero['abandon'] == 0 and zero['unresolved'] == 1
        and zero['trace'] == ((F(1), F(0), F(0)),))
    checks['terminal_initial_state_counts_at_K0_and_stays'] = all(
        all(mass[terminal] == 1 and sum(mass, F(0)) == 1 for mass in
            bounded_outcomes(retry, terminal, 1, 2, horizon)['trace'])
        for terminal in (1, 2) for horizon in range(STEP_CAP + 1))
    chain = ((0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 1, 0), (0, 0, 0, 1))
    before = bounded_outcomes(chain, 0, 2, 3, 1)
    boundary = bounded_outcomes(chain, 0, 2, 3, 2)
    checks['arrival_at_boundary_counts_by_K_not_before'] = (
        before['success'] == 0 and before['unresolved'] == 1
        and boundary['success'] == 1 and boundary['unresolved'] == 0)
    closed = ((0, 1, 0, 0), (1, 0, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))
    trapped = bounded_outcomes(closed, 0, 2, 3, STEP_CAP)
    checks['closed_transient_cycle_unresolved_not_abandoned'] = (
        trapped['success'] == trapped['abandon'] == 0 and trapped['unresolved'] == 1
        and trapped['state_mass'] == (F(1), F(0), F(0), F(0)))
    branch = ((0, 3, 1, 0), (0, 0, 1, 1), (0, 0, 1, 0), (0, 0, 0, 1))
    branched = bounded_outcomes(branch, 0, 2, 3, 2)
    later = bounded_outcomes(branch, 0, 2, 3, STEP_CAP)
    checks['distinct_terminal_outcomes_do_not_recirculate'] = (
        branched['success'] == later['success'] == F(5, 8)
        and branched['abandon'] == later['abandon'] == F(3, 8)
        and branched['unresolved'] == later['unresolved'] == 0)
    scaled = ((2, 4, 2), (0, 3, 0), (0, 0, 4))
    checks['row_rescaling_preserves_exact_probabilities'] = (
        bounded_outcomes(scaled, 0, 1, 2, 3) == example)
    permutation = (2, 0, 3, 1)  # old state -> new state
    renamed_rows = [[0] * 4 for _ in range(4)]
    for source, row in enumerate(branch):
        for target, weight in enumerate(row):
            renamed_rows[permutation[source]][permutation[target]] = weight
    renamed = bounded_outcomes(tuple(map(tuple, renamed_rows)), 2, 3, 1, 2)
    checks['renaming_states_permutes_trace_only'] = (
        all(renamed[key] == branched[key] for key in ('success', 'abandon', 'unresolved'))
        and all(new[permutation[i]] == old[i] for old, new in
                zip(branched['trace'], renamed['trace']) for i in range(4)))
    traces = (example['trace'], later['trace'], trapped['trace'])
    checks['all_trace_mass_conserved_exactly'] = all(
        sum(mass, F(0)) == 1 and all(type(x) is F and x >= 0 for x in mass)
        for trace in traces for mass in trace)
    monotone = True
    for weights, success, abandon in ((retry, 1, 2), (branch, 2, 3), (closed, 2, 3)):
        trace = bounded_outcomes(weights, 0, success, abandon, STEP_CAP)['trace']
        monotone &= all(right[success] >= left[success] and right[abandon] >= left[abandon]
                        and 1 - right[success] - right[abandon] <= 1 - left[success] - left[abandon]
                        for left, right in zip(trace, trace[1:]))
    checks['terminal_cumulative_mass_monotone_unresolved_nonincreasing'] = bool(monotone)
    changed = bounded_outcomes(retry, 0, 1, 2, 3)
    changed['success'] = F(0)
    def mutate_snapshot():
        example['trace'][0][0] = F(0)
    checks['fresh_trace_and_report_do_not_mutate_inputs'] = (
        retry == ((1, 2, 1), (0, 1, 0), (0, 0, 1))
        and all(type(mass) is tuple for mass in example['trace'])
        and raises(TypeError, mutate_snapshot)
        and bounded_outcomes(retry, 0, 1, 2, 3) == example)

    # All nonempty binary rows for two transient states; terminal rows fixed.
    # 15*15 row pairs *5 horizons *4 starts, counted as ONE oracle family.
    binary_rows = [row for row in product((0, 1), repeat=4) if any(row)]
    cases, leaves, oracle_ok = 0, 0, True
    for first, second in product(binary_rows, repeat=2):
        weights = (first, second, (0, 0, 1, 0), (0, 0, 0, 1))
        for horizon in range(5):
            for start in range(4):
                observed = bounded_outcomes(weights, start, 2, 3, horizon)
                expected = stopped_path_oracle(weights, start, 2, 3, horizon)
                oracle_ok &= all(observed[key] == expected[key]
                                 for key in ('success', 'abandon', 'unresolved', 'state_mass'))
                cases += 1
                leaves += expected['stopped_leaves']
    checks['independent_stopped_path_family_ONE_group'] = bool(oracle_ok and cases == 4500)

    class TupleChild(tuple):
        def __len__(self):
            raise AssertionError('subclass hook must not run')

    class IntChild(int):
        pass

    def fail_if_consumed():
        raise AssertionError('generator must not be consumed')
        yield ()

    invalid = [(TypeError, (list(retry), 0, 1, 2, 3)),
               (TypeError, (TupleChild(retry), 0, 1, 2, 3)),
               (TypeError, (fail_if_consumed(), 0, 1, 2, 3)),
               (ValueError, ((), 0, 0, 1, 0)),
               (ValueError, (((1,),), 0, 0, 0, 0)),
               (ValueError, (tuple((1,) * 6 for _ in range(6)), 0, 1, 2, 0)),
               (TypeError, (([1, 2, 1], retry[1], retry[2]), 0, 1, 2, 3)),
               (ValueError, (((1, 2), retry[1], retry[2]), 0, 1, 2, 3)),
               (TypeError, (((True, 2, 1), retry[1], retry[2]), 0, 1, 2, 3)),
               (TypeError, (((IntChild(1), 2, 1), retry[1], retry[2]), 0, 1, 2, 3)),
               (ValueError, (((-1, 2, 1), retry[1], retry[2]), 0, 1, 2, 3)),
               (ValueError, (((9, 0, 0), retry[1], retry[2]), 0, 1, 2, 3)),
               (ValueError, (((8, 8, 1), retry[1], retry[2]), 0, 1, 2, 3)),
               (ValueError, (((0, 0, 0), retry[1], retry[2]), 0, 1, 2, 3)),
               (ValueError, (retry, -1, 1, 2, 3)), (ValueError, (retry, 3, 1, 2, 3)),
               (ValueError, (retry, 0, 1, 1, 3)), (ValueError, (retry, 0, 1, 2, -1)),
               (ValueError, (retry, 0, 1, 2, 7))]
    for position in range(1, 5):
        for value in (True, 1.0, '1', None, IntChild(1)):
            arguments = [retry, 0, 1, 2, 3]
            arguments[position] = value
            invalid.append((TypeError, tuple(arguments)))
    checks['strict_types_shapes_and_own_resource_caps'] = all(
        raises(error, lambda args=args: bounded_outcomes(*args)) for error, args in invalid)
    malformed_terminal = ((1, 2, 1), (1, 0, 0), (0, 0, 1))
    malformed_unvisited = ((1, 0, 0), (0, 1, 0), (0, 0, 0))
    checks['all_rows_validated_even_at_K0_or_terminal_start'] = all([
        raises(ValueError, lambda: bounded_outcomes(malformed_terminal, 0, 1, 2, 0)),
        raises(ValueError, lambda: bounded_outcomes(malformed_terminal, 2, 1, 2, 6)),
        raises(ValueError, lambda: bounded_outcomes(malformed_unvisited, 1, 1, 2, 0))])
    assert len(checks) == 14 and all(type(value) is bool and value for value in checks.values()), checks
    return {'python_version': platform.python_version(), 'implementation': platform.python_implementation(),
            'dependencies': 'standard library only',
            'contract': {'state_count': '2..5', 'row_weights': 'exact tuple rows / int0..8 / total1..16',
                         'horizon': 'exact int0..6 abstract transitions, not elapsed time',
                         'terminals': 'two distinct supplied absorbing rows',
                         'interpretation': 'finite synthetic outcome probability, no fitted caller data'},
            'manual_retry_weights': retry, 'manual_retry_start_success_abandon_K': (0, 1, 2, 3),
            'manual_retry': example, 'zero_horizon': zero,
            'two_transient_branch': branched, 'closed_cycle': trapped,
            'independent_oracle_cases_in_ONE_group': cases,
            'independent_stopped_leaves_across_that_family': leaves,
            'invalid_inputs_inside_ONE_group': len(invalid),
            'unique_executed_checks': len(checks), 'checks': checks,
            'actual_calls_audio_clocks_network_PRISM_or_simulation': False,
            'eventual_absorption_expected_time_or_performance_claimed': False}


if __name__ == '__main__':
    sys.stdout.buffer.write((json.dumps(as_json(self_check()), ensure_ascii=True,
                                      indent=2, allow_nan=False) + '\n').encode('utf-8'))

۴. مسیرهای متوقف‌شده را مستقل بشماریم

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

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

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

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

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

{
  "python_version": "3.14.5",
  "implementation": "CPython",
  "dependencies": "standard library only",
  "contract": {
    "state_count": "2..5",
    "row_weights": "exact tuple rows / int0..8 / total1..16",
    "horizon": "exact int0..6 abstract transitions, not elapsed time",
    "terminals": "two distinct supplied absorbing rows",
    "interpretation": "finite synthetic outcome probability, no fitted caller data"
  },
  "manual_retry_weights": [
    [
      1,
      2,
      1
    ],
    [
      0,
      1,
      0
    ],
    [
      0,
      0,
      1
    ]
  ],
  "manual_retry_start_success_abandon_K": [
    0,
    1,
    2,
    3
  ],
  "manual_retry": {
    "success": "21/32",
    "abandon": "21/64",
    "unresolved": "1/64",
    "state_mass": [
      "1/64",
      "21/32",
      "21/64"
    ],
    "trace": [
      [
        "1",
        "0",
        "0"
      ],
      [
        "1/4",
        "1/2",
        "1/4"
      ],
      [
        "1/16",
        "5/8",
        "5/16"
      ],
      [
        "1/64",
        "21/32",
        "21/64"
      ]
    ]
  },
  "zero_horizon": {
    "success": "0",
    "abandon": "0",
    "unresolved": "1",
    "state_mass": [
      "1",
      "0",
      "0"
    ],
    "trace": [
      [
        "1",
        "0",
        "0"
      ]
    ]
  },
  "two_transient_branch": {
    "success": "5/8",
    "abandon": "3/8",
    "unresolved": "0",
    "state_mass": [
      "0",
      "0",
      "5/8",
      "3/8"
    ],
    "trace": [
      [
        "1",
        "0",
        "0",
        "0"
      ],
      [
        "0",
        "3/4",
        "1/4",
        "0"
      ],
      [
        "0",
        "0",
        "5/8",
        "3/8"
      ]
    ]
  },
  "closed_cycle": {
    "success": "0",
    "abandon": "0",
    "unresolved": "1",
    "state_mass": [
      "1",
      "0",
      "0",
      "0"
    ],
    "trace": [
      [
        "1",
        "0",
        "0",
        "0"
      ],
      [
        "0",
        "1",
        "0",
        "0"
      ],
      [
        "1",
        "0",
        "0",
        "0"
      ],
      [
        "0",
        "1",
        "0",
        "0"
      ],
      [
        "1",
        "0",
        "0",
        "0"
      ],
      [
        "0",
        "1",
        "0",
        "0"
      ],
      [
        "1",
        "0",
        "0",
        "0"
      ]
    ]
  },
  "independent_oracle_cases_in_ONE_group": 4500,
  "independent_stopped_leaves_across_that_family": 11524,
  "invalid_inputs_inside_ONE_group": 39,
  "unique_executed_checks": 14,
  "checks": {
    "hand_retry_K3_exact_outcomes": true,
    "zero_horizon_does_not_take_a_transition": true,
    "terminal_initial_state_counts_at_K0_and_stays": true,
    "arrival_at_boundary_counts_by_K_not_before": true,
    "closed_transient_cycle_unresolved_not_abandoned": true,
    "distinct_terminal_outcomes_do_not_recirculate": true,
    "row_rescaling_preserves_exact_probabilities": true,
    "renaming_states_permutes_trace_only": true,
    "all_trace_mass_conserved_exactly": true,
    "terminal_cumulative_mass_monotone_unresolved_nonincreasing": true,
    "fresh_trace_and_report_do_not_mutate_inputs": true,
    "independent_stopped_path_family_ONE_group": true,
    "strict_types_shapes_and_own_resource_caps": true,
    "all_rows_validated_even_at_K0_or_terminal_start": true
  },
  "actual_calls_audio_clocks_network_PRISM_or_simulation": false,
  "eventual_absorption_expected_time_or_performance_claimed": false
}

۵. چهار پرسش دربارهٔ اتمام، زمان و رفتارِ واقعی

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

تمرین اول: اگر نمونه از خودِ حالتِ پایانِ مطلوب آغاز شود و K صفر باشد، سهمِ آن پایان چقدر است؟ پاسخ: یک است؛ قراردادِ «تا گامِ K» لحظهٔ آغاز را هم شامل می‌شود. اگر آغاز در منوی تکرار باشد، در همان افق سهمِ حل‌نشده یک و هر دو سهمِ پایانی صفرند. صفرِ افق به معنیِ خالی‌بودنِ توزیع نیست.

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

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

تمرین چهارم: اگر سطرِ پایانی راهی به منو داشته باشد، آیا بهتر است کد آن راه را خودکار صفر کند؟ پاسخ: در این قرارداد خیر؛ ورودی رد می‌شود، حتی در گامِ صفر. صفرکردنِ بی‌خبر، مدلِ دیگری می‌سازد و تفسیرِ رسیدن تا افق را پنهان می‌کند. منابع در ۲۹ مهر ۱۴۰۵ خوانده شدند؛ دورهٔ MIT مربوط به بهارِ ۲۰۱۴ است و تاریخِ دقیقِ انتشارِ فایل معلوم نیست. مستندات و نسخهٔ اجرای پایتون نیز جدا ثبت می‌شوند.

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

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