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

سه عمل را بهترتیب تعریف میکنیم: میان نمونههای ورودی جایگاهِ صفر قرار میدهیم، کانولوشنِ کامل با یک FIR انجام میدهیم، سپس از جایگاهِ صفر هر چندمین عضو را برمیداریم. مستندات رسمی upfirdn همین زنجیرهٔ عمل و ساختارِ چندفازی را توضیح میدهد. کدِ حاضر یک قراردادِ کوچکِ صحیح است و تمامِ نوعها، محورهای آرایه و حالتهای مرزیِ آن API را پیاده نمیکند.
ورودیِ نمونهها یک tuple با یک تا هشت عضوِ صحیحِ منفی هشت تا هشت است. دامنه را میلیولتِ ساختگی مینامیم؛ اندازهگیریِ دستگاه نیست. ضرایب، tuple دیگری با یک تا پنج صحیحِ بیبعدِ منفی چهار تا چهار هستند. دو عاملِ up و down هرکدام صحیحِ یک تا چهارند. bool، شناور، فهرست، مولد و رشته تبدیلِ ضمنی نمیشوند؛ ورودیِ خالی نیز بیرونِ قرارداد است.
عاملِ بالابری، جایگاهِ هر نمونهٔ قبلی را در شبکهٔ ریزتر تعیین میکند. برای سه نمونهٔ یک، دو و سه و عاملِ سه، دنبالهٔ صریح یک، صفر، صفر، دو، صفر، صفر و سه میشود. پس از نمونهٔ آخر دو صفرِ اضافی نمیچسبانیم. صفرگذاری اطلاعاتِ اندازهگیریشدهٔ تازه تولید نمیکند؛ فقط شبکهای را میسازد که ضرایب روی آن اعمال میشوند.
طولِ طبیعی و دنبالهٔ انتهاییِ فیلتر

اگر تعدادِ نمونهها N، تعدادِ ضرایب M و عاملِ بالابری P باشد، طولِ دنبالهٔ صفرگذاریشده برابر (N-1)*P+1 است. کانولوشنِ کامل، طولِ طبیعیِ L=(N-1)*P+M دارد. سپس جایگاههای صفر، D، دوبرابرِ D و مانند آن را تا پیش از L انتخاب میکنیم. تعدادِ خروجی، سقفِ L تقسیم بر D است؛ در کد با تقسیمِ صحیحِ (L+D-1)//D محاسبه میشود.
این طول را از مقدارِ نمونهٔ آخر حدس نمیزنیم. ممکن است آخرین خروجی صفر باشد و همچنان به قرارداد تعلق داشته باشد. ورودیِ تکنمونهای نیز میتواند چند خروجی از دنبالهٔ FIR بسازد. در مثالِ تکنمونهٔ دو، ضرایبِ یک، منفی دو، صفر، چهار و صفر و عاملِ پایینبریِ دو، خروجی دو، صفر و صفر است؛ حذفِ صفرهای پایانی، شکلِ قراردادی را عوض میکند.
در آزمایشِ پیشینِ تبدیلِ صوت با SciPy، خروجیِ مرکزشده و مدتِ قطعه با آزمونِ مؤلفههای فرکانسی بررسی میشد. اینجا اثرِ کامل و علّیِ ضرایب را نگه میداریم. فاصلهٔ زمانیِ خروجی نسبت به ورودی در D تقسیم بر P ضرب میشود، ولی تعدادِ اعضای دارای دنبالهٔ فیلتر را نمیتوان بیتوضیح، مدتِ حفظشدهٔ همان ضبط نامید.
فازهای FIR و اندیسِ هر خروجی

لازم نیست صفرهای میانی را واقعاً ذخیره کنیم. برای جایگاهِ خروجیِ t، باقیماندهٔ تقسیمِ t بر P را r و خارجقسمت را j میگیریم. از ضرایب فقط اندیسهای r، r بهاضافهٔ P و ادامهٔ همین الگو میتوانند با نمونهٔ غیرصفرِ شبکه همجایگاه شوند. اینها یک شاخه یا فاز هستند؛ هر شاخه را یک بار از ضرایب جدا میکنیم.
عضوِ q از آن شاخه، ضریبِ اصلیِ h[r+q*P] است و با نمونهٔ x[j-q] ضرب میشود. اگر اندیسِ ورودی بیرونِ قطعه باشد، سهمش صفر است. فازی که هیچ ضریبی ندارد هم جمعِ صفر میدهد. این بازنویسی از رابطهٔ کانولوشن میآید؛ ترتیبِ ضرایب را برای شبیهشدن به یک پنجرهٔ دلخواه وارونه نمیکنیم.
مثالِ دستیِ ما ورودیِ دو، منفی یک و سه، ضرایبِ یک، دو و یک و عواملِ دو و سه است. دنبالهٔ صریح پنج عضو و کانولوشنِ کامل هفت عضو دارد: دو، چهار، یک، منفی دو، دو، شش و سه. جایگاههای انتخابی صفر، سه و ششاند؛ نتیجه دو، منفی دو و سه میشود. فازها بهترتیب صفر، یک و صفر هستند؛ پاسخِ آخر هنوز سهمِ انتهاییِ آخرین نمونه را دارد.
درسِ چیدمانِ Conv1d همبستگیِ لغزشیِ معتبر و محورهای چندکاناله را بررسی میکرد. اینجا ورودی تکدنبالهای، کانولوشنِ علّیِ کامل و تغییرِ شبکه داریم؛ بایاس، شبکهٔ آموزشدیده و تبدیلِ چیدمانِ تانسور جزو مسئله نیستند.
شاهدِ دستی و مرجعِ صفرگذاریِ مستقل

فایلِ rational_resampling_polyphase.py فقط کتابخانهٔ استانداردِ پایتون را به کار میبرد. مسیر اصلی، شاخهٔ لازم را انتخاب و حاصلضربهای مربوط را جمع میکند. مرجعِ مستقل آرایهٔ صفرگذاریشده را میسازد، با دو حلقه روی نمونه و ضریب، کانولوشنِ کامل را پر میکند و در پایان برش میزند. این مرجع تابعِ اصلی، سازندهٔ فاز و فرمولِ اندیسِ آن را فراخوانی نمیکند.
"""Finite supplied-integer-FIR rate-grid lesson; no SciPy or audio device."""
import json
import platform
import sys
from itertools import product
def _contract(x, h, up, down):
if type(x) is not tuple or not 1 <= len(x) <= 8:
raise ValueError("x must be an exact tuple of length 1..8")
if any(type(v) is not int or not -8 <= v <= 8 for v in x):
raise ValueError("x requires exact integers in -8..8")
if type(h) is not tuple or not 1 <= len(h) <= 5:
raise ValueError("h must be an exact tuple of length 1..5")
if any(type(v) is not int or not -4 <= v <= 4 for v in h):
raise ValueError("h requires exact integers in -4..4")
if type(up) is not int or type(down) is not int or not 1 <= up <= 4 or not 1 <= down <= 4:
raise ValueError("up/down require exact integers in 1..4")
def polyphase_resample(x, h, up=1, down=1):
"""Return every down-th sample of the natural full causal convolution.
x is zero outside its declared indices; no inserted zero slots follow its
last sample. There is no implicit gain, centering, gcd reduction or design.
"""
_contract(x, h, up, down)
phases = tuple(h[r::up] for r in range(up))
natural_length = (len(x) - 1) * up + len(h)
result = []
for t in range(0, natural_length, down):
base, phase = divmod(t, up)
value = 0
for q, coefficient in enumerate(phases[phase]):
i = base - q
if 0 <= i < len(x):
value += coefficient * x[i]
result.append(value)
return tuple(result)
def explain_phases(x, h, up=1, down=1):
"""An immutable diagnostic trace; not the independent expected-value path."""
_contract(x, h, up, down)
rows = []
for m, t in enumerate(range(0, (len(x) - 1) * up + len(h), down)):
base, phase = divmod(t, up)
terms = []
for tap in range(phase, len(h), up):
i = base - (tap - phase) // up
if 0 <= i < len(x):
terms.append((tap, i, h[tap], x[i], h[tap] * x[i]))
rows.append((m, t, phase, base, tuple(terms)))
return tuple(rows)
def explicit_reference(x, h, up, down):
"""Independent finite expansion, whole convolution, then ordinary slicing.
Valid finite fixtures only. No main validator, phase bank, division/modulo
index formula, resampling function or diagnostic trace constructs expected.
"""
inserted = [0] * ((len(x) - 1) * up + 1)
cursor = 0
for value in x:
inserted[cursor] = value
cursor += up
filtered = [0] * (len(inserted) + len(h) - 1)
for i, value in enumerate(inserted):
for j, coefficient in enumerate(h):
filtered[i + j] += value * coefficient
return {"inserted": tuple(inserted), "filtered": tuple(filtered),
"selected": tuple(filtered[::down])}
def rejected(*args):
try:
polyphase_resample(*args)
except ValueError:
return True
return False
def run_example():
x, h, up, down = (2, -1, 3), (1, 2, 1), 2, 3
expected = (2, -2, 3)
answer = polyphase_resample(x, h, up, down)
reference = explicit_reference(x, h, up, down)
trace = explain_phases(x, h, up, down)
manual_trace = (
(0, 0, 0, 0, ((0, 0, 1, 2, 2),)),
(1, 3, 1, 1, ((1, 1, 2, -1, -2),)),
(2, 6, 0, 3, ((2, 2, 1, 3, 3),)),
)
checks = {}
checks["manual_expanded_convolution_and_selected_causal_tail"] = (
answer == expected
and reference == {"inserted": (2, 0, -1, 0, 3),
"filtered": (2, 4, 1, -2, 2, 6, 3), "selected": expected})
checks["manual_phase_banks_and_each_output_term"] = (
tuple(h[r::up] for r in range(up)) == ((1, 1), (2,))
and trace == manual_trace
and tuple(sum(term[4] for term in row[4]) for row in trace) == expected)
insertion_only = polyphase_resample((1, 2, 3), (1,), 3, 1)
checks["insertion_stops_at_last_input_not_after_extra_terminal_slots"] = (
insertion_only == (1, 0, 0, 2, 0, 0, 3) and len(insertion_only) == 7)
checks["downsampling_phase_zero_and_selected_filter_tail"] = (
polyphase_resample((1, 2, 3, 4, 5), (1,), 1, 2) == (1, 3, 5)
and polyphase_resample((1,), (1, 2, 3, 4), 1, 2) == (1, 3))
checks["unit_rates_mean_full_convolution_not_valid_only_output"] = (
polyphase_resample(x, h, 1, 1) == (2, 3, 3, 5, 3)
and len(polyphase_resample(x, h, 1, 1)) == len(x) + len(h) - 1)
checks["asymmetric_causal_taps_are_not_reversed"] = (
polyphase_resample((1, 0, 0), (1, 2, 4), 1, 1) == (1, 2, 4, 0, 0)
and polyphase_resample((1, 0, 0), (4, 2, 1), 1, 1) == (4, 2, 1, 0, 0))
checks["one_tap_interpolation_retains_zero_grid_positions"] = (
polyphase_resample(x, (2,), 2, 1) == (4, 0, -2, 0, 6))
checks["zero_signal_and_zero_filter_preserve_structural_length"] = (
polyphase_resample((0, 0, 0), (1, -2, 1), 3, 2) == (0,) * 5
and polyphase_resample(x, (0, 0, 0), 3, 2) == (0,) * 5)
checks["single_sample_and_zero_valued_selected_tail_are_not_trimmed"] = (
polyphase_resample((2,), (1, -2, 0, 4, 0), 4, 2) == (2, 0, 0)
and polyphase_resample((2,), (1, -2, 0, 4, 0), 1, 2) == (2, 0, 0))
unreduced = polyphase_resample((1, 2), (1, 1), 2, 2)
reduced = polyphase_resample((1, 2), (1, 1), 1, 1)
checks["same_rate_ratio_does_not_reduce_a_fixed_filter_grid"] = (
unreduced == (1, 2) and reduced == (1, 3, 2) and unreduced != reduced)
checks["no_implicit_gain_or_filter_design_for_constant_input"] = (
polyphase_resample((1, 1, 1), (1,), 2, 1) == (1, 0, 1, 0, 1)
and polyphase_resample((1, 1, 1), (1, 1), 2, 1) == (1, 1, 1, 1, 1, 1)
and polyphase_resample((1, 1, 1), (2, 1), 2, 1) == (2, 1, 2, 1, 2, 1))
base = ((1, 2), (1, 1), 2, 3)
invalid = []
for bad in (None, [], (), [1], (True,), (1.0,), (float("nan"),),
(float("inf"),), ("1",), (9,), (-9,), (0,) * 9, (10**80,)):
invalid.append((bad, base[1], base[2], base[3]))
for bad in (None, [], (), [1], (True,), (1.0,), (float("nan"),),
("1",), (5,), (-5,), (0,) * 6):
invalid.append((base[0], bad, base[2], base[3]))
for rate in (True, False, 0, -1, 5, 1.0, "2", None):
invalid.append((base[0], base[1], rate, base[3]))
invalid.append((base[0], base[1], base[2], rate))
consumed = []
def source():
consumed.append("pulled")
yield 1
generator_x, generator_h = source(), source()
checks["strict_complete_input_contract_and_unconsumed_generators_ONE_group"] = (
all(rejected(*args) for args in invalid)
and rejected(generator_x, base[1], 2, 3)
and rejected(base[0], generator_h, 2, 3) and consumed == [])
profiles = tuple(values for size in (1, 2) for values in product((-1, 0, 1), repeat=size))
family_cases = compared_values = 0
family_ok = True
for data in profiles:
for taps in profiles:
for p in (1, 2, 3, 4):
for q in (1, 2, 3, 4):
actual = polyphase_resample(data, taps, p, q)
wanted = explicit_reference(data, taps, p, q)["selected"]
natural = (len(data) - 1) * p + len(taps)
family_ok = family_ok and actual == wanted and len(actual) == (natural + q - 1) // q
family_cases += 1
compared_values += len(wanted)
checks["finite_2304_explicit_expansion_comparisons_are_ONE_family_group"] = (
family_ok and family_cases == 2304)
cap_x, cap_h = (8, -8) * 4, (4, -4, 4, -4, 4)
before = (cap_x, cap_h)
cap = polyphase_resample(cap_x, cap_h, 4, 1)
intervening = polyphase_resample((-8, 8), (-4,), 1, 4)
cap_again = polyphase_resample(cap_x, cap_h, 4, 1)
checks["inclusive_caps_immutable_fresh_outputs_and_no_retained_state"] = (
len(cap) == 33 and cap == explicit_reference(cap_x, cap_h, 4, 1)["selected"]
and cap_again == cap and cap_again is not cap and type(cap) is tuple
and before == (cap_x, cap_h) and intervening == (32,))
assert len(checks) == 14 and all(type(v) is bool and v for v in checks.values()), checks
return {
"python": platform.python_version(),
"dependencies": "Python standard library only; SciPy/NumPy not imported or executed",
"unit": "synthetic integer mV samples; dimensionless supplied FIR taps",
"manual": {"x": x, "h": h, "up": up, "down": down,
"inserted": reference["inserted"], "full_convolution": reference["filtered"],
"natural_high_grid_length": 7, "selected_high_grid_positions": (0, 3, 6),
"output": answer, "phase_banks": tuple(h[r::up] for r in range(up)),
"trace_fields": ("output_index", "high_grid_index", "phase", "base_input_index", "terms"),
"term_fields": ("tap_index", "input_index", "coefficient", "input_value", "product"),
"phase_trace": trace,
"output_times_as_input_interval_ratios": ((0, 2), (3, 2), (6, 2))},
"insertion_without_terminal_extra_zeros": insertion_only,
"fixed_filter_ratio_counterexample": {"up2_down2": unreduced, "up1_down1": reduced},
"constant_input_supplied_filter_examples": {
"taps_1": polyphase_resample((1, 1, 1), (1,), 2, 1),
"taps_1_1": polyphase_resample((1, 1, 1), (1, 1), 2, 1),
"taps_2_1": polyphase_resample((1, 1, 1), (2, 1), 2, 1)},
"finite_family_inside_ONE_group": {"x_profiles": len(profiles), "h_profiles": len(profiles),
"rate_pairs": 16, "cases": family_cases,
"compared_output_values": compared_values},
"invalid_argument_cases_inside_ONE_group": len(invalid) + 2,
"maximum_declared_natural_output_length": 33,
"unique_executed_checks": len(checks), "checks": checks,
"limitations": [
"Exact tuple/integer finite contract only; empty inputs rejected and selected zeros/tail retained.",
"No implicit gain compensation, gcd reduction, endpoint trimming, centering or filter design.",
"No SciPy API equivalence, anti-alias/passband specification, speech quality, streaming timing or hardware claim.",
"Finite comparisons are one family group, not a proof for all inputs or a performance benchmark.",
],
}
if __name__ == "__main__":
sys.stdout.buffer.write((json.dumps(run_example(), ensure_ascii=True, indent=2) + "\n").encode("ascii"))
چهارده گروهِ نامدار بررسی میشوند. خانوادهٔ محدود، همهٔ دنبالههای یکعضوی و دوعضوی روی منفی یک، صفر و یک را برای ورودی و ضرایب، با شانزده جفتِ عامل ترکیب میکند. مجموعِ ۲۳۰۴ مقایسه، یک گروهِ مرجع است؛ تعدادِ دادهها را گروهِ مستقل نمیشماریم. شاهدِ دستی، جهتِ ضرایبِ نامتقارن، فازِ آغاز، تکنمونه، صفرهای پایانی و مرزِ اندازه جدا بررسی میشوند.
{
"python": "3.14.5",
"dependencies": "Python standard library only; SciPy/NumPy not imported or executed",
"unit": "synthetic integer mV samples; dimensionless supplied FIR taps",
"manual": {
"x": [
2,
-1,
3
],
"h": [
1,
2,
1
],
"up": 2,
"down": 3,
"inserted": [
2,
0,
-1,
0,
3
],
"full_convolution": [
2,
4,
1,
-2,
2,
6,
3
],
"natural_high_grid_length": 7,
"selected_high_grid_positions": [
0,
3,
6
],
"output": [
2,
-2,
3
],
"phase_banks": [
[
1,
1
],
[
2
]
],
"trace_fields": [
"output_index",
"high_grid_index",
"phase",
"base_input_index",
"terms"
],
"term_fields": [
"tap_index",
"input_index",
"coefficient",
"input_value",
"product"
],
"phase_trace": [
[
0,
0,
0,
0,
[
[
0,
0,
1,
2,
2
]
]
],
[
1,
3,
1,
1,
[
[
1,
1,
2,
-1,
-2
]
]
],
[
2,
6,
0,
3,
[
[
2,
2,
1,
3,
3
]
]
]
],
"output_times_as_input_interval_ratios": [
[
0,
2
],
[
3,
2
],
[
6,
2
]
]
},
"insertion_without_terminal_extra_zeros": [
1,
0,
0,
2,
0,
0,
3
],
"fixed_filter_ratio_counterexample": {
"up2_down2": [
1,
2
],
"up1_down1": [
1,
3,
2
]
},
"constant_input_supplied_filter_examples": {
"taps_1": [
1,
0,
1,
0,
1
],
"taps_1_1": [
1,
1,
1,
1,
1,
1
],
"taps_2_1": [
2,
1,
2,
1,
2,
1
]
},
"finite_family_inside_ONE_group": {
"x_profiles": 12,
"h_profiles": 12,
"rate_pairs": 16,
"cases": 2304,
"compared_output_values": 4896
},
"invalid_argument_cases_inside_ONE_group": 42,
"maximum_declared_natural_output_length": 33,
"unique_executed_checks": 14,
"checks": {
"manual_expanded_convolution_and_selected_causal_tail": true,
"manual_phase_banks_and_each_output_term": true,
"insertion_stops_at_last_input_not_after_extra_terminal_slots": true,
"downsampling_phase_zero_and_selected_filter_tail": true,
"unit_rates_mean_full_convolution_not_valid_only_output": true,
"asymmetric_causal_taps_are_not_reversed": true,
"one_tap_interpolation_retains_zero_grid_positions": true,
"zero_signal_and_zero_filter_preserve_structural_length": true,
"single_sample_and_zero_valued_selected_tail_are_not_trimmed": true,
"same_rate_ratio_does_not_reduce_a_fixed_filter_grid": true,
"no_implicit_gain_or_filter_design_for_constant_input": true,
"strict_complete_input_contract_and_unconsumed_generators_ONE_group": true,
"finite_2304_explicit_expansion_comparisons_are_ONE_family_group": true,
"inclusive_caps_immutable_fresh_outputs_and_no_retained_state": true
},
"limitations": [
"Exact tuple/integer finite contract only; empty inputs rejected and selected zeros/tail retained.",
"No implicit gain compensation, gcd reduction, endpoint trimming, centering or filter design.",
"No SciPy API equivalence, anti-alias/passband specification, speech quality, streaming timing or hardware claim.",
"Finite comparisons are one family group, not a proof for all inputs or a performance benchmark."
]
}
خروجیِ ثبتشده تعدادِ واقعیِ مقایسهها، پاسخِ دستی، جملههای هر فاز و نسخهٔ اجرای پایتون را دارد. حسابها صحیحاند و برای این قرارداد تلورانسِ ممیزشناور نداریم. مقایسهٔ محدود با مرجع، اثباتِ همهٔ ورودیها یا اندازهگیریِ سرعت نیست. هیچ فراخوانیِ SciPy، NumPy، ابزارِ صوتی یا مدلِ گفتار در اجرای این فایل انجام نمیشود.
نسبتِ نرخ، طراحیِ فیلتر و مرزِ نتیجه

دو جفتِ عامل با نسبتِ برابر، الزاماً با ضرایبِ ثابتِ ما یک جواب ندارند. برای ورودیِ یک و دو و ضرایبِ یک و یک، عواملِ دو و دو نتیجهٔ یک و دو میدهند؛ عواملِ یک و یک، کانولوشنِ یک، سه و دو را نگه میدارند. سادهکردنِ کسر، شبکهای را که این ضرایب بر آن تعریف شدهاند عوض کرده است؛ این یک تناقضِ ریاضی دربارهٔ برابریِ دو کسر نیست.
توضیحِ رسمی resample_poly، طراحیِ فیلتر روی نرخِ بالاترِ کاهشیافته با مقسومعلیه مشترک و مرکزکردنِ خروجی را مطرح میکند. نمیتوان قواعدِ آن تابع را به ضرایبِ ثابت و دنبالهٔ علّیِ این درس منتقل کرد. بهره هم خودکار اصلاح نمیشود؛ برای ورودیِ ثابت و بالابریِ دو، ضرایبِ تکعضویِ یک، صفرهای میانی را باقی میگذارند و ضرایبِ دو و یک، دامنهٔ متناوب میسازند.
پرسش اول: چرا تعدادِ خروجی فقط سقفِ N ضرب در P تقسیم بر D نیست؟ پاسخ: قراردادِ حاضر، انتهای طبیعیِ کانولوشن را نیز انتخاب میکند؛ M در طول نقش دارد. مرکزکردن یا بریدن برای مدتِ معین، قراردادِ دیگری میخواهد.
پرسش دوم: آیا ورودیِ صفر و ورودیِ خالی یک حکم دارند؟ پاسخ: خیر؛ خالی رد میشود، اما دنبالهٔ مجازِ صفر، خروجیِ صفر با طولِ ساختاریِ معلوم دارد و کوتاه نمیشود.
پرسش سوم: میتوان عواملِ دو و دو را بیتغییرِ ضرایب به یک و یک تبدیل کرد؟ پاسخ: در این قرارداد خیر؛ مثالِ یک و دو نشان داد هم طول و هم مقدار میتواند عوض شود.
پرسش چهارم: برابریِ مسیرِ فازی و مرجع، فیلترِ مناسبِ صوت را تأیید میکند؟ پاسخ: خیر؛ فقط حسابِ قرارداد را میسنجد. ضرایبِ آموزشی، مشخصاتِ باندِ عبور، تضعیفِ همپوشانی، کیفیتِ گفتار، حالتِ جریانی یا موعدِ اجرای زنده را تضمین نمیکنند. دو مستندِ کامل در ۲۹ مهر ۱۴۰۵ خوانده شدند؛ عنوانِ نسخهٔ آنها SciPy 1.18.0 است و تاریخِ انتشارِ نخستشان معلوم فرض نشده است.
منابع و مطالعهٔ بیشتر
- SciPy1.18؛ زنجیرهٔ upfirdn، شاخههای چندفازی و قراردادِ مرز
- SciPy1.18؛ resample_poly، طراحیِ فیلتر و مرکزکردنِ خروجی
بررسی منابع: ۲۹ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

