সরাসরি পর্বে যান
পর্ব ৪৫ · ৭৯-এর মধ্যে · মডিউল ৮

Propositional logic — বাক্য, truth table আর inference

Propositional Logic — Knowledge Bases, Truth Tables, Entailment, Model Checking, CNF, Resolution, Horn Clauses, SAT and DPLL

Taught in Bangla, this lesson builds a small knowledge base of shop rules, explains connectives, truth tables, models and entailment with Python model checking, converts sentences to CNF, proves results by resolution refutation, and covers Horn clauses, DPLL and real runs of modern SAT solvers through pycosat, python-sat and sympy.

এই পর্বে যা শিখবেন

  • Knowledge base, TELL আর ASK; proposition, পাঁচটি connective আর তাদের truth table, বিশেষ করে → কেন এমন
  • Syntax আর semantics-এর পার্থক্য; model, satisfiable, valid আর unsatisfiable; De Morgan আর contraposition কোড দিয়ে যাচাই
  • Entailment KB ⊨ α, truth table গুনে model checking, আর 2n কেন দেয়াল
  • Modus ponens, sound আর complete-এর মানে, CNF রূপান্তর আর resolution refutation, হাতে ও কোডে
  • Horn clause কেন সস্তা, SAT আর DPLL-এর ধারণা, আর python-sat, pycosat ও sympy দিয়ে সত্যিকারের run

মডিউল ৭ জুড়ে (পর্ব ৩৯ থেকে পর্ব ৪৪) agent শিখেছে পথ খুঁজতে আর নিয়ম মেনে মান বসাতে। কিন্তু সেখানে নিয়মগুলো কোডের ভেতরে লুকানো ছিল। এই মডিউলে প্রশ্নটি অন্য: জ্ঞানকে কি এমন ভাষায় লেখা যায়, যা থেকে যন্ত্র নিজেই নতুন সিদ্ধান্ত টেনে বের করে, আর প্রতিটি সিদ্ধান্তের পক্ষে প্রমাণও দেখায়? সবচেয়ে সরল সেই ভাষা propositional logic. এখানের সব নিয়ম কাল্পনিক অনলাইন দোকান বাজারঘরের, বানানো।

১ · Knowledge-based agent — KB, TELL আর ASK

বাজারঘরের কাস্টমার-সাপোর্টে নতুন একজন কর্মী যোগ দিয়েছেন। প্রথম দিন তাঁকে একটি খাতা দেওয়া হলো, যাতে দোকানের নিয়ম লেখা: “অর্ডার ৳১,০০০-এর বেশি হলে delivery ফ্রি”, “ফ্রি delivery আর ঢাকার ঠিকানা হলে একই দিনে পৌঁছায়”, “pre-order পণ্য একই দিনে যায় না”। কাস্টমার ফোন করে জিজ্ঞেস করেন, “আমার ৳১,২০০-র অর্ডার, ঠিকানা মিরপুর, আজই পাব?” কর্মী খাতা দেখে, নিয়মগুলো জুড়ে উত্তর দেন। কোনো নিয়মে সরাসরি “৳১,২০০ আর মিরপুর হলে আজই” লেখা নেই; উত্তরটি বের করে আনা হয়েছে।

ডাক্তারের রোগনির্ণয়ের কথা ভাবুন। বইয়ে লেখা থাকে “জ্বর আর গায়ে র‍্যাশ হলে ডেঙ্গু পরীক্ষা করুন”, “প্লাটিলেট কম হলে …”। রোগীর লক্ষণ শুনে ডাক্তার নিয়মগুলো জুড়ে সিদ্ধান্তে পৌঁছান, আর জিজ্ঞেস করলে বলতে পারেন কোন নিয়ম থেকে কোন কথা। Knowledge-based agent ঠিক এই কাজটি যন্ত্রে করতে চায়।

Russell ও Norvig-এর পাঠ্যবই “Artificial Intelligence: A Modern Approach” এই ধরনের agent-কে দুটি কাজ দিয়ে বর্ণনা করে:

  • TELL(KB, বাক্য): নতুন একটি কথা knowledge base-এ যোগ করা। শুরুতে নিয়ম, পরে প্রতিটি নতুন তথ্য (“এই অর্ডার ৳১,২০০-র”)।
  • ASK(KB, প্রশ্ন): জিজ্ঞেস করা, প্রশ্নের কথাটি KB থেকে নিশ্চিতভাবে বেরিয়ে আসে কি না। উত্তর “হ্যাঁ”, “না”, অথবা “জানা যায় না”।

এখানে দুটি অংশ আলাদা থাকে: জ্ঞান (KB-র বাক্যগুলো) আর যুক্তির যন্ত্র (inference engine). নতুন নিয়ম এলে কোড বদলাতে হয় না, শুধু TELL. এই আলাদা রাখার ধারণা John McCarthy-র ১৯৫৮ সালের দিকের “Programs with Common Sense” প্রবন্ধের “advice taker”-এর সঙ্গে সাধারণত যুক্ত করা হয়। পর্ব ৩-এ যে নিয়মভিত্তিক বা symbolic AI-এর কথা এসেছিল, তার গাণিতিক ভিত্তি এই পর্ব।

মূল ধারণা

লক্ষ্য শুধু উত্তর নয়, নিশ্চয়তাসহ উত্তর। ASK-এর “হ্যাঁ” মানে: KB-র সব কথা সত্য হলে প্রশ্নের কথাটিও অবশ্যই সত্য, ব্যতিক্রম ছাড়া। এই “অবশ্যই”-কে নিখুঁত করতেই পরের অংশগুলো: বাক্য কী, সত্য মানে কী, আর “বেরিয়ে আসা” মানে কী।

২ · বাক্য আর connective — ¬ ∧ ∨ → ↔ আর truth table

Propositional logic-এর সবচেয়ে ছোট একক একটি proposition: এমন একটি কথা, যা হয় সত্য (True, T) নয়তো মিথ্যা (False, F), মাঝামাঝি নয়। প্রতিটিকে একটি অক্ষর দিয়ে লেখা হয়, যাকে বলে proposition symbol বা atomic sentence. বাজারঘরের পাঁচটি symbol সারা পর্বে চলবে:

Symbolমানে (একটি নির্দিষ্ট অর্ডারের জন্য)
Bঅর্ডারের দাম ৳১,০০০-এর বেশি (Big order)
FDelivery ফ্রি (Free)
Dঠিকানা ঢাকার ভেতরে (Dhaka)
Sএকই দিনে delivery (Same-day)
Pপণ্যটি pre-order, এখনো গুদামে আসেনি (Pre-order)

ছোট বাক্য জুড়ে বড় বাক্য বানাতে পাঁচটি connective:

  • ¬ (not, না): negation. ¬P মানে “pre-order নয়”। P সত্য হলে ¬P মিথ্যা, উল্টোটাও।
  • ∧ (and, এবং): conjunction. F ∧ D সত্য শুধু তখন, যখন দুটিই সত্য।
  • ∨ (or, অথবা): disjunction. অন্তত একটি সত্য হলেই সত্য; দুটিই সত্য হলেও (inclusive or).
  • → (implies, যদি … তাহলে): implication. B → F পড়ুন “B হলে F”. বাঁ দিক premise, ডান দিক conclusion.
  • ↔ (if and only if, ঠিক তখনই): biconditional. দুই দিকের মান এক হলে সত্য।

প্রতিটি connective-এর পুরো অর্থ একটি truth table-এ ধরা যায়: দুটি symbol-এর চারটি সম্ভাব্য মান-মিশ্রণ, প্রতিটিতে ফল।

AB¬AA ∧ BA ∨ BA → BA ↔ B
TTFTTTT
TFFFTFF
FTTFTTF
FFTFFTT

“যদি … তাহলে” কেন premise মিথ্যা হলে সত্য

A → B কলামটিই নতুনদের সবচেয়ে বেশি অবাক করে: A মিথ্যা হলে B যা-ই হোক, ফল সত্য। বাজারঘরের নিয়মটি দিয়ে ভাবুন: “অর্ডার ৳১,০০০-এর বেশি হলে delivery ফ্রি” (B → F). এটি একটি প্রতিশ্রুতি। প্রতিশ্রুতি কখন ভাঙল? শুধু তখন, যখন অর্ডার বড় ছিল অথচ delivery-র টাকা কাটা হলো। কেউ ৳৬০০-র অর্ডার দিলেন আর ঈদের অফারে ফ্রি delivery পেলেন: দোকান কি তার নিয়ম ভাঙল? না। নিয়মটি ছোট অর্ডার নিয়ে কিছুই বলেনি। নিচের stepper সারি ধরে ধরে টেবিলটি বানায়, পাশে আরও দুটি বাক্য রেখে: ¬F → ¬B (“ফ্রি না হলে অর্ডার বড় নয়”) আর F → B (“ফ্রি হলে অর্ডার বড়”)।

1for B, F in TT TF FT FF:2  B->F   = not B or F3  ~F->~B = F or not B4  F->B   = not F or B5compare columns
এই সারি (Watch)
সারি
B (৳১,০০০+)
F (ফ্রি)
B → F
¬F → ¬B
F → B
  1. B = T, F = T. অর্ডার বড়, delivery ফ্রি; নিয়ম যা বলেছিল তা-ই হলো। B → F সত্য। ¬F → ¬B-এর premise ¬F মিথ্যা, তাই সত্য; F → B-এর দুই দিকই সত্য, তাই সত্য।
  2. B = T, F = F. অর্ডার বড়, তবু ৳৬০ delivery চার্জ: প্রতিশ্রুতি ভাঙল, B → F মিথ্যা। এটিই একমাত্র সারি যেখানে → মিথ্যা হয়: premise সত্য, conclusion মিথ্যা। ¬F → ¬B-ও মিথ্যা (¬F সত্য, ¬B মিথ্যা); F → B সত্য, কারণ তার premise F মিথ্যা।
  3. B = F, F = T. ৳৬০০-র অর্ডারে অফারের ফ্রি delivery. নিয়মটি ছোট অর্ডার নিয়ে কিছু বলেনি, তাই ভাঙেনি: B → F সত্য। কিন্তু F → B মিথ্যা: “ফ্রি হলে অর্ডার বড়” কথাটি এই ঘটনায় ভুল প্রমাণ হলো।
  4. B = F, F = F. ছোট অর্ডার, চার্জ কাটা হলো; কোনো প্রতিশ্রুতিই ছিল না। B → F সত্য, ¬F → ¬B সত্য, F → B সত্য (premise F মিথ্যা)।
  5. তিনটি কলাম পাশাপাশি। B → F আর ¬F → ¬B প্রতিটি সারিতে একই (T F T T), তাই এরা সমার্থক: এর নাম contraposition. F → B (T T F T), যাকে converse বলে, আলাদা। “ফ্রি পেয়েছেন, তাই অর্ডার বড় ছিল” তাই একটি ভুল যুক্তি।

সংক্ষেপে, A → B আসলে ¬A ∨ B-এর সমান। এই রূপটিকে বলে material implication. দৈনন্দিন ভাষার “যদি”-তে প্রায়ই কারণ-ফলের ইঙ্গিত থাকে (“বৃষ্টি হলে রাস্তা ভেজে”), যুক্তিবিদ্যার → তা দাবি করে না। “২ + ২ = ৫ হলে ঢাকা বাংলাদেশের রাজধানী” যুক্তিবিদ্যায় সত্য বাক্য, যদিও শুনতে অদ্ভুত (ভাবনার প্রশ্ন ১)।

৩ · Syntax, semantics আর model — satisfiable, valid, equivalent

যুক্তিবিদ্যার একটি ভাষার দুটি দিক আছে। Syntax বলে কোন চিহ্নের সারি একটি বৈধ বাক্য: (B ∧ D) → S বৈধ, B ∧ → S নয়। এটি ব্যাকরণ, অর্থ নয়। Semantics বলে একটি বাক্য কোন অবস্থায় সত্য। এর মূল শব্দটি model: প্রতিটি symbol-এ একটি করে T বা F বসানো একটি সম্ভাব্য জগৎ। পাঁচটি symbol-এ 25 = ৩২টি model; truth table-এর প্রতিটি সারি একটি model. (Machine learning-এর “model” শব্দের সঙ্গে গুলিয়ে ফেলবেন না; এখানে মানে “একটি সম্ভাব্য জগৎ”।)

কোনো model-এ বাক্য α সত্য হলে বলা হয় সেই model α-কে satisfy করে। সব model ঘুরে একটি বাক্যকে তিন ভাগে ফেলা যায়:

Valid (tautology)

প্রতিটি model-এ সত্য। যেমন P ∨ ¬P: pre-order হয়, নয়তো হয় না। জগৎ সম্পর্কে নতুন কিছু বলে না, কিন্তু যুক্তির নিয়ম যাচাইয়ে দরকারি।

valid

Satisfiable

অন্তত একটি model-এ সত্য। B → F: চারটির তিনটিতে সত্য। বেশিরভাগ বাস্তব নিয়ম এমন।

satisfiable

Unsatisfiable

কোনো model-এ সত্য নয়। P ∧ ¬P, বা (B → F) ∧ B ∧ ¬F: নিয়ম আছে, অর্ডার বড়, তবু ফ্রি নয়। স্ববিরোধ।

unsatisfiable

দুটি বাক্য α আর β logically equivalent (লেখা হয় α ≡ β) যদি প্রতিটি model-এ তাদের মান এক হয়; অন্যভাবে, α ↔ β valid. নিচের কোড এই সব সংজ্ঞা সরাসরি Python-এ লেখে: একটি বাক্য মানে একটি Python function, যা symbol-গুলোর True/False নিয়ে ফল দেয়; itertools.product সব model বানায়। প্রথমবার চালালে ব্রাউজারে Python নামতে কয়েক সেকেন্ড লাগতে পারে।

from itertools import product
imp = lambda a, b: (not a) or b          # a -> b
iff = lambda a, b: a == b                # a <-> b
TF = lambda v: "T" if v else "F"

def rows(f, names):                      # every model = one row
    return [(v, f(*v)) for v in product([True, False], repeat=len(names))]

def show(f, names, label):
    print(label)
    for v, r in rows(f, names):
        print("  ", " ".join(TF(x) for x in v), "|", TF(r))

def classify(f, names):
    res = [r for _, r in rows(f, names)]
    if all(res): return "valid (tautology)"
    return "satisfiable, not valid" if any(res) else "unsatisfiable"

def equivalent(f, g, names):             # same value in every model
    return classify(lambda *v: iff(f(*v), g(*v)), names).startswith("valid")

show(lambda B, F: imp(B, F), "BF", "B F | B -> F")
for label, f, n in [("P or not P", lambda P: P or not P, "P"),
                    ("P and not P", lambda P: P and not P, "P"),
                    ("B -> F", lambda B, F: imp(B, F), "BF"),
                    ("(B -> F) and B and not F",
                     lambda B, F: imp(B, F) and B and not F, "BF")]:
    print(f"{label:26}", classify(f, n))
pairs = {"De Morgan": (lambda A, B: not (A and B), lambda A, B: (not A) or (not B), "AB"),
         "contraposition": (lambda A, B: imp(A, B), lambda A, B: imp(not B, not A), "AB"),
         "converse": (lambda A, B: imp(A, B), lambda A, B: imp(B, A), "AB"),
         "distribution": (lambda A, B, C: A or (B and C),
                          lambda A, B, C: (A or B) and (A or C), "ABC")}
for name, (f, g, n) in pairs.items():
    print(f"{name:15}", "equivalent" if equivalent(f, g, n) else "NOT equivalent")

আসল আউটপুট:

B F | B -> F
   T T | T
   T F | F
   F T | T
   F F | T
P or not P                 valid (tautology)
P and not P                unsatisfiable
B -> F                     satisfiable, not valid
(B -> F) and B and not F   unsatisfiable
De Morgan       equivalent
contraposition  equivalent
converse        NOT equivalent
distribution    equivalent

প্রথম টেবিলটি stepper-এর B → F কলামের সঙ্গে হুবহু মেলে (T F T T). চারটি বাক্যের তিন রকম ফল এসেছে। শেষ চার লাইনে তিনটি বিখ্যাত equivalence প্রতিটি model-এ মিলেছে, আর converse মেলেনি। De Morgan-এর নিয়ম ¬(A ∧ B) ≡ ¬A ∨ ¬B বলে, “দুটি একসঙ্গে সত্য নয়” মানে “অন্তত একটি মিথ্যা”। Distribution: A ∨ (B ∧ C) ≡ (A ∨ B) ∧ (A ∨ C); অংশ ৬-এ CNF বানাতে ঠিক এটিই লাগবে। খেয়াল করুন, classify সব সারি গোনে: n-টি symbol-এ 2nটি সারি। কোড ঠিক থাকলে প্রমাণ নিখুঁত, কিন্তু খরচ দ্রুত বাড়ে।

এই হিসাব Computer Fundamentals কোর্সের Boolean algebra পর্বের সঙ্গে একই গণিত: সেখানে AND, OR, NOT দিয়ে circuit সরল করা হয়, আর Computer Architecture কোর্সে সেই রাশিই logic gate হয়ে চিপে বসে। পার্থক্য উদ্দেশ্যে: circuit-এ লক্ষ্য একটি রাশি হিসাব করা, AI-তে লক্ষ্য রাশিগুলো থেকে কী বেরিয়ে আসে তা জানা।

৪ · Entailment আর model checking — KB ⊨ α

বাজারঘরের knowledge base-এ তিনটি নিয়ম:

  • R1: B → F (অর্ডার ৳১,০০০-এর বেশি হলে delivery ফ্রি)
  • R2: (F ∧ D) → S (ফ্রি delivery আর ঢাকার ঠিকানা হলে একই দিনে)
  • R3: P → ¬S (pre-order হলে একই দিনে নয়)

এখন সংজ্ঞাটি: KB ⊨ α (পড়ুন “KB entails α”, চিহ্ন ⊨) মানে যে প্রতিটি model-এ KB-র সব বাক্য সত্য, সেখানে α-ও সত্য। একে বলে entailment. লিখে বললে: M(KB) ⊆ M(α), যেখানে M(x) মানে x যেসব model-এ সত্য তাদের সেট। লক্ষ করুন, KB-র বাক্য যত বেশি, M(KB) তত ছোট: প্রতিটি নতুন তথ্য কিছু সম্ভাব্য জগৎ বাদ দেয়।

Entailment: M(KB) যদি M(S)-এর ভেতরে থাকে M(S): ১৬ KB: ১ M(S): ১৬ KB: ৪ B আর D জানা: KB ⊨ S শুধু B জানা: S অজানা ৩২টি model KB-র ২টি model S-এর ভেতরে, ২টি বাইরে
লাল = KB-র model, নীল = S সত্য এমন model, বাইরের আয়তক্ষেত্র = সব ৩২টি model. Entailment মানে লাল বৃত্তটি পুরোপুরি নীলের ভেতরে। ওপরে R1–R3 আর দুটি তথ্য (B, D), KB-র model মাত্র একটি, আর তাতে S সত্য। নিচে শুধু B জানা, KB-র চারটি model-এর দুটিতে S সত্য, দুটিতে মিথ্যা: KB ⊭ S আর KB ⊭ ¬S. সংখ্যাগুলো নিচের কোডের আসল আউটপুট থেকে; বৃত্তের আকার মাপমতো নয়।

সংজ্ঞাটিই একটি অ্যালগরিদম: সব model একে একে বানান, যেখানে KB সত্য সেখানে α দেখুন। একটিতেও α মিথ্যা হলে উত্তর “না”। একে বলে model checking, আর পাঠ্যবইয়ে এর নাম TT-ENTAILS (truth table দিয়ে entailment). নিচের কোডে TELL আর ASK দুটিই আছে। ASK তিন রকম উত্তর দেয়: KB ⊨ α হলে “yes”, KB ⊨ ¬α হলে “no”, কোনোটিই না হলে “unknown”.

from itertools import product
SYMS = ["B", "F", "D", "S", "P"]
imp = lambda a, b: (not a) or b
# B: order above 1000 tk   F: free delivery   D: address inside Dhaka
# S: same-day delivery     P: pre-order item
kb = [lambda m: imp(m["B"], m["F"]),               # R1  B -> F
      lambda m: imp(m["F"] and m["D"], m["S"]),     # R2  F and D -> S
      lambda m: imp(m["P"], not m["S"])]            # R3  P -> not S
models = [dict(zip(SYMS, v)) for v in product([True, False], repeat=len(SYMS))]

def tell(sentence):
    kb.append(sentence)

def entails(alpha):                     # TT-ENTAILS by enumerating all models
    return all(alpha(m) for m in models if all(s(m) for s in kb))

def ask(alpha):
    if entails(alpha): return "yes"
    if entails(lambda m: not alpha(m)): return "no"
    return "unknown"

queries = {"S": lambda m: m["S"], "P": lambda m: m["P"],
           "F and not P": lambda m: m["F"] and not m["P"]}
print("models checked each time:", len(models), "= 2 **", len(SYMS))
for fact, f in [("B", lambda m: m["B"]), ("D", lambda m: m["D"])]:
    tell(f)
    kb_models = [m for m in models if all(s(m) for s in kb)]
    print(f"TELL {fact} -> models of KB: {len(kb_models)}")
    for name, q in queries.items():
        print(f"   ASK {name:12} {ask(q)}")
for m in kb_models:
    print("the only model left:", {k: int(v) for k, v in m.items()})

আসল আউটপুট:

models checked each time: 32 = 2 ** 5
TELL B -> models of KB: 4
   ASK S            unknown
   ASK P            unknown
   ASK F and not P  unknown
TELL D -> models of KB: 1
   ASK S            yes
   ASK P            no
   ASK F and not P  yes
the only model left: {'B': 1, 'F': 1, 'D': 1, 'S': 1, 'P': 0}

শুধু “অর্ডার বড়” জানালে (TELL B) ৩২টির মধ্যে ৪টি model টিকে থাকে, আর তিনটি প্রশ্নেরই উত্তর “unknown”. F যদিও চারটিতেই সত্য, “F and not P” অজানা, কারণ ঢাকার বাইরে pre-order হতেই পারে। “ঠিকানা ঢাকায়” জানাতেই (TELL D) একটিমাত্র model থাকে: আজই delivery (S = yes), আর পণ্যটি pre-order নয় (P = no). দ্বিতীয়টি মজার: কাস্টমার কোনো দিন বলেননি পণ্যটি pre-order নয়, তবু R3 আর S মিলে তা নিশ্চিত হলো। “unknown” উত্তরটি জরুরি: knowledge-based agent জানে না এমন কথায় চুপ থাকে, অনুমান করে না।

2n-এর দেয়াল

Model checking sound আর complete (অংশ ৫-এ সংজ্ঞা): যা বলে তা ঠিক, আর যা entailed তা ধরে। সমস্যা খরচে। n-টি symbol-এ 2nটি model:

SymbolModel 2nপ্রতি সেকেন্ডে 109টি হারে
৫ (বাজারঘর)32চোখের পলকে
২০1,048,576প্রায় ০.০০১ সেকেন্ড
৫০≈ 1.13 × 1015প্রায় ১৩ দিন
১০০≈ 1.27 × 1030প্রায় 4 × 1013 বছর

দোকানের পুরো নিয়মের খাতায় একশো symbol খুব সাধারণ, আর চিপের নকশা যাচাইয়ে লাখ লাখ। এটা পর্ব ৪৩-এর সেই combinatorial explosion. দুটি পথে বেরোনো যায়: model না গুনে বাক্য থেকে বাক্যে যুক্তি চালানো (অংশ ৫–৭), অথবা চালাকি করে model খোঁজা (অংশ ৮)।

৫ · Inference rule — modus ponens থেকে sound আর complete

মানুষ যুক্তি দেন truth table না বানিয়ে: “অর্ডার বড়; বড় হলে ফ্রি; তাই ফ্রি”। এই ধরনের এক ধাপের ছাঁচকে বলে inference rule. লেখা হয় দাগের ওপরে যা জানা (premise), নিচে যা বের হলো। নতুন বাক্য বের করাকে বলে derive, চিহ্ন ⊢ (KB ⊢ α মানে “KB থেকে নিয়ম খাটিয়ে α পাওয়া যায়”)।

নিয়মজানা থেকেবের হয়বাজারঘরে
Modus ponensα → β, αβB → F আর B থেকে F
And-eliminationα ∧ βα (বা β)F ∧ D থেকে D
Modus tollensα → β, ¬β¬αP → ¬S আর S থেকে ¬P
And-introductionα, βα ∧ βF আর D থেকে F ∧ D
Equivalenceαযেকোনো β ≡ αB → F থেকে ¬B ∨ F

বাজারঘরের প্রশ্নে এক লাইন করে প্রমাণ: (১) B আর R1 থেকে modus ponens-এ F; (২) F আর D থেকে F ∧ D; (৩) F ∧ D আর R2 থেকে modus ponens-এ S; (৪) S আর R3 থেকে modus tollens-এ ¬P. চারটি ধাপ, অথচ model checking ৩২টি জগৎ ঘুরেছিল। প্রমাণ খোঁজা নিজেই একটি search সমস্যা: state = এ পর্যন্ত জানা বাক্যের সেট, action = একটি নিয়ম খাটানো, goal = α পাওয়া।

দুটি জনপ্রিয় ভুল যুক্তি। “B → F, আর F; তাই B” (affirming the consequent): ফ্রি delivery পেয়েছেন মানেই অর্ডার বড় নয়, অফারও থাকতে পারে। “B → F, আর ¬B; তাই ¬F” (denying the antecedent): ছোট অর্ডারে নিয়মটি কিছুই বলে না। দুটিই অংশ ২-এর stepper-এর ধাপ ৩-এ ধরা পড়ে।

Sound আর complete — দুটি আলাদা প্রতিশ্রুতি

Sound: মিথ্যা বলে না

যা derive করে (KB ⊢ α), তা সত্যিই entailed (KB ⊨ α)। Modus ponens sound, কারণ α → β আর α যেখানে সত্য, সেখানে β-ও সত্য। Affirming the consequent sound নয়।

⊢ হলে ⊨

Complete: কিছু বাদ পড়ে না

যা entailed, তার প্রতিটি derive করা যায় (KB ⊨ α হলে KB ⊢ α)। শুধু modus ponens complete নয়: KB = {A ∨ B, ¬A} থেকে B entailed, কিন্তু কোনো → নেই বলে modus ponens আটকে যায়।

complete: ⊨ হলে ⊢

পরীক্ষার খাতা দেখার সঙ্গে তুলনা করুন: sound পরীক্ষক কখনো ভুল উত্তরে নম্বর দেন না; complete পরীক্ষক কোনো সঠিক উত্তর বাদ দেন না। আমরা দুটিই চাই, সঙ্গে দ্রুতও। পরের দুই অংশে একটিমাত্র নিয়ম, resolution, যা propositional logic-এ দুটি শর্তই মেটায়, যদি বাক্যগুলো একটি বিশেষ রূপে থাকে।

৬ · CNF — যেকোনো বাক্যকে clause-এর তালিকায়

কয়েকটি শব্দ আগে। Literal মানে একটি symbol বা তার negation: S, ¬S. Clause মানে কয়েকটি literal-এর ∨, যেমন ¬F ∨ ¬D ∨ S; সংক্ষেপে সেট হিসেবে লেখা হয় {¬F, ¬D, S}। একটি বাক্য CNF (conjunctive normal form) রূপে থাকে যদি তা কয়েকটি clause-এর ∧ হয়। “Clause-গুলোর প্রতিটি সত্য হতে হবে, আর প্রতিটি clause-এ অন্তত একটি literal সত্য হতে হবে।” প্রতিটি propositional বাক্যের একটি equivalent CNF আছে, আর তা বানানোর চারটি যান্ত্রিক ধাপ:

  1. ↔ বাদ: α ↔ β ≡ (α → β) ∧ (β → α)
  2. → বাদ: α → β ≡ ¬α ∨ β
  3. ¬ ভেতরে ঢোকানো: De Morgan, ¬(α ∧ β) ≡ ¬α ∨ ¬β আর ¬(α ∨ β) ≡ ¬α ∧ ¬β; সঙ্গে ¬¬α ≡ α
  4. ∨-কে ∧-এর ওপর ছড়ানো (distribution): α ∨ (β ∧ γ) ≡ (α ∨ β) ∧ (α ∨ γ)

ধরুন বাজারঘর R2-কে আরও কড়া করল: “একই দিনে delivery হয় ঠিক তখনই, যখন delivery ফ্রি আর ঠিকানা ঢাকায়”, মানে S ↔ (F ∧ D). নিচের stepper এটিকে CNF-এ আনে।

1a<->b => (a->b)&(b->a)2a->b => ~a|b3~(a&b) => ~a|~b4a|(b&c) => (a|b)&(a|c)
বাক্য (Watch)
এখন
নিয়ম
CNF?
  1. শুরু। একটি biconditional. CNF-এ শুধু ¬, ∨ আর ∧ চলে, আর ¬ থাকবে শুধু symbol-এর ঠিক আগে।
  2. ↔ বাদ। “S হলে F ∧ D” আর “F ∧ D হলে S”, দুই দিকের দুটি implication, মাঝে ∧.
  3. → বাদ। প্রতিটি α → β হলো ¬α ∨ β. দ্বিতীয় অংশে ¬ বসল একটি পুরো বন্ধনীর আগে: ¬(F ∧ D). এটি এখনো চলবে না।
  4. De Morgan. ¬(F ∧ D) হলো ¬F ∨ ¬D. দ্বিতীয় অংশ এখন একটি clause: {¬F, ¬D, S}, যা আসলে R2 নিজেই। প্রথম অংশে ∨-এর ভেতরে ∧ রয়ে গেছে।
  5. Distribution. ¬S ∨ (F ∧ D) ভেঙে (¬S ∨ F) ∧ (¬S ∨ D). এখন CNF: তিনটি clause. প্রথম দুটি বলে “একই দিনে হলে ফ্রি” আর “একই দিনে হলে ঢাকায়”; কড়া নিয়মটি এই দুটি নতুন দাবি যোগ করেছে।

অংশ ৮-এর sympy run-এ to_cnf একই তিনটি clause দিয়েছে, শুধু ক্রম আলাদা। সতর্কতা: distribution খারাপ ক্ষেত্রে বাক্যকে বিস্ফোরিত করে। (A1 ∧ B1) ∨ (A2 ∧ B2) ∨ … ∨ (An ∧ Bn)-এর সরাসরি CNF-এ 2nটি clause. বাস্তবের solver তাই Tseitin transformation ব্যবহার করে: প্রতিটি উপ-রাশির জন্য একটি নতুন symbol, ফলে আকার রৈখিক থাকে; নতুন বাক্যটি equivalent নয়, কিন্তু satisfiable হয় ঠিক তখনই, যখন মূলটি হয় (equisatisfiable).

৭ · Resolution refutation — ¬α যোগ করে খালি clause

Resolution নিয়মটি একটি লাইনে: একটি clause-এ L আর অন্যটিতে ¬L থাকলে, দুটি জুড়ে L আর ¬L বাদ দিয়ে নতুন clause. যেমন {¬B, F} আর {B} থেকে {F}; {¬F, ¬D, S} আর {¬S} থেকে {¬F, ¬D}। নতুনটির নাম resolvent. কেন sound: যেকোনো model-এ L সত্য হলে দ্বিতীয় clause-এর বাকি অংশ সত্য হতেই হবে, মিথ্যা হলে প্রথমটির বাকি অংশ; দুই ক্ষেত্রেই resolvent সত্য। Modus ponens আসলে এর একটি বিশেষ রূপ।

প্রমাণের কৌশলটি উল্টো দিক থেকে, যাকে বলে resolution refutation বা proof by contradiction: KB ⊨ α ঠিক তখনই, যখন KB ∧ ¬α unsatisfiable. তাই KB-র clause-এর সঙ্গে ¬α-এর clause যোগ করুন, আর resolve করতে থাকুন। যদি কোনো সময় empty clause {} পাওয়া যায় (যেমন {D} আর {¬D} থেকে), তার মানে স্ববিরোধ: KB আর ¬α একসঙ্গে সত্য হতে পারে না, তাই α প্রমাণিত।

Resolution-কে একমাত্র নিয়ম হিসেবে যন্ত্রে প্রমাণের ভিত্তি করেন John Alan Robinson, ১৯৬৫ সালে Journal of the ACM-এর প্রবন্ধ “A Machine-Oriented Logic Based on the Resolution Principle”-এ (first-order logic-এর জন্য, unification-সহ, যা পর্ব ৪৬-এর বিষয়)। এর আগেই ১৯৬০ সালে Martin Davis আর Hilary Putnam propositional সংস্করণে কাছাকাছি একটি পদ্ধতি দেন। Robinson-এর কাজ পরে ১৯৭০-এর দশকে Prolog ভাষার জন্মের পটভূমি হয় (Alain Colmerauer আর Robert Kowalski-র নাম যুক্ত)। আরও আগে, ১৯৫৬ সালে Newell, Shaw আর Simon-এর Logic Theorist প্রোগ্রাম Principia Mathematica-র কয়েকটি উপপাদ্য প্রমাণ করেছিল বলে সুপরিচিত।

এবার হাতে একটি পুরো প্রমাণ। প্রশ্ন: বাজারঘরের KB (R1, R2, R3, B, D) থেকে কি ¬P বেরোয়, মানে পণ্যটি pre-order নয়? CNF-এ KB-র পাঁচটি clause: C1 {¬B, F}, C2 {¬F, ¬D, S}, C3 {¬P, ¬S}, C4 {B}, C5 {D}। প্রমাণ করতে চাই ¬P, তাই তার উল্টো ¬¬P = P যোগ: C6 {P}।

1S = CNF(KB) + CNF(~goal)2pick L in Ci, ~L in Cj3add (Ci-L) | (Cj-~L)4empty? goal proved
resolution (Watch)
জোড়া
কাটা literal
নতুন clause
  1. শুরু। KB-র পাঁচটি clause আর প্রমাণ-লক্ষ্যের উল্টো, C6 {P}। মানে আমরা ধরে নিলাম “পণ্যটি pre-order”, এখন দেখাব এই ধরে নেওয়া KB-র সঙ্গে খাপ খায় না।
  2. C6 আর C3. {P} আর {¬P, ¬S}-এ P আর ¬P বিপরীত। দুটি বাদ দিয়ে বাকি থাকে {¬S}: “pre-order হলে আজ নয়, আর এটি pre-order, তাই আজ নয়।”
  3. C7 আর C2. {¬S} আর {¬F, ¬D, S}-এ S কাটা পড়ে, থাকে {¬F, ¬D}: “আজ নয়, তাই ফ্রি আর ঢাকা একসঙ্গে সত্য নয়।”
  4. C8 আর C5. ঠিকানা ঢাকায় ({D}), তাই D কাটা পড়ে {¬F}: delivery ফ্রি নয়।
  5. C9 আর C1. {¬F} আর {¬B, F}-এ F কাটা পড়ে, থাকে {¬B}: অর্ডার বড় নয়।
  6. C10 আর C4. {¬B} আর {B} থেকে কিছুই থাকে না: empty clause. অর্ডার বড় নয় আর বড়, দুটি একসঙ্গে অসম্ভব। তাই “pre-order” ধরে নেওয়াটাই ভুল ছিল, KB ⊨ ¬P. মোট পাঁচটি resolution.

হাতে আমরা জানতাম কোন জোড়া বাছতে হবে। যন্ত্র জানে না; তাকে জোড়া খুঁজতে হয়। নিচের কোড একটি সহজ কৌশল নেয়, যার নাম set of support: প্রতিটি resolution-এ অন্তত একটি clause আসবে ¬α থেকে বা তার বংশধর থেকে। এতে KB-র নিজেদের মধ্যের অপ্রাসঙ্গিক জোড়া বাদ যায়, আর resolution-এর refutation completeness-ও টিকে থাকে (Wos ও সহকর্মীরা ১৯৬৫)। Tautology resolvent (যেমন {S, ¬S}) ফেলে দেওয়া হয়, কারণ তা কখনো খালি clause-এর দিকে এগোয় না।

def neg(l):
    return l[1:] if l.startswith("~") else "~" + l

def resolvents(c1, c2):
    out = []
    for l in c1:
        if neg(l) in c2:
            r = (c1 - {l}) | (c2 - {neg(l)})
            if not any(neg(x) in r for x in r):     # drop tautologies
                out.append(frozenset(r))
    return out

def show(c):
    return "{" + ", ".join(sorted(c)) + "}" if c else "{} EMPTY"

def refute(kb, goal):        # set of support: start from the negated goal
    known = [frozenset(c) for c in kb]
    sos = [frozenset([neg(goal)])]
    print("goal:", goal, "| add", show(sos[0]))
    step = 0
    while sos:
        c = sos.pop(0)
        for d in list(known):
            for r in resolvents(c, d):
                if r in known or r in sos or r == c: continue
                step += 1
                print(f"  {step}. {show(c)} + {show(d)} -> {show(r)}")
                if not r: return "proved"
                sos.append(r)
        known.append(c)
    return "no empty clause: not entailed"

rules = [{"~B", "F"}, {"~F", "~D", "S"}, {"~P", "~S"}]  # R1 R2 R3 in CNF
print(refute(rules + [{"B"}, {"D"}], "~P"))
print(refute(rules + [{"B"}], "S"))                     # D is not known

আসল আউটপুট:

goal: ~P | add {P}
  1. {P} + {~P, ~S} -> {~S}
  2. {~S} + {S, ~D, ~F} -> {~D, ~F}
  3. {~D, ~F} + {F, ~B} -> {~B, ~D}
  4. {~D, ~F} + {D} -> {~F}
  5. {~B, ~D} + {B} -> {~D}
  6. {~B, ~D} + {D} -> {~B}
  7. {~D} + {D} -> {} EMPTY
proved
goal: S | add {~S}
  1. {~S} + {S, ~D, ~F} -> {~D, ~F}
  2. {~D, ~F} + {F, ~B} -> {~B, ~D}
  3. {~B, ~D} + {B} -> {~D}
no empty clause: not entailed

প্রথম প্রমাণে যন্ত্র সাতটি resolvent বানিয়েছে, হাতের পাঁচটির চেয়ে দুটি বেশি, আর শেষ ধাপটিও আলাদা ({¬D} আর {D})। কারণ সে প্রতিটি clause-এর সব সম্ভাব্য জোড়া ক্রম ধরে চেষ্টা করে (breadth-first), “সেরা” জোড়া চেনে না। দ্বিতীয় প্রশ্নে D জানা নেই। তিনটি resolvent-এর পর {¬D} পাওয়া গেল, কিন্তু তার সঙ্গে কাটার মতো {D} নেই; নতুন কিছু তৈরি হয় না, তাই empty clause ছাড়াই থামে। Resolution refutation-complete বলে এই থামা নিজেই একটি উত্তর: KB ⊭ S, অংশ ৪-এর “unknown”-এর সঙ্গে মিলে যায়। বড় KB-তে অবশ্য resolvent-এর সংখ্যা বিস্ফোরিত হতে পারে; ভাবনার প্রশ্ন ২ দেখুন।

৮ · Horn clause আর SAT — DPLL থেকে আধুনিক solver

Horn clause — বাস্তবের নিয়মের সস্তা রূপ

বাজারঘরের clause-গুলো দেখুন: {¬B, F}, {¬F, ¬D, S}, {¬P, ¬S}, {B}, {D}। প্রতিটিতে বড়জোর একটি positive (¬ ছাড়া) literal. এমন clause-কে বলে Horn clause (যুক্তিবিদ Alfred Horn-এর নামে, ১৯৫১)। ঠিক একটি positive literal থাকলে তা definite clause, আর সেটিকে “যদি … তাহলে” রূপে পড়া যায়: {¬F, ¬D, S} মানে (F ∧ D) → S. Positive literal একটিও না থাকলে ({¬P, ¬S}) তা একটি নিষেধ, (P ∧ S) → মিথ্যা।

Horn clause-এর সুবিধা বিশাল। সেখানে entailment-এর জন্য resolution-এর অন্ধ জোড়া-খোঁজার দরকার নেই: জানা তথ্য থেকে শুরু করে যে নিয়মের সব শর্ত পূরণ, তার ফল যোগ করতে থাকুন, যতক্ষণ নতুন কিছু আসে (forward chaining); অথবা প্রশ্ন থেকে পিছিয়ে শর্ত খুঁজুন (backward chaining). দুটিই KB-র আকারের সমানুপাতিক সময়ে চলতে পারে (Horn-SAT-এর linear-time অ্যালগরিদম, Dowling ও Gallier ১৯৮৪)। অংশ ৫-এর চার লাইনের প্রমাণটি আসলে forward chaining. ব্যবসার নিয়ম, expert system আর Prolog এই রূপের ওপর দাঁড়িয়ে; বিস্তারিত পর্ব ৪৭-এ (Rule-based আর expert system). সীমাটি হলো “অথবা”: “ঢাকা অথবা চট্টগ্রামে একই দিনে” ধরনের {S1, S2} clause Horn নয়।

SAT সমস্যা আর DPLL

একটি CNF বাক্য দেওয়া আছে; এমন model আছে কি, যা প্রতিটি clause সত্য করে? এই প্রশ্নের নাম SAT (Boolean satisfiability). Entailment আর SAT একই মুদ্রার দুই পিঠ: KB ⊨ α মানে KB ∧ ¬α-এর SAT উত্তর “না”। ১৯৭১ সালে Stephen Cook দেখান SAT NP-complete (Leonid Levin স্বাধীনভাবে কাছাকাছি সময়ে; তাই Cook–Levin theorem), মানে খারাপতম ক্ষেত্রে দ্রুত পদ্ধতি জানা নেই, আর পাওয়া গেলে তা P বনাম NP প্রশ্নের উত্তর দেবে।

তবু truth table-এর চেয়ে অনেক ভালো করা যায়। DPLL (Davis–Putnam ১৯৬০, Davis–Logemann–Loveland ১৯৬২) হলো CNF-এর ওপর পর্ব ৪৩-এর backtracking, তিনটি কৌশলসহ:

  • Unit propagation: কোনো clause-এ একটিই literal বাকি থাকলে তার মান বাধ্যতামূলক। CSP-র MRV আর forward checking-এর মতো। Horn KB-তে শুধু এটিই forward chaining.
  • Pure literal: কোনো symbol সব জায়গায় একই চিহ্নে এলে (শুধু E, কোথাও ¬E নয়) তাকে সেই মান দিলে কোনো ক্ষতি নেই।
  • Splitting: আর কিছু না চললে একটি symbol বেছে আগে T, ব্যর্থ হলে F দিয়ে চেষ্টা; এখানেই backtracking.

নিচের stepper ছয়টি clause-এর একটি ছোট বাক্যে DPLL চালায়: 1 {A, B}, 2 {¬A, C}, 3 {¬B, C}, 4 {¬C, D}, 5 {¬A, ¬D}, 6 {E, B}। “✓” মানে clause সত্য হয়ে গেছে, তালিকা থেকে বাদ।

1while unit: assign it2pure literal: assign3empty clause: undo4else split: T, then F5none left: SAT
clause আর মান (Watch)
বাকি clause
মান
split
  1. শুরু। ছয়টি clause, পাঁচটি symbol. কোনো clause-এ একটি literal নেই, তাই unit propagation-এর কিছু নেই।
  2. Pure literal E. E আছে শুধু clause 6-এ, তাও ¬ ছাড়া। E = T দিলে clause 6 সত্য, আর কারও ক্ষতি হয় না। বাকি চারটি symbol দুই চিহ্নেই আছে।
  3. Split: A = T. আর কোনো কৌশল নেই, তাই প্রথম symbol A-তে T. Clause 1 সত্য; clause 2 থেকে ¬A বাদ পড়ে {C}, clause 5 থেকে {¬D}: দুটি নতুন unit.
  4. Unit propagation, তারপর সংঘাত। C = T: clause 3 সত্য, clause 4 হয় {D}। এখন {D} আর {¬D} দুটিই unit; D = T দিলে clause 5 খালি। এই শাখায় সমাধান নেই, A = T তুলে নেওয়া হলো।
  5. দ্বিতীয় শাখা: A = F. Clause 2 আর 5 সত্য (¬A আছে)। Clause 1 থেকে A বাদ পড়ে {B}: unit.
  6. Unit-এর ঢেউ। B = T দিলে clause 3 হয় {C}; C = T দিলে clause 4 হয় {D}; D = T. সব clause সত্য: SAT, model A = F, B = T, C = T, D = T, E = T. মাত্র একটি split; truth table-এ ৩২টি সারি।

এই তিনটি কৌশল ডেস্কটপে Python-এ লিখে (প্রায় ৪০ লাইন, শুধু standard library) seed 42-এর এলোমেলো 3-SAT-এ চালানো হলো: প্রতিটি clause-এ তিনটি literal, clause-এর সংখ্যা variable-এর প্রায় ৪.২৬ গুণ। পাশে truth table, যা প্রথম সত্য সারি পেলেই থামে। সত্যিকারের run (Python 3.14.4, একটি ল্যাপটপ, একটি run):

~/bazarghar-logic$ python dpll_run.py
Python 3.14.4
n= 10 m= 43 DPLL sat=False splits=    4 units=    20 pures=   0 time=0.000s | truth table: 1,024 of 2^10=1,024 rows, 0.00s
n= 16 m= 68 DPLL sat=True  splits=    5 units=    10 pures=   0 time=0.000s | truth table: 23,135 of 2^16=65,536 rows, 0.04s
n= 20 m= 85 DPLL sat=True  splits=   11 units=    90 pures=   1 time=0.001s | truth table: 170,168 of 2^20=1,048,576 rows, 0.27s
n= 50 m=213 DPLL sat=False splits=  202 units=  2459 pures=  39 time=0.059s
n=100 m=426 DPLL sat=True  splits= 5521 units=106594 pures=1479 time=5.499s

২০টি variable-এ truth table ১,৭০,১৬৮টি সারি দেখেছে, DPLL মাত্র ১১টি split. ৫০ আর ১০০ variable-এ truth table চালানোই হয়নি (2100). কিন্তু খেয়াল করুন, DPLL-এর split-ও দ্রুত বাড়ছে: ৫০-এ ২০২, ১০০-তে ৫,৫২১ আর সাড়ে পাঁচ সেকেন্ড। এখানেই আধুনিক solver আলাদা।

আধুনিক SAT solver — সত্যিকারের run

১৯৯০-এর দশকের শেষ থেকে CDCL (conflict-driven clause learning) পদ্ধতি এসেছে: প্রতিটি সংঘাতের কারণ বিশ্লেষণ করে একটি নতুন clause “শেখা” হয়, যাতে একই ভুল আর না হয়; সঙ্গে অনেক দূর এক লাফে পিছোনো, নিয়মিত restart আর চতুর variable-বাছাই। GRASP (Marques-Silva ও Sakallah), Chaff (২০০১) আর MiniSat (Eén ও Sörensson) এই ধারার পরিচিত নাম; CaDiCaL আর Kissat (Armin Biere ও সহকর্মীরা) সাম্প্রতিক বছরগুলোর SAT Competition-এ ভালো করেছে বলে প্রকাশিত ফলাফলে দেখা যায়। চিপের নকশা যাচাই, software verification আর কিছু package manager-এর dependency সমাধানে SAT-ভিত্তিক পদ্ধতির ব্যবহার প্রকাশিত, তবে কোন প্রতিষ্ঠান ঠিক কোনটি চালায় তা সাধারণত প্রকাশ্য নয়।

Python থেকে এগুলো ব্যবহারের দুটি পরিচিত প্যাকেজ: pycosat (PicoSAT solver-এর binding) আর python-sat (PySAT, import করার নাম pysat, যাতে MiniSat, Glucose, CaDiCaL-সহ অনেক solver). দুটিই C-তে লেখা extension, ব্রাউজারের Python-এ নেই, তাই ডেস্কটপে চালানো হলো। লেখার সময়ে PyPI-তে সর্বশেষ pycosat 0.6.6 আর python-sat 1.9.dev20; শেষেরটির প্রায় সব release-ই এমন .devN নামে প্রকাশিত হয়, আর এই প্যাকেজে pip install python-sat সেটিই বসিয়েছে। Clause লেখা হয় DIMACS ধাঁচে: প্রতিটি symbol একটি সংখ্যা, ঋণাত্মক মানে ¬. মূল অংশটুকু:

import random, time
import pycosat
from pysat.solvers import Solver
from pysat.examples.genhard import PHP

kb = [[-1, 2], [-2, -3, 4], [-5, -4], [1], [3]]    # B=1 F=2 D=3 S=4 P=5
print("pycosat model:", pycosat.solve(kb))
print("number of models:", sum(1 for _ in pycosat.itersolve(kb)))
print("KB + [-S]:", pycosat.solve(kb + [[-4]]), "-> KB entails S")

def rand3sat(n, m, seed):
    rng = random.Random(seed)
    return [[v * rng.choice((1, -1)) for v in rng.sample(range(1, n + 1), 3)]
            for _ in range(m)]

def run(name, clauses):
    with Solver(name=name, bootstrap_with=clauses) as s:
        t = time.perf_counter()
        ok = s.solve()
        dt = time.perf_counter() - t
        st = s.accum_stats()
    return ok, dt, st.get("decisions"), st.get("conflicts")

for n, ratio in [(1000, 3.0), (200, 4.26), (300, 4.26), (400, 4.26)]:
    cl = rand3sat(n, int(n * ratio), 42)
    for name in ("minisat22", "cadical195"):
        ok, dt, dec, con = run(name, cl)          # printed as aligned columns
for holes in (6, 7, 8, 9, 10):                     # n+1 pigeons, n holes
    cnf = PHP(holes)
    ok, dt, dec, con = run("cadical195", cnf.clauses)

ওপরে শুধু মূল অংশ; পুরো স্ক্রিপ্টে এর বাইরে আছে version আর সারিবদ্ধ করে ছাপানোর কয়েকটি print লাইন। সত্যিকারের run (Python 3.14.4, pycosat 0.6.6, python-sat 1.9.dev20, একটি ল্যাপটপ, একটি run):

~/bazarghar-logic$ python sat_run.py
Python 3.14.4 | pycosat 0.6.6 | python-sat 1.9.dev20

[1] bazarghar KB
pycosat model: [1, 2, 3, 4, -5]
number of models: 1
KB + [-S]: UNSAT -> KB entails S

[2] random 3-SAT (seed 42)
n= 1000 m= 3000 minisat22  sat=True  time=  0.001s decisions=     393 conflicts=      50
n= 1000 m= 3000 cadical195 sat=True  time=  0.002s decisions=     541 conflicts=      73
n=  200 m=  852 minisat22  sat=True  time=  0.011s decisions=    3244 conflicts=    2623
n=  200 m=  852 cadical195 sat=True  time=  0.016s decisions=    2181 conflicts=    1905
n=  300 m= 1278 minisat22  sat=False time= 40.066s decisions= 6277311 conflicts= 5237004
n=  300 m= 1278 cadical195 sat=False time=102.410s decisions= 2925159 conflicts= 1808152
n=  400 m= 1704 minisat22  sat=True  time= 35.026s decisions= 4698318 conflicts= 3811812
n=  400 m= 1704 cadical195 sat=True  time=  2.637s decisions=  233828 conflicts=  120032

[3] pigeonhole PHP
 7 pigeons  6 holes vars= 42 clauses= 133 sat=False time=  0.003s conflicts=    1044
 8 pigeons  7 holes vars= 56 clauses= 204 sat=False time=  0.030s conflicts=    7130
 9 pigeons  8 holes vars= 72 clauses= 297 sat=False time=  0.197s conflicts=   31719
10 pigeons  9 holes vars= 90 clauses= 415 sat=False time=  2.517s conflicts=  267023
11 pigeons 10 holes vars=110 clauses= 561 sat=False time= 25.756s conflicts= 2227032
  • বাজারঘর: model [1, 2, 3, 4, -5] মানে B, F, D, S সত্য, P মিথ্যা, ঠিক অংশ ৪-এর একমাত্র model. KB-তে ¬S যোগ করলে UNSAT, মানে KB ⊨ S.
  • আকার নয়, গঠন কঠিন করে: ১,০০০ variable, অনুপাত ৩.০: হাজার ভাগের এক সেকেন্ড। ৩০০ variable, অনুপাত ৪.২৬: ৪০ সেকেন্ড (MiniSat) আর ১০২ সেকেন্ড (CaDiCaL), উত্তর UNSAT. এলোমেলো 3-SAT-এ অনুপাত প্রায় ৪.২-এর কাছে সমস্যা সবচেয়ে কঠিন হয়, যাকে phase transition বলে (Mitchell, Selman আর Levesque ১৯৯২-এর পরীক্ষা থেকে পরিচিত)।
  • কোন solver ভালো, তা সমস্যায় নির্ভর করে: ৩০০-তে MiniSat দ্রুত, ৪০০-তে CaDiCaL ১৩ গুণ দ্রুত। এক run, এক যন্ত্র, এক seed; সাধারণ নিয়ম নয়।
  • Pigeonhole: “n+1টি পায়রা n-টি খোপে, প্রতি খোপে একটি” সবসময় অসম্ভব, মানুষ এক লাইনে বোঝেন। অথচ প্রতি খোপ বাড়ালে সময় প্রায় দশ গুণ: ১১টি পায়রায় ২৬ সেকেন্ড। ১৯৮৫ সালে Armin Haken প্রমাণ করেন, resolution দিয়ে এর প্রতিটি প্রমাণ exponential আকারের; CDCL-এর প্রমাণও resolution-এর একটি রূপ, তাই এই দেয়াল তারও।

ব্রাউজারেও একটি পরীক্ষা চালানো যায়: Pyodide-এ sympy প্যাকেজ আছে, যার sympy.logic CNF বানায়, satisfiable খোঁজে আর বাক্য সরল করে। এর SAT অংশ Python-এ লেখা DPLL-ধাঁচের, বড় সমস্যার জন্য নয়, কিন্তু হাতের কাজ যাচাইয়ে চমৎকার। প্রথমবার sympy নামতে কয়েক সেকেন্ড লাগতে পারে।

import sympy
from sympy import symbols
from sympy.logic.boolalg import Implies, Equivalent, to_cnf, simplify_logic
from sympy.logic.inference import satisfiable

B, F, D, S, P = symbols("B F D S P")
print("sympy", sympy.__version__)
print("CNF of S <-> (F & D):", to_cnf(Equivalent(S, F & D)))

kb = Implies(B, F) & Implies(F & D, S) & Implies(P, ~S) & B & D
m = satisfiable(kb)
print("a model of KB:", {str(k): int(v) for k, v in sorted(m.items(), key=str)})
print("KB & ~S satisfiable?", satisfiable(kb & ~S))   # False -> KB entails S
print("KB & P  satisfiable?", satisfiable(kb & P))    # False -> KB entails ~P

rules = Implies(B, F) & Implies(F & D, S) & Implies(P, ~S)
print("models of rules alone:", len(list(satisfiable(rules, all_models=True))))
print("simplified rules:", simplify_logic(rules, form="cnf"))
print("De Morgan valid?", not satisfiable(~Equivalent(~(B & F), ~B | ~F)))

আসল আউটপুট:

sympy 1.14.0
CNF of S <-> (F & D): (D | ~S) & (F | ~S) & (S | ~D | ~F)
a model of KB: {'B': 1, 'D': 1, 'F': 1, 'P': 0, 'S': 1}
KB & ~S satisfiable? False
KB & P  satisfiable? False
models of rules alone: 14
simplified rules: (F | ~B) & (~P | ~S) & (S | ~D | ~F)
De Morgan valid? True

sympy-তে ∧, ∨, ¬ লেখা হয় &, |, ~ দিয়ে। to_cnf অংশ ৬-এর stepper-এর তিনটি clause-ই দিয়েছে। satisfiable একটি model দেয়, অথবা False. “KB & ~S” আর “KB & P” দুটিই False, মানে refutation-এর ভাষায় KB ⊨ S আর KB ⊨ ¬P. তথ্য ছাড়া শুধু তিনটি নিয়ম ৩২টির ১৪টি model-এ সত্য; তথ্য যোগ হতে হতে তা ১-এ নামে। Valid যাচাইয়ের কৌশলও একই: α valid ঠিক তখনই, যখন ¬α unsatisfiable. ব্রাউজারের sympy-র version যন্ত্রভেদে আলাদা হতে পারে; প্রথম লাইন দেখুন।

আরও গভীরে: CDCL-এর শেখা clause-গুলো আসলে resolution-এর ধাপ, তাই একটি UNSAT উত্তরের সঙ্গে solver একটি প্রমাণ-ফাইল (যেমন DRAT ধাঁচে) লিখতে পারে, যা আলাদা ছোট একটি checker যাচাই করে; SAT Competition-এ UNSAT উত্তরের জন্য এমন প্রমাণ চাওয়া হয়। ভেতরের কৌশলের মধ্যে আছে two-watched-literal দিয়ে দ্রুত unit propagation আর VSIDS ধাঁচের variable-বাছাই। SAT-এর সঙ্গে CSP-র সম্পর্ক ঘনিষ্ঠ: CSP-কে SAT-এ লেখা যায় (প্রতিটি “variable = মান” একটি symbol), আর পর্ব ৪৩-এর CP-SAT solver উল্টো দিক থেকে CP-র model-এ SAT-এর clause learning চালায়।

৯ · Propositional logic-এর সীমা — আর symbolic AI কোথায় দাঁড়ায়

বাজারঘরের পাঁচটি symbol একটি অর্ডারের কথা বলে। দোকানে দিনে হাজারো অর্ডার। “প্রতিটি অর্ডার ৳১,০০০-এর বেশি হলে তার delivery ফ্রি” লিখতে propositional logic-এ প্রতিটি অর্ডারের জন্য আলাদা symbol আর আলাদা নিয়ম লাগবে: B1 → F1, B2 → F2, … এই ভাষায় বস্তু (অর্ডার, কাস্টমার), তাদের সম্পর্ক (“এই কাস্টমারের অর্ডার”) আর “প্রতিটি” বা “অন্তত একটি” বলার উপায় নেই। এগুলো আনে first-order logic, যার ∀ (সব) আর ∃ (অন্তত একটি) চিহ্ন পর্ব ৪৬-এর (First-order logic — বস্তু, সম্পর্ক আর quantifier) বিষয়।

আরও তিনটি সীমা মাথায় রাখুন:

  • অনিশ্চয়তা নেই: প্রতিটি কথা পুরো সত্য বা পুরো মিথ্যা। “ঢাকার ঠিকানায় সাধারণত ৯০% অর্ডার একই দিনে পৌঁছায়” লেখার জায়গা নেই। সম্ভাবনা দিয়ে যুক্তি মডিউল ৯-এ, পর্ব ৫০ (Bayes-এর উপপাদ্য) থেকে।
  • Symbol-এর মানে বাইরে থেকে আসে: “D = ঠিকানা ঢাকায়” কে ঠিক করবেন? কাস্টমারের লেখা “Mirpur 10, Dhaka” থেকে D বের করা যুক্তির কাজ নয়, পড়া আর বোঝার কাজ, যেখানে machine learning প্রায়ই দরকার।
  • জ্ঞান লেখার খরচ আর ভঙ্গুরতা: হাজারো নিয়ম হাতে লেখা, হালনাগাদ রাখা আর পরস্পরবিরোধী নিয়ম ধরা কঠিন; এর নাম knowledge acquisition bottleneck, আর নিয়মের বাইরের ঘটনায় হঠাৎ ভেঙে পড়াকে বলে brittleness.

তবু propositional logic আর SAT পুরোনো জিনিস নয়, machine learning-এর প্রতিদ্বন্দ্বীও নয়। এর শক্তি যেখানে: নিশ্চয়তা (UNSAT মানে প্রমাণসহ “অসম্ভব”), ব্যাখ্যা (প্রতিটি সিদ্ধান্তের পেছনে কোন নিয়ম, তা দেখানো যায়; explainability), আর ডেটা ছাড়া কাজ (নতুন নিয়ম এলেই চলে, হাজারো উদাহরণ লাগে না)। তাই চিপ যাচাই, নিরাপত্তা-নীতির পরীক্ষা, সময়সূচি আর AI মডেলের চারপাশে নিয়মের পাহারা (guardrail) এখনো যুক্তির ভাষায় লেখা হয়। দুটি পথ মেলানোর গবেষণাকে বলে neuro-symbolic AI; কোন মিশ্রণ শেষ পর্যন্ত কাজের, তা নিয়ে গবেষকদের মতভেদ আছে।

১০ · AI-এর যুগে — AI সহকারী দিয়ে যুক্তি আর নিয়ম যাচাই

AI সহকারীকে “এই নিয়মগুলো থেকে কি X প্রমাণ হয়?” জিজ্ঞেস করলে সে সাবলীল ভাষায় একটি “প্রমাণ” লিখে দেবে। ভাষার মডেল (পর্ব ৭০-এ বিস্তারিত) পরের শব্দের সম্ভাব্য অনুমান থেকে লেখে; লম্বা যুক্তির শৃঙ্খলে একটি ভুল ধাপ সুন্দর বাক্যের আড়ালে লুকিয়ে থাকতে পারে। নিরাপদ ভাগাভাগি: AI সহকারী নিয়মকে symbol আর clause-এ লিখতে সাহায্য করুক, truth table বা solver যাচাই করুক, আর আপনি ছোট একটি ক্ষেত্রে হাতে মিলিয়ে নিন। এ ধরনের কাজে AI সহকারীরা প্রায়ই যা করে:

  • Converse গুলিয়ে ফেলা: “B → F, তাই F → B” বা “ফ্রি পেয়েছেন, তাই অর্ডার বড়” ধরনের affirming the consequent, আত্মবিশ্বাসের সঙ্গে।
  • → ভুল কোডে লেখা: a and b দিয়ে implication, বা satisfiable আর entailment গুলিয়ে any-এর জায়গায় all (বা উল্টো)।
  • বানানো বা পুরোনো API: sympy-তে অস্তিত্বহীন function, pysat-এ ভুল solver-নাম, বা pycosat আর python-sat-এর clause-ধাঁচ মিশিয়ে ফেলা।
  • স্ববিরোধী KB না ধরা: KB নিজেই unsatisfiable হলে সব কথাই “entailed” হয়ে যায়; প্রথমে KB satisfiable কি না, তা না দেখে উত্তর দেওয়া।
  • “প্রমাণিত” বলে যাচাইহীন দাবি: solver-এর status না দেখে, বা সময় ফুরিয়ে যাওয়া run-কে UNSAT ধরে নেওয়া।

প্রম্পটে symbol-এর মানে, প্রতিটি নিয়ম আর যাচাইয়ের দাবি নিজেই লিখে দিন। নিচের প্রম্পটে কোনো আসল কাস্টমারের তথ্য নেই।

আমি Python 3.14 আর sympy 1.14 ব্যবহার করছি। একটি দোকানের delivery-নিয়ম propositional logic-এ লিখতে সাহায্য চাই। বানানো symbol: B = অর্ডার ১০০০ টাকার বেশি, F = delivery ফ্রি, D = ঠিকানা ঢাকায়, S = একই দিনে delivery, P = pre-order. নিয়ম: B হলে F; F আর D দুটিই হলে S; P হলে S নয়। ১. প্রতিটি নিয়ম symbol-এ লেখো, তারপর CNF clause-এ; প্রতিটি ধাপে কোন equivalence খাটালে তা বলো। ২. B আর D জানা থাকলে P মিথ্যা কি না, resolution refutation দিয়ে প্রতিটি resolvent দেখিয়ে প্রমাণ করো। ৩. একই প্রশ্ন sympy-র satisfiable দিয়ে যাচাইয়ের ছোট কোড দাও; আগে দেখাও যে KB নিজে satisfiable. ৪. শুধু sympy 1.14-এর অফিসিয়াল docs-এ আছে এমন function ব্যবহার করো; নিশ্চিত না হলে বানিয়ে না লিখে সরাসরি বলো। ৫. কোথাও converse বা affirming the consequent ব্যবহার করলে তা নিজেই চিহ্নিত করো।

AI-এর লেখা যুক্তি বা logic-কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে: বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে।

  • A → B কখন মিথ্যা? শুধু A সত্য আর B মিথ্যা হলে; A → B ≡ ¬A ∨ B.
  • Valid, satisfiable আর unsatisfiable-এর পার্থক্য? সব model-এ সত্য; অন্তত একটিতে সত্য; কোনোটিতেই সত্য নয়। α valid ঠিক তখনই, যখন ¬α unsatisfiable.
  • KB ⊨ α কীভাবে resolution দিয়ে দেখাবেন? KB ∧ ¬α-কে CNF-এ এনে resolve করে empty clause পাওয়া।
  • Sound আর complete? Sound: যা derive হয় তা entailed; complete: যা entailed তা derive করা যায়।
  • Horn clause কেন গুরুত্বপূর্ণ? বড়জোর একটি positive literal; forward/backward chaining দিয়ে KB-র আকারের রৈখিক সময়ে entailment.

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১“২ + ২ = ৫ হলে চাঁদ পনিরের তৈরি” যুক্তিবিদ্যায় সত্য বাক্য। তাহলে যুক্তিবিদেরা কেন → এভাবে সংজ্ঞায়িত করলেন, আর দৈনন্দিন “যদি”-র সঙ্গে এর বিরোধ কোথায়?

Material implication-এর truth table (premise মিথ্যা হলে সত্য) উনিশ শতকের শেষ আর বিশ শতকের শুরুর গাণিতিক যুক্তিবিদ্যায় (Frege, পরে Russell ও Whitehead-এর Principia Mathematica) প্রতিষ্ঠিত হয়। Truth table নিজে যে আকারে আমরা দেখি, তা ১৯২১ সালের দিকে Ludwig Wittgenstein আর Emil Post স্বাধীনভাবে জনপ্রিয় করেন বলে সাধারণত বলা হয়, যদিও আগের লেখকদের কাজে এর বীজ ছিল। প্রাচীন গ্রিসেও Philo of Megara এমন সংজ্ঞার পক্ষে ছিলেন বলে বর্ণনা পাওয়া যায়।

কেন এই সংজ্ঞা? প্রথম কারণ, connective-এর মান শুধু অংশগুলোর মান থেকে আসতে হবে (truth-functional). চারটি সারির দুটি প্রায় সবাই মেনে নেন: T → T সত্য, T → F মিথ্যা। F-premise-এর দুই সারিতেই মিথ্যা বসালে → হয়ে যায় ∧; শুধু F → T সারিতে মিথ্যা বসালে ↔; শুধু F → F সারিতে মিথ্যা বসালে ফল কেবল B-এর মান, A-র কোনো ভূমিকাই থাকে না। বাকি একমাত্র বিকল্প দুটিতেই সত্য। দ্বিতীয় কারণ গণিতের প্রয়োজন: “প্রতিটি n-এর জন্য, n ৪ দিয়ে বিভাজ্য হলে n জোড়” সত্য হতে হলে n = ৩-এর জন্যও implication-টি সত্য হতে হবে, যদিও ৩ ৪ দিয়ে বিভাজ্য নয়। বাজারঘরের প্রতিশ্রুতির উদাহরণ এটাই: ছোট অর্ডারে প্রতিশ্রুতি ভাঙে না।

বিরোধ কোথায়? দৈনন্দিন “যদি”-তে আমরা প্রায়ই কারণ-ফল, প্রাসঙ্গিকতা বা সময়ের ক্রম বুঝি। দার্শনিক Paul Grice যুক্তি দেন, অনেক অদ্ভুততা আসলে কথোপকথনের নিয়ম থেকে: কেউ জানেন premise মিথ্যা, তবু “যদি” বললে শ্রোতা বিভ্রান্ত হন, যদিও বাক্যটি যুক্তিতে সত্য। অন্যরা relevance logic-এর মতো বিকল্প যুক্তিব্যবস্থা বানিয়েছেন, যেখানে premise আর conclusion-এর মধ্যে বিষয়গত সম্পর্ক লাগে। এ নিয়ে দার্শনিকদের বিতর্ক আজও চলছে।

AI-এর জন্য শিক্ষাটি বাস্তব: নিয়মের খাতা থেকে symbol-এ অনুবাদের সময় মানুষের “যদি” প্রায়ই ↔ বোঝায় (“১,০০০-এর বেশি হলে ফ্রি” শুনে অনেকে ধরে নেন ছোট অর্ডারে কখনো ফ্রি নয়)। অংশ ৬-এর কড়া নিয়ম S ↔ (F ∧ D) ঠিক এই ধরনের আলাদা দাবি। কোন অর্থ উদ্দেশ্য, তা নিয়ম-লেখকের কাছে জিজ্ঞেস করাই ঠিক।

মূল উপলব্ধি: → একটি প্রতিশ্রুতি, যা শুধু premise সত্য আর conclusion মিথ্যা হলে ভাঙে; দৈনন্দিন “যদি”-র অর্থ এর চেয়ে বেশি বা ভিন্ন হতে পারে, তাই অনুবাদের সময় সতর্কতা লাগে।

প্র ০২Robinson-এর resolution ১৯৬৫ সালে যন্ত্রে প্রমাণের বড় আশা জাগিয়েছিল। সেই আশা কোথায় ঠেকল, আর pigeonhole সমস্যা এতে কী দেখায়?

১৯৬৫ সালের প্রবন্ধে J. A. Robinson দেখান, একটিমাত্র নিয়ম (resolution, first-order logic-এ unification-সহ) refutation-complete: যা entailed, তার প্রমাণ শেষ পর্যন্ত পাওয়া যাবেই। এর আগের পদ্ধতিগুলো অনেক নিয়ম আর অনেক অপ্রয়োজনীয় বিকল্প নিয়ে কাজ করত। ১৯৬০-এর দশকের শেষে অনেকে আশা করেছিলেন, যন্ত্র গণিতবিদের মতো উপপাদ্য প্রমাণ করবে, আর knowledge-based প্রশ্নোত্তর (যেমন Cordell Green-এর ১৯৬৯ সালের QA3) সাধারণ বুদ্ধির ভিত্তি হবে।

বাস্তবে দেখা গেল, completeness মানে দ্রুততা নয়। অংশ ৭-এর ছোট KB-তেই যন্ত্র হাতের চেয়ে বেশি resolvent বানিয়েছে; শত শত clause-এ resolvent-এর সংখ্যা বিস্ফোরিত হয়, আর প্রাসঙ্গিক জোড়া খুঁজে পাওয়া কঠিন। Set of support, unit preference, subsumption (বড় clause-এর জায়গায় তার ছোট অংশ রাখা) আর ordering-এর মতো কৌশল এসেছে, কিন্তু সাধারণ ক্ষেত্রে বিস্ফোরণ থামেনি। এই অভিজ্ঞতা ১৯৭০-এর দশকে AI-এ “combinatorial explosion” নিয়ে সমালোচনার অংশ ছিল, যেমন ১৯৭৩ সালের Lighthill report-এ এমন সমালোচনা উঠেছিল বলে উল্লেখ পাওয়া যায়।

Pigeonhole সমস্যা এর সবচেয়ে পরিষ্কার উদাহরণ। “১১টি পায়রা, ১০টি খোপ, প্রতি খোপে একটি” অসম্ভব, মানুষ গুনে বোঝেন। কিন্তু clause-এ লিখলে (প্রতিটি পায়রা কোনো খোপে; কোনো খোপে দুটি নয়) এটি এমন একটি সূত্র, যার প্রতিটি resolution-প্রমাণ exponential আকারের: ১৯৮৫ সালে Armin Haken এটি প্রমাণ করেন। অংশ ৮-এর সত্যিকারের run-এ CaDiCaL প্রতিটি খোপ বাড়ায় প্রায় দশ গুণ বেশি সময় নিয়েছে, ঠিক এই তত্ত্বের মতো। মানুষের “গোনা”-র যুক্তি (counting) resolution-এর ভাষায় সংক্ষেপে বলা যায় না; শক্তিশালী proof system (যেমন cutting planes) তা পারে।

তবু resolution হারিয়ে যায়নি। Prolog-এ Horn clause-এর জন্য তার একটি সরল রূপ (SLD resolution) কার্যকর ভাষা হয়ে উঠেছে; আধুনিক CDCL solver-এর শেখা clause-ও resolution থেকে আসে; আর Vampire বা E-এর মতো first-order theorem prover এখনো resolution-ভিত্তিক।

মূল উপলব্ধি: একটি complete নিয়ম থাকা আর দ্রুত প্রমাণ পাওয়া আলাদা কথা; কিছু সহজ সত্যের প্রমাণ resolution-এর ভাষায় অবধারিতভাবে বিশাল, আর সেই সীমা জানা ভালো engineering-এর অংশ।

প্র ০৩SAT NP-complete, মানে খারাপতম ক্ষেত্রে কঠিন। তবু আধুনিক solver লাখো variable-এর শিল্প-সমস্যা মেটায় কীভাবে? এই বিরোধের মীমাংসা কী?

Stephen Cook-এর ১৯৭১ সালের প্রবন্ধ দেখায়, NP-র প্রতিটি সমস্যা SAT-এ রূপান্তর করা যায়, তাই SAT দ্রুত সমাধান মানে সব NP সমস্যার দ্রুত সমাধান। পরের বছর Richard Karp একুশটি পরিচিত সমস্যাকে একই শ্রেণিতে দেখান। এর অর্থ শুধু খারাপতম ক্ষেত্রের কথা: এমন কিছু উদাহরণ থাকবেই (যদি P ≠ NP), যেখানে যেকোনো পদ্ধতি exponential সময় নেবে। কিন্তু খারাপতম ক্ষেত্র আর সাধারণ ক্ষেত্র আলাদা।

শিল্পের SAT সমস্যার গঠন আছে: চিপের circuit-এ variable-গুলো স্থানীয়ভাবে যুক্ত, অনেক clause দুই literal-এর, আর একটি ছোট “backdoor” সেট ঠিক করলেই বাকিটা unit propagation-এ গড়িয়ে যায় বলে গবেষকেরা পর্যবেক্ষণ করেছেন। CDCL ঠিক এই গঠন কাজে লাগায়: সংঘাত থেকে শেখা clause অনেক শাখা একসঙ্গে কাটে, আর VSIDS ধাঁচের বাছাই সাম্প্রতিক সংঘাতে জড়িত variable-কে আগে নেয়। অংশ ৮-এর সত্যিকারের run-এ ১,০০০ variable-এর অনুপাত ৩.০-এর সমস্যা হাজার ভাগের এক সেকেন্ডে মিটেছে।

অন্য দিকে, গঠনহীন এলোমেলো 3-SAT অনুপাত ৪.২-এর কাছে সত্যিই কঠিন: একই run-এ মাত্র ৩০০ variable-এ ৪০ থেকে ১০২ সেকেন্ড। ১৯৯২ সালে Mitchell, Selman আর Levesque দেখান যে অনুপাত কম হলে প্রায় সব সমস্যা satisfiable আর সহজ, বেশি হলে প্রায় সব unsatisfiable আর তুলনায় সহজ, আর মাঝের সীমারেখায় কঠিনতম। এই “phase transition” পদার্থবিদ্যার ধারণার সঙ্গে মিল রেখে statistical physics-এর গবেষকদেরও আকর্ষণ করেছে।

তাই মীমাংসা হলো: complexity theory বলে কী সব সময় সম্ভব নয়, আর engineering দেখায় বাস্তবের কোন অংশে কী সম্ভব। একটি নতুন সমস্যা solver-এ কতটা চলবে, তা আগে থেকে বলা কঠিন; ছোট আকারে পরখ করে বাড়ানোই নিরাপদ।

মূল উপলব্ধি: NP-complete মানে খারাপতম ক্ষেত্র কঠিন, প্রতিটি ক্ষেত্র নয়; বাস্তবের গঠন আর CDCL-এর শেখার কৌশল মিলে SAT-কে একটি দৈনন্দিন engineering হাতিয়ার বানিয়েছে, যদিও কঠিন ক্ষেত্র থেকেই যায়।

প্র ০৪২০১৬ সালে একটি SAT solver এমন একটি গাণিতিক প্রশ্নের উত্তর দেয়, যার প্রমাণ-ফাইল প্রায় ২০০ টেরাবাইট। এমন প্রমাণকে কি বিশ্বাস করা যায়?

প্রশ্নটির নাম Boolean Pythagorean triples problem: ১, ২, ৩, … সংখ্যাগুলোকে লাল আর নীল দুই রঙে এমনভাবে রং করা যায় কি, যাতে কোনো a² + b² = c² ত্রয়ীর (যেমন ৩, ৪, ৫) তিনটিই এক রঙের না হয়? Marijn Heule, Oliver Kullmann আর Victor Marek ২০১৬ সালে দেখান, ৭,৮২৪ পর্যন্ত এমন রং করা যায়, কিন্তু ৭,৮২৫ পর্যন্ত অসম্ভব। প্রতিটি সংখ্যার রং একটি Boolean symbol, প্রতিটি ত্রয়ীর জন্য দুটি clause (“তিনটিই লাল নয়”, “তিনটিই নীল নয়”)। প্রকাশিত বিবরণ অনুযায়ী সমস্যাটি ভাগ করে একটি supercomputer-এ প্রায় দুই দিন চালানো হয়, আর UNSAT-এর প্রমাণ-ফাইল ছিল প্রায় ২০০ টেরাবাইট; সংকুচিত রূপ অনেক ছোট।

বিশ্বাসের প্রশ্নে মূল কৌশল অংশ ৫-এর sound আর অংশ ৮-এর “আরও গভীরে”: solver শুধু “UNSAT” বলে না, প্রতিটি শেখা clause-এর একটি তালিকা (DRAT ধাঁচের প্রমাণ) লেখে। আলাদা একটি checker প্রতিটি clause নিয়ে দেখে, সেটি আগের clause থেকে unit propagation-এ বেরোয় কি না। Checker-টি solver-এর চেয়ে অনেক ছোট আর সরল, তাই তার ভুলের সম্ভাবনা কম; পরে এমন checker-এর যন্ত্র-যাচাই করা সংস্করণও (যেমন proof assistant-এ লেখা) বানানো হয়েছে। তাই বিশ্বাসের ভার বিশাল solver থেকে সরে ছোট checker-এ আসে।

তবু প্রশ্ন থাকে। Clause-গুলো কি প্রশ্নটিকে ঠিকঠাক লিখেছে (encoding-এর ভুল)? সমস্যা ভাগ করার যুক্তি কি নিখুঁত? একজন মানুষ কখনো ২০০ টেরাবাইট পড়বেন না, তাই প্রমাণ থেকে “কেন” সত্য, তার কোনো অন্তর্দৃষ্টি মেলে না। অনেক গণিতবিদ এ কারণে এমন প্রমাণকে মূল্যবান কিন্তু অসম্পূর্ণ সন্তোষজনক মনে করেন, যেমন ১৯৭৬ সালের চার রঙের উপপাদ্যের কম্পিউটার-প্রমাণ নিয়েও বিতর্ক হয়েছিল।

AI-এর জন্য শিক্ষা: যন্ত্রের উত্তরের পাশে যাচাইযোগ্য সাক্ষ্য (একটি model, বা একটি প্রমাণ) চাওয়া ভালো অভ্যাস। SAT উত্তরের সাক্ষ্য একটি model, যা যেকেউ সব clause-এ বসিয়ে মিনিটে দেখতে পারেন।

মূল উপলব্ধি: বিশাল প্রমাণে বিশ্বাস আসে ছোট, স্বাধীন checker থেকে, যা প্রতিটি ধাপ sound কি না দেখে; কিন্তু encoding ঠিক কি না আর “কেন” সত্য, এই দুই প্রশ্ন মানুষের ভাবনা দাবি করে।

প্র ০৫Large language model কি logical inference করতে পারে? Symbolic logic আর ভাষার মডেলকে একসঙ্গে কীভাবে কাজে লাগানো যায়?

Propositional logic-এর inference একটি যান্ত্রিক, যাচাইযোগ্য প্রক্রিয়া: model checking বা resolution ঠিকমতো লেখা থাকলে উত্তর নিশ্চিত। Large language model কাজ করে ভিন্নভাবে: বিপুল লেখা থেকে শেখা প্যাটার্নে পরের token অনুমান। এ ধরনের মডেল সহজ যুক্তির প্রশ্নে প্রায়ই ঠিক উত্তর দেয়, কিন্তু বিভিন্ন গবেষণায় রিপোর্ট হয়েছে যে প্রশ্নের শব্দ বা নাম বদলালে, অপ্রাসঙ্গিক তথ্য যোগ করলে, বা যুক্তির শৃঙ্খল লম্বা হলে ভুল বাড়তে পারে। নতুন মডেল আর “ধাপে ধাপে ভেবে উত্তর” কৌশলে ফল দ্রুত বদলাচ্ছে, তাই নির্দিষ্ট কোনো সংখ্যা দেওয়া নিরাপদ নয়। মডেলের ভেতরে “যুক্তি” কতটা ঘটে, তা নিয়ে গবেষকদের মধ্যে সত্যিকারের মতভেদ আছে।

একটি পরিচিত সমস্যা হলো affirming the consequent ধরনের ভুল: প্রশিক্ষণের লেখায় “ফ্রি পেয়েছেন, তাহলে নিশ্চয়ই বড় অর্ডার” ধরনের দৈনন্দিন অনুমান প্রচুর, যা যুক্তিবিদ্যায় ভুল কিন্তু ভাষায় স্বাভাবিক। আরেকটি সমস্যা যাচাই: একটি সাবলীল প্রমাণের ভেতরের একটি ভুল ধাপ পড়ে ধরা কঠিন।

তাই অনেকে একটি মিশ্র পথ দেখেন। ভাষার মডেল প্রাকৃতিক ভাষার নিয়মকে symbol আর clause-এ অনুবাদ করে (যেখানে সে শক্তিশালী), solver বা model checker entailment যাচাই করে (যেখানে নিশ্চয়তা দরকার), আর ব্যর্থ হলে error দেখিয়ে আবার চেষ্টা। কিছু গবেষণা-প্রকল্পে ভাষার মডেলকে SAT বা SMT solver বা theorem prover-এর সঙ্গে জোড়া হয়েছে; Lean-এর মতো proof assistant-এ মডেলের লেখা প্রমাণ যন্ত্রে যাচাই করা হয়। এর দুর্বল জায়গা অনুবাদ: ভুল symbol-এ লেখা নিয়মকে solver নিখুঁতভাবে “প্রমাণ” করবে, কিন্তু প্রমাণটি ভুল প্রশ্নের।

বাজারঘরের মতো দোকানে বাস্তব পরামর্শ: নীতিমালা-সংক্রান্ত সিদ্ধান্তে (ফেরত, ছাড়, delivery) ভাষার মডেল কাস্টমারের কথা বুঝে symbol বের করুক, কিন্তু সিদ্ধান্তটি স্পষ্ট নিয়মের ইঞ্জিন দিক, যার প্রতিটি সিদ্ধান্তের পেছনের নিয়ম দেখানো যায়।

মূল উপলব্ধি: ভাষার মডেল অনুবাদ আর আলাপে, symbolic logic নিশ্চয়তা আর যাচাইয়ে; দুটির মাঝে স্বাধীন যাচাই রাখলে দুই জগতের শক্তি একসঙ্গে পাওয়া যায়, আর দুর্বলতা ধরা পড়ে।

অনুশীলন

প্রথমটি হাতে হিসাবের, দ্বিতীয়টি আউটপুট অনুমানের, তৃতীয়টি নকশা ও সমালোচনার, শেষটি কোড লেখার।

  1. হাতে হিসাব: (ক) চারটি symbol-এর একটি বাক্যের truth table-এ কয়টি সারি? (খ) ¬(A → B) ∨ C-কে CNF-এ আনুন, প্রতিটি ধাপের নিয়ম বলে। (গ) KB = {A → B, B → C, A}. Resolution refutation দিয়ে KB ⊨ C প্রমাণ করুন, প্রতিটি resolvent লিখে।

    (ক) 24 = ১৬টি।

    (খ) → বাদ: ¬(¬A ∨ B) ∨ C. De Morgan আর ¬¬: (A ∧ ¬B) ∨ C. Distribution: (A ∨ C) ∧ (¬B ∨ C). দুটি clause: {A, C} আর {¬B, C}।

    (গ) Clause: {¬A, B}, {¬B, C}, {A}; ¬C যোগ: {¬C}। {¬C} + {¬B, C} → {¬B}; {¬B} + {¬A, B} → {¬A}; {¬A} + {A} → { }। Empty clause, তাই KB ⊨ C, তিনটি resolution-এ।

  2. আউটপুট অনুমান করুন: চালানোর আগে তিন লাইনের আউটপুট লিখুন, তারপর যেকোনো Python ব্লকে বসিয়ে মিলিয়ে দেখুন।
    from itertools import product
    imp = lambda a, b: (not a) or b
    f = lambda p, q: imp(p, q) and imp(q, p)
    rows = list(product([True, False], repeat=2))
    print([f(p, q) for p, q in rows])
    print(sum(imp(p, q) for p, q in rows))
    print(all(imp(p and q, p) for p, q in rows), all(imp(p or q, p) for p, q in rows))
    [True, False, False, True]
    3
    True False

    f হলো দুই দিকের implication, মানে ↔: মান এক হলে সত্য। দ্বিতীয় লাইনে → চারটির তিনটি সারিতে সত্য, আর Python-এ True যোগ হয় ১ হিসেবে। তৃতীয় লাইনে (p ∧ q) → p valid, এটিই and-elimination-এর sound হওয়ার কারণ; (p ∨ q) → p valid নয়, p = F, q = T সারিতে মিথ্যা (লেখকের যন্ত্রে Python 3.14 দিয়ে মেলানো)।

  3. নকশা ও সমালোচনা: একটি AI সহকারী বাজারঘরের জন্য নিচের entailment-চেকার লিখেছে, আর বলেছে “পরীক্ষা করেছি, কাস্টমার ফ্রি delivery পেলে KB থেকে প্রমাণ হয় অর্ডার ৳১,০০০-এর বেশি”। অন্তত চারটি সমস্যা চিহ্নিত করুন আর ভালো নকশা বলুন।
    imp = lambda a, b: a and b                     # a -> b
    kb = lambda m: imp(m["B"], m["F"]) and m["F"]  # rule + fact F
    def entails(kb, alpha, models):
        return any(kb(m) and alpha(m) for m in models)
    print(entails(kb, lambda m: m["B"], models))   # True -> "proved"
    • Implication ভুল: a and b আসলে ∧; → হলো (not a) or b. এই ভুলে B → F নিয়মটি নিজেই “B সত্য” দাবি করে বসে, তাই উত্তর ভুল।
    • Entailment নয়, satisfiability: any(kb and alpha) শুধু দেখে KB ∧ α একসঙ্গে সত্য হতে পারে কি না। Entailment চায় প্রতিটি KB-model-এ α: all(alpha(m) for m in models if kb(m)).
    • যুক্তির ভুল: সঠিক → দিলে KB = {B → F, F}-এ B = F, F = T একটি model, তাই KB ⊭ B. দাবিটি affirming the consequent; ছোট অর্ডারেও অফারে ফ্রি হতে পারে।
    • যাচাই নেই: জানা-উত্তরের ক্ষেত্রে (যেমন {B → F, B} ⊨ F সত্য, {B → F} ⊨ B মিথ্যা) পরীক্ষা নেই, KB satisfiable কি না দেখা নেই, আর models কোথা থেকে এল বলা নেই।

    ভালো নকশা: imp ঠিক করা; অংশ ৪-এর মতো entails আর তিন-উত্তরের ask; KB unsatisfiable হলে সতর্কবার্তা; কয়েকটি হাতে-জানা উত্তরের assert দিয়ে পরীক্ষা; আর sympy-র satisfiable(kb & ~alpha) দিয়ে স্বাধীন মিলিয়ে দেখা।

  4. কোড লিখুন: clause-এর তালিকা নিয়ে শুধু unit propagation চালানোর একটি function লিখুন (literal হলো "B" বা "~B" ধরনের string), যা ক্রমে বের হওয়া literal-গুলো ফেরত দেয়, আর empty clause হলে "CONFLICT" যোগ করে। বাজারঘরের KB-তে চালান, তারপর {P} যোগ করে আবার। ফলটি forward chaining-এর সঙ্গে কীভাবে মেলে?
    def unit_propagate(clauses):
        clauses = [set(c) for c in clauses]
        neg = lambda l: l[1:] if l.startswith("~") else "~" + l
        derived = []
        while True:
            unit = next((next(iter(c)) for c in clauses if len(c) == 1), None)
            if unit is None:
                return derived, clauses
            derived.append(unit)
            new = []
            for c in clauses:
                if unit in c:
                    continue                      # clause satisfied
                c = c - {neg(unit)}               # literal now false
                if not c:
                    return derived + ["CONFLICT"], []
                new.append(c)
            clauses = new
    
    kb = [{"~B", "F"}, {"~F", "~D", "S"}, {"~P", "~S"}, {"B"}, {"D"}]
    print(unit_propagate(kb))
    print(unit_propagate(kb + [{"P"}]))

    লেখকের যন্ত্রে সত্যিকারের run (Python 3.14.4):

    (['B', 'F', 'D', 'S', '~P'], [])
    (['B', 'F', 'D', 'S', '~P', 'CONFLICT'], [])

    প্রথম লাইনে বের হলো B, F, D, S, ¬P, আর কোনো clause বাকি নেই: এটি ঠিক forward chaining-এর ক্রম (B থেকে F, F আর D থেকে S, S থেকে ¬P). Horn KB-তে unit propagation আর forward chaining একই কাজ। {P} যোগ করলে ¬P আর P-র সংঘাতে empty clause: refutation-এ যা পাঁচটি resolution লেগেছিল, unit propagation তা সরাসরি ধরে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমResolution refutation দিয়ে KB ⊨ α প্রমাণের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📝 নিয়ম আর তথ্য symbol-এ লিখে KB বানানো
  2. 🔁 KB-র প্রতিটি বাক্য CNF clause-এ রূপান্তর
  3. ➖ প্রমাণ-লক্ষ্যের উল্টো ¬α-কে clause হিসেবে যোগ
  4. 🔗 বিপরীত literal-ওয়ালা দুটি clause resolve করে নতুন clause যোগ
  5. ∅ Empty clause পেলে থামা: KB ∧ ¬α অসম্ভব
  6. ✅ সিদ্ধান্ত: KB ⊨ α প্রমাণিত

নিয়ম B → F, যেখানে B = “অর্ডার ৳১,০০০-এর বেশি”, F = “delivery ফ্রি”। একটি অর্ডারে B মিথ্যা আর F সত্য। এই model-এ B → F-এর মান কী?

  • সত্য
  • মিথ্যা
  • অজানা
  • অন্য নিয়ম না জেনে বলা যায় না

কোন বাক্যটি B → F-এর logically equivalent?

  • F → B
  • ¬B → ¬F
  • ¬F → ¬B
  • B ∧ F

একটি knowledge base-এ ৬টি আলাদা proposition symbol আছে। Truth table দিয়ে model checking করলে সর্বোচ্চ কয়টি model পরীক্ষা করতে হবে?

  • 12
  • 64
  • 36
  • 720

KB ⊨ α (KB entails α) কোন শর্তের সমান?

  • KB ∧ α satisfiable
  • α valid
  • KB ∨ ¬α valid
  • KB ∧ ¬α unsatisfiable

দুটি clause {A, ¬B, C} আর {B, D}-কে B literal-এ resolve করলে resolvent কী?

  • {A, C, D}
  • {A, B, C, D}
  • {A, C}
  • { } (empty clause)

নিচের কোন clause-টি Horn clause?

  • {A, B}
  • {¬A, ¬B, C}
  • {A, B, ¬C}
  • {A, B, C}

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Knowledge-based agent জ্ঞান আর যুক্তির যন্ত্র আলাদা রাখে: TELL দিয়ে KB-তে বাক্য যোগ, ASK দিয়ে জিজ্ঞেস; উত্তর হ্যাঁ, না বা অজানা।
  2. A → B মিথ্যা শুধু A সত্য আর B মিথ্যা হলে; A → B ≡ ¬A ∨ B ≡ ¬B → ¬A, কিন্তু B → A আলাদা।
  3. KB ⊨ α মানে KB-র প্রতিটি model-এ α সত্য; model checking 2nটি model গোনে, sound আর complete কিন্তু দামি।
  4. CNF + ¬α + resolution = refutation: empty clause পেলে KB ∧ ¬α অসম্ভব, তাই α প্রমাণিত; Horn clause-এ forward/backward chaining রৈখিক সময়ে।
  5. SAT NP-complete, তবু DPLL আর CDCL বাস্তবে শক্তিশালী; কঠিনতা আসে গঠন থেকে (অনুপাত প্রায় ৪.২, pigeonhole), আর propositional logic-এ বস্তু, quantifier আর অনিশ্চয়তা নেই।

পরের পর্বে: First-order logic: বস্তু, predicate, function আর ∀ ও ∃ quantifier; বাংলা আর ইংরেজি বাক্যকে যুক্তির ভাষায় অনুবাদ, unification আর Prolog-এর ধারণা।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান