পাঠ ১০ · ৫৭-এর মধ্যে · মডিউল ৩
Home / Courses / Digital Signal Processing / ডিসক্রিট কনভোলিউশন

ডিসক্রিট কনভোলিউশন — হাতে গণনা

Discrete convolution — computing it by hand
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • কনভোলিউশন সাম-কে একটি সাধারণ, পুনঃব্যবহারযোগ্য ফাংশন হিসেবে ইমপ্লিমেন্ট করা
  • কেন আউটপুটের দৈর্ঘ্য 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() ফাংশন লিখে এই একই মান কোড থেকে সত্যিকারভাবে গণনা করে মিলিয়ে দেখানো হয়েছে।

Python
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 ব্যবহার করে বড় কনভোলিউশনকে অনেক দ্রুত করা যায় (ফ্রিকোয়েন্সি-ডোমেইনে গুণ করে)।

অনুশীলন

  1. চিন্তা করুন: উপরের উদাহরণে 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-এর ওভারল্যাপ সবচেয়ে কম।

  2. পরীক্ষা করুন: কোড সেলটি 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 সলভিং-এর গভীর কভারেজ — এই কোর্স সেই একই "হাতে-লেখা" দর্শনের উপর সিগন্যাল-প্রসেসিং-নির্দিষ্ট দিকটি যোগ করে।
আগের পাঠ
কনভোলিউশন — মূল অপারেশন