Немного отклонюсь от плана "Как читать Барендрегта" и от третьей главы монографии внезапно перейду аж сразу к четвертой. Внимание: будут ссылки на упражнения второй главы.

4.3.1. См. упр. 2.4.12 (i).

4.3.2 (Виссер). Замкнутый терм M называется легким, если для всех замкнутых термов N теория λ + (M = N) непротиворечива. В предположении легкости терма M докажем следующие утверждения.
(i) M N легкий для любого замкнутого терма N:
в непротиворечивой λ + (M = K M) выводимо M N = K M N => M N = M,
откуда следует, что λ + (M N = Q) непротиворечива для любого замкнутого Q;
(ii) M неразрешим, иначе бы M N1 ... Nn = I для некоторых Ni,
а из (i) следовало бы, что λ + (I = K) непротиворечива, но I # K (упр. 2.4.2 (i));
(iii)-(iv) если функция f(n) = #Pn рекурсивна, то она представима некоторым лямбда-термом F, таким, что F [n] = Pn, и
тогда в непротиворечивой λ + (M = F) выводимы M [n] = F [n] => M [n] = Pn для всех n;
если же f(i) не является рекурсивной функцией, то я в эти игры не играю, потому что не считаю вопрос "Есть ли Б-г на Марсе?" уместным в этой теме.
(v) λ + {M = N | M и N легкие} непротиворечива:
мы уже знаем о непротиворечивости осмысленной теории H, которая приравнивает все неразрешимые термы, а в пунтке (ii) мы доказали, что все легкие термы неразрешимы.

4.3.3. Модельные соображения не включены в программу намеренно.

4.3.4. Покажем, что не существует такого M, чтобы для любых N теория H + (M = N) была непротиворечивой:
в 4.3.2 (ii) мы доказали, что M должен быть неразрешимым, но
в H все неразрешимые термы приравнены к W3 = (x: x x x) (x: x x x), а
из упр. 2.4.12 (ii) мы знаем, что I # W3.
Вчера были упражнения ко второй главе монографии, а сегодня пробежка галопом по третьей.

3.5.1. Нарисовать графы G(M) для каких угодно термов каждый может и сам.

3.5.2. А вот найти термы по заданному бета-графу - это уже интересно:
(i) M1 -> ... -> Mn -> M1 для произвольного n:
M1 = N N~n, где N = x1,..., xn: xn ... x2 x1 x1;
(ii) чтобы добавить к (i) Mi -> Mi для всех i, возьмем M1 Ω;
(iii) чтобы добавить к (i) Mi -> I для всех i, возьмем (x: I) M1;
второй граф имеет терм (x: x ω (y: x y)) ω -> Ω (y: ω y) -> Ω ω, Ω (y: ω y) -> Ω (y: ω y), Ω ω -> Ω ω;
третий граф имеет терм K I Ω -> (x: I) Ω -> I, K I Ω -> Κ Ι Ω, (x: I) Ω -> (x: I) Ω;
(iv) чтобы сделать бесконечный цилиндр из (i), возьмем M1 ((x: x x x) (x: x x x)).

3.5.3. Построим терм M0, такой, что M0 ->>β M1 ->η M2 ->>β M3 ->η M4 ->>β ...:
M0 = x: (y: M) x x ->>β x: M x ->η M ->>β Μ0;
M найдем, как будто мы до сих пор никогда не слышали про Y:
M = (f, x: (y: f) x x) M, M = W W, W = w: (f, x: (y: f) x x) (w w).

3.5.4. Пусть M = (b, x, z: z ( b b x)) (b, x, z: z (b b x)) x. Чтобы получить кайф, построив G(M) и установив, что для каждого n этот граф имеет n-мерный куб в качестве подграфа, нужно сначала покурить каннабис на родине автора.

3.5.5 (Бем). Бем не обидится, если не все будут рисовать G(M).

3.5.6 (Виссер). (i) Это мое самое любимое упражнение. Покажем, что имеется единственный редекс R с одной вершиной в G(R).
Если R = (x: M) N, то M[x := N] = (x: M) N. Рассмотрим все случаи:
1) x не входит в M => M = R => R = (x: R) N - это не терм;
2) M = x => x[x := N] = R => N = R => R = (x: x) R - это не терм;
3) M = P Q;
4) M = y: M' => R = y: M'[x := N] - это не редекс.
Значит, R = (x: P Q) N =
= (P Q)[x := N] = P[x := N] Q[x := N] = (x: P Q) N => P[x := N] = x: P Q и Q[x := N] = N;
Рассмотрим все случаи для P:
1) x не входит в P => P = (x: P Q) - это не терм;
2) P = x => N = x: x Q;
3) P = y: P', но тогда в R было бы два редекса, а не один;
4) P = P1 P2 => P[x := N] = P1[x := N] P2[x := N] - это аппликация, а не абстракция.
Значит, R = (x: x Q) (x: x Q) -> (x: x Q) Q[x := x: x Q] => Q[x := N] = x: x Q.
Рассмотрим все случаи для Q:
1) x не входит в Q => Q = x: x Q - это не терм;
2) Q = x;
3) Q = Q1 Q2 => Q1[x := N] Q2[x := N] - это аппликация, а не абстракция;
4) Q = y: Q' => y: Q'[x := N] = x: x Q => Q'[x := N] = y Q, но тогда x не входит в Q.
Таким образом, R = (x: x x) (x: x x).
(ii) А вот это не самое любимое мое упражнение.

3.5.7-3.5.10. Бла-бла-бла про хитровытраханные графы и понятия редукции.
Может быть, они и полезные упражнения, но я иду дальше.

3.5.11. Будем писать M ^ N, если L ->> M и L ->> N для некоторого терма L. Докажем, что
(i) K I K ^ K I S: возьмем K (K I S) K;
(ii) (x: a x) b ^ (y: y b) a: возьмем (y: (x: y x) b) a;
(iii) (x: x c) c ^ (x: x x) c: возьмем (y: (x: x y) y) c;
(iv) (x: b x) c ^ (x: x) b c: возьмем (y, x: y x) b c;
(v) (x: b x (b x)) c ^ (x: x x) (b c): возьмем (z, y: (x: x x) (z y)) b c;
(vi) (x: b x) c ^ (x: x) (b c): возьмем I ((y, x: y x) b c);
(vii)* (Плоткин) ...ах, если бы я смог доказать, что не имеет места (x: b x (b c)) c ^ (x: x x) (b c), то я бы прочувствовал, что бета-редукция не обладает "перевернутым свойством CR".

3.5.12-3.5.15. Дальше в редукционные дебри углубляться не буду.
Я решил пробежаться еще раз по плану "Как читать Барендрегта". По дороге буду срать в вашу френд-ленту следующим содержимым. (Чтобы не переключаться все время еще и на греческую раскладку, воспользуюсь синтаксисом, похожим на MLC.)

2.4.1. Покажем, что следующие термы имеют нормальную форму:
(i) (y: y y y) ((a, b: a) I (S S)) = (y: y y y) I = I I I = I;
(ii) (y, z: z y) ((x: x x x) (x: x x x)) (w: I) = (w: I) ((x: x x x) (x: x x x)) = I;
(iii) S S S S S S S = S S (S S S S S) = S S (S S (S S S)), если заметить S S M S = S S (M S);
(iv)* S (S S) (S S) (S S) S S... сдаюсь без калькулятора.

2.4.2. Покажем, что следующие пары термов несравнимы:
(i) I # K, иначе I I M = K I M => M = I;
(ii) I # S, иначе I K M I = S K M I => K M I = K I (M I) => M = I;
(iii) x y # x x, иначе x, y: x y = x, y: x x => (x, y: x y) I M = (x, y: x x) I M => M = I.

2.4.3. Построим замкнутые термы M0, M1..., такие, что Mi # Mj для всех i != j:
Mi = x0, x1,..., xi: xi;
Mi = Mj, i > j => Mi y1 ... yj = Mj y1 ... yj => Mk = I, k = i - j;
Mk = I => Mk I ...(k-1)... I = I I ...(k-1)... I => x: I = I => (x: I) M = I M => I = M.

2.4.4. Покажем, что аппликация не ассоциативна, точнее x (y z) # (x y) z:
x (y z) = (x y) z => x, y, z: x (y z) = x, y, z: (x y) z => K (I M) I = (K I) M I => M = I.

2.4.5 (К. Е. Шаап). Пусть X = S I. Покажем, что X X X X = X (X (X X)), и вообще для всех n имеет место X^n X = X X~n:
n = 1: X X = X X;
n = 2: X X X = S I X X = I X (X X) = X (X X).
n > 2: X^n X = X X~n =>
X X~n+1 = X X~n X = (X^n X) X = X (X^n-1 X) X =
= S I (X^n-1 X) X = X ((X^n-1 X) X) = X (X X~n-1 X) =
= X (X X~n) = X (X^n X) = X^n+1 X.

2.4.6. Покажем, что не существует такого F, чтобы для любых M и N выполнялось бы F (M N) = M:
I I = (x: I) I => F (I I) = F ((x: I) I) => I = x: I => I M = (x: I) M => M = I.

2.4.7. Покажем, что существует такой M, что для любого N выполняется M N = M M, применив теорему о неподвижной точке:
M x = M M => M = x: M M => M = (f, x: f f) M;
M = W W, W = w: (f, x: f f) (w w);
M N = (f, x: f f) M N = (x: M M) N = M M.

2.4.8. Бла-бла-бла про одновременную подстановку.
В пизду эту болтологическую муть.

2.4.9. Покажем, что (x, y: M) N = y: (x: M) N:
(x, y: M) N = (y: M)[x := N] = y: M[x := N] = y: (x: M) N.

2.4.10. Используем теорему о неподвижной точке, чтобы построить терм M, такой, что
(i) M = M S:
M = (f: f S) M, M = W W, W = w: (f: f S) (w w);
M = (f: f S) M = M S;
(ii) M I S S = M S:
M = (f, s: f I s s) M, M = W W, W = w: (f, s: f I s s) (w w);
M S = (f, s: f I s s) M S = M I S S.

2.4.11. Построим F, такой, что F I = x и F K = y, вспомнив упражнение 2.4.2 (i):
если F = f: f I M N, тогда
F K = K I M N = N => N = y;
F I = I I M N = M N => M = z: x;
cтало быть, F = f: f I (z: x) y.

2.4.12 (Якопини). Пусть w3 = x: x x x и W3 = w3 w3. Покажем, что
(i) I # w3, иначе I (x, y: I) = w3 (x, y: I) => x, y: I = I => (x, y: I) I M = I I M => I = M;
(ii) I # W3, иначе I = W3 => I w3 = W3 w3 => w3 = W3 w3, а если заметить
W3 = w3 w3 w3 = W3 w3, то W3 = I => I = I w3, что возвращает нас к пункту (i).

2.4.13. Покажем, что для любой абстракции M имеет место равенство x: M x = M:
M = y: N;
x: M x = x: (y: N) x = x: N[y := x] = y: N = M.

2.4.14. Пусть M = x: x (y: y y) (y: y y). Покажем, что M I-разрешим:
M (x, y: x I (y I)) = I I (I I) = I.

2.4.15 (почему-то есть только в оригинале, Минц не перевел). Suppose a symbol of the lambda calculus alphabet is always 0.5 cm wide. Let us write down a lambda term with length less than 20 cm having a normal form with length at least 10^(10^10) lightyear. The speed of light is c = 3 * 10^10 cm/sec.
The length of a Church numeral in cm is about the natural number it represents, so it is more than enough to write a term that reduces to the Church numeral for the number 2^(2^(2^(2^(2^2)))). The exponential function for Church numerals is just application, so the term can be (x: x x x x x x) (f, x: f (f x)) which is, in turn, much shorter than requested.
http://arxiv.org/pdf/1304.2290v7.pdf

Interaction system of MLC implements token-passing nets and embeds read-back mechanism.

Specifically, starting from initial configuration that encodes a λ-term

<x | r[ ](x) = y, Γ(M, y)>,

the r(ead) agent will trigger duplication and application:

s[c(x, rC[ ](y)), x] >< rC[ ][y];
@[λ(x, rC[ ](y)), x] >< rC[ ][y];

and will construct normal form of the encoded λ-term:

aM >< λ[rM [ ](x), x];
aM >< rC[ ][aC[M]];
λ[ay, rC[λy.[ ]](x)] >< rC[ ][x], where y ∈ Λ is a new variable.

If normal form N of the λ-term exists, interaction results in configuration

<aN | ∅>

which consists of only one a(tom) agent in its interface.
We work in interaction calculus. However, we will need a special non-deterministic agent Amb. To represent this agent, we also extend interaction calculus, but in a more conservative fashion than it was previously suggested in the original paper.

Instead, we prepend the list of its auxiliary ports with its extra principal port and introduce conversion for configurations to represent non-deterministic behavior of Amb:

<t1,..., tn | t = Amb(u, v, w), Δ> = <t1,..., tn | u = Amb(t, v, w), ∆>.

We assume that any interaction system’s signature Σ is implicitly extended by Amb with Ar(Amb) = 3, while its set of rules is implicitly extended with

α[x1,..., xn] >< Amb[y, α(x1,..., xn), y].
https://www.scribd.com/doc/260727360

The goal of our Macro Lambda Calculus project (MLC) is to encode λ-terms into interaction nets. Its software implementation will accept input in the notation similar to λ-calculus allowing macro definitions. Output is similar to interaction calculus and is suitable for our Interaction Nets Compiler program (INC). In this paper, we describe the interaction system for call-by-need evaluation and the mechanism of encoding λ-terms into this system which MLC is based on.

(The paper provides formal definition for the interaction system and encoding of λ-terms that were previously successfully tested for Y (K M) = M using INC.)
Encoding ω Α (K ω), where ω ≡ λx.x x, A ≡ λx.λf.f (x x f), and K ≡ λx.λy.x:

$ cat init.txt 
term = \read_{strdup("")}(\print);

term = \apply(\apply(omega1, a), \apply(k, omega2));

omega1 = \lambda(x1, \apply(\amb(y1, \share(x1, z1), z1), y1));
omega2 = \lambda(x2, \apply(\amb(y2, \share(x2, z2), z2), y2));

a = \lambda(self, \lambda(func, \apply(func1, rec)));
rec = \apply(\apply(self1, self2), func2);
self1 = \amb(self2, \share(self, back1), back1);
func1 = \amb(func2, \share(func, back2), back2);

k = \lambda(x, \lambda(\erase, x));
$ make
        printf '%s\n\n$$\n\n%s\n\n$$\n\n%s\n' >test.in \
                "`cat rset.txt`" \
                "`cat init.txt`" \
                "`cat tail.txt`"
        inc <test.in
        mv in.tab.c test.c
        cc    -o test test.c 
        valgrind ./test
==20346== Memcheck, a memory error detector
==20346== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==20346== Command: ./test
==20346== 
v1: (v1 (v1))
==20346== 
==20346== HEAP SUMMARY:
==20346==     in use at exit: 0 bytes in 0 blocks
==20346==   total heap usage: 606 allocs, 606 frees, 23,934 bytes allocated
==20346== 
==20346== All heap blocks were freed -- no leaks are possible
==20346== 
==20346== For counts of detected and suppressed errors, rerun with: -v
==20346== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
$ 
https://gist.github.com/codedot/24f277ef4df5828c70a8

Call-by-need evaluation strategy using non-deterministic extension in Interaction Nets Compiler:

@@ -14,10 +14,18 @@
 \print {
 	/* Output results of read-back. */
 	puts(RVAL);
-	exit(EXIT_SUCCESS);
+	free(RVAL);
 } \atom;
 
 \read[a] {
+	/* Unshare variable. */
+} \share[\copy(b, \read_{LVAL}(a)), b];
+
+\read[a] {
+	/* Initiate application. */
+} \apply[\lambda(b, \read_{LVAL}(a)), b];
+
+\read[a] {
 	/* Read back abstraction. */
 } \lambda[\atom_{var(1)}, \read_{ABST(LVAL, var(0))}(a)];
 
@@ -29,10 +37,6 @@
 	/* Read back an atom. */
 } \atom;
 
-\bind[a, \atom_{RVAL}, a] {
-	/* Bind variable to an atom. */
-} \atom;
-
 \copy[\atom_{RVAL}, \atom_{strdup(RVAL)}] {
 	/* Copy an atom. */
 } \atom;
@@ -47,40 +51,56 @@
 } \atom;
 
 \lambda[a, b] {
+	/* Unshare variable. */
+} \share[\copy(c, \lambda(a, b)), c];
+
+\lambda[a, b] {
+	/* Initiate application. */
+} \apply[\lambda(c, \lambda(a, b)), c];
+
+\lambda[a, b] {
 	/* Apply a closed term. */
 } \lambda[a, b];
 
+\copy[a, b] {
+	/* Unshare variable. */
+} \share[\copy(c, \copy(a, b)), c];
+
+\copy[a, b] {
+	/* Initiate application. */
+} \apply[\lambda(c, \copy(a, b)), c];
+
 \copy[\lambda(a, b), \lambda(c, d)] {
 	/* Initiate copy of a closed term. */
 } \lambda[\dup(a, c), \dup(b, d)];
 
-\bind[a, \lambda(b, c), a] {
-	/* Bind variable to a closed term. */
-} \lambda[b, c];
+\dup[\amb(a, \share(b, c), c), \amb(d, \share(e, f), f)] {
+	/* Duplicate sharing. */
+} \share[\dup(b, e), \dup(a, d)];
+
+\dup[\apply(a, b), \apply(c, d)] {
+	/* Duplicate application. */
+} \apply[\dup(a, c), \dup(b, d)];
 
 \dup[\lambda(a, b), \lambda(c, d)] {
-	/* Duplicate abstraction or application. */
+	/* Duplicate abstraction. */
 } \lambda[\dup(a, c), \dup(b, d)];
 
-\dup[\copy(a, b), \copy(c, d)] {
-	/* Duplicate copy initiator. */
-} \copy[\dup(a, c), \dup(b, d)];
-
-\dup[\bind(a, b, c), \bind(d, e, f)] {
-	/* Duplicate variable binding. */
-} \bind[\dup(a, d), \dup(b, e), \dup(c, f)];
-
 \dup[a, b] {
 	/* Finish duplication. */
 } \dup[a, b];
 
 \erase {
-	/* Erase abstraction or application. */
-} \lambda[\erase, \erase];
+	/* Erase sharing. */
+} \share[a, a];
 
 \erase {
-	/* Erase variable binding. */
-} \bind[\erase, \erase, \erase];
+	/* Erase application. */
+} \apply[\erase, \erase];
+
+\erase {
+	/* Erase abstraction. */
+} \lambda[\erase, \erase];
 
 \erase {
 	/* Erase copy initiator. */
@@ -96,17 +116,12 @@
 
 $$
 
-{"Application"} = result;
-function = \lambda(argument, result);
-shared1 = \copy(first1, second1);
-shared2 = \copy(first2, second2);
-shared3 = \copy(first3, second3);
-
-{"Abstraction"} = bind0;
-bv1 = \bind(bind1, fv1, bind0);
-bv2 = \bind(bind2, fv2, bind1);
-bv3 = \bind(bind3, fv3, bind2);
-bindn = \lambda(variable, body);
+{"Application"} = \apply(function, argument);
+first1 = \amb(second1, \share(shared1, back1), back1);
+first2 = \amb(second2, \share(shared2, back2), back2);
+first3 = \amb(second3, \share(shared3, back3), back3);
+
+{"Abstraction"} = \lambda(variable, body);
 
 term = \read_{strdup("")}(\print);
 term = {"Encoding"};
В компиляторе сетей взаимодействия INC (Interaction Nets Compiler) теперь есть поддержка недетерминированного расширения в виде специального бинарного агента Amb с двумя главными портами. Это расширение необходимо при реализации стратегии "call-by-need" для λ-исчисления.

Синтаксис для недетерминированного расширения сетей взаимодействия выбран наиболее консервативным образом (по отношению к основной части языка программирования) и отличается от того, который встречается в литературе: при описании правил взаимодействия и начальной конфигурации агент Amb обозначается как тернарный, первый дополнительный порт которого выполняет роль второго главного. К примеру, следующее изменение не меняет поведение тестовой программы:

 
 $$
 
-\fan_{1}(x, x) = \fan_{2}(\erase, {&wire1});
+\fan_{1}(x, x) = \amb({&wire1}, \fan_{2}(\erase, y), y);
 
 {&wire2} = \fan_{3}({&erase}, \erase);
 
Decoding extension for Implementation of Closed Reduction using Interaction Nets Compiler:

${
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *var(int fresh);
char *append(char *format, char *buf, char *str);

#define ABST(BUF, STR) append("%s%s: ", (BUF), (STR))
#define APPL(BUF, STR) append("%s%s ", (BUF), (STR))
#define ATOM(BUF, STR) append("%s(%s)", (BUF), (STR))
}$

\print {
	/* Output results of read-back. */
	puts(RVAL);
	exit(EXIT_SUCCESS);
} \atom;

\read[a] {
	/* Read back abstraction. */
} \lambda[\atom_{var(1)}, \read_{ABST(LVAL, var(0))}(a)];

\lambda[\read_{APPL(strdup(""), RVAL)}(a), a] {
	/* Read back application. */
} \atom;

\read[\atom_{ATOM(LVAL, RVAL)}] {
	/* Read back an atom. */
} \atom;

\bind[a, \atom_{RVAL}, a] {
	/* Bind variable to an atom. */
} \atom;

\copy[\atom_{RVAL}, \atom_{strdup(RVAL)}] {
	/* Copy an atom. */
} \atom;

\dup[\atom_{RVAL}, \atom_{strdup(RVAL)}] {
	/* Duplicate an atom. */
} \atom;

\erase {
	/* Erase an atom. */
	free(RVAL);
} \atom;

$$

term = \read_{strdup("")}(\print);
term = {"Encoding"};

$$

char *var(int fresh)
{
	static int id;

	char buf[BUFSIZ];

	if (fresh)
		++id;

	sprintf(buf, "v%d", id);
	return strdup(buf);
}

char *append(char *format, char *buf, char *str)
{
	size_t size = strlen(format) + strlen(str);
	char *result = malloc(strlen(buf) + size);

	sprintf(result, format, buf, str);

	free(buf);
	free(str);
	return result;
}
An Interaction Net Implementation of Closed Reduction by Ian Mackie using Interaction Nets Compiler:

\lambda[a, b] {
	/* Apply a closed term. */
} \lambda[a, b];

\copy[\lambda(a, b), \lambda(c, d)] {
	/* Initiate copy of a closed term. */
} \lambda[\dup(a, c), \dup(b, d)];

\bind[a, \lambda(b, c), a] {
	/* Bind variable to a closed term. */
} \lambda[b, c];

\dup[\lambda(a, b), \lambda(c, d)] {
	/* Duplicate abstraction or application. */
} \lambda[\dup(a, c), \dup(b, d)];

\dup[\copy(a, b), \copy(c, d)] {
	/* Duplicate copy initiator. */
} \copy[\dup(a, c), \dup(b, d)];

\dup[\bind(a, b, c), \bind(d, e, f)] {
	/* Duplicate variable binding. */
} \bind[\dup(a, d), \dup(b, e), \dup(c, f)];

\dup[a, b] {
	/* Finish duplication. */
} \dup[a, b];

\erase {
	/* Erase abstraction or application. */
} \lambda[\erase, \erase];

\erase {
	/* Erase variable binding. */
} \bind[\erase, \erase, \erase];

\erase {
	/* Erase copy initiator. */
} \copy[\erase, \erase];

\erase {
	/* Erase duplicator. */
} \dup[\erase, \erase];

\erase {
	/* Finish erasing. */
} \erase;

$$

{"Application"} = result;
function = \lambda(argument, result);
shared1 = \copy(first1, second1);
shared2 = \copy(first2, second2);
shared3 = \copy(first3, second3);

{"Abstraction"} = bind0;
bv1 = \bind(bind1, fv1, bind0);
bv2 = \bind(bind2, fv2, bind1);
bv3 = \bind(bind3, fv3, bind2);
bindn = \lambda(variable, body);
$ cat >hello.in 
${
#include <stdio.h>
}$

\alpha {
        printf("%s %s!\n", LVAL, RVAL);
} \beta;

$$

\alpha_{"Hello"} = \beta_{"World"};

$$

#include <stdlib.h>

inagent *inaux(void *aux, void *offline)
{
        return NULL;
}

int main()
{
        interact();
        return 0;
}
$ inc <hello.in
$ c99 in.tab.c
$ ./a.out
Hello World!
$ 
В компиляторе сетей взаимодействия INC (Interaction Nets Compiler) теперь есть поддержка начальной конфигурации. Конфигурация описывается на языке, близком к исчислению взаимодействия. Эта функциональность должна сильно упростить проект MLC: текст на языке MLC (Macro Lambda Calculus) можно будет целиком транслировать в исходный код для INC, а уже оттуда - в исполняемый код на Си. Трансляцию из MLC в INC можно сделать с помощью компактного представления λ-термов в сетях взаимодействия и механизма "readback" в виде системы взаимодействия с побочными действиями.

Ниже пример работы INC.

$ inc <example.in
$ c99 in.tab.c
$ ./a.out
inaux: rewired
inaux: returns
inaux: returns
inaux: returns
fan_1 >< fan_2
fan_3 >< fan_3
erase >< fan_3
fan_3 >< fan_3
erase >< erase
erase >< erase
$ 
В связи с идеей распределенных сетей взаимодействия, хотелось бы вернуться к проекту компилятора inet, который используется в MLC для механизма read-back. Приложение было реализовано уже после подготовки формального описания проекта и строго следуя проектной документации; имели место лишь мелкие уточнения и исправления по ходу работы.

Как модифицировать компилятор inet, чтобы получить вместо него распределенную универсальную P2P-сеть взаимодействия? Обозначим гипотетическую распределенную версию inet как p2pinet.

P2P-сеть всегда основана на дублировании информации для случаев, когда одна из нод отключается от сети. Заметим, что стэк активных пар, используемый в проекте inet, - это исчерпывающая информация о редуцируемой сети взаимодействия. Чаще всего (псевдо-)активная пара состоит не из двух агентов, а из агента и (псевдо-)агента wire, который служит для разыменования. Последнее в случае P2P-сети служило бы простым запросом на дублирование информации.

В p2pinet активные пары технически нигде не хранятся в явном виде, и ноды могут даже не знать, активна ли данная конкретная связь между одним идентификатором (портом) и каким-то другим в сети. Обмен информацией и (неявное) создание новых активных пар произходило бы в такой сети с помощью операции разыменования, а собственно применение правил взаимодействия - локально на нодах ввиду нескольких причин.

Во-первых, правила могут быть приватными для группы нод. Во-вторых, правила могут иметь побочные действия, как в проекте inet. В-третьих, набор правил может быть бесконечным и очень сложным. Наконец, одно из правил может быть вычислительно затратно, например вычисление n-ного простого числа.

Следует подчеркнуть, что сами по себе сети взаимодействия неэффективны с вычислительной точки зрения - существующие процессоры справляются с вычислительными задачами несравнимо лучше. Но оптимальная редукция, вопреки названию, вообще не рассматривает вопрос о том, как проводить собственно редукцию и не предлагает никаких решений на этот счет. Вместо этого, она предлагает стратегию для объединения работы (optimal sharing). Последнее и можно было бы позаимствовать от систем взаимодействия для оптимального разделения работы и данных между частями распределенной вычислительной системы.

Конкретно эту задачу сети взаимодействия и решают на качественном уровне. Они служат своеобразным планировщиком задач, который уже, в свою очередь, управляет остальными процессорами как устройствами, чем-то похожим на OpenCL образом. Однако, в отличие от OpenCL (который тоже, в принципе, может быть использован в распределенной сети), сети взаимодействия позволили бы делать и само планирование децентрализованным, при этом избегая лишних обмена данных и дублирования работы.

Наброски )
Так называемые жесткие комбинаторы принадлежат более узкому классу graph relabeling systems, чем graph rewriting systems, к которым относятся сети взаимодействия в общем случае. По научным меркам, они введены буквально вчера - в нулевых. По их поводу написано полторы статьи, а наивная реализация в современных FPGA/ASIC и для жестких комбинаторов не очень многообещающая, так как агенты одновременно служат и для вычисления и для хранения данных, что слишком затратно для FPGA/ASIC, где суммарный объем памяти регистров всегда на порядок, если не на два, меньше доступной на той же доске встроенной RAM. У последнего неизбежно оказывается бутылочное горлышко.

В определенной перспективе жесткие сети взаимодействия - логичное продолжение развития процессоров. Но на данном этапе этот подход кажется слишком далеким от практики, - возможно, им потребуется еще несколько лет побыть в академических кругах. Впрочем, коммерческие компании типа ARM и сами метят в ту же сторону. К сожалению, пока это только пресс-релизы и провалившиеся проекты, тем не менее, ключевые слова правильные - "clockless computation".

С точки зрения исследований, сети взаимодействия - гораздо более зрелое направление. Первые работы в эту стороны были в конце 80-ых, а первая система взаимодействия (Lamping) решала задачу оптимальной по Levy редукции, даже не называясь собственно системой взаимодействия (Lafont формализовал и обобщил такие системы уже пост-фактум). По данной теме доступны сотни статей, учебник (см. черновой перевод на русский язык соответствующей главы), монография, а также десятки различных программных реализаций, хотя большинство из них представляет собой лишь приложения к публикациям.

В отличие от жестких комбинаторов, сети взаимодействия в чистом виде неприменимы для архитектуры процессоров из-за необходимой перезаписи графов. Однако, возможен совсем другой подход.

Интернет позволяет передавать петабайты данных одновременно по тысячам разных каналов, когда как ни один из существующих суперкомпьютеров не может в одиночку добиться той же суммарной скорости в RAM. Можно посмотреть на интернет как на распределенную RAM. В связи с этим интересно было бы рассмотреть произвольные вычисления с использованием этой памяти.

Если пойти этим путем, естественным образом возникает проблема синхронизации. В случае BOINC она решается наличием серверов, проверяющих корректность работы. В случае Bitcoin проверка работы тривиальна и не отнимает у всех нод никакого существенного времени ценой дублирования десятков гигабайт информации на всех нодах. Заметим, что в обоих упомянутых случаях работа заранее выбрана так, что ее можно легко проверить, а также строго выполняется единая последовательность событий.

Если же мы хотели бы иметь децентрализованную вычислительную сеть для произвольной работы без требования единого timeline, то неизбежно возникают проблемы локальности и конфлюэнтости. К счастью, системы взаимодействия как раз и обладают последними двумя свойствами, давая возможность продолжать вычисления оптимальным образом без необходимости немедленной синхронизации.


Один из вариантов того, как можно читать классическую монографию по λ-исчислению [1]:

параграф 2.1;
упр. 2.4.1 (i)-(iii), 2.4.2-2.4.13;
упр. 2.4.15 (только в оригинале [2]);
параграф 2.2;
упр. 2.4.14;

параграфы 3.1-3.3;
упр. 3.5.1 (v), 3.5.1 (i), 3.5.6 (i), 3.5.2, 3.5.3, 3.5.11;
параграфы 13.1-13.2 до приложения 13.2.3 включительно;

часть II (главы 6-10);

параграф 4.1;
упр. 4.3.2, 4.3.4;
главы 15 и 16.

В каком-то приближении именно этот материал изложен чрезвычайно кратко в [3] (по-русски).

[1] Х. Барендрегт. Ламбда-исчисление, его синтаксис и семантика. Москва, 1985.
[2] H. P. Barendregt. The Lambda Calculus, Its Syntax and Semantics. North-Holland, 1984.
[3] A. Salikhmetov. Lambda Calculus Synopsis. arXiv:1304.0558, 2013.
http://iospress.metapress.com/content/l5r8t7n6755286h1/

Universal Hard Interaction for Clockless Computation
Dem Glücklichen schlägt keine Stunde!
Sylvain Lippi

We give a self-contained presentation of Hard Interaction, a rewriting system on fixed graphs. We discuss the universality of natural subclasses of hard systems and highlight the main ideas that lead to a universal system with 7 rules called Hard Combinators.

Keywords: hard interaction nets, abstract machine, asynchronous computation, digital circuits, graph relabeling.
http://tinyurl.com/q9x9k4c

module smm(clk, dst, src, idx, val);
	input clk;
	input [3:0] dst;
	input [3:0] src;
	output [7:0] idx;
	output [7:0] val;

	reg [7:0] ram [0:255];

	wire [7:0] map [0:15];

	assign map[0] = 8'b0;

	genvar i;

	generate
		for (i = 1; i < 16; i = i + 1) begin: anl
			if (i % 2)
				assign map[i] = map[i / 2] + 1;
			else
				assign map[i] = ram[map[i / 2]];
		end
	endgenerate

	assign idx = map[dst];
	assign val = map[src];

	always @(posedge clk)
		ram[idx] <= val;
endmodule

`ifdef SIM
module sim;
	reg clk = 1'b0;
	reg [3:0] dst = 4'b1;
	reg [3:0] src = 4'b1;

	wire [7:0] idx, val;

	smm blk(clk, dst, src, idx, val);

	always begin
		if ($time >= 20)
			$finish;

		#1 $display(val);
		#1 clk <= ~clk;
		#1 clk <= ~clk;
		#1 src <= 4'b101;
	end
endmodule
`endif
module brain (clk, sensor, effect);
	parameter nbit = 12;
	parameter size = 2 ** nbit;
 
	input clk;
	input [7:0] sensor;
	output [7:0] effect;
 
	reg sig [0:size - 1];
	reg trace [0:size - 1];
	reg [nbit - 1:0] ram [0:size - 1];
 
	assign effect[3:0] = {sig[3], sig[2], sig[1], sig[0]};
	assign effect[7:4] = {sig[7], sig[6], sig[5], sig[4]};
 
	integer i;
 
	initial for (i = 0; i < size; i = i + 1) begin
		trace[i] = 0;
		sig[i] = 0;
		ram[i] = i;
	end
 
	always @(posedge clk) begin
		for (i = 0; i < 8; i = i + 1)
			sig[ram[i]] <= sensor[i];
 
		for (i = 0; i < size; i = i + 1) begin
			case ({trace[i], sig[ram[i]]})
			2'b00: ram[i] <= ram[i] + 1;
			2'b01: sig[i] <= 1;
			2'b10: sig[i] <= 0;
			2'b11: ram[i] <= ram[i] - 1;
			endcase
 
			trace[i] <= sig[ram[i]];
		end
	end
endmodule
 
`ifdef SIM
module sim;
	reg clk = 0;
	reg [7:0] key;
	wire [7:0] led;
 
	brain core(clk, key, led);
 
	integer c;
 
	always begin
		c = $fgetc(32'h8000_0000);
		if (-1 == c)
			$finish;
 
		key = c[7:0];
		#1 clk = ~clk;
		#1 clk = ~clk;
		$displayb(led);
	end
endmodule
`endif
`define OPZ 4'b1111
`define OPA 4'b0001
`define OPL 4'b0010
`define OPN 4'b0100
`define OPS 4'b1000

`define BIT 15
`define SIZE (2 ** `BIT)

module ram0 (clk, op, led);
	input clk;
	input [3:0] op;
	output [7:0] led;

	reg [`BIT - 1:0] ram [0:`SIZE - 1];
	reg [`BIT - 1:0] n, z;

	assign led = z[7:0];

	always @(posedge clk) begin
		case (op)
		`OPZ: z = 0;
		`OPA: z = z + 1;
		`OPL: z = ram[z];
		`OPN: n = z;
		`OPS: ram[n] = z;
		endcase
	end
endmodule

`ifdef SIM
module sim;
	reg clk = 0;
	reg [3:0] op;
	wire [7:0] led;

	ram0 core(clk, op, led);

	integer ch;

	always begin
		ch = $fgetc(32'h8000_0000);
		case (ch)
		"z", "Z": op = `OPZ;
		"a", "A": op = `OPA;
		"l", "L": op = `OPL;
		"n", "N": op = `OPN;
		"s", "S": op = `OPS;
		-1: $finish;
		default: op = 0;
		endcase

		if (op) begin
			#1 clk = ~clk;
			#1 clk = ~clk;
			$displayb(led);
		end
	end
endmodule
`endif

Profile

Anton Salikhmetov

November 2018

S M T W T F S
    123
45678 910
11121314151617
18192021222324
252627282930 

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Aug. 13th, 2025 07:41 pm
Powered by Dreamwidth Studios