ডিসক্রিট কনভোলিউশন — হাতে গণনা
এই পাঠে যা শিখবেন
- কনভোলিউশন সাম-কে একটি সাধারণ, পুনঃব্যবহারযোগ্য ফাংশন হিসেবে ইমপ্লিমেন্ট করা
- কেন আউটপুটের দৈর্ঘ্য
len(x) + len(h) - 1হয় তার যুক্তি ও প্রমাণ - একটি নতুন উদাহরণে হাতে-গণনা করে কোডের ফলাফলের সাথে নির্দিষ্ট মান মিলিয়ে যাচাই করা
- কনভোলিউশনের এই ইমপ্লিমেন্টেশন কীভাবে পরবর্তী মডিউলগুলোতে (FIR/IIR ফিল্টার) পুনঃব্যবহৃত হবে তা বোঝা
১ · কনভোলিউশন সাম — সাধারণ ইমপ্লিমেন্টেশন
L09-এ আমরা কনভোলিউশন সূত্র y[n] = \sum_k x[k]\,h[n-k] একটি নির্দিষ্ট উদাহরণে হাতে প্রয়োগ
করেছিলাম। এখন সেই একই যুক্তিকে একটি সাধারণ ফাংশনে রূপ দেওয়া যাক, যা যেকোনো দৈর্ঘ্যের
দুটো সিকোয়েন্স x ও h-এর জন্য কাজ করবে। মূল আইডিয়া — প্রতিটি আউটপুট ইনডেক্স
n-এর জন্য, ইনপুটের প্রতিটি সম্ভাব্য ইনডেক্স k নিয়ে চেক করা হয় যে
n - k আসলেই h-এর একটি বৈধ ইনডেক্স কিনা (অর্থাৎ ফ্লিপ করা h-এর
সাথে x[k]-এর "ওভারল্যাপ" আছে কিনা), থাকলে তবেই গুণ করে যোগ করা হয়।
২ · ফলাফলের দৈর্ঘ্য — কেন len(x) + len(h) - 1
ধরা যাক x-এর দৈর্ঘ্য M (ইনডেক্স 0 থেকে M-1) এবং
h-এর দৈর্ঘ্য K (ইনডেক্স 0 থেকে K-1)। আউটপুট
y[n]-এ অবদান থাকে শুধুমাত্র তখনই যখন k আর n-k দুটোই তাদের বৈধ
সীমার মধ্যে থাকে। সবচেয়ে ছোট সম্ভাব্য n হলো 0 (যখন k=0, n-k=0),
আর সবচেয়ে বড় সম্ভাব্য n হলো (M-1) + (K-1) (যখন k=M-1, n-k=K-1)।
তাই মোট বৈধ n-এর সংখ্যা হলো (M-1+K-1) - 0 + 1 = M + K - 1 — এটাই
len(x) + len(h) - 1 নিয়মের পেছনের যুক্তি।
৩ · একটি নতুন উদাহরণ — কোড দিয়ে হাতে-গণনা যাচাই
এবার x = [1, 2, 3] আর h = [4, 5, 6] নিয়ে হাতে হিসেব করা যাক:
y[1] = x[0]\cdot h[1] + x[1]\cdot h[0] = 1\times 5 + 2\times 4 = 5 + 8 = 13, এবং
y[2] = x[0]\cdot h[2] + x[1]\cdot h[1] + x[2]\cdot h[0] = 1\times 6 + 2\times 5 + 3\times 4 = 6 + 10 + 12 = 28।
নিচের কোড সেলে একটি সাধারণ convolve() ফাংশন লিখে এই একই মান কোড থেকে সত্যিকারভাবে গণনা করে
মিলিয়ে দেখানো হয়েছে।
def convolve(x, h):
"""সাধারণ ডিসক্রিট কনভোলিউশন -- নেস্টেড লুপ দিয়ে y[n] = sum_k x[k]*h[n-k]"""
N = len(x) + len(h) - 1
y = [0.0] * N
for n in range(N):
total = 0.0
for k in range(len(x)):
if 0 <= n - k < len(h):
total += x[k] * h[n - k]
y[n] = total
return y
x = [1, 2, 3]
h = [4, 5, 6]
y = convolve(x, h)
print("x =", x, " len(x) =", len(x))
print("h =", h, " len(h) =", len(h))
print("y = x * h =", y)
print("len(y) =", len(y), " expected len(x)+len(h)-1 =", len(x) + len(h) - 1)
print()
print(f"{'n':>3} | {'y[n]':>6}")
for n, val in enumerate(y):
print(f"{n:>3} | {val:>6.1f}")
# হাতে-গণনার সাথে সরাসরি মিলিয়ে দেখা
manual_y1 = x[0]*h[1] + x[1]*h[0]
manual_y2 = x[0]*h[2] + x[1]*h[1] + x[2]*h[0]
print()
print("manual y[1] = x[0]*h[1] + x[1]*h[0] =", manual_y1, " code y[1] =", y[1])
print("manual y[2] = x[0]*h[2] + x[1]*h[1] + x[2]*h[0] =", manual_y2, " code y[2] =", y[2])
y = [4.0, 13.0, 28.0, 27.0, 18.0], দৈর্ঘ্য ৫ — ঠিক
len(x)+len(h)-1 = 3+3-1 = 5 নিয়ম অনুযায়ী। আর y[1] = 13.0 ও
y[2] = 28.0 কোড ও হাতে-গণনা দুটোতেই হুবহু মিলে যায় — এটাই নিশ্চিত করে যে আমাদের
convolve() ফাংশনটি সঠিক।
এই convolve() ফাংশনটি এই কোর্সে বারবার পুনঃব্যবহৃত হবে — M7-এ FIR ফিল্টার ডিজাইনের পর
(L31) ফিল্টার ট্যাপগুলোকে একটি টেস্ট সিগন্যালের সাথে প্রয়োগ করতে এই একই লজিক ব্যবহার করা হবে, আর
M12/L53-এ 2D ইমেজ কনভোলিউশনে এই আইডিয়াকেই দুই ডাইমেনশনে প্রসারিত করা হবে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
যদি x আর h দুটোরই দৈর্ঘ্য ১ হতো (মানে দুটোই একক সংখ্যা), তাহলে
len(x)+len(h)-1 নিয়ম অনুযায়ী আউটপুটের দৈর্ঘ্য কত হতো? এটা কি যুক্তিসঙ্গত?
1 + 1 - 1 = 1 — আউটপুটও একটি একক সংখ্যা হবে, যা যুক্তিসঙ্গত: দুটো একক সংখ্যা কনভলভ
করলে শুধু তাদের গুণফল পাওয়া যায় (y[0] = x[0]\cdot h[0]), আর কোনো "স্লাইডিং" সম্ভবই না
কারণ দুটো সিকোয়েন্সেরই মাত্র একটি করে ইনডেক্স আছে।
প্র ০২
উপরের কোডে x আর h-এর ভূমিকা যদি অদল-বদল করা হয় (অর্থাৎ
convolve(h, x) কল করা হয়), তাহলে ফলাফল কি পরিবর্তিত হবে?
না — কনভোলিউশন কম্যুটেটিভ (commutative), অর্থাৎ x * h = h * x। সূত্রের
দিক থেকেও এটি প্রতিসম: \sum_k x[k]h[n-k]-এ চলক পরিবর্তন করলে
\sum_k h[k]x[n-k]-এ রূপান্তরিত হয়, যা একই যোগফল দেয়। ফলাফলের দৈর্ঘ্যও একই থাকবে কারণ
len(x)+len(h)-1 = len(h)+len(x)-1।
প্র ০৩
উপরের নেস্টেড-লুপ ইমপ্লিমেন্টেশনটির টাইম কমপ্লেক্সিটি কত (M-দৈর্ঘ্যের x আর
K-দৈর্ঘ্যের h-এর জন্য)? এটি কি বড় সিগন্যালের জন্য দক্ষ?
এটি O(M \times K) — প্রতিটি আউটপুট স্যাম্পলের জন্য x-এর প্রতিটি স্যাম্পল
একবার করে চেক করা হয়। ছোট ফিল্টারের জন্য (যেমন এই পাঠের ২-৩ ট্যাপ) এটি সম্পূর্ণ যথেষ্ট, কিন্তু বড়
সিগন্যাল ও বড় ফিল্টারের জন্য এটি ধীর হয়ে যেতে পারে — M6-এ আমরা দেখব কীভাবে FFT ব্যবহার করে বড়
কনভোলিউশনকে অনেক দ্রুত করা যায় (ফ্রিকোয়েন্সি-ডোমেইনে গুণ করে)।
অনুশীলন
-
চিন্তা করুন: উপরের উদাহরণে
y[0]আরy[4]-এর মান হাতে হিসেব করুন (মনে রাখুনx = [1, 2, 3],h = [4, 5, 6]) — এই দুটো "প্রান্তের" মান সহজ, কারণ শুধুমাত্র একটি করে পদ থাকে।y[0] = x[0]\times h[0] = 1\times 4 = 4, এবংy[4] = x[2]\times h[2] = 3\times 6 = 18— দুটোতেই শুধু একটি পদ থাকে কারণ এগুলো কনভোলিউশনের একদম শুরু ও শেষ প্রান্তের মান, যেখানেxআর ফ্লিপ করাh-এর ওভারল্যাপ সবচেয়ে কম। -
পরীক্ষা করুন: কোড সেলটি Run চেপে চালিয়ে প্রিন্ট করা
yতালিকায়y[0]আরy[4]-এর মান আপনার হাতে-করা হিসেবের সাথে মেলে কিনা যাচাই করুন।রান করলে
y = [4.0, 13.0, 28.0, 27.0, 18.0]দেখাবে — অর্থাৎy[0] = 4.0আরy[4] = 18.0, যা আপনার হাতে-করা হিসেব ৪ ও ১৮-এর সাথে হুবহু মিলে যায়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ — কোরিলেশন L11 কনভোলিউশনের নিকটাত্মীয় অপারেশন কোরিলেশন — অটোকোরিলেশন ও ক্রস-কোরিলেশন দিয়ে সিগন্যাল ডিটেকশনের ভিত্তি।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ স্যাম্পলিং, কনভোলিউশন, Z-ট্রান্সফর্ম, DTFT/DFT, FFT, FIR/IIR ফিল্টার ডিজাইন, মাল্টিরেট প্রসেসিং, স্পেকট্রাল এস্টিমেশন, র্যান্ডম সিগন্যাল প্রসেসিং, বাস্তব প্রয়োগ ও ক্যাপস্টোন।
- Numerical Methods কোর্স সহোদর কোর্স সাধারণ সংখ্যাগত অ্যালগরিদম, রুট-ফাইন্ডিং, ইন্টিগ্রেশন ও ODE সলভিং-এর গভীর কভারেজ — এই কোর্স সেই একই "হাতে-লেখা" দর্শনের উপর সিগন্যাল-প্রসেসিং-নির্দিষ্ট দিকটি যোগ করে।