Що ми знаємо про достовірно правильні програми?


37

Постійно зростаюча складність комп’ютерних програм і все більш важливе становище комп’ютерів у нашому суспільстві залишає мене цікавим, чому ми все ще не використовуємо колективно мови програмування, на яких ви повинні дати офіційний доказ того, що ваш код працює правильно.

Я вважаю, що термін - це "сертифікуючий компілятор" (я знайшов його тут ): компілятор, що компілює мову програмування, в якій потрібно не тільки написати код, але також вказати специфікацію коду та довести, що код дотримується специфікація (або використовувати для цього автоматизований доказ).

Шукаючи в Інтернеті, я знайшов лише проекти, які використовують дуже просту мову програмування або провалили проекти, які намагаються адаптувати сучасні мови програмування. Це призводить мене до мого питання:

Чи є які-небудь сертифікаційні компілятори, що реалізують повномасштабну мову програмування, чи це дуже важко / теоретично неможливо?

Крім того, я ще не бачив будь-якого класу складності, що включає перелічені програми, наприклад, "клас усіх мов, який можна визначити машиною Тюрінга, для якого існує доказ того, що ця машина Тьюрінга зупиняється", яку я називаю ProvableR , як аналог R , безліч рекурсивних мов.

Я бачу переваги вивчення такого класу складності: наприклад, для ProvableR проблема зупинки є вирішуваною (я навіть здогадуюсь ProvableRE визначеною очевидним чином. найбільший клас мов, для яких його можна вирішити). Крім того, я сумніваюся, що ми виключаємо будь-які практично корисні програми: хто буде використовувати програму, коли ви не можете довести, що вона припиняється?

Отже, моє друге питання:

Що ми знаємо про класи складності, які вимагають, щоб їх мови, що містять, мати певні властивості?


1
Компілятор міг перерахувати всі можливі докази довжини i, відпустивши перехід від 1 до нескінченності, поки не знайде доказ того, що програма зупиняється. Якщо нам потрібно, щоб введення для компілятора було припустимо зупинене, тоді компілятор завжди знайде це підтвердження. Оскільки проблема зупинки не вирішена, ми повинні зробити висновок, що існують програми, які зупиняються, але доказів для цього не існує. Найважливішим моментом є те, що програми не в змозі з’ясувати, чи існує доказ, а не те, що вони не можуть знайти доказ, якщо він існує.
Олексій десять Бринк

3
Я думаю, ви повинні розділити їх. Вони різні питання з різними відповідями.
Марк Рейтблатт

4
На перше питання впливовий документ - "Соціальні процеси та докази теорем і програм", portal.acm.org/citation.cfm?id=359106
Колін МакКійлан

1
Підтвердження програми не можна визначити. Тож одна проблема - сказати, що є хорошим рішенням. Дивіться cstheory.stackexchange.com/questions/4016/…
Раду ГРИГо

2
@Colin: цей документ варто прочитати для його аналізу доказів, але його прогнози були сфальсифіковані. Сьогодні ми маємо коректно правильні компілятори, ядра операційної системи, сміттєзбірники та бази даних, усі вони вважали неможливими. Трюком ухилятися від їхньої критики було уникнути перевірки людьми детальних офіційних доказів низького рівня, але використовувати машинну перевірку доказів та використовувати людей для перевірки перевірки доказів. Теорія відмови Ноама полягає в тому, де знаходиться найсучасніший стан, який залишає імперативні програми в чомусь обов'язковому, оскільки теорія типів функціональна.
Ніл Крішнасвамі

Відповіді:


28

"Сертифікуючий компілятор" зазвичай означає дещо інше: це означає, що у вас є компілятор, який може довести, що машинний код, який він випромінює, правильно реалізує семантику високого рівня. Тобто це доказ того, що помилок компілятора немає. Програми, які люди дають компілятору, все ще можуть помилятися, але компілятор створить правильну версію машинного коду неправильної програми. Найбільша історія успіху в цих напрямках - це перевірений компілятор CompCert , який є компілятором для великої підмножини C.

Сам компілятор Compcert - це програма з підтвердженням коректності (зроблена в Coq), яка гарантує, що якщо вона генерує код для програми, вона буде правильною (стосовно оперативної семантики складання та C, яку використовували дизайнери CompCert). Зусилля для машинної перевірки цих речей досить великі; зазвичай доказ коректності буде розміром від 1х до 100х розмірів програми, яку ви перевіряєте. Написання перевірених машинами програм та доказів - це нова майстерність, яку ви повинні засвоїти - це не математика чи програмування, як зазвичай, хоча це залежить від того, щоб вміти робити як добре. Ви відчуваєте, що починаєте з нуля, як знову початківець програміст.

Однак для цього немає спеціальних теоретичних бар'єрів. Єдине, що випливає з цих рядків, - це теорема розміру Blum, що для будь-якої мови, на якій всі програми є загальними, ви можете знайти програму загальною рекурсивною мовою, яка буде, принаймні, експоненціально більшою при програмуванні на загальній мові. Спосіб зрозуміти цей результат полягає в тому, що загальна мова кодує не просто програму, а й доказ припинення. Таким чином, ви можете мати короткі програми з довгим підтвердженням припинення. Однак це насправді не має значення на практиці, оскільки ми збираємося коли-небудь писати програми з керованими підтвердженнями припинення.

EDIT: Дай Ле попросив детально розробити останній пункт.

Це здебільшого прагматичне твердження, яке базується на тому, що якщо ви можете зрозуміти, чому працює програма, то навряд чи причиною є велика інваріантна мільйони сторінок. (Найдовші інваріанти, якими я користувався, - це кілька сторінок, і хлопці, чи змушують вони рецензентів бурчати! Розуміється, що теж, оскільки інваріант є причиною, що програма працює позбавленою всієї розповіді, яка допомагає людям зрозуміти це.)

Але є і деякі теоретичні причини. В основному, ми не знаємо дуже багато способів систематично вигадувати програми, чиї докази коректності дуже довгі. Основним методом є (1) прийняти логіку, в якій ти доводиш правильність, (2) знайти властивість, яка не може бути безпосередньо виражена в цій логіці (доказів узгодженості є типовим джерелом), і (3) знайти програму, чию Доказ коректності покладається на сімейство виразних наслідків невимовної властивості. Оскільки (2) є невимовними, це означає, що доведення кожного виразного наслідку потрібно робити самостійно, що дозволяє підібрати розмір доказу правильності. Як простий приклад, зауважте, що в логіці першого порядку з родительським відношенням ви не можете виразити відношення предків.kk) виражається для кожного фіксованого . Таким чином, надаючи програмі, яка використовує певну глибину властивості родини (скажімо, 100), ви можете примусити доказ коректності у FOL містити докази цих властивостей у сто разів.k

Складне заняття цим предметом називається "зворотна математика", і це вивчення яких аксіом потрібно для доведення заданих теорем. Я про це не знаю багато, але якщо ви опублікуєте питання щодо його застосування в CS, і я впевнений, що принаймні Тімоті Чоу, і, мабуть, кілька інших людей, зможуть розповісти вам цікаві речі.


1
Чи можете ви, будь ласка, детальніше розібратись у цьому пункті: "Ми лише збираємось писати програми з керованими доказами припинення" ще трохи?
Dai Le

Дякуємо за вашу оновлену відповідь! Ваша відповідь справді відкриває мою точку зору. Насправді я також трохи працюю над "зворотною математикою", але я не усвідомлював згаданий вами зв'язок. Знову дякую!
Dai Le

1
Суть у вашій редакції пов'язана з тим, що у нас навряд чи є кандидати на тавтологи, які потребують тривалого підтвердження в системах природного доказування (як, скажімо, Фреге). Частина причини цього полягає в тому, що єдиний спосіб, коли ми знаємо тавтологію, є тавтологічною, в першу чергу, тому що ми маємо на увазі доказ, який обов'язково був не таким довгим!
Джошуа Грохов

22

Я думаю, що відповідь на перше питання полягає в тому, що загалом це занадто багато роботи з сучасними інструментами. Щоб отримати таке відчуття, я пропоную спробувати довести правильність сортування бульбашок у Coq (або якщо ви віддаєте перевагу трохи більше проблем, скористайтеся швидким сортуванням). Я не думаю, що доцільно очікувати, що програмісти пишуть перевірені програми до тих пір, поки довести правильність таких основних алгоритмів так складно і забирає багато часу.

Це питання схоже на запитання, чому математики не пишуть офіційних доказів, перевірених перевіряючими? Написання програми з формальним підтвердженням правильності означає доведення математичної теореми про написаний код, і відповідь на це питання також стосується вашого запитання.

Це не означає, що не було успішних випадків перевірених програм. Я знаю, що є групи, які доводять правильність таких систем, як гіпервізор Microsoft . Пов’язаний випадок - це підтверджений компілятор Microsoft . Але загалом сучасні інструменти потребують значного розвитку (включаючи їх аспекти SE та HCI), перш ніж стати корисними для загальних програмістів (і математиків).

Щодо останнього пункту відповіді Ніла про зростання розміру програми для мов, які мають лише загальні функції, насправді легко довести ще більше (Якщо я правильно це зрозумів). Доцільно розраховувати, що синтаксис будь-якої мови програмування буде ce і набір загальних обчислюваних функцій не ce, тому для будь-якої мови програмування, де всі програми є загальною, існує загальна обчислювальна функція, яку неможливо обчислити жодна програма ( будь-якого розміру) на цій мові.


На друге запитання я відповів на подібне запитання в блозі Скотта десь тому. В основному, якщо клас складності має хорошу характеристику і є виразно представницьким (тобто це ce), то ми можемо довести, що деяке представлення проблем у класі складності виявляється сумарним у дуже слабких теоріях, що відповідають класу складності. Основна ідея полягає в тому, що суто загальні функції теорії містять усі функції і задачу, яка A C 0AC0AC0-комплект для класу складності, тому він містить усі проблеми класу складності і може довести сукупність цих програм. Співвідношення між доказом і теорією складності вивчається в доказівній складності, див. С.А. Кука та останню книгу П.Нгуєна " Логічні основи складності доказування ", якщо вас цікавить. ( Проект від 2008 р. Доступний.) Отже, основна відповідь полягає в тому, що для багатьох класів "Доведено C = C".

Це взагалі не вірно, оскільки існують класи семантичної складності, які не мають синтаксичної характеристики, наприклад, загальні обчислювані функції. Якщо під рекурсивним способом ви маєте на увазі загальну рекурсивну функцію, то обидві не є рівними, а набір обчислюваних функцій, які в теорії є загальнодоступними, добре вивчений у літературі теорії доказів і називається доказівно сумарними функціями теорії. Наприклад: суттєво сумарні функції є ϵ 0 -рекурсивними функціями (або еквівалентними функціями в системі Годеля Гольделя T ), а суттєво сумарні функції P A 2 є функцією в системі Жирара F , а сумарні функціїPAϵ0TPA2F - примітивні рекурсивні функції, ....IΣ1

Але мені не здається, що це значно означає в контексті верифікації програми, оскільки є також програми, які розширюють одну і ту ж функцію, але ми не можемо довести, що обидві програми обчислюють одну і ту ж функцію, тобто програми є рівними, але не навмисно. (Це схоже на "Ранкова зірка" та "Вечірня зірка". Більше того, легко змінити задану суттєво загальну програму, щоб отримати ту, яку теорія не може довести її сукупність.


Я думаю, що два питання пов'язані між собою. Мета - отримати перевірену програму. Перевірені програми означають, що програма задовольняє опису, який є математичним твердженням. Один із способів - написати програму мовою програмування, а потім довести її властивості так, як вона задовольняє опису, що є звичайнішою практикою. Інший варіант - спробувати довести математичне твердження, що описує проблему, за допомогою обмежених засобів, а потім витягнути з неї перевірену програму. Наприклад, якщо ми докажемо в теорії, що відповідає що для будь-якого заданого числа n існує послідовність простих чисел, їх добуток дорівнює n , то ми можемо отримати PPnnPалгоритм факторизації з доведення. (Є також дослідники, які намагаються максимально автоматизувати перший підхід, але перевірити цікаві нетривіальні властивості програм обчислювально важко і їх неможливо повністю перевірити без помилкових позитивів і негативів.)


3
Гарна відповідь! Ви згадуєте, що одним із способів є вилучення програм з доказів, що, наприклад, можна зробити автоматично в Coq. Суміжним напрямком є добування доказів , де люди (працюючи, як правило, в математичній логіці) намагаються витягти інформацію з даного доказу. Наприклад, в деяких випадках можливо (автоматично) знайти інтуїтивні докази, надані класичним.
Раду ГРИГо

1
Π20PAHA

1
У Андрія Бауера є новий цікавий пост у своєму блозі, в якому він доводить тлумачення Годеля Діалектики в Coq .
Каве

18

Те, про що ви задаєте в першому запитанні, іноді називають "перевіряючим компілятором", і кілька років тому Тоні Хоаре запропонував це як великий виклик для обчислювальних досліджень . Певною мірою це вже існує і активно використовується в таких інструментах, як доказ теореми Кока , які організовують проблему за допомогою теорії типів та принципу пропозицій як типів (" Кері-Говард ").

EDIT: просто хотів би надати акцент на "певній мірі". Це далеко не вирішена проблема, але успіх Coq дає надію, що це не трудна мрія.


8
Я б сказав, що в 1956 році було створено перевірене програмне забезпечення, коли було створено звичайне старе програмне забезпечення. Вже було очевидно, що програмне забезпечення буде неймовірно важливим, і вже існують великі історії успіху. Однак у людей все ще було багато відсутніх фундаментальних концепцій (чітке розуміння, що таке процедури та змінні, наприклад), а відстань від теорії до практики може бути такою ж короткою, як реалізація Ліспа шляхом програмування коду в теоремі. Це НЕВІДОМНО хвилюючий час для роботи над мовами та перевірки.
Neel Krishnaswami

12

Інструмент, який перевіряє правильність програми, іноді називається верифікатором програми. У цьому контексті "правильний" зазвичай означає дві речі: програма ніколи не дає певних результатів (помилка сегментації, помилка NullPointerException тощо) і що програма погоджується із специфікацією.

Код та специфікація можуть узгоджуватися та все ж сприйматися як неправильні. У певному сенсі просити розробників написати специфікацію - це як попросити двох розробників вирішити проблему. Якщо дві реалізації погоджуються, то ви більше впевнені, що вони нормальні. В іншому сенсі, однак, технічні характеристики кращі, ніж друга реалізація. Оскільки специфікація не повинна бути ефективною або навіть виконуваною, вона може бути набагато більш короткою і, отже, важче помилитися.

Маючи на увазі ці застереження, рекомендую переглянути перевірку програми Spec # .


Наскільки я розумію Spec # (і його розширення Sing #), це дає програмісту можливість статично перевіряти твердження, але він не вимагає від цього програміста, а також не дає можливості доводити довільні властивості коду.
Олексій десять Бринк

В принципі довільні властивості можуть бути кодовані як фол твердження. Я не впевнений, що ви хочете вимагати від цього інструменту. Ви хочете, щоб специфікації сказали, яким повинен бути вихід для всіх можливих входів?
Раду ГРИГо

4

У загальному випадку неможливо створити алгоритм, який підтверджує, чи алгоритм еквівалентний специфікації. Це неофіційний доказ:

Практично всі мови програмування закінчені Тьюрінгом. Отже, будь-яка мова, яку вирішує ТМ, також може бути визначена програмою, написаною цією мовою.

Проблема визначення того, чи приймають дві ТМ одну і ту ж мову, відому як Еqуivаленcе/ТМ is undecidable. As a consequence of Turing completeness, the same holds for the programs of the given language. In other words, it is undecidable to know whether the inputs you want your program to accept and the inputs it really does are the same.

Furthermore, Equivalence/TM is not even recursive enumerable. That is because Nonemptiness/TM (whether a TM accepts at least one input) is an acceptable language, since you can iterate over all possible inputs. If the TM is non-empty, eventually you will find a word that is accepted. Therefore Emptiness/TM is unacceptable, otherwise Emptiness/TM would be decidable (which we know it is not). However Emptiness/TM can be reduced to Equivalence/TM, therefore Equivalence/TM is also unacceptable. Therefore you can use an algorithm whether or not two machines are not equivalent, but you cannot be sure if they are equivalent, or you haven't given your algorithm enough time.

However, this is only for the general case. It is possible that you can decide whether or not the specifications are equivalent to the program, by solving a more relaxed version of the problem. For example, you might examine only a number of inputs or say that the two programs are equivalent with some uncertainty. This is what software testing is all about.

As for the rest of your questions:

Note: This part has been edited for clarification. It turns out that I did the mistake I was trying to avoid, sorry.

Let TrueR be the collection of languages that we know to be decidable. Of course TrueRR. One can then prove the following:

ProvableR=TrueR. It is easy to see that ProvableRTrueR. However, it is also TrueRProvableR . The proof is very easy: Let AϵTrueR . By definition , in every input A returns either YES or NO. Therefore, A halts on every input. It follows that AϵProvableR .

Informally , this can be summarized as: You don't know that a language is decidable until you have proven it to be. So if in a formal system you have the knowledge that a language is decidable, this knowledge can also serve as a proof for that. Therefore, you cannot simultaneously have the knowledge that a language is both decidable and it cannot be proven so, these two statements are mutually exclusive.

My final point is that our knowledge is partial and that the only way of studying R is through ProvableR. The distinction might make sense in mathematics and we can acknowledge it, but we possess no tools to study it. Since ProvableRR , we are bound to never completely know R in its entirety.

@Kaveh summarizes it best: Provable always means provable in some system/theory and does not coincide with truth in general.

The same holds for any other complexity class: To determine membership you need a proof first. This is why I believe that your second question is too general, since it contains not only complexity theory, but also computation theory as well, depending on the property you want the language to have.


1
That bit about RProvableR seems a bit fishy. Why must there be a proof for a language to be decidable? Actually, it seems there is not (in general): determining whether a TM's halting set is a recursive language is Σ30 complete. If there were a proof that a TM's halting set was Recursive, it would be in Σ10.
Mark Reitblatt

1
Provable always means provable in some system/theory and does not coincide with truth in general.
Kaveh

1
I see now that for my question to be interesting, one should talk about the set of halting Turing machines, not the set of decidable languages.
Alex ten Brink

1
@Alex Well, you need some way of talking about languages, but there's uncountably many. So, if you want to talk about languages connected to some finite object (like a proof), you need to restrict yourself to languages identifiable by a finite object, like a TM.
Mark Reitblatt

2
chazisop, as I said, provable means provable in a (fixed) theory, and for every reasonable theory (c.e., consistent, ...) there is some total computable function which cannot be proven to be total in that theory. There are total functions which cannot be proven to be total in ZFC (and reasonable extensions of it). You are confusing the notion of truth with provability. So the proof does not work (assuming that R means total recursive).
Kaveh

3

The following classic monograph studies almost exactly your second question:

Hartmanis, J. Feasible computations and provable complexity properties, CBMS-NSF Regional Conference Series in Applied Mathematics, 30. Society for Industrial and Applied Mathematics (SIAM), Philadelphia, Pa., 1978.

For example, he defines the class "F-TIME[T(n)]" (where F is some reasonable formal proof system) to be {L(Mi)|Ti(n)T(n) is provable in F} (where Mi is the i-th Turing machine in a complete list of TMs, and Ti(n) is the maximum running time of Mi on inputs of length n). Let me just highlight a few results relevant to your question - many more can be found in the monograph and references therein:

Cor. 6.4: Let T(n)nlog(n) be a computable function, and let g(n)1 be an unbounded computable function. Then F-TIME[T(n)]TIME[T(n)g(n)].

Theorem 6.5: There exist computable monotonically increasing time bounds T(n) for which F-TIME[T(n)]TIME[T(n)].

Theorem 6.14: For any computable T(n)nlog(n), TIME[T(n)]={L(Mi)|F proves(j)[L(Mj)=L(Mi)Tj(n)T(n)]}.

For space, however, the situation is better controlled:

Theorem 6.9: (1) If s(n)n is space-constructible, then SPACE[s(n)]=F-SPACE[s(n)].

(2) If SPACE[S(n)]=F-SPACE[S(n)] (S(n)n) then there is a space-constructible s(n) such that SPACE[S(n)]=SPACE[s(n)].


1

The question has to be posed correctly. For example, nobody ever wants to know whether an actual program would complete given infinite memory and some means of accessing it (maybe an operation to move the base address by some number). Turing's theorem is irrelevant to program correctness in any concrete sense and people who cite it as a barrier to program verification are confusing two quite different things. When engineers/programmers talk of program correctness they want to know about finite properties. This is also pretty much true for mathematicians who are interested in whether something is provable. Godel's letter http://vyodaiken.com/2009/08/28/godels-lost-letter/ explains this in some detail.

Namely, it would obviously mean that in spite of the undecidability of the Entscheidungsproblem, the mental work of a mathematician concerning Yes-or-No questions could be completely replaced by a machine. After all, one would simply have to choose the natural number n so large that when the machine does not deliver a result, it makes no sense to think more about the problem.

It may well be infeasible to examine the immense state set of a program executing on an actual computer and detect bad states, there is no theoretical reason why it can't be done. In fact, there has been a lot of progress in this field - for example see http://www.cs.cornell.edu/gomes/papers/SATSolvers-KR-book-draft-07.pdf (thanks to Neil Immerman for telling me about this)

A different and more difficult problem is specifying exactly what properties one wants a program to have in order to be correct.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.