آموزش هوش مصنوعی با پایتون - برنامه‌نویسی منطقی

در این فصل، بر برنامه‌نویسی منطقی و نحوه کمک آن به هوش مصنوعی تمرکز خواهیم کرد.

ما از قبل می‌دانیم که منطق، مطالعه اصول استدلال درست است یا به بیان ساده‌تر، مطالعه این است که چه چیزی پس از چه چیزی می‌آید. برای مثال، اگر دو گزاره درست باشند، می‌توانیم هر گزاره سومی را از آن‌ها استنتاج کنیم.

مفهوم

برنامه‌نویسی منطقی ترکیبی از دو کلمه منطق و برنامه‌نویسی است. برنامه‌نویسی منطقی یک پارادایم برنامه‌نویسی است که در آن مسائل به صورت حقایق و قوانین توسط دستورات برنامه اما در درون یک سیستم منطق صوری بیان می‌شوند. درست مانند سایر پارادایم‌های برنامه‌نویسی مانند شیءگرا، تابعی، اظهاری و رویه‌ای و غیره، این نیز یک روش خاص برای مواجهه با برنامه‌نویسی است.

نحوه حل مسائل با برنامه‌نویسی منطقی

برنامه‌نویسی منطقی از حقایق و قوانین برای حل مسئله استفاده می‌کند. به همین دلیل به آن‌ها بلوک‌های سازنده برنامه‌نویسی منطقی می‌گویند. برای هر برنامه در برنامه‌نویسی منطقی باید یک هدف مشخص شود. برای درک نحوه حل یک مسئله در برنامه‌نویسی منطقی، باید درباره بلوک‌های سازنده − حقایق و قوانین − بدانیم −

حقایق

در واقع، هر برنامه منطقی برای کار کردن به حقایق نیاز دارد تا بتواند به هدف تعیین‌شده دست یابد. حقایق اساساً گزاره‌های درستی درباره برنامه و داده‌ها هستند. برای مثال، دهلی پایتخت هند است.

قوانین

در واقع، قوانین محدودیت‌هایی هستند که به ما امکان می‌دهند درباره دامنه مسئله نتیجه‌گیری کنیم. قوانین اساساً به صورت بندهای منطقی نوشته می‌شوند تا حقایق مختلف را بیان کنند. برای مثال، اگر در حال ساخت هر نوع بازی هستیم، باید تمام قوانین تعریف شوند.

قوانین برای حل هر مسئله‌ای در برنامه‌نویسی منطقی بسیار مهم هستند. قوانین اساساً نتیجه‌گیری‌های منطقی هستند که می‌توانند حقایق را بیان کنند. در ادامه ساختار نوشتاری (سینتکس) قانون آمده است −

A B1,B2,...,Bn.

در اینجا، A سر (head) و B1, B2, ... Bn بدنه (body) است.

برای مثال − ancestor(X,Y) :- father(X,Y).

ancestor(X,Z) :- father(X,Y), ancestor(Y,Z).

این را می‌توان به این صورت خواند: به ازای هر X و Y، اگر X پدر Y باشد و Y نیاکان Z باشد، X نیاکان Z است. به ازای هر X و Y، X نیاکان Z است، اگر X پدر Y باشد و Y نیاکان Z باشد.

نصب پکیج‌های کاربردی

برای شروع برنامه‌نویسی منطقی در پایتون، باید دو پکیج زیر را نصب کنیم −

Kanren

این پکیج راهی برای ساده‌سازی نحوه کدنویسی منطق کسب‌وکار در اختیار ما قرار می‌دهد. این پکیج به ما اجازه می‌دهد منطق را در قالب قوانین و حقایق بیان کنیم. دستور زیر به شما در نصب kanren کمک می‌کند −

pip install kanren

SymPy

پکیج SymPy یک کتابخانه پایتون برای ریاضیات نمادین است. هدف آن تبدیل شدن به یک سیستم جبر کامپیوتر (CAS) کامل و در عین حال نگه داشتن کد تا حد امکان ساده است تا قابل فهم و به راحتی قابل گسترش باشد. دستور زیر به شما در نصب SymPy کمک می‌کند −

pip install sympy

مثال‌هایی از برنامه‌نویسی منطقی

در ادامه برخی از مثال‌هایی که می‌توانند توسط برنامه‌نویسی منطقی حل شوند آورده شده است −

تطبیق عبارات ریاضی

در واقع ما می‌توانیم مقادیر مجهول را با استفاده از برنامه‌نویسی منطقی به روشی بسیار مؤثر پیدا کنیم. کد پایتون زیر به شما کمک می‌کند یک عبارت ریاضی را تطبیق دهید −

ابتدا وارد کردن پکیج‌های زیر را مد نظر قرار دهید −

from kanren import run, var, fact
from kanren.assoccomm import eq_assoccomm as eq
from kanren.assoccomm import commutative, associative

ما باید عملیات ریاضی مورد استفاده خود را تعریف کنیم −

add = 'add'
mul = 'mul'

هر دو عمل جمع و ضرب، فرآیندهای جابه‌جایی‌پذیر هستند. بنابراین، باید آن را مشخص کنیم و این کار را می‌توان به صورت زیر انجام داد −

fact(commutative, mul)
fact(commutative, add)
fact(associative, mul)
fact(associative, add)

تعریف متغیرها اجباری است؛ این کار را می‌توان به صورت زیر انجام داد −

a, b = var('a'), var('b')

ما باید عبارت را با الگوی اصلی تطبیق دهیم. ما الگوی اصلی زیر را داریم که اساساً b*(a+5) است −

Original_pattern = (mul, (add, 5, a), b)

ما دو عبارت زیر را برای تطبیق با الگوی اصلی داریم −

exp1 = (mul, 2, (add, 3, 1))
exp2 = (add,5,(mul,8,1))

خروجی را می‌توان با دستور زیر چاپ کرد −

print(run(0, (a,b), eq(original_pattern, exp1)))
print(run(0, (a,b), eq(original_pattern, exp2)))

پس از اجرای این کد، خروجی زیر را دریافت خواهیم کرد −

((3,2))
()

خروجی اول نشان‌دهنده مقادیر برای a و b است. عبارت اول با الگوی اصلی مطابقت داشت و مقادیر a و b را برگرداند، اما عبارت دوم با الگوی اصلی مطابقت نداشت و بنابراین چیزی برگردانده نشده است.

بررسی اعداد اول

با کمک برنامه‌نویسی منطقی، می‌توانیم اعداد اول را از یک لیست از اعداد پیدا کنیم و همچنین اعداد اول تولید کنیم. کد پایتون داده‌شده در زیر، عدد اول را از یک لیست از اعداد پیدا می‌کند و همچنین ۱۰ عدد اول نخست را تولید خواهد کرد.

بیايید ابتدا وارد کردن پکیج‌های زیر را مد نظر قرار دهیم −

from kanren import isvar, run, membero
from kanren.core import success, fail, goaleval, condeseq, eq, var
from sympy.ntheory.generate import prime, isprime
import itertools as it

اکنون، تابعی به نام prime_check تعریف می‌کنیم که اعداد اول را بر اساس اعداد داده‌شده به عنوان داده بررسی می‌کند.

def prime_check(x):
if isvar(x):
   return condeseq([(eq,x,p)] for p in map(prime, it.count(1)))
else:
   return success if isprime(x) else fail

اکنون، باید متغیری را که مورد استفاده قرار خواهد گرفت اعلام کنیم −

x = var()
print((set(run(0,x,(membero,x,(12,14,15,19,20,21,22,23,29,30,41,44,52,62,65,85)),
(prime_check,x)))))
print((run(10,x,prime_check(x))))

خروجی کد بالا به صورت زیر خواهد بود −

{19, 23, 29, 41}
(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)

حل معماها

برنامه‌نویسی منطقی می‌تواند برای حل مسائل زیادی مانند پازل ۸، معمای زبرا، سودوکو، مسئله N-وزیر و غیره استفاده شود. در اینجا ما یک مثال از نوعی از معمای زبرا را می‌آوریم که به شرح زیر است −

There are five houses.
The English man lives in the red house.
The Swede has a dog.
The Dane drinks tea.
The green house is immediately to the left of the white house.
They drink coffee in the green house.
The man who smokes Pall Mall has birds.
In the yellow house they smoke Dunhill.
In the middle house they drink milk.
The Norwegian lives in the first house.
The man who smokes Blend lives in the house next to the house with cats.
In a house next to the house where they have a horse, they smoke Dunhill.
The man who smokes Blue Master drinks beer.
The German smokes Prince.
The Norwegian lives next to the blue house.
They drink water in a house next to the house where they smoke Blend.

ما در حال حل آن برای این سوال هستیم که چه کسی صاحب گورخر است با کمک پایتون.

بیايید پکیج‌های لازم را وارد کنیم −

from kanren import *
from kanren.core import lall
import time

اکنون، باید دو تابع تعریف کنیم − ()left و ()next تا بررسی کنیم که خانه چه کسی در سمت چپ یا کنار خانه چه کسی قرار دارد −

def left(q, p, list):
   return membero((q,p), zip(list, list[1:]))
def next(q, p, list):
   return conde([left(q, p, list)], [left(p, q, list)])

اکنون، متغیر house را به صورت زیر تعریف می‌کنیم −

houses = var()

ما باید قوانین را با کمک پکیج lall به صورت زیر تعریف کنیم.

۵ خانه وجود دارد −

rules_zebraproblem = lall(
   (eq, (var(), var(), var(), var(), var()), houses),

   (membero,('Englishman', var(), var(), var(), 'red'), houses),
   (membero,('Swede', var(), var(), 'dog', var()), houses),
   (membero,('Dane', var(), 'tea', var(), var()), houses),
   (left,(var(), var(), var(), var(), 'green'),
   (var(), var(), var(), var(), 'white'), houses),
   (membero,(var(), var(), 'coffee', var(), 'green'), houses),
   (membero,(var(), 'Pall Mall', var(), 'birds', var()), houses),
   (membero,(var(), 'Dunhill', var(), var(), 'yellow'), houses),
   (eq,(var(), var(), (var(), var(), 'milk', var(), var()), var(), var()), houses),
   (eq,(('Norwegian', var(), var(), var(), var()), var(), var(), var(), var()), houses),
   (next,(var(), 'Blend', var(), var(), var()),
   (var(), var(), var(), 'cats', var()), houses),
   (next,(var(), 'Dunhill', var(), var(), var()),
   (var(), var(), var(), 'horse', var()), houses),
   (membero,(var(), 'Blue Master', 'beer', var(), var()), houses),
   (membero,('German', 'Prince', var(), var(), var()), houses),
   (next,('Norwegian', var(), var(), var(), var()),
   (var(), var(), var(), var(), 'blue'), houses),
   (next,(var(), 'Blend', var(), var(), var()),
   (var(), var(), 'water', var(), var()), houses),
   (membero,(var(), var(), var(), 'zebra', var()), houses)
)

اکنون، حل‌کننده را با محدودیت‌های قبلی اجرا کنید −

solutions = run(0, houses, rules_zebraproblem)

با کمک کد زیر، می‌توانیم خروجی را از حل‌کننده استخراج کنیم −

output_zebra = [house for house in solutions[0] if 'zebra' in house][0][0]

کد زیر به چاپ راه حل کمک می‌کند −

print ('\n'+ output_zebra + 'owns zebra.')

خروجی کد بالا به صورت زیر خواهد بود −

German owns zebra.

پیشرفت مطالعه: وضعیت خوانده‌شدن این درس فقط در همین مرورگر ذخیره می‌شود؛ بدون نیاز به حساب کاربری.