(পর্ব ৪)
ডয়েশ অ্যালগরিদমের জন্য আমাদের দুরকমের কোয়ান্টাম গেট মূলত কাজে লাগবে। তার মধ্যে প্রথম গেটটি প্রায় সব কোয়ান্টাম অ্যালগরিদমেই কাজে লাগে। এর নাম হ্যাডামার্ড গেট, চিহ্ন H । এটা একটা কিউবিটের উপর কাজ করে। এর কাজ হলো
H(|0⟩) = (|0⟩ + |1⟩)/√2
H(|1⟩) = (|0⟩ – |1⟩)/√2
|0⟩ বা |1⟩ বাদে বাকি যে কোনো দশাকেই যেহেতু u|0⟩ + v|1⟩ আকারে লেখা যায়, তাই যেকোনো ভেক্টরের উপর এই গেটের ক্রিয়াও সহজেই বের করা যাবে
H (u|0⟩ + v|1⟩) = u H(|0⟩) + v H(|1⟩)
= u (|0⟩ + |1⟩)/√2 + v (|0⟩ – |1⟩)/√2
= (u+v)/√2 |0⟩ + (u-v)/√2 |1⟩
সহজেই দেখানো যায় যে
|(u+v)/√2|^2 + |(u-v)/√2|^2 = |u|^2+ |v|^2
তার মানে হ্যাডামার্ড গেট ইউনিটারি হবার একটা শর্ত – ভেক্টরের দৈর্ঘ্য অপরিবর্তিত রাখা – মেনে চলে। আর একটু আগেই বলেছি – তার মানে এই গেটের কাজ উলটে দেওয়া যাবে। আর উল্টোনোর কাজ কিন্তু করবে সে নিজেই! দেখা যাক
H(H |0⟩) = H( (|0⟩ + |1⟩)/√2)
= 1/√2 ( H |0⟩ + H |1⟩)
= 1/√2 ( (|0⟩ + |1⟩)/√2 + (|0⟩ – |1⟩)/√2)
= |0⟩
ঠিক একইভাবে দেখানো যায় যে H(H |1⟩) = |1⟩ – দুবার পরপর H ব্যবহার করলে শুরুতেই ফিরে যাওয়া যায়।
আগেই বলেছি – হ্যাডামার্ড গেটের কমবেশি ব্যবহার কোয়ান্টাম কম্পিউটারের সব কাজেই হয়। ডয়েশ অ্যালগরিদম কাজে লাগাতে গেলে আরেকটা গেট লাগবে – এই গেটটার নাম আমরা দেবো U_f। এর নামে f দেখেই বোঝা যাচ্ছে যে এটা ডয়েশ অ্যালগরিদমের জন্যই তৈরি – এই f হলো সেই ফাংশন যেটা কনস্ট্যান্ট না ব্যালান্সড সেটাই এর বিচার্য। U_f কাজ করে একসাথে দুটো কিউবিটের উপর। এর কাজ হলো এরকমঃ
x,y যদি 0 বা 1 হয় তাহলে
U_f ( |x⟩ |y⟩) = |x⟩ |y⊕f(x)⟩
দেখাই যাচ্ছে যে এখানে একটা একক দৈর্ঘ্যের ভেক্টর ( |x> |y> এর দৈর্ঘ্য হলো 1*1=1) এর উপর কাজ করে আরেকটা একক দৈর্ঘ্যের ভেক্টর তৈরি হচ্ছে।
আর হ্যাডামার্ড গেটের মতোই এটাকেও দুবার ব্যবহার করলে শুরুর দশায় ফিরে যাওয়া যায়
U_f ( U_f ( |x⟩ |y⟩)) = U_f ( |x⟩ |y⊕f(x)⟩)
= |x⟩ |( y⊕f(x) ) ⊕f(x)⟩ = |x⟩ |y⟩
যেখানে আমরা (a⊕b ) ⊕b = a, XOR এর এই ধর্মটা কাজে লাগিয়েছি। এই U_f বাকি লেখাটায় বারবার আসবে – তাই এটাকে আরেকটু গভীরে গিয়ে দেখা দরকার।
যদি আমাদের ফাংশন f টা হয়
f(0) = f(1) = 0, তাহলে
U_f( |0⟩ |0⟩ ) = |0⟩ |0⊕f(0) ⟩
= |0⟩ |0⊕0⟩ = |0⟩ |0⟩
U_f( |0⟩ |1⟩ ) = |0⟩ |1⊕f(0) ⟩
= |0⟩ |1⊕0⟩ = |0⟩ |1⟩
U_f( |1⟩ |0⟩ ) = |1⟩ |0⊕f(1) ⟩
= |1⟩ |0⊕0⟩ = |1⟩ |0⟩
U_f( |1⟩ |1⟩ ) = |1⟩ |1⊕f(1) ⟩
= |1⟩ |1⊕0⟩ = |1⟩ |1⟩
মানে এই কনস্ট্যান্ট ফাংশনটার জন্য U_f হলো দুটো কিউবিটের জগতে আইডেন্টিটি অপারেটর – সে যা পায়, সেটাই ফিরিয়ে দেয়।
আবার যদি
f(0) = f(1) = 1 হয় , তাহলে
U_f( |0⟩ |0⟩ ) = |0⟩ |0⊕f(0) ⟩
= |0⟩ |0⊕1⟩ = |0⟩ |1⟩
U_f( |0⟩ |1⟩ ) = |0⟩ |1⊕f(0) ⟩
= |0⟩ |1⊕1⟩ = |0⟩ |0⟩
U_f( |1⟩ |0⟩ ) = |1⟩ |0⊕f(1) ⟩
= |1⟩ |0⊕1⟩ = |1⟩ |1⟩
U_f( |1⟩ |1⟩ ) = |1⟩ |1⊕f(1) ⟩
= |1⟩ |1⊕1⟩ = |1⟩ |0⟩
হবে – এটা অবশ্যই আইডেন্টিটি অপারেটর নয়।
এছাড়া f ফাংশনটা আর যে দুরকমের হতে পারে তাদের জন্য একই ভাবে U_f এর ক্রিয়া বোঝা যায়।
ডয়েশ অ্যালগরিদমের জন্য লাগবে দুটি কিউবিট। U_f কে কাজে লাজানোর জন্য তাদের যথাক্রমে (|0⟩ + |1⟩)/√2 আর (|0⟩ – |1⟩)/√2 দশায় রাখতে হবে। এর জন্য কিউবিট দুটোকে |0⟩ আর |1⟩ দশা থেকে শুরু করে, তারপর একটা করে হ্যাডামার্ড গেট দুটোর উপরে কাজ করালেই এই দশায় পৌঁছে যাওয়া যাবে। তার মানে এবার আমাদের দুটো কিউবিটের যৌথ দশা হচ্ছে
(|0⟩ + |1⟩)/√2 (|0⟩ – |1⟩)/√2
= 1/2 ( |0⟩ [ |0⟩ – |1⟩ ]+ |1⟩ [ |0⟩ – |1⟩ ])
এটাকে আরো খুলে লেখা যেতো – কিন্তু পরের ধাপটাতে কষার সুবিধার জন্য আপাতত এখানেই থামছি।
পরের ধাপটাই সবচেয়ে গুরুত্বপূর্ণ – কারণ এখানেই আমাদের ফাংশন f এর কেরামতি। এই দুটো কিউবিটের উপর এবার U_f কে লাগাতে হবে। তাহলে এবার এই দুটো কিউবিটের দশা দাঁড়াবে
U_f ( 1/2 { |0⟩ [ |0⟩ – |1⟩ ]
+ |1⟩ [ |0⟩ – |1⟩ ] } )
= 1/2 { |0⟩ [ |0⊕f(0)⟩ -|1⊕f(0)⟩ ]
+ |1⟩ [ |0⊕f(1)⟩ – |1⊕f(1)⟩ ] }
এবার লক্ষ্য করে দেখো – ভেতরের দুটো বন্ধনীই |0⊕a⟩-|1⊕a⟩ আকারের। এখন a = 0 হলে, |0⊕a⟩-|1⊕a⟩ = |0⟩-|1⟩। আবার a = 1 হলে |0⊕a⟩ – |1⊕a⟩ = |1⟩-|0⟩। এই দুটোকে মেলালে লেখা যায়
|0⊕a⟩-|1⊕a⟩ = (-1)^a (|0⟩-|1⟩ )
তার মানে, U_f ব্যবহার করার পর কিউবিট দুটোর দশাকে লেখা যায়
1/2 {(-1)^f(0) |0⟩ [ |0⟩-|1⟩ ]
+ (-1)^f(1) |1⟩ [ |0⟩ – |1⟩ ] }
= (-1)^f(0)/2 [ |0⟩ + (-1)^{f(1)- f(0)} |1⟩ ]
[ |0⟩-|1⟩ ]
আমাদের আলোচনা প্রায় শেষের দিকে। আমাদের এবার দুটো বিষয়ে মন দিতে হবে। প্রথম বিষয় হলো বাইরের (-1)^f(0) – যার মান হয় +1 বা -1। এই সাইনটা বর্গ করলেই উড়ে যাবে – আর শেষমেশ যেকোনো বাস্তব গননাতে সেই বর্গই করতে হবে। তাই এটা আমরা নির্দ্বিধায় উপেক্ষা করতে পারি। দ্বিতীয় বিষয়টা হলো আরেকটা প্লাস বা মাইনাস সাইনঃ (-1)^{f(1)- f(0)} – এটাকে কিন্তু উপেক্ষা করার উপায় নেই। এটা ঠিক করবে যে প্রথম কিউবিটে দুটো ভেক্টরের মধ্যে যোগ হবে, না কি বিয়োগ। এখানেই আমাদের সমস্যা সমাধানের চাবিকাঠি। ফাংশনটা যদি কনস্ট্যান্ট হয় তাহলে f(1)- f(0) = 0 – মানে প্রথম কিউবিটের দশা হবে |0⟩ + |1⟩। উল্টোদিকে ফাংশনটা যদি ব্যালান্সড হয়, তাহলে f(1)- f(0) হয় +1 নাহয় -1 হবে। সেক্ষেত্রে প্রথম কিউবিটের দশা হবে |0⟩ – |1⟩। তাহলে মোদ্দা কথা দাঁড়ালো যে এই ধাপে আমাদের কিউবিট দুটোর দশা দাঁড়াচ্ছেঃ
১। যদি f কনস্ট্যান্ট হয়, তাহলে
[ |0⟩ + |1⟩ ] [ |0⟩ – |1⟩ ] /√2
২। যদি f ব্যালেন্সড হয়, তাহলে
[ |0⟩ – |1⟩ ] [ |0⟩ – |1⟩ ] /√2
একটা মজার বিষয় হয়তো তোমাদের চোখে পড়েছে। U_f হলো controlled gate । এর কাছে প্রথম কিউবিটটা হলো কন্ট্রোল। সে নিজে পালটায় না – কিন্তু অন্য কিউবিটের দশা পাল্টাবে কি না সেটা তার উপর (এক্ষেত্রে সরাসরি x এর উপর না হলেও f(x) এর উপর) নির্ভর করে। কিন্তু টেন্সর গুণের যাদুতে শেষমেশ এই + বা – সাইনটা প্রথম কিউবিটের দশাই পাল্টাচ্ছে!
এবার শেষ ধাপ। প্রথম কিউবিটের উপর একটা হ্যাডামার্ড গেট লাগালেই কিউবিট দুটোর দশা হবে
১। যদি f কনস্ট্যান্ট হয়, তাহলে
|0⟩ [ |0⟩ – |1⟩ ] /√2]
২। যদি f ব্যালেন্সড হয়, তাহলে
|1 ⟩ [ |0⟩ – |1⟩ ] /√2
এবার আমাদের শেষ কাজ – প্রথম কিউবিটের দশাটা মেপে ফেলা। মেপে যদি 0 পাওয়া যায় তার মানে আমাদের ফাংশনটা কনস্ট্যান্ট! আর যদি মাপার ফলে 1 পাওয়া যায় তার মানে ফাংশনটা ব্যালান্সড।
মাপাটা কি করে হবে সেটা নির্ভর করে কিউবিট ঠিক কোন বাস্তব সিস্টেম দিয়ে তৈরি করা হয়েছে। এমনিতে পরিমাপ জিনিসটা কোয়ান্টাম মেকানিক্সের একটা বেশ জটিল বিষয় কিন্তু এখানে আমাদের কাজটা অনেক সহজ। আমাদের শুধু |0⟩ আর |1⟩ – এই দুটো দশার মধ্যে ফারাক করতে হবে। এই কাজটা ১০০% নিশ্চয়তা নিয়ে করা যায়। যেমন ধরো, যদি কিউবিটটা পোলারাইজড আলোর ফোটন হয় তাহলে একটা X – পোলারয়েড দিয়ে পাঠালেই হবে – ফোটনটা পার করে অন্যদিকে এলে দশাটা |0⟩, শোষিত হলে |1⟩।
খেয়াল করো যে এই গোটা প্রক্রিয়াতে একবারই f কষা হয়েছে – যে একবার U_f ব্যবহার করা হলো। তাই যেখানে সনাতন কম্পিউটারে দুবার f কষতে হতো, সেখানে ডয়েশ অ্যালগরিদম দিয়ে একবারেই কাজ হয়ে যায়।
পরবর্তী পর্বে ডয়েশ- যোসজা অ্যালগরিদম নিয়ে কথা বলার ইচ্ছে রইলো।
