Как стать автором
Обновить

Автоматы и разумное поведение. Основные положения концепции (подхода) Н.М. Амосова

Время на прочтение121 мин
Количество просмотров6.7K



В продолжении одной из тем, поднятых в публикации Александра Ершова (Ustas) «Нейросетевой визуальный поиск», предлагаю читателям Хабра погрузиться в мир концепции Н.М. Амосова, ее $i$-моделей, М-сетей и автоматов. Как я надеюсь, именно они — наиболее вероятные кандидаты на роль «серебряной пули», которая позволит энтузиастам «сильного интеллекта» или, в другой терминологии, «искусственного разума» приблизиться к пониманию путей его реализации.


В данной статье автор попытался предельно сжато (конспективно) изложить основные положения концепции Николая Михайловича Амосова. Этот подход достаточно детально изложен в коллективной монографии «Автоматы и разумное поведение. Опыт моделирования», авторами которой был Н.М. Амосов и его соратники: A.M. Касаткин, Л.М. Касаткина и С.А. Талаев. Могу сказать, что это единственная монография, из всех работ по теме «искусственного разума», с которыми я смог познакомиться до сегодняшнего дня, содержащая ясное, обстоятельное, всестороннее, систематическое и в тоже время убедительное, а, в отдельных местах — даже высокохудожественное (говорю это без малейшей доли иронии) — изложение теоретических основ авторской концепции «искусственного разума», а также полученных на ее основе экспериментальных результатов.


Обращаюсь ко всем, у кого есть задор, жгучий интерес к теме «искусственного разума», а также желание поближе познакомиться с подходом Н.М. Амосова — читайте дальше...


Предисловие


Делайте то, во что верите, и верьте в то, что делаете. Все остальное – пустая трата энергии и времени.
Нисаргадатта Махарадж

Работа, изданная уже более полувека назад (1973), казалось бы, не может представлять серьезного интереса для современных исследователей и разработчиков систем искусственного интеллекта: с тех пор в таких областях как психология, нейрофизиология, информатика и др. накопилось немало фактов и достижений которые впрямую касаются существа рассматриваемых в работе вопросов и способных перечеркнуть полученные авторами результаты. Автор статьи придерживается иного мнения — это не так. Да, работы Н.М. Амосова — подзабылись, на них практически никто не ссылается (я даже предполагаю, что найдутся читатели, которые впервые узнали из этого предисловия имя Николая Михайловича Амосова). Его подход к моделированию расценивался в научной среде даже в момент публикации [3] как устаревший и чересчур трудоемким (см. например, предисловия А.Г. Ивахненко к книгам Н.М. Амосова). Несмотря на это как мне стало известно, работы в этом направлении продолжались коллективом единомышленников, во главе с Н.М. Амосовым, практически, до середины 1990-х. История науки и техники знает немало примеров, когда давно и, казалось бы, навсегда отвергнутые и забытые идеи, гипотезы и новаторские разработки, после достижения обществом определенного интеллектуального рубежа вновь возрождались, становились востребованными и оказывались «краеугольным камнем» нового научного и технологического прорыва. Не буду приводить примеры, они достаточно хорошо известны. Это, по мнению автора, в полной мере относиться к концепции Н.М. Амосова. Надеюсь, что работы, проводимые в рамках концепции Н.М. Амосова, в ближайшее время в той или иной форме возродятся и уверен, что в перспективе, их ждет успех.


Также требуется сделать следующие предварительные замечания:


1. Автор не будет рассматривать методический подход авторов концепции, рассмотренный ими в первой части рассматриваемой работы. Считаю, что он будет малоинтересен с практической точки зрения, хотя представляет собой методологический фундамент всей остальной работы. Также рассмотренная в этом разделе методология моделирования может вызвать у читателей не нужные в данном контексте возражения, что отвлечет их в сторону от основного содержания концепции Н.М. Амосова.


2. В статье будут рассмотрены лишь основные положения рабочей гипотезы и понятия концепции Амосова Н.М. Данную статью следует рассматривать лишь как конспективное изложение содержания второй части указанной работы «M-автоматы и программы разумной деятельности». Подчас, это будет простое перечисление основных тезисов авторов работы.


3. Везде в тексте статьи слово «концепция» или «подход» означает отсылку к содержанию работы [3]. Также «авторы» — это ссылка на авторов указанной работы [3].


4. Для облегчения и максимально точной передачи основного содержания концепции автор статьи отказался от дальнейших ссылок на саму работу. Все, авторство изложенного в статье материала принадлежит творческому коллективу Н.М. Амосова. Поэтому читатель, не желающий погружаться в чтение тяжелого по стилю текста данной статьи, может непосредственно перейти к изучению самой работы — ссылки на нее даны.


5. Также автор счел необходимым не разрывать концептуальную часть от описания возможной базовой реализации. Поэтому вся статья делиться на две больших части:
– Концепция и
– Реализация.
Вторая часть получилась достаточно большой из-за необходимости включения в нее кодов (их скорее нужно воспринимать как некий псевдокод), – с ней можно познакомиться опционально (при наличии большого желания).
В ближайшем будущем я планирую подготовить и опубликовать дополнительную статью, в которой хочу рассмотреть пример реализации простейшего М-автомата.


5. Замечания и пояснения автора статьи будут даваться либо во врезках (под спойлерами) к основному тексту, либо в комментариях в конце статьи.


6. Автор статьи будет считать свою миссию выполненной если:


– у читателя сложится предварительное представление о существе концепции Н.М. Амосова;
– у читателя возникнет интерес к самостоятельному изучению подхода Н.М. Амосова к проблематике искусственного интеллекта (искусственного разума), а возможно к его самостоятельному развитию;
– у читателя хотя бы на миг возникнет «разрыв» нейросетевого «шаблона» — современной парадигмы, рассчитывающей на статус «естественного» и единственно верного подхода к реализации модели человеческой психики.


Часть 1. Концепция


Рабочая гипотеза


В основе рабочей гипотезы лежат следующие основные представления:


1. Основу обработки информации в центральной нервной системе человека составляют иерархически организованные функциональные подсистемы.


2. Отношения между этими подсистемами есть отношения моделирования: каждая высшая подсистема отражает в состояниях некоторых своих элементов состояния нижележащих подсистем или, иначе говоря, моделирует их.


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


4. Модель — это некоторый функциональный элемент коры, фиксирующий определенную информацию, репрезентацию объектов и явлений внешнего и внутреннего мира человека.


5. Активирование такой модели сопровождается специфичным субъективным переживанием, например, осознанием чувства голода.


6. Та же самая модель может быть активирована не только возникновением соответствующего ей состояния (моделей) подкорковых образований, но и влиянием на нее других моделей, имеющихся в коре.


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

7. Активированная корковая модель, как правило, поддерживает соответствующее ей состояние подкорковых образований. Таким образом, между «корковыми» и «подкорковыми» моделями могут существовать как прямые, так и обратные связи.


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


9. Активность корковой модели переживается человеком субъективно в виде мысли о соответствующем объекте.


10. Активность корковой модели (мысль об определенном объекте) вызывает настройку анализаторов на его восприятие.


11. Активность модели может быть вызвана не только непосредственным восприятием соответствующего объекта, но и влиянием со стороны других моделей — «по ассоциации».


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


13. Результатом процессов переработки информации в коре является принятие тех или иных решений.


14. Можно говорить о мере использования той или иной информации в формировании решения. Для принятия определенного решения не обязательно использовать весь объем имеющейся информации или активации всех моделей. Одни модели могут играть в этом случае вспомогательную, другие — ведущую роль. Эта мера характеризуется степенью активности моделей. Чем активнее информационная модель в данный момент, тем большую роль в процессе формирования решения играет в этот момент соответствующая этой модели информация.


15. Активность в концепции характеризуется специальной числовой характеристикой — возбужденностью модели (arousal, ароузал). Возбужденность может изменяться от нулевого до некоторого максимального положительного значения. Если возбужденность модели в некоторый момент равна нулю, это означает, что соответствующая информация в данный момент не используется и модель является пассивным элементом системы, осуществляющим функцию хранения информации.


Замечание 2
Тем самым авторы неявно, говорят о том, что наличие модели — это уже актуальное присутствие в памяти определенной информации.

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


17. Рост возбужденности указывает на вовлечение модели в процесс подготовки решения, а значение возбужденности — на степень участия модели в этом процессе. Введенное таким образом понимание активности является несколько упрощенным, и в дальнейшем мы уточним его в ходе описания аппарата моделирования.


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


19. Чем выше возбужденность выбранной модели, тем сильнее ее воздействие на другие модели, связанные с ней.


20. Воздействие одной модели на другую может быть как усиливающим, так и тормозным.


21. Передача воздействий осуществляется по определенным каналам — связям, имеющимся между моделями.


22. Правила возникновения, исчезновения и изменения состояния связей определяются специфическими условиями для каждой конкретной функциональной подсистемы.


23. Авторами вводится также специфическая подсистема, названная ими система усиления-торможения (СУТ). Назначение данной подсистемы — изменение активности различных моделей в зависимости от степени важности соответствующей им информации и дает возможность быстро перераспределять эту активность при изменении внешней обстановки или состояния организма.


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


25. В приложении к моделированию психики могут быть выделены следующие модели:


– состояний нижележащих систем организма,
– внешнего мира (квалиа),
– эффекторных систем организма, их состояний и моторных программ (навыки),
– более высоких уровней — модели моделей, соответствующие абстрактным понятиям и представлениям (денотаты).


«Все эти модели могут связываться между собой, образуя в целом некоторую динамическую систему, которая отображает в своих состояниях внутренние побуждения и внешние условия организма, интегрируя их и вырабатывая на этой основе сложные поведенческие реакции» [3, стр. 57].


26. Следующим понятием, вводимым в концепции, является понятие программы. Под программой понимается определенная последовательность изменений системы (или ее подсистем), которая заложена в ее структуре и реализуется при определенном входном воздействии. Понятие программы непременно включает в себя указание на связь любой конкретной программы с определенным внешним воздействием, «запускающим» ее.


27. Совокупность программ системы характеризует, следовательно, совокупность ее возможных изменений во времени, присущих системе потенциально, определяемых ее структурой и реализующихся при поступлении на ее вход тех или иных воздействий.


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


29. Программы поведения подсистем, соответствующих связям между корковыми информационными моделями, также воспроизводятся с помощью специальных автоматов, разрабатываемых на основе содержательных представлений. Эти представления могут определять совокупность функциональных характеристик подсистемы (связи) и последовательность реализуемых ею операций. То и другое используется при построении алгоритма, задающего программы поведения связи.


30. Задание программ переработки информации осуществляется путем построения структур, элементами которых являются автоматы-модели и автоматы-связи. Процессы, протекающие в такой структуре, т.е. ее программы переработки информации, определяются программами поведения элементов. Структуры такого рода и являются автоматами, задающими программы переработки информации, или, иначе говоря, действующими моделями процессов переработки информации корой головного мозга.


31. Программы выполняются в ходе взаимодействия моделей, принадлежащих трем условным уровням:


– врожденными – жесткими;
– выработанных в процессе обучения;
– созданными в процессе самоорганизации.


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


Среду также можно задать в виде некоторого автомата, выходы которого соединены со входами действующей модели, а входы — с ее выходами.


М-сети и автоматы


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


В нем будут представлены сведения о функциональных единицах — информационных моделях объектов внешнего и внутреннего мира, а также сформулированы гипотетические правила взаимодействия и доминирования моделей. Определен вид статических и динамических характеристик моделей и связей между ними. Описаны программы формирования новых моделей. Развитая в этой части гипотетическая система не претендует на полное сходство с устройством и работой нервной системы, однако может быть использована при построении действующих моделей в рамках подхода, развитого Амосовым и его коллегами.


Основные понятия


1. Искусственные системы, строящиеся на основе концепции, реализуются в виде специфических сетей, названных М-сетями.


2. Узлы М-сети есть формальные элементы, которые ставятся в соответствие отдельным информационным моделям, названным $i$-моделями.


3. Связи между $i$-моделями отвечают предполагаемым связям между моделями.


4. С содержательной стороны $i$-модели могут быть поставлены в соответствие внешним или внутренним образам, понятиям и отдельным моторным программам. Поэтому с помощью М-сети можно представлять взаимосвязанные системы образов и понятий, предположительно используемые человеком в процессе мышления.


5. М-сеть является, таким образом, сетью с семантикой.


Дадим более формальные определения.


6. $i$-Модель есть формальный элемент, которому может быть поставлен в соответствие определенный образ, моторная программа или понятие, или иначе внутренняя информационная модель события внешнего или внутреннего мира.


6.1. С конструктивной точки зрения $i$-модель есть элемент некоторой структуры, который может находиться в ряде отличных друг от друга состояний.


6.2. С функциональной точки зрения $i$-модель есть набор некоторых операторов или алгоритмов переработки информации. Опишем $i$-модель как элемент, обладающий следующими свойствами.


6.2.1. Каждая $i$-модель имеет конечное число входов и один выход.


6.2.2. Каждая $i$-модель может находиться в состоянии возбуждения, степень которого характеризуется числовой величиной $\Pi$, называемой возбужденностью. Возбужденность некоторой $j$-ой $i$-модели в момент времени $t$ будем обозначать $\Pi_{j}^{t}$.


6.2.3. $i$-Модели могут быть соединены направленными связями, по которым возбуждение передается от одних $i$-моделей к другим.


7. Связь между $i$-моделями есть формальный элемент, нейрофизиологическим аналогом которого является феномен взаимозависимости возбуждений различных ансамблей или ассоциативная связь, при другом уровне рассмотрения. Основные свойства связи таковы:


7.1. Каждая связь может быть направлена от выхода $i$ какой-либо $i$-модели к одному из входов $j$ другой $i$-модели.


7.2. От выхода $i$-модели может отходить более чем одна связь, а к одному входу может подходить только одна связь.


7.3. Между двумя $i$-моделями может существовать только одна связь.


7.4. Каждая связь характеризуется упорядоченным набором параметров $R$, называемым проходимостью связи, или, для краткости, просто связью.


Проходимость связи, направленной от $i$-модели $j$ к $i$-модели $i$, в момент дискретного времени $t$ обозначается $R_{\text{ij}}^{t}$.


8. Семантика $i$-моделей задается двумя путями: во-первых, ее определяет соответствие, установленное между данной $i$-моделью и некоторым содержательным понятием, а, во-вторых — совокупность связей, соединяющих данную $i$-модель с другими.


9. Связь $R_{\text{ij}}^{t}$ есть вектор $R_{\text{ij}}^{t}=\left\langle r_{\text{ij}},{\widetilde{r}}_{\text{ij}},r_{(0)ij}{\widetilde{,r}}_{(0)ij} \right\rangle$, где параметры $r_{\text{ij}},{\widetilde{r}}_{\text{ij}}$ — усиливающий и тормозной компоненты проходимости связи, а параметры $r_{(0)ij},{\widetilde{r}}_{(0)ij}$ — остаточные составляющие этих компонент.


10. Возбуждение, поступающее по связи $R_{\text{ij}}^{t}$, может, как увеличивать, так и уменьшать, тормозить возбудимость $i$-модели $i$. Численной мерой этих воздействий и являются значения $r_{\text{ij}},{\widetilde{r}}_{\text{ij}}$. Эти значения могут меняться во времени, так что в случае $r_{\text{ij}} \gg {\widetilde{r}}_{\text{ij}}$ можно говорить об усиливающем характере связи $R_{\text{ij}}^{t}$, а в обратном случае — об ее тормозном характере. Остаточные составляющие связи всегда удовлетворяют соотношениям


${r_{(0)\text{ij}} \leq r}_{\text{ij}}\tag{1а}$


и


${\widetilde{r}}_{(0)\text{ij}} \leq {\widetilde{r}}_{\text{ij}}.\tag{1б}$


Они составляют долговременную память связей.


10.1. $R_{\text{ij}}^{t}=0$ обозначает равенство нулю всех компонент вектора $R_{\text{ij}}^{t}$, т.е. соответствующий вектор имеет вид $\left\langle 0.0,0.0,0.0,0.0 \right\rangle$.


10.2. Характеристика проторения есть функция, описывающая зависимость проходимости связи от возбужденностей соединяемых ею $i$-моделей:


$R_{\text{ij}}^{t}=R\left(\Pi_{i}^{t},\Pi_{j}^{t},R_{\text{ij}}^{t - 1},\mathrm{\Delta}^{t} \right)\tag{2}$


где $\mathrm{\Delta}^{t}$ — интегральная оценка качества функционирования М-сети. Эта оценка формируется с помощью специальных $i$-моделей сети. Она имеет смысл и может быть определена в тех случаях, когда на основе М-сети уже построена некоторая действующая модель — М-автомат, заданы цели его функционирования и определены критерии качества его работы. По отношению к этим критериям и формируется оценка $\mathrm{\Delta}^{t}$. Конкретные механизмы и правила вычисления значений $\mathrm{\Delta}^{t}$ должны быть определены при построении той или иной конкретной модели.


10.3. Непроторенной связью в момент $t$ назовем такую связь, если для нее $R_{\text{ij}}^{t} = 0$. Если для некоторой связи $R_{\text{ij}}^{t - 1} = 0$ и $R_{\text{ij}}^{t} > 0$, то можно говорить, что в момент $t$ произошло установление связи $R_{\text{ij}}^{t}$. Таким образом, установление является частным случаем проторения. Для непроторенной связи функция (2) может иметь иной вид, чем для проторенной, так что в случае установления связи она будет иметь вид


$R_{\text{ij}}^{t}=R_{1}\left(\Pi_{i}^{t},\Pi_{j}^{t},\mathrm{\Delta}^{t} \right)\tag{3}$


Функция (3) названа характеристикой установления.


10.4. Характеристика затухания связи есть набор функций, описывающих уменьшение значений ее параметров во времени. Эта характеристика описывает процесс уменьшения проходимости связи $R_{\text{ij}}^{t}$ при условии, что в некоторый начальный момент $t_{0}$ значение $R_{\text{ij}}^{t} \neq 0$ и во все последующие моменты времени $\Pi_{i}=0$ и $\Pi_{j}=0$. Для усиливающих и тормозных компонентов связи характеристики затухания имеют вид


$r_{\text{ij}}^{t}=r_{1}\left( r_{\text{ij}}^{t - 1},r_{(0)\text{ij}}^{t} \right)\tag{4а}$


${\widetilde{r}}_{\text{ij}}^{t}=r_{2}\left( {\widetilde{r}}_{\text{ij}}^{t - 1},{\widetilde{r}}_{(0)\text{ij}}^{t} \right)\tag{4б}$


Функции (4а) и (4б) описывают такой процесс затухания, при котором значения $r_{\text{ij}}^{t}$ и ${\widetilde{r}}_{\text{ij}}^{t}$, уменьшаясь, стремятся к значениям их остаточных составляющих. Для остаточных составляющих характеристики затухания


$r_{(0)\text{ij}}^{t} = r_{3}\left( r_{(0)\text{ij}}^{t - 1} \right)\tag{5а}$


${\widetilde{r}}_{(0)\text{ij}}^{t} = r_{4}\left( {\widetilde{r}}_{(0)\text{ij}}^{t - 1} \right)\tag{5б}$


таковы, что описывают стремление значений $r_{(0)ij}^{t}$ и ${\widetilde{r}}_{(0)ij}^{t}$ к нулю. Усиливающие и тормозные компоненты затухают во времени намного быстрее, чем их остаточные составляющие. Поэтому, начиная с момента $t_{0}$, проходимость $R_{\text{ij}}^{t}$ уменьшается сравнительно быстро («кратковременная память связей»). Затем значения $r_{\text{ij}}^{t}$ и ${\widetilde{r}}_{\text{ij}}^{t}$ достигают значений $r_{(0)ij}^{t}$ и ${\widetilde{r}}_{(0)ij}^{t}$, и, поскольку условия (1) должны всегда сохраняться, дальнейшее уменьшение $R_{\text{ij}}^{t}$ происходит в соответствии с характеристиками (5а) и (5б), т.е. существенно медленнее («долговременная память связей»).


10.5. Характеристика передачи связи определяет значение воздействия $E_{i}$ (${\widetilde{E}}_{i}$) на входе $i$-модели $i$ в зависимости от проходимости $r_{\text{ij}}^{t}$ (${\widetilde{r}}_{\text{ij}}^{t}$) связи между $i$-й и $j$$i$-моделями и величины возбужденности $\Pi_{j}$. В общем случае для $i$$i$-модели будут рассматриваться величины входного воздействия по усиливающим ($E_{i}$) и тормозным (${\widetilde{E}}_{i}$) связям:


$E_{i}=E(\Pi_{1}^{t},\Pi_{2}^{t},\ldots,r_{i1}^{t},r_{i2}^{t},...)\tag{6a}$


${\widetilde{E}}_{i} = \widetilde{E}( \Pi_{1}^{t},\Pi_{2}^{t},\ldots,{\widetilde{r}}_{i1}^{t},{\widetilde{r}}_{i2}^{t},...)\tag{6б}$


11. Характеристики $i$-модели.


11.1. В каждый момент времени каждая $i$-модель обладает определенной возбудимостью, под которой понимается способность $i$-модели отвечать собственным возбуждением на входное воздействие по усиливающим связям. Чем выше возбудимость $i$-модели, тем большей возбудимостью ответит она на постоянное входное воздействие. Возбудимость $i$-модели может изменяться во времени.


11.2. Возбудимость $i$$i$-модели определяется двумя параметрами. Один из них — порог возбуждения $i$-модели $\theta_{i}^{t}$, представляющий собой минимальное значение $E_{i}^{t}$ необходимое для возбуждения $i$$i$-модели в момент $t$. Другой параметр — условный коэффициент возбудимости $К$. Будем различать два значения $К$текущее ($K_{i}^{t}$), изменяющееся в зависимости от величины тормозных воздействий ${\widetilde{E}}_{i}^{t}$, и начальное ($K_{нi}^{t}$). Значения параметров $K_{нi}^{t}$ и $\theta_{i}^{t}$ могут изменяться во времени значительно медленнее, чем $K_{i}^{t}$.


11.3. Характеристика торможения есть функция, определяющая изменение возбудимости $i$-модели в зависимости от величины, суммарного воздействия на нее по тормозным связям:


$K_{i}^{t} = \Phi\left( K_{нi}^{t},{\widetilde{E}}_{i}^{t} \right)\tag{7}$


11.4. Характеристика затухания есть функция, определяющая изменение возбужденности $i$-модели во времени:


$\Pi_{i}^{t} = T\left(Pi_{i}^{t - 1},K_{нi}^{t} \right)\tag{8}$


Функция (8) описывает процесс уменьшения возбужденности при отсутствии воздействий на входах $i$-модели и характеризует «временную память возбуждений» $i$-моделей.


11.4. Характеристика возбуждения есть функция, определяющая значение возбужденности на выходе $i$-модели в зависимости от возбудимости $i$-модели и величины суммарного воздействия на нее по усиливающим связям:


$\Pi_{i}^{t}=\Pi\left( K_{i}^{t},\theta_{i}^{t},E_{i}^{t} \right)\tag{9}$


11.5. Характеристики гипертрофии и адаптации определяют значения $K_{нi}^{t}$ и $\theta_{i}^{t}$ в зависимости от возбужденности $i$-модели:


$\theta_{i}^{t}=\theta\left( \theta_{i}^{t - 1},\Pi_{i}^{t} \right)\tag{10a}$


$K_{нi}^{t}=K\left(K_{нi}^{t},\Pi_{i}^{t} \right)\tag{10б}$


Функции (10а) и (10б) таковы, что определяемые ими изменения параметров невелики в каждый момент дискретного времени. Эти функции задаются таким образом, чтобы для $i$-моделей, которые в течение длительного времени обладают малой возбужденностью, значение порога увеличивалось, а значение коэффициента возбудимости уменьшалось («адаптация»). В результате редко или слабо возбуждающиеся $i$-модели должны становиться трудновозбудимыми и мало влияющими на процессы в сети. Если $i$-модель возбуждается часто и сильно, функции (10а) и (10б) обеспечивают уменьшение значения ее порога и увеличение коэффициента возбудимости («гипертрофия»).


12. М-сеть есть совокупность $i$-моделей и связей между ними.


12.1. М-сеть является, по существу, статической моделью, отображающей взаимосвязь определенных образов и понятий, а также степень их участия в процессах формирования воспроизводимой деятельности.


12.2. Построение модели некоторого объекта с помощью М-сети в соответствии со спецификой этого объекта и целями моделирования заключается:


– определении совокупности $i$-моделей и их содержательных интерпретаций;
– задании связи между $i$-моделями или определении их начальной конфигурации в сети;
– задании исходного распределения $C$ их проходимостей;
– фиксации исходного значения параметров и вид характеристик $i$-моделей и связей;
– фиксации исходного распределения $J$ значений возбужденностей $i$-моделей.


В дальнейшем — в ходе функционирования М-сети все первоначально заданные элементы могут изменяться.


12.3. М-сеть может иметь иерархичную структуру, обеспечивающую возможность многоуровневой переработки информации. Связи между $i$-моделями различных уровней отражают родовидовые отношения соответствующих понятий. Кроме того, между $i$-моделями сети могут быть установлены «ассоциативные» связи.


12.4. В М-сети можно выделить непересекающиеся подмножества $i$-моделей, каждое из которых удовлетворяет требованию смысловой однородности, близости содержательных интерпретаций входящих в него $i$-моделей. Авторы назвали такие подмножества сферами М-сети. Например, к эмоциональной сфере должно относиться множество таких $i$-моделей, каждой из которых поставлена в соответствие представление об определенном эмоциональном состоянии психики. Можно выделять также сферы понятий, двигательных реакций и т.п. Взаимосвязь сфер осуществляется благодаря наличию связей между $i$-моделями, входящими в разные сферы.


Процессы в М-сети


Общая картина


Процессы в М-сети и собственно деятельность моделирующего автомата могут быть реально воспроизведены только после того, как будет задан некоторый алгоритм, обеспечивающий функционирование М-сети во времени или должно быть реализованы правила изменения состояний $i$-моделей и связей М-сети в соответствии с введенными характеристиками.


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


Пусть в некоторый момент $t - 1$ каждая из $i$-моделей сети имела определенную возбужденность. Используя характеристики передачи связей (6), можно определить для каждой $i$-модели суммарные значения поступающих в нее входных воздействий. Далее, по характеристике торможения (7) для каждой $i$-модели можно установить значение коэффициента ее возбудимости, а по характеристике затухания (8) — степень влияния, или «переноса», ее возбужденности в момент $t - 1$ на ее же возбужденность в момент $t$. И, наконец, с помощью характеристики возбуждения (9) для каждой $i$-модели можно найти значение того компонента ее возбужденности в момент $t$, который возникает как ответ на входное воздействие по усиливающим связям. Используя найденные величины, можно определить окончательное значение возбужденности каждой $i$-модели в момент $t$ с помощью специальной формулы пересчета, которая строится на основе отдельных характеристик (6-9) и в общем виде может быть представлена так:


$\Pi_{i}^{t}=\Phi\left(E_{i}^{t},{\widetilde{E}}_{i}^{t}{,\Pi_{i}^{t - 1},K}_{нi}^{t - 1},\theta_{i}^{t - 1} \right)\tag{11}$


Конкретный вид этого выражения может быть определен однократно при построении модели и не изменяется при ее применении к каждой из $i$-моделей данной М-сети, а может различаться для различных $i$-моделей, входящих в М-сеть. Но на протяжении всего функционирования М-сети не изменяется.


Выполнив пересчет к каждой из $i$-моделей сети, мы определим значения их возбужденностей в момент $t$. Будем полагать, что расчет возбужденности производится для всех $i$-моделей сети одновременно (синхронно). Повторяя пересчет, можно определить возбужденности в моменты $t + 1$, $t + 2$, $...$ Такие последовательные пересчеты и реализуются в любой модели, построенной с помощью М-сети.


Распространение возбуждения по имеющимся между $i$-моделями связям в сети моделируется выполнением таких последовательных пересчетов. Через некоторое время оказываются в разной степени возбужденными определенные (может быть, все) $i$-модели М-сети. При достижении возбуждения в некоторой $i$-модели, соответствующей некоторой реакции, — М-сеть вырабатывает действие на воспринятую ситуацию.


Действие модели изменяет среду, эти изменения фиксируются в сфере восприятия М-сети путем возбуждения новых $i$-моделей. Возбуждение распространяется по сети до момента выполнения условий действия. Затем выполняется новое действие и т.д. Таким образом, модель осуществляет в среде некоторое поведение.


В М-сети могут быть выделены $i$-модели, возбуждение которых можно интерпретировать как интегральную оценку состояния М-сети в каждый момент времени. Эти $i$-модели по замыслу авторов, должны соответствовать центрам общей оценки состояния организма. Авторы назвали такие модели: $i$-модели «приятно» (Пр) и «неприятно» (НПр). В процессе функционирования М-сети возбужденность $i$-моделей Пр и НПр постоянно изменяется, так что в любой момент времени может быть вычислено значение общей оценки состояния, например в виде $\mathrm{\Delta}^{t}=\Pi_{Пр}^{t}-\Pi_{НПр}^{t}$.


Замечание 3
Конкретный вид выражения, используемого для вычисления характеристики $\mathrm{\Delta}^{t}$, может быть совершенно иным.

В своей работе Н.М. Амосов и его соавторы при выборе вышеуказанного выражения скорее всего опирались на положения так называемой информационной теории эмоций П.В. Симонова [5, 6]. По крайней мере, они очень близки. Существуют и иные подходы числовой оценке эмоционального состояния. Но это другая не менее интересная и обширная тема.

Авторы отмечают, что состояние М-сети тесно связано с эффективностью вырабатываемых ею решений. Такая оценка $\mathrm{\Delta}$ характеризует не только состояние М-сети, но и эффективность поведения модели в целом.


Основные процессы


При функционировании М-сети должны быть реализованы следующие основные процессы:


1. В зависимости от «истории» возбуждений каждой $i$-модели и в соответствии с характеристиками гипертрофии и адаптации (10) изменяются параметры ее возбудимости $\theta$ и $K$. Соответственно изменяются и характеристики возбуждения, торможения и затухания $i$-модели.


2. В зависимости от «истории» совместных возбуждений каждой пары $i$-моделей и в соответствии с характеристиками проторения (2) и затухания (4) и (5) связей изменяется проходимость связей М-сети.


3. В ходе распространения возбуждений в М-сети и в соответствии с характеристикой установления (2) начинают функционировать новые, т.е. бывшие ранее непроторенными, связи между $i$-моделями. Таким образом, изменяется общая конфигурация связей сети.


4. В М-сети имеется некоторое множество $i$-моделей, в исходном состоянии не связанных ни друг с другом, ни с другими $i$-моделями сети. Для этих $i$-моделей не устанавливаются также соответствия с содержательными понятиями. Элементы такого рода, строго говоря, не являются $i$-моделями. Такие модели авторы назвали резервными элементами. На множестве резервных элементов может быть задан закон их случайного возбуждения.


Пусть в некоторый момент времени спонтанно возбуждается один из резервных элементов. В этот же момент оказывается возбужденной некоторая совокупность других $i$-моделей М-сети. Между $i$-моделями этой совокупности и возбудившимся резервным элементом в соответствии с характеристикой (2) устанавливаются новые связи. Элемент становится, таким образом, «представителем», т.е. $i$-моделью совокупности $i$-моделей, и получает некоторое семантическое значение, определяемое семантикой $i$-моделей, входящих в возбужденную совокупность.


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


По мнению авторов, процессы самообучения и самоорганизации могут приводить к образованию $i$-моделей «второго слоя», т.е. ансамблей из исходных $i$-моделей, которые, в свою очередь, могут образовывать ансамбли «третьего слоя» и т.д. Ансамбли такого рода можно рассматривать как новые функциональные элементы М-сети, а процесс их образования — как процесс формирования новых сложных понятий на базе имевшихся ранее.



Рисунок 1. Объект и его отражение в моделях разного уровня обобщенности. «Входы» и «выходы»: Эн — энергия; В — вещество. Воспроизведен рис. 12 из работы [4].

Процессы, описанные в п.п. 1 и 2, авторы называют процессами самообучения, а процессы, описанные в п.п. 3 и 4, назвали процессами самоорганизации М-сети.


Формальное определение М-сети


Введенные ранее характеристики элементов М-сети ($i$-моделей и связей) удобно разделять на несколько основных групп:


– характеристики пересчета;
– характеристики самообучения;
– характеристики самоорганизации.


В группу характеристик пересчета попадают те характеристики элементов сети, которые непосредственно используются при пересчете возбуждений и объединены в формуле пересчета (11). Сюда входят характеристики (6)-(9).


Группу характеристик самообучения составляют характеристики (3)-(5), описывающие процессы изменения проходимости связей и параметров возбудимости $i$-моделей.


Группу характеристик самоорганизации составляют характеристики установления связей (2) и законы спонтанного возбуждения резервных элементов М-сети.


Дадим формальное определение М-сети. М-сеть $\mu$ есть семерка:


$\mu = \left\langle P,S,R,L,F,C,I \right\rangle,\tag{12}$


где $P$ — множество $i$-моделей; $S$ — множество связей между $i$-моделями; $R$ — группа характеристик пересчета; $L$ — группа характеристик самообучения; $F$ — группа характеристик самоорганизации; $C$ — начальное распределение проходимостей связей; $I$ — начальное распределение возбуждений $i$-моделей.


Система усиления–торможения (СУТ)


Мотивация введения СУТ


Переработка информации в М-сети состоит в передаче возбуждения $i$-моделей по связям. Различные $i$-модели возбуждаются в разной степени. При переходе на другие $i$-модели возбуждение может погаснуть, если сопротивление связи достаточно велико – коэффициенты активации небольшие. Если же коэффициенты активации $i$-моделей, связанных между собой (образующих некоторый кластер), будут достаточно велики и приблизительно одинаковыми, то возбуждение какой-либо части кластера $i$-моделей приведет к монотонному росту возбужденностей всего кластера по мере работы М-автомата. Например, подобная ситуация возникшая в $i$-моделях, отвечающих за действия М-автомата, означала бы в определенных ситуациях активацию разнонаправленных воздействий М-автомата на внешнюю среду. Что не есть хорошо. В этом случае М-автомат может перейти в состояние «статического равновесия», когда в продолжении «всей последующей его жизни» будет наблюдаться рост возбужденности только одной группы $i$-моделей: М-автомат «поглощал» бы все имеющиеся у него «ресурсы» на выполнение «работы» только одного кластера $i$-моделей (М-автомат переходил бы некоторое стационарное состояние с высоким уровнем возбуждения одной из $i$-моделей или отдельной группы).



Хуже всего, если при этом еще существуют прямые или косвенные замкнутые цепочки связей между $i$-моделями – при этом велика вероятность образования у М-автомата положительной обратной связи: это приведет к ситуации неограниченного (экспоненциального) роста возбужденности такого кластера $i$-моделей. Возникновение положительной обратной связи между $i$-моделями возможно также при их «замыкании связей» через внешнюю среду: природным аналогом этого могут служить случаи возникновение так называемых «муравьиных кругов смерти» или круговое движение у гусениц походного шелкопряда (похожая ситуация может возникнуть не только в мире насекомых – см. например, круги у баранов; к сожалению, в более сложных ситуациях подобное «хождение по кругу» встречается и в человеческой популяции).


Муравьиный круг смерти

Круговое движение у гусениц походного шелкопряда
Замечание 4
Эти примеры из живой природы будут соответствовать постоянной высокой активности только одной $i$-модели (или выделенной группы).

В аналогичном случае может возникнуть нестабильность, при которой возбужденность отдельной $i$-модели начинала бы неограниченно возрастать до момента исчерпания всех необходимых и доступных для этого ресурсов (в том числе и М-автомата). Это режим самоуничтожения.



Для предотвращения возникновения сильной положительной обратной связи можно воспользоваться следующими защитными механизмами:


1. введение тормозных связей – коэффициенты торможения ${\widetilde{r}}$ – по своей сути это введение отрицательной связи от других $i$-моделей, в том числе отрицательной обратной связи;


2. увеличение значения порога возбуждения $i$-модели (ограничение снизу) – $\theta$;


3. введение верхнего порога возбуждения $i$-модели (ограничение сверху) – в концепции Н.М. Амосова в явном виде отсутствует, но может быть введена в характеристику возбуждения (см. выше п. 11.4 – выражение (9));


4. снижение уровня активации (возбуждения) с течением времени – характеристика торможения $K$ (см. выше п. 11.3 – выражение (8)).


За примерами не нужно далеко ходить… В том или ином виде эти механизмы замечены и изучены в нервной системе животных (различные типы торможения популяций нейронов), а математическими методами отрицательная обратная связь достаточно хорошо изучена в теории автоматического управления (ТАУ) и широко применяется в технических устройствах и системах.
Следует заметить, что при наличии отрицательной обратной связи может возникнуть ситуация «замкнутого круга» при которой возбуждение будет передаваться внутри достаточно узкого круга $i$-моделей (своеобразный «предельный цикл») – М-автомат выйдет на режим периодически повторяемых воздействий на внешнюю среду



и, возможно, без очень сильного воздействия извне не сможет из него выйти.


Что также есть «очень нехорошо»! М-автомат должен иметь возможность смены режима функционирования в зависимости от внутренних или внешних обстоятельств. А это означает необходимость его переключения на иную группу $i$-моделей (их возбуждении).


Замечание 5
Внешнее поведение в режиме «предельного цикла» будет сильно походить на поведение муравьев или гусениц в хороводе смерти, но внутренний механизм будет совершенно иным.

Возможность подобного поведения М-автомата Н.М. Амосов предполагал (кстати сказать, что подобное поведение авторским коллективом Н.М. Амосова было обнаружено на достаточно простой модели М-автомата «Робот Спиди») не считал, что введение локальных одноуровневых обратных связей, будет достаточно для преодоления вышеуказанных проблем. На врезке приведу цитату из его книги «Искусственный разум»:


Однако этой функцией дело не исчерпывается. Для правильной деятельности системы нужны еще некоторые этажи торможения, начиная от «местных» через «регионарные» (областные), которые координируют отношения между более крупными областями, и кончая «центральным», осуществляющим наиболее важную координацию между программами на уровне внимания – сознания. Такая деятельность не может быть выполнена только диффузной тормозной сетью, нужна некоторая иерархия в структуре и функции. Ее назначение – обеспечить доминирование между отдельными близкими моделями и в целом мозгу.
… Суть местного и общего доминирования заключается в следующем. Для того чтобы в данный момент осуществлялась преимущественно или исключительно какая-нибудь функция, одна из моделей по степени активности должна превосходить другие – родственные или окружающие – модели. С переменой ситуации во внешнем мире и внутри системы получает возможность для доминирования иная программа, соответствующая новым условиям. В простейших случаях из всех программ или моделей наиболее сильно возбуждается одна, а все прочие возбуждены в значительно меньшей степени либо заторможены. Через некоторое время возможно перераспределение уровня возбуждения. Для сложных систем, кроме одной сильно возбужденной модели, к которой привлечено внимание (или которая находится в сознании), должно быть предусмотрено несколько уровней активности. За их счет обеспечивается переработка информации в подсознании, с тем чтобы к последующему моменту перераспределения активности подготовились те модели, которые наиболее соответствуют изменившейся внешней и внутренней обстановке. Следовательно, задача состоит в том, чтобы создать систему, которая обеспечила бы разные степени активности (доминирования) для более и менее важных в данный момент моделей и дала бы возможность относительно быстро перераспределять эту активность при изменении обстановки. При этом нужно не только выделить одну – главную – модель, но и обеспечить несколько степеней активности для других моделей – в зависимости от их важности.
Амосов Н.М. ([2], стр. 49-51)

Таким образом, Н.М. Амосов пришел к необходимости введения специальной отдельной функциональной подсистемы ($i$-модели) – системы усиления-торможения (СУТ), обеспечивающей перераспределение активности (и соответственно ресурсов М-автомата) между отдельными высокоактивными $i$-моделями в пользу наиболее возбужденной $i$-модели.


Метафорой к необходимому типу смены режимов функционирования М-автомата может служить следующие рисунки (из обзора [9]):


Метафора
Структурно-устойчивый гетероклинический канал (рисунок 6(б) из обзора [9])

Пример метастабильной системы из двух сёдел (рисунок 6(а) из обзора [9]: гетероклиническая структура из двух сёдел). На предыдущем рисунке схематически изображена целая цепочка (сеть) таких седлообразных «потенциальных поверхностей»

Метафоричность этих рисунков (в контексте данной статьи) следует воспринимать в следующем смысле:
1. красными кружочками (шариками) обозначены наиболее возбужденные $i$-модели на данный конкретный момент времени;
2. сплошными линиями со стрелками обозначены переходы между последовательно возбуждаемыми $i$-моделями;
3. пунктирными стрелками обозначены переходы, возможные в тот или иной момент времени;
4. «потенциальный рельеф» задает СУТ;
5. «потенциальная энергия» обратна уровням возбужденности $i$-моделей.



Также интересно замечание авторов обзора [9, стр. 379]:


Поскольку время, проводимое системой в окрестности седлового равновесия обратно пропорционально логарифму уровня шума [109, 110], характерное время рассматриваемого переходного процесса может меняться в широких пределах. В устойчивых гетероклинических последовательностях порядок сменяющихся «победителей» фиксирован, шум же может лишь ускорить процесс. Таким образом, некоторый уровень шума нейронной системе необходим, чтобы она не «засыпала», но он не должен быть слишком большим, иначе переходный процесс становится невоспроизводимым.
Наличие в фазовом пространстве диссипативной системы устойчивой гетероклинической цепочки (рис. 6б) означает существование в её окрестности «гетероклинического канала», который не могут покинуть попавшие в него траектории. Само существование данных метастабильных состояний определяется входной информацией (возбуждением от других нейронных групп), порядок последовательных переключений в цепочке также зависит от величины и топологии связей между конкурирующими объектами, которые функционально зависимы от входной информации. Благодаря этим обстоятельствам гетероклинический канал оказывается одновременно устойчивым по отношению к шумам и чувствительным по отношению к слабым информационным сигналам. На сегодняшний день гетероклинический канал – это единственная известная динамическая конструкция, с помощью которой разрешается фундаментальное противоречие между чувствительностью и надёжностью. Основной нейрофизиологический механизм, обеспечивающий в нейронных системах мозга существование гетероклинического канала – это взаимное торможение нейронных групп [111-114].
Имеющиеся сейчас экспериментальные данные [62, 89, 115] говорят о том, что метастабильность и устойчивые переходы – это ключевые динамические объекты, которые способны перевести моделирование нейронных процессов мозга на новый уровень понимания и предсказания.

И далее (см. [9, стр. 381]):


В то же время сама идея об иерархической организации информационных потоков мозга (см., например. [127]) представляется плодотворной. Только говорить нужно о потоках не в физическом пространстве, а в фазовом пространстве соответствующей динамической модели.
На рисунке 9 показана иерархическая структура таких сходящихся информационных потоков. Как видно, информация распространяется вдоль гетероклинических каналов, которые, как притоки рек, вливаются в общее информационное русло и заканчиваются в состоянии, представляющем достигнутое решение. Предлагаемое представление информационных процессов мозга естественным образом удовлетворяет требованиям причинно-следственных связей и, кроме того, создаёт предпосылки для правильной постановки и решения новых проблем. Например, таких, как ёмкость рабочей памяти [128] или зависимость устойчивости процессов обработки информации от эмоционального состояния.

Рисунок 2. «Гетероклиническое дерево» в многомерном фазовом пространстве динамической системы, описывающей параллельную и последовательную активность большого числа взаимодействующих мод, задействованных в выполнении определённой когнитивной функции (состояние, представляющее окончательное решение или выработанную стратегию поведения, отмечено светлым диском). Рис. 9 из обзора [9]

Такой высокоуровневый механизм не только позволяет сдержать неконтролируемый рост возбужденности отдельных групп $i$-моделей, но и при необходимости разорвать «порочные круги» передачи активности внутри некоторой совокупности ансамблей $i$-моделей.


Основные свойства


1. В концепции роль СУТ состоит в организации локальных положительной и отрицательной обратной связи в процессах переработки информации, протекающих в М-сети. Это обеспечивает на каждом временном промежутке доминирование наиболее важной в приспособительном плане программы переработки информации над другими программами, параллельно развивающимися в М-сети.


2. СУТ функционирует следующим образом:


2.1. Пусть задана некоторая М-сеть.


2.2. В процессе переработки информации возбужденности $i$-моделей сети изменяются. Величина возбуждения каждой $i$-модели косвенно свидетельствует о «важности», или ценности, зафиксированной в ней информации.


Схема взаимодействия СУТ с другими элементами М-сети представлена на следующем рисунке.



Рисунок 3. Схема СУТ. $S$ — СУТ; $F$ — усиление главной модели; $B$ — торможение остальных; $I_{1}$ и $I_{2}$ — этажные модели смысла окружающего мира; $A_{1}$, $A_{2}$ — этажные модели действий, направленных вовне; $C_{1}, C_{2}$ — модели программ сознания; $e\mathbf{,\ }f$ — модели чувств и эмоций. Воспроизведен рис. 19 из работы [2].

2.3. Предполагается, что выделение в каждый момент времени наиболее возбужденной $i$-модели и усиление ее влияния на общий ход переработки информации увеличит эффективность работы сети. Как раз эту задачу и решает СУТ.


2.4. Работа СУТ:


2.4.1. СУТ в каждый момент времени выбирает наиболее возбужденную $i$-модель, дополнительно повышает ее возбужденность и уменьшает возбудимость остальных $i$-моделей (притормаживает их). Если в некоторый момент времени одинаковое наибольшее возбуждение имеют $n$ $i$-моделей, то дополнительная возбужденность от СУТ для каждой из них будет в $n$ раз уменьшена.


2.4.2. Для каждой «активной» $i$-модели необходим тормозной «двойник» (комплементарная $i$-модель), имеющий многочисленные связи с другими тормозными и активными $i$-моделями. Через него должно осуществляться торможение модели. Таким образом, тормозная система в некотором роде явится отражением активной (комплементарной).


2.4.3. В каждый конкретный момент времени функционирования СУТ выделяет одну или несколько наиболее возбужденных $i$-моделей. Количество одновременно выделяемых СУТ $i$-моделей должно определяться, в зависимости от различных факторов на этапе построения М-сети.


2.4.4. Возбужденность одновременно выделяемых СУТ $i$-моделей не обязательно должна быть строго одинаковой по величине. Иначе говоря, реализуемая СУТ точность сравнения возбужденностей $i$-моделей может быть невелика. Это должно приводить к тому, что СУТ, кроме максимального возбужденной, выделяет и те $i$-модели, возбужденность которых попадает в определенный, в заранее заданных пределах, диапазон значений, близких к максимальному. Таким образом, чем ниже «погрешность» при сравнении возбужденностей, тем меньше вероятность одновременного выделения СУТ нескольких $i$-моделей. В зависимости от условий конкретных задач, решаемых с помощью М-автомата, «меру близости» возбуждений выделяемых СУТ $i$-моделей можно изменять путем соответствующего изменения определенных параметров СУТ.


2.4.5. В рассматриваемых авторами моделях общая величина дополнительного возбуждения не зависела от количества выделяемых $i$-моделей. Это ограничение может быть отброшено. Распределение дополнительного возбуждения от СУТ может быть равномерным или зависеть от собственной возбужденности каждой $i$-модели. Вид распределения может также меняться в зависимости от ряда внешних и внутренних факторов, например от наличия нерешенной задачи или текущего значения интегративной оценки состояния М-сети (разности возбуждений $i$-моделей Пр и НПр). Суммарная величина дополнительного возбуждения от СУТ также может быть различной в разные моменты времени и зависеть, например, от той же интегративной оценки состояния М-сети.


2.4.6. СУТ обладает конечным «энергетическим» запасом, величина которого зависит от общего состояния сети (в частности, от состояния $i$-моделей Пр и НПр).


2.4.7. Алгоритм функционирования СУТ должен быть реализован таким образом, чтобы возбужденность выделенных ею $i$-моделей в последующем постепенно уменьшалась во времени. В то время как остальные $i$-модели пропорционально растормаживались: возбуждение от $i$-моделей, первоначально выделенных СУТ, распространяясь по сети должно увеличивать возбужденность связанных с ними $i$-моделей. Ожидается, что в результате такого процесса одна из ранее заторможенных моделей, станет максимально возбужденной, и СУТ в последующем выполнит переключение (выделит) ее. Далее весь процесс повториться заново.


3. СУТ, по замыслу авторов, может содержать иерархически организованные подсистемы, принадлежащих различным уровням организации М-сети. Чем ниже уровень подсистемы, тем меньшее количество $i$-моделей находится под ее влиянием. Подсистемы СУТ более высоких уровней производят сравнение не возбужденностей отдельных $i$-моделей, а интегральных активностей более или менее обширных зон или сфер сети.


Такая, иерархически организованная, СУТ может выглядеть следующим образом:



Рисунок 4. Схема организации иерархической СУТ. Показаны три поля «рабочих» моделей, а также нейроны первого ($I$) и второго ($\text{II}$) этажей СУТ с их усиливающими ($У$) и тормозными ($T$) элементами. Воспроизведен рис. 20 из работы [2].

3.1. Пусть все $i$-модели сети по какому-либо признаку условно объединены в несколько непересекающихся групп. Это группы первого (низшего) уровня. Эти группы могут, в свою очередь, быть объединены в более крупные группы второго уровня, последние — в группы третьего уровня и т.д. Группа самого верхнего уровня включает в себя все группы предыдущего и, следовательно, все $i$-модели сети. Общее количество уровней такой «пирамиды» в каждом конкретном случае может быть определено в зависимости от сложности М-сети и сложности задач, для решения которых строится автомат.


На следующих рисунках показаны схемы организации иерархической СУТ и принцип ее взаимодействия с уровнями $i$-моделей М-сети.


3.2. Каждой такой группе $i$-моделей ставиться в соответствие одна из подсистем СУТ: в М-сети одновременно функционирует столько подсистем СУТ, сколько групп различных уровней в ней выделено.



Рисунок 5. Схема взаимодействия иерархической СУТ с уровнями $i$-моделей М-сети. $A$, $B$ — «рабочие» зоны коры с моделями $\text{a–a–a},\text{b–b–b},\text{c–c–c},\text{d–d–d}$.

Наиболее возбуждена модель $\text{a–a–a}$ в зоне $A$, в зоне $B$ модель $\text{c–c–c}$ менее заторможена, чем $\text{d–d–d}$.


Элемент $N\ СУТ - I$ усилен, элемент $M$ заторможен от $СУТ - II$; $Т$ — торможение; $У$ — усиление.


Воспроизведен рис. 21 из работы [2].


3.3. В качестве одного из возможных вариантов реализации может предусматривать след. алгоритм работы:


3.3.1. подсистемы СУТ одного уровня (уровни подсистем будем выделять в соответствии с уровнями групп, на которых они работают) производят сравнение возбуждений, усиление выделенных и торможение остальных $i$-моделей только в пределах своих групп. Подсистемы СУТ следующего уровня, каждая в пределах своей группы, производят сравнение уже средних возбуждений групп $i$-моделей нижнего уровня.



Рисунок 6. Пример взаимоотношений $i$-моделей M-сети. $A$ — модель высшего этажа; $\text{a,b,c}$ — модели нижнего этажа; $1, 2, ...$ — соседствующие модели; $Д$ — модель-антагонист; $T$ — торможение; $K$ — модели качеств; $Ч_{1}, Ч_{2}$ — модели чувств. Пунктиром показаны пути торможения. Воспроизведен рис. 22 из работы [2].

3.3.2. Усиление применяется при этом не к отдельной $i$-модели, а ко всем $i$-моделям выделяемой в данный момент группы; $i$-модели остальных групп того же уровня пропорционально притормаживаются.


3.4. Влияние всех подсистем СУТ сказывается на активности $i$-моделей, составляющих исходное множество, так что каждая конкретная $i$-модель сети может получать дополнительное возбуждение от подсистем одних уровней и притормаживаться подсистемами других уровней.


3.5. Количество уровней в иерархически организованной СУТ должно определяться условиями конкретной задачи, стоящей перед создателями М-сети.


Ансамбли $i$-моделей


Авторы, на основе проведенных экспериментов, отмечают следующую интересную особенность: «Если СУТ по каким-либо причинам выделяет одну и ту же $i$-модель несколько раз, то повышается возбудимость не только выделяемой $i$-модели, но и ее непосредственных «соседей», их активность увеличивается, связи между ними проторяются и они начинают взаимно поддерживать возбуждение друг друга. В результате повышается вероятность переключения СУТ на одну из них, и, когда это происходит, описанные процессы повторяются. При этом дополнительное возбуждение перераспределяется в основном между теми $i$-моделями, связи между которыми уже дополнительно проторены в результате предыдущих выборов. Это приводит к тому, что и при следующем переключении СУТ его «захватывает» одна из $i$-моделей той же группы. В результате формируется более или менее ограниченная совокупность сильно связанных между собой $i$-моделей, которая может «удерживать» СУТ внутри себя длительное время» [3].


При этом даже и после переключения СУТ на другие, не связанные с нею, $i$-модели указанная группа продолжает функционировать, поддерживая собственную минимальную активность за счет разнообразия внутренних связей. По аналогии с нейронными ансамблями авторы назвали такую группу — ансамблем $i$-моделей.


1. Всякий раз, когда СУТ выделяет входящую в ансамбль $i$-модель, то другие $i$-модели, входящие в такой ансамбль «пытается захватить» СУТ: вероятность последующего возбуждения другой $i$-модели, входящей в этот ансамбль, многократно возрастает. Как бы сегодня сказали: ансамбль представляет собой аттрактор активности входящих в него $i$-моделей.


2. Между отдельными ансамблями может существовать взаимосвязь, реализуемая связями между $i$-моделями, которые входят в разные ансамбли: ансамбли могут и «пересекаться». За счет этого ансамбли также могут образовывать связные группы — ансамбли «второго слоя». Активация СУТ ансамблей тем самым может «перетекать» от одного ансамбля к другому: каждый из отдельных ансамблей стремится или удержать ее, или «передать» другому, связанному с ним, — в пределах системы «второго слоя», в которую он входит.


Такое поведение системы ансамблей в М-сети внешне выражается в формировании более или менее жестких типичных последовательностей или программ переключений СУТ. Как отмечают авторы: «В этих случаях может возникнуть впечатление, что, кроме алгоритмов функционирования автомата (описанного ранее алгоритма А), в М-сети реализованы еще некоторые «мета-алгоритмы» принятия решений, достижений целей и т.п.» [3].


3. Если активность отдельных составляющих ансамбль $i$-моделей постоянно поддерживается, то они могут существовать в М-сети длительное время, даже не привлекая «внимания» СУТ. Такие ансамбли выступают в качестве «скрытых очагов возбуждения».


4. Ансамбль $i$-моделей, как правило, должен представлять собой самостоятельную функциональную единицу сети — блок, функционирование которого реализует определенную, иногда весьма сложную совокупность операций по переработке информации — ансамбли фиксируют известные, заученные алгоритмы. Работа таких блоков может быть описана с помощью специальных алгоритмов. Однако, оказалось также, что ансамбли типа «скрытого очага возбуждения» часто и формируются при обучении автомата решению тех или иных задач. В процессе взаимодействия $i$-моделей ансамбля могут выполняться и такие системы операций, которые не соответствуют ни одному из когда-либо «преподанных» автомату алгоритмов, а отражают его «индивидуальные», сложившиеся в процессе деятельности приемы и методы решения задач.


5. Ансамбли $i$-моделей могут формироваться:


– вокруг отдельной $i$-модели, если ее возбужденность в течение некоторого времени велика.
– в результате многократного выделения СУТ некоторой фиксированной последовательности $i$-моделей. На этом основывался один из эффективных приемов целенаправленного обучения М-автомата, предполагающий многократное повторение учителем постоянной последовательности входных сигналов, сопровождающейся сигналами «поощрения» и «наказания» ответных реакций автомата.
– как внутреннее отражение (модель) регулярных свойств внешней или внутренней среды, воспринимаемых автоматом, или как модель определенной совокупности действий, приводящих к успеху в той или иной внешней ситуации.


Память в М-сети


1. Память о прошлом воздействии в концепции рассматривается как однажды возбужденная $i$-модель, сохраняющая некоторое время состояние возбужденности даже при отсутствии активных входных воздействий. Каждая такая модель и есть элемент памяти М-сети. Также, при наличии активных входных воздействий на уже возбужденную $i$-модель происходит «суммирование» собственной возбужденности $i$-модели, отражающей предыдущие активные воздействия, и вновь поступивших воздействий. Это позволяет сделать вывод, о том, что в каждый момент времени в возбужденности $i$-модели отражена память о целом ряде последовательных входных воздействий. Длительность этой памяти зависит от вида характеристики затухания $i$-модели.


2. Возможны следующие варианты памяти М-сети:


2.1. Если $i$-модель длительное время имеет достаточно высокую возбужденность, т.е. важность зафиксированной в ней информации постоянно велика, то характеристики $i$-модели изменяются таким образом, чтобы ее возбуждение со стороны остальных $i$-моделей сети ослаблялось — осуществлялась гипертрофия характеристик. В противном случае происходит обратный процесс — деградация, в результате которой возбудимость $i$-модели уменьшается. Таким образом, изменения возбудимости $i$-модели, определяемые гипертрофией или адаптацией ее характеристик, реализуют память об «истории» активности данной $i$-модели. Память такого типа является, естественно, более длительной по сравнению с памятью возбуждений.


2.2. Пусть между двумя возбужденными $i$-моделями устанавливается связь. Впоследствии ее проходимость увеличивается или уменьшается в зависимости от изменения возбужденности обеих $i$-моделей. Состояние связи как бы отражает «историю» (память) их совместных возбуждений, усредненную во времени. Если связь установлена с достаточно высокой проходимостью или достигла определенного уровня проходимости в процессе последовательных проторений, то значительно увеличивается и постоянный компонент проходимости, что, в свою очередь, увеличивает вероятность длительного сохранения этой связи.


2.3. При длительном возбуждении группы $i$-моделей происходит их объединение в некоторую устойчивую структуру — ансамбль, элементы которого сильно взаимосвязаны друг с другом. Наличие связей с высокой проходимостью между $i$-моделями ансамбля обеспечивает память об определенном состоянии М-сети даже после затухания возбуждения всех входящих в ансамбль $i$-моделей. «Вспоминание» этого состояния происходит уже при возбуждении части элементов ансамбля.


2.4. Авторы отмечают наличие в М-сети следующего вида памяти: формирование $i$-модели — «представителя» ансамбля. Такая $i$-модель имеет прямые и обратные связи со всеми $i$-моделями ансамбля. Возбуждение «представителя» ведет к возбуждению всего ансамбля, т.е. к восстановлению, «вспоминанию» состояния сети даже в том случае, когда сам ансамбль уже частично распался из-за затухания связей.


2.5. При установлении связей между $i$-моделями различных ансамблей, сила (проходимость) которых зависит от частоты совместных возбуждений входящих в ансамбль $i$-моделей, состояния интегральных центров оценки Пр и НПр и некоторых других факторов, сильно связанные $i$-модели имеют большую вероятность последовательного выделения системой усиления-торможения по сравнению с остальными $i$-моделями сети. Соответственно и при выделении СУТ $i$-модели, принадлежащей одному из двух сильно связанных ансамблей, велика вероятность того, что через несколько моментов времени произойдет переключение СУТ на одну из $i$-моделей второго ансамбля.



Рисунок 7. Возможная (и не окончательная) версия диаграммы классов для отдельных элементов и их отношений в концепции Н.М. Амосова.

3. Изменения возбужденности $i$-модели можно интерпретировать как кратковременную, а изменения проходимостей связей — как долговременную память. При этом по длительности запоминания фиксируемой связями информации можно различать два вида долговременной памяти, реализуемых временным и постоянным компонентами связи.


4. Запоминание информации в М-сети может происходить как при участии СУТ, так и без нее. Установление и проторение связей зависит в основном от возбужденности соединяемых ею $i$-моделей. Однако если одна из них выделена СУТ, то направленная к ней связь будет усилена в большей степени, чем это произошло бы в «обычных» условиях, поскольку $i$-модель получает дополнительное возбуждение от СУТ.


М-автомат


Формальное определение М-автомата и его функционирования


Пусть, согласно определению (12), задана некоторая М-сеть $\mu$. Совокупность конкретных реализаций каждого из элементов семерки (12) есть состояние М-сети. Алгоритм функционирования преобразовывает состояние М-сети в момент $t$ в ее состояние в момент $t + 1$.


Алгоритм содержит следующие основные блоки:


1) блок пересчета, выполняющий операции в соответствии с формулой пересчета (11); в этом блоке определяются возбужденности всех $i$-моделей М-сети в момент $t + 1$;


2) блок обучения, в котором в соответствии с характеристиками обучения определяются новые значения проходимостей связей и параметров возбужденности $i$-моделей;


3) блок дополнения, или «роста», М-сети; здесь в соответствии с характеристиками самоорганизации устанавливаются новые связи между $i$-моделями и формируются «спонтанные» возбуждения резервных элементов;


4) блок СУТ, в котором производятся операции, реализующие алгоритм работы системы усиления-торможения;


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


Функционирование М-сети обеспечивается многократным применением описанного алгоритма. Порядок выполнения различных блоков строго не фиксируется и может быть частично изменен при построении конкретных моделей. Совокупность операций, выполняемых при однократном применении алгоритма, назовем тактом функционирования М-сети. За один такт, следовательно, осуществляется полное определение состояния М-сети в определенный момент дискретного времени. Таким образом, задается алгоритм $A$ функционирования М-сети.


В дальнейшем М-автоматом называется пара $\left\langle \mu, A \right\rangle$: такой автомат построен на основе М-сети $\mu$ и включает в себя алгоритм ее функционирования $A$. В целом работа М-автомата может быть представлена следующим образом:



Рисунок 8. Блок-схема алгоритма функционирования М-автомата. Воспроизведен риc. 5 из работы [3].

Если М-сеть $\mu$ задана в виде (12), такой М-автомат является полным.


Возможно построение М-автоматов, в которых реализованы не все функции М-сети.


В зависимости от полноты задания М-сети различают самообучающиеся М-автоматы:


$\mu = \left\langle P,S,R,L,\varnothing,C,I \right\rangle,\tag{13}$


и необучающиеся М-автоматы;


$\mu = \left\langle P,S,R,\varnothing,\varnothing,C,I \right\rangle,\tag{14}$


знак $\varnothing$ обозначает, что соответствующий элемент не вводится.


Алгоритм $А$ в случае самообучающегося М-автомата не содержит блока $F$ или группы характеристик самоорганизации, а в случае необучающегося — блоков $F$ и $L$ — группы характеристик самообучения.


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


Вырожденным М-автоматом авторы концепции назвали М-автомат, алгоритм $А$ которого не содержит блока СУТ.


Следует дополнительно особо выделить сопутствующие обстоятельства:


1. любая программа реализуется в М-сети (М-автомате) функционированием некоторой совокупности сильно связанных между собой $i$-моделей.


2. Такие $i$-модели могут быть связаны также с другими $i$-моделями сети, которые условно можно называть вторичными элементами данной программы. Через вторичные $i$-модели осуществляется косвенное влияние программы на изменение активности иных $i$-моделей (ансамблей) М-автомата.


Процедура построения М-автомата


Представим, что перед вами поставлена задача построения модели некоторой сложной функции и определена цель моделирования. Авторы концепции видят процедуру построения соответствующего М-автомата следующим образом:


1. Прежде всего, собираются и систематизируются сведения из предметной области (например: психологии) о функции.


2. Исходя из целей моделирования и сведений п. 1 определяется необходимый тип М-автомата. Если принимается решение о разработке неполного М-автомата, конструируется алгоритмическая модель соответствующих функций.


3. Выдвигается гипотеза о составе программ, участвующих в формировании моделируемой функции.


4. Исходя из целей моделирования задаются «внешние объекты» и законы их взаимодействия с М-автоматом, т.е. задается среда модели.


5. Определяется «уровень» моделирования.


6. В соответствии с гипотезой п. 3 фиксируется набор понятий, необходимый для описания на выбранном уровне. Каждому понятию ставится в соответствие $i$-модель.


7. В соответствии с гипотезой п. 3 задается множество связей между $i$-моделями.


8. Определяются проходимости связей, вид и параметры характеристик $i$-моделей и связей. Для их уточнения могут понадобиться специальные эксперименты. Однако, как правило, они могут быть определены эвристически.


9. Аналогично определяются (если необходимо) характеристики обучения и самоорганизации. При выполнении п.п. 6-8 широко используются аналогии, сопоставления, правдоподобные рассуждения и т.п. Направляющим здесь является содержание гипотезы п. 3.


Следует отметить, что от удачного выбора величин в п.п. 7 и 8 во многом зависит успех моделирования. Именно здесь, прежде всего, необходимы дальнейшая систематизация, совершенствование и разработка методов эвристического моделирования.


10. Задается исходное состояние М-сети.


11. Задается алгоритм функционирования $A$.


12. М-автомат и его среда реализуются в виде действующих устройств или программ.


Часть 2. Реализация


Start by doing what's necessary then do what's possible and suddenly you are doing the impossible.
Начните делать то, что нужно. Затем делайте то, что возможно. И вы вдруг обнаружите, что делаете невозможное.
Св. Франциск Ассизский

Фактически завершив окончательную верстку статьи для Хабра я решил, что было бы неправильно остановиться на публикации только теоретических положений концепции Н.М. Амосова. Все-таки для статьи на ИТ-ресурсе нужны какие-либо коды. Тем более что они у меня есть. Поэтому и написал дополнительно эту часть.


В дальнейшем изложении буду в основном опираться на диаграмму классов, приведенную на рисунке 6.


Классы


Перечислим базовые классы необходимые для дальнейшей реализации:


//------------------------------------------------------------------------------
  type
//------------------------------------------------------------------------------
{$REGION 'Список классов'}
//------------------------------------------------------------------------------
// Соединение
    TConnection    = class;
// Коллекция соединений
    TConnections   = class;
//------------------------------------------------------------------------------
// Связь
    TRelation      = class;
// Коллекция связей
    TRelations     = class;
// Выходные связи i-модели
    TOutputs       = class;
// Входные связи i-модели
    TInputs        = class;
//------------------------------------------------------------------------------
// i-модель
    TModel         = class;
// Коллекция i-моделей
    TModels        = class;
//------------------------------------------------------------------------------
// Частные типы i–моделей:
// Внутреннее состояние автомата
    TQualia        = class;
// Перцепт
    TPercept       = class;
// Моторная программа - эффектор
    TEffector      = class;
//------------------------------------------------------------------------------
// М-сеть, группа i-моделей (сфера)
    TModelGroup    = class;
// Система усиления-торможения
    TActivateInhibiteSystem = class;
//------------------------------------------------------------------------------
// М-автомат
    TAutomate      = class;
//------------------------------------------------------------------------------
// Среда
    TPlatform      = class;
//------------------------------------------------------------------------------
{$ENDREGION 'Список классов'}
//------------------------------------------------------------------------------

Возможно, этот перечень не полон, но пока остановимся на нем. При необходимости в процессе изложения материала он может быть скорректирован и дополнен.


Связи


Основные поля и свойства


В соответствии с положениями концепции Н.М. Амосова класс связи – TRelation должен иметь следующие свойства
– текущий коэффициент активации:


      property CurrActivateCoeff: double read GetCurrActivateCoeff write SetCurrActivateCoeff;

– остаточный коэффициент активации:


      property ResidualActivateCoeff: double read GetResidualActivateCoeff write SetResidualActivateCoeff;

– текущий коэффициент торможения:


      property CurrInhibitCoeff: double read GetCurrInhibitCoeff write SetCurrInhibitCoeff;

– остаточный коэффициент торможения:


      property ResidualInhibitCoeff: double read GetResidualInhibitCoeff write SetResidualInhibitCoeff;

Реализация методов доступа для этих свойств тривиальна и поэтому ограничимся только их определением.


Для хранения же основных данных связей $i$-моделей будем использовать следующие поля:


  TRelation = class(..............)
  private
      FModelSource: TModel;              // Ссылка на модель-источник    
      FModelTarget: TModel;              // Ссылка на целевую модель
      FConnection: TConnection;          // Соединение, соотв. данной связи
  private
      FSourceId: int64;                  // Идентификатор модели-источника 
      FTargetId: int64;                  // Идентификатор целевой модели
  private
      FCurrActivateCoeff: double;
      FResidualActivateCoeff: double;
      FCurrInhibitCoeff: double;
      FResidualInhibitCoeff: double;
  protected
//................................................    
//................................................    
//................................................    
  public
//................................................    
//................................................    
//................................................    
  end;    

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


Я посчитал, что такая избыточность неизбежна и необходима, поскольку существуют случаи, когда при вставке связь может иметь только числовые идентификаторы связываемых $i$-моделей. Это, например, имеет место, когда экземпляр TRelation создается и должен быть использован, в форме ввода данных, а возможная инициализация ссылок на $i$-модели еще невозможна. С другой стороны, использование только числовых идентификаторов приведет излишнему вызову методов поиска связываемых $i$-моделей.


Замечание 6
Частично, это обстоятельство обусловлено использованием мною определенной технологии построения пользовательского интерфейса CRUD-форм, что необходимо для работы с объектами в конфигураторе.

Характеристики связей


Осталось определить методы, реализующие характеристики связей моделей.
В соответствии с концепцией Н.М. Амосова для связей устанавливаются следующие характеристики и вводся также несколько методов, соответствующие отдельным характеристикам связей:
1. Характеристика проторения (2):


    procedure Defrosting(ACoModel: TModel; const AQuality: double); overload; virtual;

2. Характеристика установления (3):


    procedure Setting(ACoModel: TModel; const AQuality: double); overload; virtual;

3. Характеристика затухания коэффициента текущей активации (4а):


    procedure AttenuationCurrActivation(); overload; virtual;

4. Характеристика затухания коэффициента текущего торможения (4б):


    procedure DampingCurrentInhibit(); overload; virtual;

5. Характеристика затухания коэффициента остаточной активации (5а):


    procedure AttenuationResidualActivation(); overload; virtual;

6. Характеристика затухания коэффициента остаточного торможения (5б):


    procedure AttenuationResidualInhibit(); overload; virtual;

7. Характеристика передачи активации (6а):


    function Activation(): double; overload; virtual;

8. Характеристика передачи торможения (6б):


    function Inhibition(): double; overload; virtual;

Замечание 7
В скобках указаны номера определений характеристик, приведенных в первой части статьи.

Соответствующее определение TRelation принимает вид


  TRelation = class(..............)
  private
//................
  protected
// Характеристика проторения (2):
    procedure Defrosting(ACoModel: TModel; const AQuality: double); overload; virtual;
// Характеристика установления (3):
    procedure Setting(ACoModel: TModel; const AQuality: double); overload; virtual;
// Характеристика затухания коэффициента текущей активации (4а):
    procedure AttenuationCurrActivation(); overload; virtual;
// Характеристика затухания коэффициента текущего торможения (4б):
    procedure DampingCurrentInhibit(); overload; virtual;
// Характеристика затухания коэффициента остаточной активации (5а):
    procedure AttenuationResidualActivation(); overload; virtual;
// Характеристика затухания коэффициента остаточного торможения (5б):
    procedure AttenuationResidualInhibit(); overload; virtual;
// Характеристика передачи активации (6а):
    function Activation(): double; overload; virtual;
// Характеристика передачи торможения (6б):
    function Inhibition(): double; overload; virtual;
//................
  end;    

Все эти методы на этом уровне реализации вполне можно было бы объявить как абстрактные методы, однако для демонстрации места возможного их вызова определим их как виртуальные и соответственно в секции реализации тело методов оставим пустым.
Характеристики:
– передачи активации (6а) – Activation(…),
– передачи торможения (6б) – Inhibition(…)


следует отнести операциям определяемым методами класса TModel, однако мы оставляем за собой возможность реализации соответствующих методов в классе TRelation.


Перед тем как привести возможную реализацию вышеуказанных методов следует сделать несколько замечаний:


1. При определении новых моделей автоматов может возникнуть необходимость переопределения базового алгоритма вычислений активации и торможения. Это потребует создания дочерних от TRelation классов, реализующих нужную функциональность. Хотя это препятствие может быть преодолено введением в классе коллекции связи методов класса, определяющих их тип.
2. В силу того что на каждое соединение двух моделей создается только одна связь как для выходов модели-источника, таки для входов сопряженной целевой модели.
3. Методы Activation(…) и Inhibition(…) вызываются при расчете текущей активации модели только для входящих связей.
4. На данном этапе возможна реализация только для двух методов 7 и 8:


//................
    function TRelation.Activation(): double;
    begin
      Result := ResidualActivateCoeff + (System.Math.Max(CurrActivateCoeff, 0.0) * FModelSource.Arousal);
    end;
    function TRelation.Inhibition(): double;
    begin
      Result := ResidualInhibitCoeff + (System.Math.Max(CurrInhibitCoeff, 0.0) * FModelSource.Arousal);
    end;
//................

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


Реализацию остальных методов-характеристик отложим до момента реализации конкретного М-автомата.


Соединения


1. Для обеспечения удобства сериализации/десериализации (в XML) введем служебный класс TConnection – класс соединения:


  TConnection = class
  private
      FOwner: TConnections;
  private
      FSourceId: int64;
      FTargetId: int64;
  private
      FCurrActivateCoeff: double;
      FResidualActivateCoeff: double;
      FCurrInhibitCoeff: double;
      FResidualInhibitCoeff: double;
  protected
  ....
  public
    function Equals(AObject: TObject): boolean; overload; override;
  public
    function IsDefrosting(): boolean; overload; virtual;
  published
  [XMLAttribute('Source')]
      property SourceId: int64 read GetSourceId write SetSourceId;
  [XMLAttribute('Target')]
      property TargetId: int64 read GetTargetId write SetTargetId;
  published
  // Текущий коэффициент активации.
  [XMLAttribute('CurrActivate')]
      property CurrActivateCoeff: double read GetCurrActivateCoeff write SetCurrActivateCoeff;
  // Остаточный коэффициент активации.
  [XMLAttribute('ResidualActivate')]
      property ResidualActivateCoeff: double read GetResidualActivateCoeff write SetResidualActivateCoeff;
  // Текущий коэффициент торможения.
  [XMLAttribute('CurrInhibitCoeff')]
      property CurrInhibitCoeff: double read GetCurrInhibitCoeff write SetCurrInhibitCoeff;
  // Остаточный коэффициент торможения.
  [XMLAttribute('ResidualInhibitCoeff')]
      property ResidualInhibitCoeff: double read GetResidualInhibitCoeff write SetResidualInhibitCoeff;
  end;

2. Как видим, в TConnection мы добавили поля и свойства, соответствующие идентификаторам модели-источника и целевой модели (модели-приемника) – опять-таки для обеспечения удобства сериализации/десериализации.
3. По своей структуре TConnection во многом повторяет структуру класса TRelation: наличие тех же самых коэффициентов и числовых идентификаторов. Казалось бы, он тем самым претендует на роль родительского класса для TRelation. Однако, наличие свойства Owner – ссылки на коллекцию соединений делает эту замечательную идею практически неприемлемой. Выделение общего класса прародителя для TRelation и TConnection при всей очевидности такого решения также неприемлемо: неочевидно, что в будущем не возникнет ситуации, при которой класс связи TRelation должен быть унаследован от какого-либо другого класса, отличного от класса родителя TConnection. Третья возможность: вообще отказаться от класса TConnection, перенести ссылку на коллекцию соединений в TRelation и работать везде с экземплярами этого класса. Но дело в том, что в этом случае придется разделять объект-связь как минимум между тремя коллекциями: общей коллекцией соединений М-автомата и коллекциями входов/выходов $i$-моделей. Поэтому, несмотря на очевидную избыточность принятого определения – остановимся на нем.
4. Переопределенный единственный прикладной метод Equals должен в случае необходимости застраховать нас от возможности создания дубликатов связей (здесь мы отступаем от некоторых положений концепции, допускающей дублирование связей):


    function TConnection.Equals(AObject: TObject): boolean;
    begin
      if (SourceId <> TConnection(AObject).SourceId) then exit(false);
      Result := (TargetId = TConnection(AObject).TargetId);
    end;

5. Естественно, что должны быть определены и реализованы соответствующие конструкторы TConnection


    constructor Create(); overload; virtual;
    constructor Create(const ASourceId, ATargetId: int64;
      const AResidualActivateCoeff: double = 0.0;
      const AResidualInhibitCoeff: double = 0.0;
      const ACurrActivateCoeff: double = 0.0;
      const ACurrInhibitCoeff: double = 0.0); overload; virtual;
    // Копирующий конструктор.
    constructor Create(const ASourceConnection: TConnection); overload; virtual;

6. Для полноты реализации определим также метод-предикат IsDefrosting(): boolean – указывающий на то, что связь является проторенной:


//................
function TConnection.IsDefrosting(): boolean;
begin
  if (FCurrActivateCoeff <> 0.0) then exit(true);
  if (FResidualActivateCoeff <> 0.0) then exit(true);
  if (FCurrInhibitCoeff <> 0.0) then exit(true);
  if (FResidualInhibitCoeff <> 0.0) then exit(true);
  exit(false);
end;

Инициализация и утилизация экземпляра TRelation


Возвращаясь к классу TRelation следует особое внимание уделить его конструкторам и деструктору.
Для этого, забегая вперед, следует сделать следующие замечания:
1. экземпляр класса $i$-модели TModel будет иметь две коллекции связей: входы и выходы;
2. один и тот же экземпляр связи будет храниться в коллекции выходов модели-источника и в коллекции входов модели-приемнике;
3. экземпляр класса TRelation должен хранить ссылку на соответствующий экземпляр соединения TConnection.
4. Как уже ранее отмечалось, экземпляр класса TRelation хранит ссылки, как на модель-источник, так и на модель-приемник, а также на соответствующее ему соединение:


  TRelation = class(..............)
  private
      FModelSource: TModel;
      FModelTarget: TModel;
      FConnection: TConnection;
  //................................................    
  protected
  //................................................    
      property Connection: TConnection read GetConnection write SetConnection;
  //................................................    
  public
  //................................................    
  published
      property ModelSource: TModel read GetModelSource write SetModelSource;
      property ModelTarget: TModel read GetModelTarget write SetModelTarget;
  published
      property SourceId: int64 read GetSourceId write SetSourceId;
      property TargetId: int64 read GetTargetId write SetTargetId;
      property SourceName: string read GetSourceName write SetSourceName;
      property TargetName: string read GetTargetName write SetTargetName;
  //................................................    
  end;

5. На данный момент очевидными вариантами инициализации экземпляров класса TRelation стали следующие случаи:
– инициализация по умолчанию — неопределенными остаются все значения полей связи;
– инициализация при наличии известных значений числовых идентификаторов $i$-моделей;
– инициализация при вставке во входы целевой модели;
– инициализация при вставке в выходы модели-источника;
– инициализация в момент загрузки (связывания) при десериализации.
Поэтому, были определены и реализованы соответствующие этим случаям конструкторы


//................................................    
  public
// Конструктор по умолчанию.
    constructor Create(); overload; virtual;
// Конструктор на основе значений числовых идентификаторов связываемых i-моделей.
    constructor Create(const ASourceId, ATargetId: int64); overload; virtual;
// Конструктор на основе значения идентификатора модели-источника (вставка во входы целевой модели).
    constructor Create(const ASourceId: int64; ATarget: TModel); overload; virtual;
// Конструктор на основе значения идентификатора целевой модели (вставка в выходы модели-источника).
    constructor Create(ASource: TModel; const ATargetId: int64); overload; virtual;
// Конструктор для инициализации в момент загрузки-связывания при десериализации.
    constructor Create(ASource, ATarget: TModel; AConnection: TConnection); overload; virtual;
//................................................    
  end;

6. Деструктор TRelation будет иметь следующий вид:


    destructor TRelation.Destroy();
    begin
// Устанавливаем "неактивные" значения параметрам связи. 
      FResidualInhibitCoeff := -1.0;
      FCurrInhibitCoeff := -1.0;
      FResidualActivateCoeff := -1.0;
      FCurrActivateCoeff := -1.0;
// "Обнуляем" идентификаторы связываемых моделей.
      FTargetId := -1;
      FSourceId := -1;
// Удаляем из коллекции выходов.
      if (Assigned(FModelSource.Outputs)) then
        FModelSource.Outputs.Extract(Self);
// Удаляем из коллекции входов.
      if (Assigned(FModelTarget.Inputs)) then
        FModelTarget.Inputs.Extract(Self);
// Утилизируем соотв. соединение.
      if (Assigned(FConnection.Owner)) then
        FConnection.Owner.Remove(FConnection);
      System.SysUtils.FreeAndNil(FConnection);
      inherited Destroy();
    end;

Из приведенного кода видно, что свзь достаточно удалить только в одной из свзанных ею $i$-моделей.


Коллекция соединений


Определим следующим образом коллекцию соединений


[XMLROOT('Connections')]
  TConnections = class(TVector<TConnection>)
  private
      FOwner: TAutomate;
  public
    constructor Create(); overload; override;
    constructor Create(AOwner: TAutomate); overload; virtual;
    destructor Destroy(); override;
  public
    function Add(const AValue: TConnection): int64; overload; override;
    function Insert(const AIndex: longint; const AValue: TConnection): int64; overload; override;
  end;
  TConnectionsClass = class of TConnections;

Замечание 8
Для желающих воспроизвести код самостоятельно заметим, что шаблонный класс TVector<T> можно без особых усилий заменить на стандартный класс TList<T>.

К этому определению следует сделать следующие замечания:
1. Владельцем коллекции соединений является М-автомат. Действительно, коллекция соединений, содержит сведения обо всех установленных связях между всеми $i$-моделями М-автомата, и поэтому создается единственный экземпляр для всего М-автомата. Эта коллекция используется в основном в целях сериализации/десериализации М-автомата.
2. Конструкторы коллекции соединений устанавливают сортировку соединений – элементов коллекции и накладывают ограничение уникальности на пару числовых идентификаторов связанных моделей.
3. Для обеспечения уникальности пар числовых идентификаторов связанных моделей применяется соответствующий класс-компаратор:


  TConnectionComparer = class(TComparer<TConnection>)
  protected
    function Equal(const ALeft, ARight: TConnection): boolean; overload; override;
    function LessThan(const ALeft, ARight: TConnection): boolean; overload; override;
    function GreaterThan(const ALeft, ARight: TConnection): boolean; overload; override;
  public
    constructor Create(); overload; override;
    destructor Destroy(); override;
  end;
  TConnectionComparerClass = class of TConnectionComparer;

От класса TConnectionComparer для нас важно не допустить вставку дублирующих соединений, поэтому реализация его методов будет иметь следующий вид


    function TConnectionComparer.Equal(const ALeft, ARight: TConnection): boolean;
    begin
      if (ALeft.SourceId <> ARight.SourceId) then exit(false);
      Result := (ALeft.TargetId = ARight.TargetId);
    end;
    function TConnectionComparer.LessThan(const ALeft, ARight: TConnection): boolean;
    begin
      if (ALeft.SourceId < ARight.SourceId) then exit(true);
      if (ALeft.SourceId > ARight.SourceId) then exit(false);
      Result := (ALeft.TargetId < ARight.TargetId);
    end;
    function TConnectionComparer.GreaterThan(const ALeft, ARight: TConnection): boolean;
    begin
      if (ALeft.SourceId > ARight.SourceId) then exit(true);
      if (ALeft.SourceId < ARight.SourceId) then exit(false);
      Result := (ALeft.TargetId > ARight.TargetId);
    end;

4. Для метода Add мы просто изменяем область видимости.
5. Метод Insert не только осуществляет вставку в определенное место коллекции, но и инициализирует свойство Owner для соединения: владельцем соединения является сама коллекция.
6. Конструктор по умолчанию для этой коллекции будет иметь следующий вид:


    constructor TConnections.Create();
    begin
      inherited Create(TConnectionComparer.Create()); // инициализирем экземпляр класса-компаратора.
      FreeObjects := true;
      Sorted := true;
      Unique := true;
      ClassUnique := dupError;
      FOwner := nil;
    end;

Коллекции связей


Определения


В силу выбранного технического решения, в нашу реализацию введем класс коллекции связей — TRelations и два производных от него класса – входящие (TInputs) и исходящие (TOutputs).
Рассмотрим определение класса коллекции связей — TRelations:
TRelations:


  TRelations = class(TVector<TRelation>)
  private
      FOwner: TModel;
  protected
    function GetOwner(): TModel; overload; virtual;
    procedure SetOwner(const AValue: TModel); overload; virtual;
  protected
    function TryInnerInsert(const AIndex: longint; const AValue: TRelation; out AOutIndex: int64): boolean; overload; virtual;
    function Add(const AValue: int64): int64; overload; virtual;
  public
    constructor Create(); overload; override;
    constructor Create(AOwner: TModel); overload; virtual;
    destructor Destroy(); override;
  public
    function Add(const AValue: TRelation): int64; overload; override;
    function Insert(const AIndex: longint; const AValue: TRelation): int64; overload; override;
  public
      property Owner: TModel read GetOwner write SetOwner;
  end;

Вставка связей


1. Вставка связей в коллекцию возможна в нескольких различных случаях:
– при десериализации соединений;
– прямая вставка в одну из коллекций входов или выходов;
– при автоматическом создании экземпляра связи в CRUD-форме.
2. Для корректного выполнения этих операций в классе TRelations потребуется реализовать четыре метода:
2.1. добавление по числовому идентификатору $i$-модели (источник или целевой модели)


      function Add(const AValue: int64): int64; overload; virtual;

Такой метод нам понадобиться для того чтобы упростить прямые вставки связей во входы (для целевой модели) или выходы (модели-источника). Реализацию этого метода отложим до момента определения классов выходов TOutputs и входов TInputs.
2.2. основной метод добавления инициализированной (полностью или нет) связи $i$-моделей


      function Add(const AValue: TRelation): int64; overload; override;

Перекрытие такого же метода родительского класса, связано с необходимостью изменения его области видимости.
2.3. основной метод вставки связи в определенное место коллекции


      function Insert(const AIndex: longint; const AValue: TRelation): int64; overload; override;

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


      function TRelations.Insert(const AIndex: longint; const AValue: TRelation): int64;
      var
        AConnection: TConnection;
      begin
        try
// Метод TryInnerInsert необходим для прямой вставки связи!
          if (TryInnerInsert(AIndex, AValue, Result)) then exit;
          Result := inherited Insert(AIndex, AValue);
          if (Result < 0) then exit;
          if (not Assigned(AValue.Connection)) then
            begin
              Owner.Automate.Connections.Add(AValue.CreateConnect());
            end;
        except
          Result := -1;
        end;
      end;

Как видим в нем появляется некоторый метод TryInnerInsert.
2.4. Дополнительный метод вставки связи TryInnerInsert


//.......................................................
    protected
      function TryInnerInsert(const AIndex: longint; const AValue: TRelation; out AOutIndex: int64): boolean; overload; virtual;
//.......................................................
    end; 

необходим нам по нескольким причинам:
– прежде всего, он проверяет в какую из коллекций – входы или выходы производиться вставка;
– проверяет необходимость дополнительной инициализации связи и если это необходимо и возможно выполняет инициализацию ссылок на связываемые модели, а затем
– выполняет требуемые вставки в коллекции входов и выходов;
– при отсутствии необходимых сведений для инициализации вызывает исключение.
Код метода TryInnerInsert достаточно сложно прокомментировать в статье. Поэтому для желающих разобраться с ним смотрите его код, помещенный на врезке. Там приведено достаточно много комментариев, я полагаю с этим кодом можно при наличии желания разобраться.


Код метода TryInnerInsert
      function TRelations.TryInnerInsert(const AIndex: longint; const AValue: TRelation; out AOutIndex: int64): boolean;
      var
        AIndexOf: integer;
      begin
        AOutIndex := AIndex;
        Result := false;
        if (Assigned(AValue.ModelSource) and Assigned(AValue.ModelTarget)) then exit;
// Модель-источник явно не задана!
        if (Self is TInputs) then
          begin
            if ((AValue.TargetId >= 0) and (AValue.TargetId <> Owner.ObjectID)) then
              begin
// Вставляем не туда куда надо! Ошибка!
                raise Exception.CreateFmt(RSErrorUnknowIds,[AValue.SourceId,AValue.TargetId, Owner.ObjectID, Self.ClassName]);
              end;
// Вставляем туда куда надо!
            if (AValue.SourceId < 0) then
              begin
// Неопределен приемник!
                raise Exception.CreateFmt(RSErrorUnknowIds,[AValue.SourceId,AValue.TargetId, Owner.ObjectID, Self.ClassName]);
              end;
// Заданы SourceId и TargetId!
// Проверяем на уникальность связи!
            for AIndexOf := 0 to Self.Count - 1 do
              begin
                if (this[AIndex].SourceId <> AValue.SourceId) then continue;
                raise Exception.CreateFmt(RSErrorNotUniqueRelation,[AValue.SourceId,AValue.TargetId, Owner.ObjectID, Self.ClassName]);
              end;
            if (AValue.TargetId < 0) then
              begin
                AValue.TargetId := Owner.ObjectID;
              end;
// Задали модель-источник и модель-приемник.
            if (not Assigned(AValue.ModelSource)) then
              AValue.ModelSource := Owner.Automate.FindById(AValue.SourceId);
            if (not Assigned(AValue.ModelTarget)) then
              AValue.ModelTarget := Owner;
          end;
        if (Self is TOutputs) then
          begin
            if ((AValue.SourceId >= 0) and (AValue.SourceId <> Owner.ObjectID)) then
              begin
// Вставляем не туда куда надо! Ошибка!
                raise Exception.CreateFmt(RSErrorUnknowIds,[AValue.SourceId,AValue.TargetId, Owner.ObjectID, Self.ClassName]);
              end;
// Вставляем туда куда надо!
            if (AValue.TargetId < 0) then
              begin
// ... Но неопределен источник!
                raise Exception.CreateFmt(RSErrorUnknowIds,[AValue.SourceId,AValue.TargetId, Owner.ObjectID, Self.ClassName]);
              end;
// Заданы SourceId и TargetId!
// Проверяем на уникальность связи!
            for AIndexOf := 0 to Self.Count - 1 do
              begin
                if (this[AIndex].TargetId <> AValue.TargetId) then continue;
                raise Exception.CreateFmt(RSErrorNotUniqueRelation,[AValue.SourceId,AValue.TargetId]);
              end;
            if (AValue.SourceId < 0) then
              begin
                AValue.SourceId := Owner.ObjectID;
              end;
// Задали модель-источник и модель-приемник.
            if (not Assigned(AValue.ModelSource)) then
              AValue.ModelSource := Owner;
            if (not Assigned(AValue.ModelTarget)) then
              AValue.ModelTarget := Owner.Automate.FindById(AValue.TargetId);
          end;
        AOutIndex := AValue.ModelSource.Outputs.Insert(AIndex, AValue);
        AOutIndex := AValue.ModelTarget.Inputs.Insert(AIndex, AValue);
        Result := true;
      end;

Удаление элементов коллекции


Для TRelations нам не понадобилось перекрывать методы удаления связей из коллекций – для этого достаточно стандартных: все необходимые операции выполняются при утилизации экземпляра TRelation.


Входы и выходы


Для идентификации входов и выходов необходимо ввести их специальные определения:


    TInputs = class(TRelations)
    protected
      function Add(const AValue: int64): int64; overload; override;
    public
      constructor Create(); overload; override;
      destructor Destroy(); override;
    public
      function Insert(const AIndex: longint; const AValue: TRelation): int64; overload; override;
    end;
    TInputsClass = class of TInputs;
//..............................................................................
    TOutputs = class(TRelations)
    protected
      function Add(const AValue: int64): int64; overload; override;
    public
      constructor Create(); overload; override;
      destructor Destroy(); override;
    public
      function Insert(const AIndex: longint; const AValue: TRelation): int64; overload; override;
    end;
    TOutputsClass = class of TOutputs;

Таким образом, мы можем для этих коллекций определить перекрытые методы Add, выполняющие добавление связи в связанные между собой $i$-модели:


      function TInputs.Add(const AValue: int64): int64;
      begin
        Result := Add(TRelation.Create(AValue, Owner));
      end;
//..............................................................................
      function TOutputs.Add(const AValue: int64): int64;
      begin
        Result := Add(TRelation.Create(Owner, AValue));
      end;

Аргумент этих методов AValue означает идентификатор сопряженной $i$-модели.


i-Модели


Определения


Свойства


$i$-модель, согласно концепции Н.М. Амосова, должна иметь следующие свойства:
– текущий уровень возбужденности (Arousal),
– текущий порог возбуждения (Threshold),
– условный начальный коэффициент возбудимости (ResidualActivateCoeff),
– условный текущий коэффициент возбудимости (ActivateCoeff),
– коллекцию входящих связей (Inputs).
– коллекцию исходящих связей (Outputs),


Дополнительно в данную реализацию я включил:
– свойства, служащие идентификации модели, основным из которых будет считаться некоторый целочисленный идентификатор ObjectId (int64).


Свойства, идентифицирующие модель унаследуем от некоторого имеющегося на вооружении класса именованных объектов TNamedObject. В их число включены:
– числовой идентификатор модели (ObjectId: int64);
– полное имя модели (Name: string);
– короткое имя модели (NameShort: string);
– символьный код (Code: string).


Характеристики модели


В определении класса $i$-моделей необходимо учесть следующие характеристики $i$-модели:
– характеристика возбуждения модели – Aсtivation(…) (9);
– характеристика торможения – Inhibit(…) (7);
– характеристика затухания возбужденности модели – Attenuation(…) (8);
– характеристика гипертрофии модели – Overgrowth(…) (10а);
– характеристика адаптации модели – Adaptation(…) (10б).
Таким образом, получаем следующее предварительное определение класса $i$-моделей


    TModel = class(TNamedObject)
    private
//................................................    
    protected
// Характеристика проторения: R (4.2).
      function Winding(AR: TRelation): TRelation; overload; virtual;
// Характеристика установления: R_1 (4.3).
      function Setting(AR: TRelation): TRelation; overload; virtual;
// Характеристика затухания: (4.4а, 4.4б, 4.5а и 4.5б).
      function Attenuation(AR: TRelation): TRelation; overload; virtual;
    protected
// Характеристика возбуждения модели (9).
      function Aсtivation(): double; overload; virtual;
// Характеристика торможения: (7).
      function Inhibit(): double; overload; virtual;
// Характеристика затухания возбужденности модели (8).
      function Attenuation(): double; overload; virtual;
// Характеристика гипертрофии модели (10а).
      function Overgrowth(): double; overload; virtual;
// Характеристика адаптации модели (10б).
      function Adaptation(): double; overload; virtual;
    protected
// Текущий уровень возбуждения модели - возбужденность модели.
        property Arousal: double read GetArousal write SetArousal;
// Активирующий эффект: (4.6а).
        property ActivateEffect: double read GetActivateEffect write SetActivateEffect;
// Тормозной эффект: (4.6б).
        property InhibitEffect: double read GetInhibitEffect write SetInhibitEffect;
// Порог возбуждения модели.
        property Threshold: double read GetThreshold write SetThreshold;
// Коэффициент возбуждения модели.
        property ActivateCoeff: double read GetActivateCoeff write SetActivateCoeff;
// Остаточный (начальный) коэффициент возбуждения модели.
        property ResidualActivateCoeff: double read GetResidualActivateCoeff write SetResidualActivateCoeff;
   protected
// Текущий уровень возбуждения модели - возбужденность модели.
        property Arousal: double read GetArousal write SetArousal;
// Активирующий эффект: (4.6а).
        property ActivateEffect: double read GetActivateEffect write SetActivateEffect;
// Тормозной эффект: (4.6б).
        property InhibitEffect: double read GetInhibitEffect write SetInhibitEffect;
// Порог возбуждения модели.
        property Threshold: double read GetThreshold write SetThreshold;
// Коэффициент возбуждения модели.
        property ActivateCoeff: double read GetActivateCoeff write SetActivateCoeff;
// Остаточный (начальный) коэффициент возбуждения модели.
        property ResidualActivateCoeff: double read GetResidualActivateCoeff write SetResidualActivateCoeff;
    protected
//................................................    
    end;

Указанные методы могут быть полностью реализованы только для конкретного М-автомата и поэтому они могли бы быть объявлены как абстрактные, что позволило бы их перекрыть в дальнейшем. В данном же конкретном случае они просто имеют «пустую» реализацию, которую я приводить не буду: все эти методы просто возвращают 0.0.


Методы пересчета


Для выполнения пересчета свойств $i$-модели мною в класс TModel были включены два основных виртуальных метода Exec(…) и InternalExec(…). Эти два метода возвращают true, если пересчет $i$-модели выполнен корректно и работа М-сети еще не завершена.


    TModel = class(TNamedObject)
    private
//................................................    
    protected
      function InternalExec(): boolean; overload; virtual;
//................................................    
    public
      function Exec(): boolean; overload; virtual;
//................................................    
    end;

Основным мотивом введения метода InternalExec(…) является получение возможности более гибкой подстройки поведения отдельных классов $i$-модели в реализациях конкретных М-сетей. В базовой реализации оба метода просто возвращают false.


Класс i-модели


В соответствии с этими соображениями строиться следующее определение базового класса $i$-модели:


    TModel = class(TNamedObject)
    private
        FAutomate: TAutomate;
        FModels: TModels;
        FOwner: TModel;
    private
        FArousal: double;
        FActivateEffect: double;
        FInhibitEffect: double;
    private
        FThreshold: double;
        FActivateCoeff: double;
        FResidualActivateCoeff: double;
    private
        FOutputs: TOutputs;
        FInputs: TInputs;
    protected
      function GetThis(): TObject; overload; virtual;
    protected
      procedure SetObjectName(const AValue: TNameObject); overload; override;
      procedure SetObjectNameShort(const AValue: TNameShort); overload; override;
      procedure SetObjectCode(const AValue: TObjectName); overload; override;
    protected
      function GetOwner(): TModel; overload; virtual;
      procedure SetOwner(const AValue: TModel); overload; virtual;
    protected
      function GetThreshold(): double; overload; virtual;
      procedure SetThreshold(const AValue: double); overload; virtual;
      function GetArousal(): double; overload; virtual;
      procedure SetArousal(const AValue: double); overload; virtual;
      function GetActivateEffect(): double; overload; virtual;
      procedure SetActivateEffect(const AValue: double); overload; virtual;
      function GetInhibitEffect(): double; overload; virtual;
      procedure SetInhibitEffect(const AValue: double); overload; virtual;
      function GetActivateCoeff(): double; overload; virtual;
      procedure SetActivateCoeff(const AValue: double); overload; virtual;
      function GetResidualActivateCoeff(): double; overload; virtual;
      procedure SetResidualActivateCoeff(const AValue: double); overload; virtual;
    protected
      function GetOutputs(): TOutputs; overload; virtual;
      procedure SetOutputs(const AValue: TOutputs); overload; virtual;
      function GetInputs(): TInputs; overload; virtual;
      procedure SetInputs(const AValue: TInputs); overload; virtual;
    protected
      function GetAutomate(): TAutomate; overload; virtual;
      procedure SetAutomate(const AValue: TAutomate); overload; virtual;
      function GetPlatform(): TPlatform; overload; virtual;
      procedure SetPlatform(AValue: TPlatform); overload; virtual;
    protected
      procedure InternalExtract(); overload; virtual;
    protected
        property Models: TModels read FModels;
    protected
//................................................    
    protected
// Текущий уровень возбуждения модели - возбужденность модели.
        property Arousal: double read GetArousal write SetArousal;
// Активирующий эффект: (4.6а).
        property ActivateEffect: double read GetActivateEffect write SetActivateEffect;
// Тормозной эффект: (4.6б).
        property InhibitEffect: double read GetInhibitEffect write SetInhibitEffect;
// Порог возбуждения модели.
        property Threshold: double read GetThreshold write SetThreshold;
// Коэффициент возбуждения модели.
        property ActivateCoeff: double read GetActivateCoeff write SetActivateCoeff;
// Остаточный (начальный) коэффициент возбуждения модели.
        property ResidualActivateCoeff: double read GetResidualActivateCoeff write SetResidualActivateCoeff;
    protected
      class function GetClassInputs(): TInputsClass; overload; virtual;
      class function GetClassOutputs(): TOutputsClass; overload; virtual;
    protected
      function InternalExec(): boolean; overload; virtual;
      procedure ChangeArousal(AActivateInhibiteSystem: TActivateInhibiteSystem); overload; virtual;
    public
      constructor Create(); overload; override;
      constructor Create(const AObjectId: int64; const AName: string; const ANameShort: string = ''; const ACode: string = ''); overload; virtual;
      destructor Destroy(); override;
    public
      function Exec(): boolean; overload; virtual;
      function Perform(): boolean; overload; virtual;
    public
      procedure Reset(); overload; virtual;
    public
//................................................    
    public
        property Owner: TModel read GetOwner write SetOwner;
    published
        property Outputs: TOutputs read GetOutputs write SetOutputs;
        property Inputs: TInputs read GetInputs write SetInputs;
    published
        property Automate: TAutomate read GetAutomate write SetAutomate;
        property Platform: TPlatform read GetPlatform write SetPlatform;
    end;
    TModelClass = class of TModel;
Дополнительно были включены методы облегчающие построение соединения текущей модели с другими моделями М-сети:
    TModel = class(TNamedObject)
//................................................    
    public
//................................................    
      function Connect(ATarget: TModel; AConnection: TConnection): TModel; overload; virtual;
//................................................    
    end;

Коллекции i-моделей


Для реализации более крупных понятийных блоков концепции Н.М. Амосова нам необходимо определиться с реализацией хранения некоторого набора $i$-моделей. Для этого в нашу реализацию мы должны ввести определение группы $i$-моделей.
Прежде всего, определим класс коллекции $i$-моделей:


[XMLROOT('Models')]
    TModels = class(TVector<TModel>)
    private
        FOwner: TAutomate;
    private
        FMapById: TMap<int64, int64>;
        FMapByName: TMap<string, int64>;
    private
        FIsTerminated: boolean;
    protected
      function GetOwner(): TAutomate; overload; virtual;
      procedure SetOwner(const AValue: TAutomate); overload; virtual;
      procedure InnerSetAutomate(const AModel: TModel); overload; virtual;
    protected
        property Owner: TAutomate read GetOwner;
    public
      constructor Create(); overload; override;
      constructor Create(const AComparer: JOBLIB.Core.Comparers.IComparer<TModel>); overload; override;
      constructor Create(AOwner: TAutomate); overload; virtual;
      destructor Destroy(); override;
    public
      function GetObjectId(): int64; overload; virtual;
    public
      function Add(const AValue: TModel): int64; overload; override;
      function Insert(const AIndex: longint; const AModel: TModel): int64; overload; override;
      function Extract(const AModel: TModel): TModel; overload; override;
      procedure Delete(const AIndex: longint); overload; override;
    public
      function FindById(const AModelId: int64): TModel; overload; virtual;
      function FindByName(const AModelName: string): TModel; overload; virtual;
    public
      procedure Reorder(const AStartIndex: integer); overload; override;
    end;
    TModelsClass = class of TModels;

  1. Класс TModels является наследником шаблонного класса TVector
    <TModel>
    .
  2. Класс TModels должен иметь возможность управлять утилизацией своих элементов (в моем случае: я могу от делать с помощью унаследованного свойства FreeObjects := true или false).
  3. Необходимо реализовать следующий метод
      procedure TModels.InnerSetAutomate(const AModel: TModel);
      begin
        AModel.Automate := Owner;
      end;

    Который, при необходимости, может быть в дальнейшем перекрыт в наследниках класса.


    Должна быть возможность перекрытия методов добавления и вставки моделей в коллекцию (в моем случае достаточно перекрыть метод Insert):
      function TModels.Insert(const AIndex: longint; const AModel: TModel): int64;
      begin
        if (AModel.Name.Trim().IsEmpty()) then exit(-1);
        Result := inherited Insert(AIndex, AModel);
        if (AModel.ObjectID < 0) then
          begin
            AModel.ObjectID := GetObjectId();
          end;
        try
          if (AModel.ObjectID < 0) then
            raise Exception.Create(RSErrorIncorrectAutomate);
          FMapById.Add(AModel.ObjectID, Result);
          FMapByName.Add(AModel.Name, Result);
          InnerSetAutomate(AModel);
          Reorder(AIndex);
        except
          Result := -1;
        end;
      end;

    Это позволит в дальнейшем использовать методы

      function FindById(const AModelId: int64): TModel; overload; virtual;
      function FindByName(const AModelName: string): TModel; overload; virtual;

    для быстрого поиска моделей по числовому идентификатору или по имени модели. Для этого и используются словари:

    FMapById: TMap<int64, int64>;
    FMapByName: TMap<string, int64>;

    Можно также при необходимости добавить словарь и поиск по GUID $i$-модели.


    Для реализации части вышеперечисленных требований необходимо существенно перекрыть методы удаления
      function TModels.Extract(const AModel: TModel): TModel;
      var
        AIndexOf: int64;
        AModelGroup: TModelGroup;
        ASubmodel: TSubmodel;
        AIndexSubmodelOf: integer;
      begin
        Result := inherited Extract(AModel);
        if (FIsTerminated) then exit;
        if (Assigned(AModel)) then
          begin
            if (FMapById.TryGetValue(AModel.ObjectID, AIndexOf)) then
              begin
                FMapById.Remove(AModel.ObjectID);
                FMapByName.Remove(AModel.Name);
                Reorder(AIndexOf);
              end;
          end;
      end;
      procedure TModels.Delete(const AIndex: longint);
      begin
        FMapById.Remove(this[AIndex].ObjectID);
        FMapByName.Remove(this[AIndex].Name);
        inherited Delete(AIndex);
        Reorder(AIndex);
      end;

    Для получения числового идентификатора ObjectId при вставке в коллекцию необходимо реализовать метод:
      function GetObjectId(): int64; overload; virtual;

    и свойство

        property Owner: TAutomate read GetOwner;

    В моей реализации коллекция получает идентификатор, запрашивая его у М-автомата (именно он имеет в своем составе генератор уникальных числовых последовательностей), как правило, при посредничестве модели-владельце коллекции (а это может быть группа – сфера моделей или М-сеть):

      function TModels.GetObjectId(): int64;
      begin
        Result := Owner.GetSequence().NextVal();
      end;

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


    Метод
      procedure Reorder(const AStartIndex: integer); overload; override;

    несуществен, хотя и полезен при использовании упорядоченных наборов $i$-моделей, и служит для переопределения порядковых номеров $i$-моделей в коллекции.



    Разновидности i-моделей


    Рассмотрим возможные разновидности $i$-моделей.
    После некоторого знакомства с устройством некоторых частных случаев М-автоматов можно прийти к выводу, что возможна следующая классификация $i$-моделей:
    перцепты$i$-модели, получающие свои входы из внешней среды – TPercept,
    квалиа (qualia)$i$-модели, замкнутые по входам и выходам на другие $i$-модели – TQualia,
    эффекторы – класс $i$-моделей, своими выходами влияющие на стояние внешней среды – TEffector.


    Квалиа (qualia)


    Самый простой из наследуемых от TModel класс. Его отличие от TModel заключается только в том, что изменяются области видимости двух свойств Threshold и Arousal (они из защищенных стали публикуемыми):


        TQualia = class(TModel)
        public
          constructor Create(); overload; override;
          destructor Destroy(); override;
        published
    [XMLAttribute('Threshold')]
            property Threshold;
    [XMLAttribute('Arousal')]
            property Arousal;
        end;

    Перцепты и эффекторы


    Перцепты (TPercept) и эффекторы (TEffector) также имеют достаточно простую специализацию и наследуются от TQualia:


        TPercept = class(TQualia)
        private
            FPlatform: TPlatform;
        protected
          function GetPlatform(): TPlatform; overload; override;
          procedure SetPlatform(AValue: TPlatform); overload; override;
        protected
          function InternalExec(): boolean; overload; override;
        public
          constructor Create(); overload; override;
          destructor Destroy(); override;
        public
          function Exec(): boolean; overload; override;
        published
            property Platform;
        end;

    и


        TEffector = class(TQualia)
        private
            FPlatform: TPlatform;
        protected
          function GetPlatform(): TPlatform; overload; override;
          procedure SetPlatform(AValue: TPlatform); overload; override;
        protected
          function InternalExec(): boolean; overload; override;
        public
          constructor Create(); overload; override;
          destructor Destroy(); override;
        public
          function Exec(): boolean; overload; override;
        published
            property Platform;
        end;

    Как видим, они получили дополнительное свойство Platform, позволяющее получать и передавать воздействие из (во) «внешней» среды (внешнюю среду):


        private
            FPlatform: TPlatform;
        protected
          function GetPlatform(): TPlatform; overload; override;
          procedure SetPlatform(AValue: TPlatform); overload; override;
        protected
    ..................................
        public
    ..................................
        public
    ..................................
        published
            property Platform;
        end;

    И перекрытые методы


          function Exec(): boolean; overload; override;

    реализация которых состоит в проверке наличия «живой» ссылки на экземпляр «внешней» среды, как в TPercept:


          function TPercept.Exec(): boolean;
          begin
            try
              if (not Assigned(Platform)) then
                raise Exception.Create('Не задан источник информации о внешней среде!');
              Result := InternalExec();
            except
              Result := false;
            end;
          end;

    и для TEffector:


          function TEffector.Exec(): boolean;
          begin
            try
              if (not Assigned(Platform)) then
                raise Exception.Create('Не задана среда воздействия!');
              Result := InternalExec();
            except
              Result := false;
            end;
          end;

    Вся основная нагрузка по пересчету состояния $i$-модели таким образом ложиться на метод InternalExec(). Именно его, как правило, и нужно будет перекрыть для наследников TPercept и TEffector.


    Замечание 9
    Под термином «внешняя среда» следует понимать не только среду внешнюю по отношению ко всему автомату, а также внешнюю по отношению к некоторой групповой $i$-модели, в которой содержаться перцепты или эффекторы.

    Групповые i-модели


    С другой точки зрения, $i$-модели могут быть расклассифицированы на два класса:
    унитарные (атомарные)$i$-модели, не содержащие в себе другие $i$-модели и
    групповые$i$-модели, объединяющие в каком-либо отношении наборы (коллекции) других $i$-моделей, возможно в некоторых случаях временно («транзитные» $i$-модели), входящие в другие групповые $i$-модели (сферы, СУТ, очереди исполнения М-автомата и т.п.). В концепции Н.М. Амосова принято считать, что групповые модели должны подчиняться иерархическому принципу (но авторы монографии [3] сами же отходят от этого принципа: см. например, определение сферы моделей). Принцип иерархичности означает, что любые две группы не могут между собой пресекаться (т.е. их коллекции не могут содержать одновременно одну и ту же $i$-модель). Этот принцип достаточно жесткий. И нам придется от него отказаться, хотя последствия этого отказа весьма серьезные. О чем мы будем говорить в дальнейшем.
    Групповые $i$-модели могут делиться на
    формальные (наборы, сферы) – в этих $i$-моделях состояние группы не определяется состоянием их $i$-моделей. Критерием объединения в такую группу может стать прагматика задачи, стоящей перед М-автоматом, или иные внешние требования, стоящие перед разработчиком М-автомата;
    сущностные (ансамбли, уровни контроля или управления, этажи, функциональные подсистемы, М-сеть, моторные программы, СУТ, М-автомат и пр.) – относительно замкнутые совокупности $i$-моделей, изменение состояния которых в процессе их функционирования (пересчета) может изменять состояние (свойства и характеристики) самой агрегирующей их группы.


    Группы (сферы) i-моделей


    Требования, предъявляемые к реализации класса субмодели


    Как я писал выше, в концепции Н.М. Амосова допустимы групповые $i$-модели, содержащие $i$-модели (субмодели), не входящие в другие групповые $i$-модели, так же как и содержащие $i$-модели, включенные в другие групповые $i$-модели. В дальнейшем не будем строго придерживаться принципа иерархичности использования групповых $i$-моделей: т.е. будем исходить из предположении, что любая $i$-модель может быть включена в любую групповую модель, за исключением случаев образования «циклов» (это отдельная большая проблема, которой мы не будем касаться в этой статье).
    Для того чтобы обеспечить возможность управления субмоделями упомянутой мною второй группы и выполнения их «прозрачной» сериализации/десериализации нам необходимо:
    – определенным образом усложнить функциональность класса TModel;
    – определить и реализовать класс TGroupModel;
    – определить и реализовать класс TSubmodel, а также соответствующий класс коллекции субмоделей TSubmodels.
    К реализации класса субмодели TSubmodel мною будут установлены следующие требования:
    1. экземпляр класса TSubmodel (образ) должен представлять единственный экземпляр класса TModel (прообраз);
    2. экземпляру класса TModel может соответствовать неограниченное количество экземпляров класса TSubmodel (в определенном смысле: субмодель есть представитель (клон) экземпляра класса TModel);
    3. при удалении прообраза удаляются все соответствующие ему субмодели-представители;
    4. удаление представителя из соответствующей коллекции не должно вызывать автоматического удаления прообраза;
    5. автоматическое удаление прообраза возможно при выполнении следующих условий;
    5.1. во всех коллекциях субмоделей М-автомата отсутствуют его представители,
    5.2. в удаляемой $i$-модели (образ) выставлен специальный флаг и/или получено соответствующее подтверждение пользователя,
    6. при сериализации/десериализации для идентификации должен использоваться только числовой идентификатор $i$-модели (образа);
    7. совместимость с применяемой технологией доступа к свойствам объекта субмодели в конфигураторе.


    Дополнительные соображения, необходимые для дальнейшей реализации: варианты реализации


    Рассмотрим возможные альтернативные подходы для реализации вышеуказанных требований:
    Вариант 1. Состоит в использовании интерфейсов. Весьма удобный инструмент во многих случаях, привлекательный с точки зрения простоты; неоднократно опробованный. Подходит для реализации требований 1, 2, 4, 5.1, 5.2 (при соответствующие доработке класса субмоделей). Не совместим с требованиями: 3, 6 и 7; требует определения соответствующих интерфейсов и наследования класса TModel от TInterfacedObject или его аналога.
    Вариант 2. Использовать слабые ссылки на прообраз. Не выполняется целый ряд вышеперечисленных требований.
    Вариант 3. Состоит в использовании прокси-класса в комбинации с интерфейсами (аналогично интеллектуальным указателям). Можно реализовать все требования. Опробован: получилось очень громоздко и ненадежно.
    Вариант 4. Прокси-класс без использования интерфейсов. Идея состоит в следующем:
    1. создать класс TSubmodel, содержащий:
    – ссылку на представляемую модель, возможно первоначально неинициализированную – Prototype: TModel;
    – числовой идентификатор представляемой модели – Refer: int64;
    – ссылки на предыдущий и последующие образы представляемой $i$-модели; такие ссылки должны позволить организовать двунаправленную очередь образов указанной $i$-модели – Prev: TSubmodel и Next: TSubmodel;
    – добавить ссылку на групповую $i$-модель в коллекцию субмоделей которой будет или была добавлена указанная субмодель;
    – в некоторых случаях будет важен порядок размещения субмодели в коллекции. Поэтому стоит добавить свойство OrderId: int64, указывающие на порядковый номер субмодели в каждой конкретной коллекции субмоделей.
    2. В класс TModel:
    – добавить ссылку на последний созданный образ (HeadSubmodel: TSubmodel) указанной $i$-модели. Эта ссылка должна всегда ссылаться на последнюю созданную субмодель, соответствующую указанной $i$-модели;
    – добавить защищенное свойство-флаг, регулирующее режим утилизации прообраза при удалении всех соответствующих субмоделей;
    – добавить метод FreeSubmodel(…) управляющий удалением субмодели из двунаправленного списка;
    – добавить событие OnAfterDisposeSubmodel, позволяющее переопределить стратегию утилизации прототипа после удаления всех связанных с ним субмоделей. Непосредственно для определения стратегии утилизации реализуем метод CheckFreeModel(ASubmodel: TSubmodel): boolean.
    3. Добавление субмодели в коллекцию TSubmodels может выглядеть таким образом:
    3.1. Создаем экземпляр класса TSubmodel и инициализируем его значением числового идентификатора выбранной модели-прототипа;
    3.2. В замещенном методе Insert коллекции субмоделей TSubmodels:
    – вызываем метод BindSubmodel М-автомата; в качестве параметра метода передаем созданный экземпляр субмодели:


          procedure TAutomate.BindSubmodel(ASubmodel: TSubmodel);
          begin
            if (not Ready) then exit;
            if (not Assigned(ASubmodel.Prototype)) then
              ASubmodel.Prototype := FindById(ASubmodel.Refer);
            if (not Assigned(ASubmodel.Owner)) then
              raise Exception.CreateFmt('Связывание субмодели. Неверно задан идентификатор субмодели: %d',[ASubmodel.Refer]);
            ASubmodel.Prototype.HeadSubmodel := ASubmodel;
          end;

    – выполняем проверку возможного возникновения циклической зависимости после вставки субмодели (такая проверка будет рассмотрена в п. 8). Если такая ситуация возможна, то вставку отменяем;
    – выполняет непосредственную вставку в коллекцию субмоделей.
    3.2. В методе BindSubmodel М-автомата:
    – инициализируем ссылку Prototype на модель, идентифицируемую свойством Refer;
    – подменяем HeadSubmodel и выполняем вставку переданной в метод субмодели в начало (или конец – это как кому нравиться) двунаправленной очереди.
    4. Связывание (инициализация) субмоделей после десериализации М-автомата будет выглядеть следующим образом (некоторые детали устройства М-автомата будут описаны далее):
    4.1. Получаем десериализованную коллекцию моделей М-автомата.
    4.2. Обратим внимание на следующее обстоятельство: на момент десериализации М-автомата: Ready = false.
    4.3. Проходим по всей коллекции моделей М-автомата; для каждой групповой модели просматриваем выполняем метод BindSubmodel.
    4.4. Тоже самое необходимо выполнить для
    – СУТ М-автомата и
    – очереди исполнения М-автомата (см. детали ниже).
    5. Удаление субмодели из коллекции должно состоять из нескольких шагов:
    5.1. в методе Delete(…) коллекции субмоделей:
    – извлечение из коллекции субмоделей (если возможно);
    – передача утилизируемой субмодели модели-прообразу для предварительной обработки (метод FreeSubmodel).
    – утилизация (вызов inherited Delete(…));
    – при необходимости вызов метода удаления модели-прообраза (необходимые данные для этого должен вернуть метод FreeSubmodel) – метод FreePrototype М-автомата.
    5.2. В модели-прообразе (метод FreeSubmodel класса TModel):
    – при необходимости выполняется переустановка ссылки HeadSubmodel на предыдущую субмодель-образ;
    – удаление из двунаправленного списка субмоделей;
    – определение необходимости утилизации модели-прототипа
    – возврат необходимых значений в вызвавший метод.
    6. Что должно происходить при удалении модели-прототипа?
    6.1. Перед последним шагом утилизации модели-прототипа мы должны освободить двусвязный список субмоделей. Для этого:
    6.1.1. начинаем двигаться от HeadSubmodel;
    6.1.2. на каждом шагу извлекаем (Extract) субмодель из соответствующей коллекции;
    6.1.3. запоминаем ссылку на предыдущую (Prev) в списке субмодель;
    6.1.4. если Prev не nil: «обнуляем» ссылку Prev.Next;
    6.1.5. утилизируем ACurrSubmodel;
    6.1.6. повторяем предыдущие шаги до тех пор пока не очистим весь список субмоделей.
    6.2. Окончательно утилизируем модель прототип.
    7. Деструкция групповой модели-прототипа, прежде всего, должна состоять из прохода по списку (коллекции) ее субмоделей и вызова метода FreeSubmodel (или какого-то его варианта) для каждого из ее элементов. После чего можно завершить утилизацию всего контейнера.
    8. Возможна такая ситуация при формировании (настройке) М-сети при вставке групповой модели в качестве субмодели может включить (возможно – косвенно) себя же в качестве субмодели. Это в последующем однозначно приведет к «зацикливанию» операции пересчета возбужденности. Поэтому при вставке очередной субмодели, безусловно, необходимо выполнять соответствующую проверку:


    Замечание 10
    Для дальнейших рассуждений важно иметь ввиду, что на момент проведения проверки М-сеть корректна, т.е. в ней отсутствуют циклические зависимости между моделями и нам необходимо лишь проверить приведет ли вставка данной модели к образованию такой циклической зависимости.

    8.1. проверка цикличности субмоделей должна проводиться только при вставке групповой модели;
    8.2. проверка цикличности субмоделей имеет смысл лишь в том случае если вставляемая групповая модель уже имеет в своем составе непустую коллекцию субмоделей (not Submodels.IsEmpty()). Выполнение условия «вставляемая модель не имеет субмоделей» означает, что вставляемая модель нигде ранее не могла быть родительской и поэтому ее вставка не может привести к возникновению циклической зависимости;
    8.3. модель не может быть включена в собственную коллекцию субмоделей;
    8.4. модель не может включать в собственную коллекцию субмоделей непосредственного своего «нового владельца»: для этого выполним проверку наличия в собственной коллекции этого «потенциального владельца». Если он присутствует, то проверка – неуспешна. Если он отсутствует, это означает только то, что непосредственное «зацикливание» не возникнет, но полностью не исключает образования косвенной циклической зависимости.


    Замечание 11
    Тут мне по сценарию надо было бы впасть в истерику и завопить «Шеф всё пропало…! Гипс снимают…! Клиент уезжает…!». Что же делать? Как оценить потенциальную возможность возникновения косвенной циклической зависимости? Успокоимся – не все так плохо…

    8.5. Еще раз: на момент любой вставки М-сеть корректна, для этого и проводиться проверка – до этого момента мы не допустили вставки такой субмодели, которая привела бы к косвенной циклической зависимости!
    В каком же случае может образоваться косвенная циклическая зависимость? Ответ на этот вопрос прост: только в том случае если вставляемая модель содержит хотя бы одну субмодель, являющуюся «прародителем» «нового владельца» указанной модели. И это последний «железный» критерий, подлежащий проверке на наличие/отсутствие возможности образования косвенной циклической зависимости. Таким образом:
    8.5.1. проходим от «родителя родителя» «вверх по дэгу (DAG, directed acyclic graph)» моделей до некоторой «корневой» модели;
    8.5.2. для каждого «прародителя» проверяем наличие его в коллекции субмоделей вставляемой модели;
    8.5.3. ответ – вставка не допустима — может дан в двух случаях:
    – при наличии хотя бы одного «прародителя» в коллекции субмоделей вставляемой модели;
    – «прародитель» совпадает со вставляемой моделью.
    В ином случае необходимо продолжить обход и дойти до всех доступных «корневых» моделей. Ответ при прохождении всех доступных, таким образом, моделей – вставка допустима.
    8.5.4. Остается ответить на вопрос: а что это такое – «корневая» модель? Ответ прост: это групповая модель, которая не включена как субмодель ни в одну из других групповых моделей, что также означает, что ее двусвязный список пуст. Само существование подобных групповых моделей обеспечивается как раз описываемым алгоритмом.
    8.6. Таким образом, в п. 8.1-8.5 сформулирован алгоритм проверки наличия/отсутствия циклической зависимости при вставке субмодели.
    8.7. Алгоритм п. 8.5. может быть реализован таким образом, чтобы включить в себя проверки п. 8.3. и 8.4.
    Реализация этого алгоритма может быть выполнена следующим образом:


    //.......................................................................
          function TSubmodel.IsEqualsChild(AModel: TModel): boolean;
          begin
            // 8.3.
            if (Prototype.Equals(AModel)) then exit(false);
            // 8.4.
            if ((Prototype as TModelGroup).Contains(AModel.ObjectID)) then exit(false);
            Result := true;
          end;
    //.......................................................................
          function TSubmodels.CyclicDependencyCheck(ASubmodel: TSubmodel): boolean;
          var
            AParent: TModel;
            ACurrSubmodel: TSubmodel;
            AMapUnique: TMapUnique;
            AModelQueue: Queue<TModel>;
          begin
            if (not Assigned(Owner.Automate)) then exit(true);
            if (not Owner.Automate.Ready) then exit(true); // автомат в состоянии загрузки!
          // 8.1.
            if (not (ASubmodel.Prototype is TModelGroup)) then exit(true);
          // 8.2. Выполнение этого условия означает, что вставляемая модель нигде не ранее не могла быть родительской!
            if ((ASubmodel.Prototype as TModelGroup).Submodels.IsEmpty()) then exit(true);
          // 8.5. Реализуем нерекурсивно подъем по дэгу!
            AMapUnique := TMapUnique.Create();
            try
              AModelQueue.Enqueue(Owner);
              Result := true;
              while (not AModelQueue.IsEmpty()) do
                begin
                  AParent := AModelQueue.Dequeue();
                  Result := ASubmodel.IsEqualsChild(AParent);
                  if (not Result) then break;
                  ACurrSubmodel := AParent.HeadSubmodel;
    // Assigned(ACurrSubmodel) = false - означает что AParent - корневая модель!
                  while (Assigned(ACurrSubmodel)) do
                    begin // пробегаем по всем прародителям! заносим их в очередь!
                      if (ACurrSubmodel.Owner is TModelGroup) then
                        begin
                          if (not AMapUnique.ContainsKey(ACurrSubmodel.Refer)) then
                            begin
                              AMapUnique.Add(ACurrSubmodel.Refer,0);
                              AModelQueue.Enqueue(ACurrSubmodel.Owner);
                            end;
                        end;
                      ACurrSubmodel := ACurrSubmodel.Prev;
                    end;
                end;
              while (not AModelQueue.IsEmpty()) do
                AModelQueue.Dequeue();
            finally
              System.SysUtils.FreeAndNil(AMapUnique);
            end;
          end;

    Замечание 12
    Если вам не нравиться «подъем по дэгу» можно легко переформулировать данный алгоритм как «спуск по дереву» (либо с обходом в глубину, либо с обходом в ширину).

    9. При создании формы редактирования списка субмоделей необходимо запретить прямое создание новой $i$-модели; необходимо будет сделать доступными лишь операции: вставки выбранной (уже существующей) модели, редактирования, просмотра свойств и удаления субмоделей.
    Выглядит очень привлекательно: все требования могут быть реализованы, последовательность реализации – четкая, тестируемость – высокая. Несколько высокая степень накладных расходов на проверку допустимости вставки субмодели, но и для остальных рассмотренных нами вариантов она не может быть снижена. Поэтому для дальнейшей реализации выбираем данный вариант.


    Субмодель


    Определение субмодели


    В соответствии с приведенным выше п. 1 дадим классу TSubmodel следующее определение:


    [XMLROOT('Submodel')]
        TSubmodel = class(TReference<TModel>)
        private
            FOwner: TModel;
            FOrderId: int64;
            FSubmodelName: string;
        private
            FPrevSubmodel: TSubmodel;
            FNextSubmodel: TSubmodel;
        protected
          function GetRefer(): int64; overload; override;
        protected
          function GetOrderId(): int64; overload; virtual;
          procedure SetOrderId(const AValue: int64); overload; virtual;
          function GetName(): string; overload; virtual;
          procedure SetName(const AValue: string); overload; virtual;
        protected
          function GetOwner(): TModel; overload; virtual;
          procedure SetOwner(const AValue: TModel); overload; virtual;
        protected
          function GetPrevSubmodel(): TSubmodel;
          procedure SetPrevSubmodel(const AValue: TSubmodel);
          function GetNextSubmodel(): TSubmodel;
          procedure SetNextSubmodel(const AValue: TSubmodel);
        public
          constructor Create(); overload; override;
          constructor Create(APrototype: TModel); overload; virtual;
          destructor Destroy(); override;
        public
          function IsEqualsChild(AModel: TModel): boolean; overload; virtual;
        public
            property Prototype;
        public
            property Prev: TSubmodel read GetPrevSubmodel write SetPrevSubmodel;
            property Next: TSubmodel read GetNextSubmodel write SetNextSubmodel;
        published
            property Owner: TModel read GetOwner write SetOwner;
        published
    [XMLAttribute('Refer')]
            property Refer;
    [XMLAttribute('OrderId')]
            property OrderId: int64 read GetOrderId write SetOrderId;
    [XMLAttribute('Name')]
            property Name: string read GetName write SetName;
        end;
        TSubmodelClass = class of TSubmodel;

    Как можно видеть класс TSubmodel порожден от некоторого шаблонного класса TReference<T: class, constructor>. Структура и реализация его методов достаточно простая, поэтому приведем для ясности только его определение:


        TReference<T: class, constructor> = class
        private
            FRefer: int64;
            FPrototype: T;
        protected
          function GetPrototype(): T; overload; virtual;
          procedure SetPrototype(const AValue: T); overload; virtual;
          function GetRefer(): int64; overload; virtual;
          procedure SetRefer(const AValue: int64); overload; virtual;
        protected
            property Prototype: T read GetPrototype write SetPrototype;
            property Refer: int64 read GetRefer write SetRefer;
        public
          constructor Create(); overload; virtual;
          destructor Destroy(); override;
        end;

    По традиции, не будем останавливаться на простой реализации методов доступа класса TSubmodel.


    Корректировка класса TModel


    Следуя указаниям к варианту 4, приведенным в п. 2 (см. выше), внесем необходимые корректировки в определение класса TModel.
    1. Прежде всего, нам необходимо определиться с типом флага режимов утилизации прообраза субмодели. Достаточно рассмотреть три случая:
    – безусловно утилизировать,
    – запросить разрешение на удаление,
    – использовать установленное значение при отсутствии возможности запроса на утилизацию.
    Что означает последний случай, станет ясно после рассмотрения реализации метода FreePrototype() М-автомата.
    Таким образом, определим перечислимый тип:


       TModeFreePrototype =
         (
             mdpFreePrototype              // 1-ый случай.
           , mdpUseRequestFreePrototype    // 2-ой случай.
           , mdpUseDefaultMode             // 3-ий случай.
         );
       TModesFreePrototype = set of TModeFreePrototype;

    2. Тип обрабатываемого события определим следующим образом:


       TOnAfterDisposeSubmodel = function (APrototypeModel: TModel; ASubmodel: TSubmodel): boolean of object;

    Таким образом, мы подготовились к тому, чтобы скорректировать практически законченное определение класса TModel (см. под спойлером):


    Скорректированное определение класса TModel
    [XMLROOT('Model')]
        TModel = class(TNamedObject)
        private
            FAutomate: TAutomate;
            FOwner: TModel;
            FModesFreePrototype: TModesFreePrototype;
            FHeadSubmodel: TSubmodel;
            FOnAfterDisposeSubmodel: TOnAfterDisposeSubmodel;
        private
            FArousal: double;
            FActivateEffect: double;
            FInhibitEffect: double;
        private
            FThreshold: double;
            FActivateCoeff: double;
            FResidualActivateCoeff: double;
        private
            FOutputs: TOutputs;
            FInputs: TInputs;
        protected
          function GetThis(): TObject; overload; virtual;
        protected
          procedure SetObjectName(const AValue: TNameObject); overload; override;
          procedure SetObjectNameShort(const AValue: TNameShort); overload; override;
          procedure SetObjectCode(const AValue: TObjectName); overload; override;
        protected
          function GetOwner(): TModel; overload; virtual;
          procedure SetOwner(const AValue: TModel); overload; virtual;
        protected
          function GetThreshold(): double; overload; virtual;
          procedure SetThreshold(const AValue: double); overload; virtual;
          function GetArousal(): double; overload; virtual;
          procedure SetArousal(const AValue: double); overload; virtual;
          function GetActivateEffect(): double; overload; virtual;
          procedure SetActivateEffect(const AValue: double); overload; virtual;
          function GetInhibitEffect(): double; overload; virtual;
          procedure SetInhibitEffect(const AValue: double); overload; virtual;
          function GetActivateCoeff(): double; overload; virtual;
          procedure SetActivateCoeff(const AValue: double); overload; virtual;
          function GetResidualActivateCoeff(): double; overload; virtual;
          procedure SetResidualActivateCoeff(const AValue: double); overload; virtual;
        protected
          function GetOutputs(): TOutputs; overload; virtual;
          procedure SetOutputs(const AValue: TOutputs); overload; virtual;
          function GetInputs(): TInputs; overload; virtual;
          procedure SetInputs(const AValue: TInputs); overload; virtual;
        protected
          function GetAutomate(): TAutomate; overload; virtual;
          procedure SetAutomate(const AValue: TAutomate); overload; virtual;
          function GetPlatform(): TPlatform; overload; virtual;
          procedure SetPlatform(AValue: TPlatform); overload; virtual;
        protected
          procedure InternalExtract(); overload; virtual;
          procedure FreeHeadSubmodels(); overload; virtual;
        protected
          function CheckFreeModel(ASubmodel: TSubmodel): boolean;
          function FreeSubmodel(ASubmodel: TSubmodel): boolean; overload; virtual;
        protected
          function GetHeadSubmodel(): TSubmodel; overload; virtual;
          procedure SetHeadSubmodel(const AValue: TSubmodel); overload; virtual;
          function GetModesFreePrototype(): TModesFreePrototype; overload; virtual;
          procedure SetModesFreePrototype(const AValue: TModesFreePrototype); overload; virtual;
        protected
          function GetOnAfterDisposeSubmodel: TOnAfterDisposeSubmodel;
          procedure SetOnAfterDisposeSubmodel(const AValue: TOnAfterDisposeSubmodel); overload; virtual;
        protected
    // Характеристика проторения: R (4.2).
          function Winding(AR: TRelation): TRelation; overload; virtual;
    // Характеристика установления: R_1 (4.3).
          function Setting(AR: TRelation): TRelation; overload; virtual;
    // Характеристика затухания: (4.4а, 4.4б, 4.5а и 4.5б).
          function Attenuation(AR: TRelation): TRelation; overload; virtual;
        protected
    // Характеристика возбуждения модели (9).
          function Aсtivation(): double; overload; virtual;
    // Характеристика торможения: (7).
          function Inhibit(): double; overload; virtual;
    // Характеристика затухания возбужденности модели (8).
          function Attenuation(): double; overload; virtual;
    // Характеристика гипертрофии модели (10а).
          function Overgrowth(): double; overload; virtual;
    // Характеристика адаптации модели (10б).
          function Adaptation(): double; overload; virtual;
        protected
    // Текущий уровень возбуждения модели - возбужденность модели.
            property Arousal: double read GetArousal write SetArousal;
    // Активирующий эффект: (4.6а).
            property ActivateEffect: double read GetActivateEffect write SetActivateEffect;
    // Тормозной эффект: (4.6б).
            property InhibitEffect: double read GetInhibitEffect write SetInhibitEffect;
    // Порог возбуждения модели.
            property Threshold: double read GetThreshold write SetThreshold;
    // Коэффициент возбуждения модели.
            property ActivateCoeff: double read GetActivateCoeff write SetActivateCoeff;
    // Остаточный (начальный) коэффициент возбуждения модели.
            property ResidualActivateCoeff: double read GetResidualActivateCoeff write SetResidualActivateCoeff;
        protected
            property HeadSubmodel: TSubmodel read GetHeadSubmodel write SetHeadSubmodel;
        protected
          class function GetClassInputs(): TInputsClass; overload; virtual;
          class function GetClassOutputs(): TOutputsClass; overload; virtual;
        protected
          function InternalExec(): boolean; overload; virtual;
          procedure ChangeArousal(AActivateInhibiteSystem: TActivateInhibiteSystem); overload; virtual;
        public
          constructor Create(); overload; override;
          constructor Create(const AObjectId: int64; const AName: string; const ANameShort: string = ''; const ACode: string = ''); overload; virtual;
          destructor Destroy(); override;
        public
          function Exec(): boolean; overload; virtual;
          function Perform(): boolean; overload; virtual;
        public
          procedure Reset(); overload; virtual;
        public
          function Connect(ATarget: TModel; AConnection: TConnection): TModel; overload; virtual;
        public
            property Owner: TModel read GetOwner write SetOwner;
        published
            property Outputs: TOutputs read GetOutputs write SetOutputs;
            property Inputs: TInputs read GetInputs write SetInputs;
        published
            property Automate: TAutomate read GetAutomate write SetAutomate;
            property Platform: TPlatform read GetPlatform write SetPlatform;
        published
            property ModesFreePrototype: TModesFreePrototype read GetModesFreePrototype write SetModesFreePrototype;
        published
            property OnAfterDisposeSubmodel: TOnAfterDisposeSubmodel read GetOnAfterDisposeSubmodel write SetOnAfterDisposeSubmodel;
        end;
        TModelClass = class of TModel;

    Перечислим методы, важные в контексте обсуждаемой выше проблемы:
    CheckFreeModel – проверка необходимости удаления модели, связанной с освобождаемой субмоделью:


          function TModel.CheckFreeModel(ASubmodel: TSubmodel): boolean;
          begin
            Result := false;
            if (TModeFreePrototype.mfpUseRequestFreePrototype in ModesFreePrototype) then
              begin
                if (Assigned(OnAfterDisposeSubmodel)) then
                  begin
                    Result := OnAfterDisposeSubmodel(Self, ASubmodel);
                  end
                else if (TModeFreePrototype.mfpUseDefaultMode in ModesFreePrototype) then
                  begin
                    Result := (TModeFreePrototype.mfpFreePrototype in FModesFreePrototype);
                  end;
              end
            else
              begin
                Result := (TModeFreePrototype.mfpFreePrototype in ModesFreePrototype);
              end;
          end;

    FreeSubmodel – освобождение связанной субмодели:


          function TModel.FreeSubmodel(ASubmodel: TSubmodel): boolean;
          var
            APrevSubmodel: TSubmodel;
            ANextSubmodel: TSubmodel;
          begin
            Result := false;
            if (ASubmodel.Equals(FHeadSubmodel)) then
              begin
                FHeadSubmodel := ASubmodel.Prev;
                ASubmodel.Prev := nil;
                if (Assigned(FHeadSubmodel)) then
                  begin
                    FHeadSubmodel.FNextSubmodel := nil;
                  end
                else
                  begin // Освободили последнюю субмодель данной модели-прототипа!
    // Выполняем проверки на утилизацию прототипа!
                    Result := ASubmodel.Owner.CheckFreeModel(ASubmodel);
                    exit;
                  end;
              end
            else
              begin
                Result := false;
    // Удаление субмодели из двунаправленного списка!
                if (Assigned(ASubmodel.Prev)) then
                  begin
                    ASubmodel.Prev.Next := ASubmodel.Next;
                    if (Assigned(ASubmodel.Next)) then
                      begin
                        ASubmodel.Next.Prev := ASubmodel.Prev;
                      end;
                  end
                else
                  begin
                    if (Assigned(ASubmodel.Next)) then
                      begin
                        ASubmodel.Next.Prev := nil;
                      end;
    // Случай когда not Assigned(ASubmodel.Next) - соотвествовал бы последней
    // субмодели прототипа или (ASubmodel = FHeadSubmodel), а такой случай мы уже
    // отсекли вначале!
                  end;
    // Освободили субмодель!
                ASubmodel.Prev := nil;
                ASubmodel.Next := nil;
              end;
          end;

    SetHeadSubmodel – установка новой связанной субмодели:


          procedure TModel.SetHeadSubmodel(const AValue: TSubmodel);
          begin
            if (not Assigned(FHeadSubmodel)) then
              begin // До этого момента не было субмоделей!
                FHeadSubmodel := AValue;
    // На всякий случай!
                FHeadSubmodel.Prev := nil;
              end
            else
              begin
                if (FHeadSubmodel.Equals(AValue)) then exit;
                FHeadSubmodel.Next := AValue;
                AValue.Prev := FHeadSubmodel;
                FHeadSubmodel := AValue;
              end;
      // На всякий случай!
              FHeadSubmodel.Next := nil;
          end;

    И наконец, деструктор Destroy() – ничего особенного из себя не представляет, хотя следовало бы ожидать более серьезной обработки:


          destructor TModel.Destroy();
          begin
            InternalExtract();
            FResidualActivateCoeff := 0.0;
            FActivateCoeff := 0.0;
            FInhibitEffect := 0.0;
            FActivateEffect := 0.0;
            FArousal := 0.0;
            FThreshold := 0.0;
            System.SysUtils.FreeAndNil(FOutputs);
            System.SysUtils.FreeAndNil(FInputs);
            FHeadSubmodel := nil;
            FModesFreePrototype := [TModeFreePrototype.mfpFreePrototype];
            FOwner := nil;
            FOnAfterDisposeSubmodel := nil;
            inherited Destroy();
          end;

    Определение групповой модели


    Определение групповой модели


    Таким образом, мы подошли непосредственно к определению групповой модели TModelGroup:


    [XMLROOT('Group')][CINC(10,'Группа')][ClassVarInherited()]
        TModelGroup = class(TQualia)
        private
            FSubmodels: TSubmodels;
        protected
          function GetSubmodels(): TSubmodels; overload; virtual;
          procedure SetSubmodels(const AValue: TSubmodels); overload; virtual;
        protected
          class function GetClassSubmodels(): TSubmodelsClass; overload; virtual;
        protected
          function InternalExec(): boolean; overload; override;
        public
          constructor Create(); overload; override;
          destructor Destroy(); override;
        public
          procedure Prepare(); overload; virtual;
        public
          function Exec(): boolean; overload; override;
          procedure Reset(); overload; override;
        public
          function Add(AModel: TModel): TModel; overload; virtual;
          procedure Clear(); overload; virtual;
        public
          function FindById(const AModelId: int64): TSubmodel; overload; virtual;
          function FindByName(const AModelName: string): TSubmodel; overload; virtual;
          function Contains(const ASubmodel: int64): boolean; overload; virtual;
        published
    [XMLARRAY('Submodels','Submodel')]
            property Submodels: TSubmodels read GetSubmodels write SetSubmodels;
        end;
        TModelGroupClass = class of TModelGroup;

    Отметим основные черты класса TModelGroup:
    1. TModelGroup – наследник TQualia.
    2. Добавлена коллекция субмоделей, а также метод для класса, определяющий ссылку на их класс.
    3. Добавлены некоторые методы управления элементами коллекции групповой модели:


    //.......................................................
        public
          function Add(AModel: TModel): TModel; overload; virtual;
          procedure Clear(); overload; virtual;
        public
          function FindById(const AModelId: int64): TModel; overload; override;
          function FindByName(const AModelName: string): TModel; overload; override;
    //.......................................................
        end;

    4. Добавлен метод Prepare():


    //......................................................
        public
    //......................................................
          procedure Prepare(); overload; virtual;
    //......................................................
        end;

    Он может предоставить дополнительные возможности по инициализации коллекции субмоделей. В контексте данной статьи у него реализация отсутствует – он может быть объявлен как абстрактный метод, однако, он вызывается в конструкторе и поэтому в существующей реализации он виртуализирован.


    1. Гораздо более содержательно перекрытие методов InternalExec(…) и Exec(…):
      ..............................
      protected
        function InternalExec(): boolean; overload; virtual;
      ..............................
      ..............................
      ..............................
      public
      ..............................
      ..............................
      public
        function Exec(): boolean; overload; override;
      ..............................
      ..............................
      end;

      Реализация может иметь следующий вид:

        function TModelGroup.InternalExec(): boolean;
        var
          AModel: TModel;
        begin
          for AModel in Models do
            begin
              Result := AModel.Exec();
              if (not Result) then break;
            end;
        end;
        function TModelGroup.Exec(): boolean;
        begin
          Result := InternalExec();
        end;

      Как видим, основная обработка выполняется методом InternalExec(): она состоит в последовательном проходе и пересчете всех $i$-моделей в том порядке, в котором они размещены в коллекции. В случае если хотя бы одна из $i$-моделей не срабатывает или устанавливает факт достижения какой-либо «цели» данной группы модели – пересчет останавливается и метод возвращает false. Если такая стратегия пересчета не допустима, то можно перекрыть методы пересчета $i$-моделей, входящих в данную группу, либо изменить в наследнике InternalExec().
      Альтернативным способом проверки завершения цикла пересчета (или реализации InternalExec(…)) может быть включение в работу управления со стороны СУТ:
      Вариант 2.

        function TModelGroup.InternalExec(): boolean;
        var
          AModel: TModel;
        begin
          for AModel in Models do
            begin
              Result := AModel.Exec();
              Result := AIS.Exec(Result, AModel);
              if (not Result) then break;
            end;
        end;

      Вариант 3.

        function TModelGroup.InternalExec(): boolean;
        var
          AModel: TModel;
        begin
          for AModel in Models do
            begin
              Result := AModel.Exec();
              if (not Result) then break;
            end;
          Result := AIS.Exec(Result);
        end;

      Вариант 4.

        function TModelGroup.Exec(): boolean;
        begin
          Result := InternalExec();
          Result := AIS.Exec(Result);
        end;

      После определения группы моделей можно рассматривать М-сеть как специальную разновидность групп моделей, для которой может быть определен свой собственный алгоритм пересчета с помощью соответствующего перекрытие методов InternalExec(…) и Exec(…).



      Система усиления-торможения


      Следующим важным классом $i$-моделей является СУТ – система усиления-торможения.


      Определение и реализация класса СУТ


      В соответствии с ранее приведенными описаниями принципов функционирования М-автоматов и реализацией соответствующих классов примем следующие основное определение класса СУТ:


          TActivateInhibiteSystem = class(TModelGroup)
          private
              FClamping: double;
              FQueuePerformModels: TQueuePerformModels;
          protected
            function GetClamping(): double; overload; virtual;
            procedure SetClamping(const AValue: double); overload; virtual;
          protected
            function InternalExec(const AStatus: boolean): boolean; overload; virtual;
            function InternalExec(const AStatus: boolean; AModel: TModel): boolean; overload; virtual;
          protected
            class function GetClassSubmodels(): TSubmodelsClass; overload; override;
          protected
            function Check(ASubmodel: TSubmodel): boolean; overload; virtual;
            procedure BeforePerform(); overload; virtual;
            function EnqueuePerformModels(): TQueuePerformModels; overload; virtual;
            function Accept(ASubmodel: TSubmodel): boolean; overload; virtual;
            function InternalExec(): boolean; overload; override;
          public
            constructor Create(); overload; override;
            destructor Destroy(); override;
          public
            procedure OrderBy(AIndexStart: longint = 0; AIndexFinish: longint = -1); overload; virtual;
            procedure OrderBy(AComparer: JOBLIB.Core.Comparers.IComparer<TSubmodel>; AIndexStart: longint = 0; AIndexFinish: longint = -1); overload; virtual;
          public
            function Perform(): boolean; overload; override;
          public
            function Exec(const AStatus: boolean): boolean; overload; virtual;
            function Exec(const AStatus: boolean; AModel: TModel): boolean; overload; virtual;
          published
              property Clamping: double read GetClamping write SetClamping;
          end;

      Таким образом, мы имеем:
      1. Базовый класс СУТ является групповой моделью (наследуемся от TModelGroup).
      2. Основным свойством СУТ является параметр Clamping – некоторая величина, задающее ограничение на свойства дополнительно активируемых $i$-моделей. В концепции Н.М. Амосова это граница возбужденности (Arousal), устанавливающее следующее правило дополнительного изменения состояния $i$-моделей: все $i$-модели у которых возбужденность меньше (меньше или равна) значения этого ограничения СУТ получают некоторое снижение возбужденности, а в ином случае их возбужденность увеличивается. По умолчанию, в нашей базовой реализации значение параметра Clamping устанавливается равным нулю. При реализации конкретного М-автомата необходимо обязательно инициализировать параметр Clamping некоторым разумно выбранным ненулевым значением.
      3. Очередь исполнения активных моделей QueuePerformModels: TQueuePerformModels. На ней мы остановимся отдельно.
      4. Для поддержки возможных вышеупомянутых 4-х вариантов использования СУТ зарезервируем виртуальные (абстрактные) методы выполнения:


          TActivateInhibiteSystem = class(TModelGroup)
      ..............................
      ..............................
          protected
            function InternalExec(const AStatus: boolean): boolean; overload; virtual;
            function InternalExec(const AStatus: boolean; AModel: TModel): boolean; overload; virtual;
      ..............................
      ..............................
          public
      ..............................
      ..............................
          public
            function Exec(const AStatus: boolean): boolean; overload; virtual;
            function Exec(const AStatus: boolean; AModel: TModel): boolean; overload; ..............................
      ..............................
      virtual;
          published
      ..............................
      ..............................
          end;

      5. Относительно коллекции субмоделей, инкапсулируемых СУТ, следует сделать следующие замечания:
      5.1. СУТ для $i$-моделей, помещаемых в его коллекцию, не является их «владельцем». Коллекция $i$-моделей СУТ служит «транзитным» хранилищем $i$-моделей, отобранных в реализации конкретного М-автомата для их «обработки» системой. В этой коллекции могут находиться только те $i$-модели, которые важны для «выработки решения» СУТ.
      5.2. Для этого необязательно переопределять класс субмоделей, размещаемых в СУТ. Достаточно сделать так чтобы значение, возвращаемое свойством ModesFreePrototype, было всегда равным [] (что означает «модель-прообраз субмодели не утилизируется»). Для этого, в конструктор TActivateInhibiteSystem реализуем следующим образом:


            constructor TActivateInhibiteSystem.Create();
            begin
              inherited Create();
      // модель-прообраз субмодели при удалении из СУТ не утилизируется!
              FModesFreePrototype := [];
              FClamping := 0.0;
              FQueuePerformModels := TQueuePerformModels.Create();
            end;

      А метод доступа SetModesFreePrototype замещаем следующим образом:


            procedure TActivateInhibiteSystem.SetModesFreePrototype(const AValue: TModesFreePrototype);
            begin
            end;

      , что делает указанное значение «константой класса».


      Замечание 13
      Точно такой же прием можно использовать для «сфер» и др. аналогичных групповых $i$-моделей.

      6. В большинстве случаев для ускорения работы СУТ полезно использовать предварительную сортировку моделей в соответствии с некоторым критерием. Для этого необходимо использовать методы:


      procedure OrderBy(AIndexStart: longint = 0; AIndexFinish: longint = -1); overload; virtual;
      procedure OrderBy(AComparer: JOBLIB.Core.Comparers.IComparer<TModel>; AIndexStart: longint = 0; AIndexFinish: longint = -1); overload; virtual;

      6.1. Первый метод использует порядок сортировки по умолчанию (например, по убыванию значения возбужденности (Arousal) $i$-моделей). Второй метод может быть применен для сортировки $i$-моделей в соответствии с более сложными критериями и требует явного указания экземпляра класса-компаратора. В обоих случаях, требуется реализация класса-компаратора.
      6.2. Определим класс-компаратор для выполнения сортировки $i$-моделей в соответствии со стандартным критерием упорядочения. Определение:


          TDefaultAISModelComparer = class(TComparer<TSubmodel>)
          protected
            function Equal(const ALeft, ARight: TSubmodel): boolean; overload; override;
            function LessThan(const ALeft, ARight: TSubmodel): boolean; overload; override;
            function GreaterThan(const ALeft, ARight: TSubmodel): boolean; overload; override;
          public
            constructor Create(); overload; override;
            destructor Destroy(); override;
          end;
          TDefaultAISModelComparerClass = class of TDefaultAISModelComparer;

      Для применяемого мною шаблонного класса-компаратора TComparer<TSubmodel> достаточно перекрытие трех методов: Equal, LessThan и GreaterThan. Вы же можете использовать шаблоны из модуля System.Generics.Defaults.
      Реализация:


            function TDefaultAISModelComparer.Equal(const ALeft, ARight: TSubmodel): boolean;
            begin
      // Не забываем, что мы имеем дело с вещественными числами!
      // Погрешность сравнения может быть переопределена или параметризована.
              Result := (Abs(ALeft.Prototype.Arousal - ARight.Prototype.Arousal) < 1.0e-5);
            end;
            function TDefaultAISModelComparer.LessThan(const ALeft, ARight: TSubmodel): boolean;
            begin
              Result := (ALeft.Prototype.Arousal < ARight.Prototype.Arousal);
            end;
            function TDefaultAISModelComparer.GreaterThan(const ALeft, ARight: TSubmodel): boolean;
            begin
              Result := (ALeft.Prototype.Arousal > ARight.Prototype.Arousal);
            end;

      7. И если мы все-таки будем использовать какую-либо сортировку $i$-моделей, нам обязательно потребуется переопределить класс коллекции $i$-моделей СУТ! Дело в том, что компаратор по умолчанию должен инициализироваться в конструкторе коллекции:


            constructor TAISSubmodels.Create();
            begin
              inherited Create(TDefaultAISModelComparer.Create());
            end;
            constructor TAISSubmodels.Create(AOwner: TModel);
            begin
              Create();
              FOwner := AOwner;
            end;

      Правда при этом следует учесть следующие моменты:
      7.1. В TActivateInhibiteSystem необходимо заместить метод


            class function GetClassSubmodels(): TSubmodelsClass; overload; override;

      на следующую реализацию


            class function TActivateInhibiteSystem.GetClassSubmodels(): TSubmodelsClass;
            begin
              Result := TAISSubmodels; // inherited GetClassSubmodels();
            end;

      7.2. После всего этого стоит забыть о возможности быстрого поиска $i$-моделей по ее ObjectId и имени в коллекции с использованием встроенных словарей: иначе нам придется увеличивать накладные расходы на реинициализацию словарей после каждой выполненной сортировки.
      7.3. Сразу поменяем несколько других методов:
      – вставку владельца; на всякий случай


            procedure TAISModels.InnerSetOwner(const AModel: TSubmodel);
            begin
            end;

      – методы поиска модели по ObjectId или имени модели


            function TAISModels.FindById(const AModelId: int64): TSubmodel;
            var
              AIndexOf: int64;
            begin
              if (not FMapById.TryGetValue(AModelId, AIndexOf)) then exit(nil);
              Result := this[AIndexOf];
            end;
            function TAISModels.FindByName(const AModelName: string): TSubmodel;
            var
              AIndexOf: int64;
            begin
              if (not FMapByName.TryGetValue(AModelName, AIndexOf)) then exit(nil);
              Result := this[AIndexOf];
            end;

      – метод реинициализации словарей


            procedure TAISModels.Reorder(const AStartIndex: integer);
            var
              AIndexOf: integer;
            begin
              for AIndexOf := AStartIndex to Count - 1 do
                begin
                  FMapById.AddOrSetValue(this[AIndexOf].ObjectID, AIndexOf);
                  FMapByName.AddOrSetValue(this[AIndexOf].Name, AIndexOf);
                end;
              end;
            end;

      Последний метод лучше не использовать со значением AStartIndex, отличным от 0!
      7.3. Таким образом, мы должны дать классу-коллекции СУТ следующие определения:


          TAISSubmodels = class(TSubmodels)
          protected
            procedure InnerSetOwner(const AModel: TSubmodel); overload; override;
          protected
              property Owner: TModel read GetOwner;
          public
            constructor Create(); overload; override;
            constructor Create(AOwner: TModel); overload; override;
            destructor Destroy(); override;
          public
            function FindById(const AModelId: int64): TSubmodel; overload; override;
            function FindByName(const AModelName: string): TSubmodel; overload; override;
          public
            procedure Reorder(const AStartIndex: integer); overload; override;
         end;
         TAISSubmodelsClass = class of TAISSubmodels;

      Работа СУТ


      1. Рассматривая определение и реализацию методов класса СУТ мы практически ничего не сказали о работе СУТ.
      Согласно Н.М. Амосову после цикла пересчета активностей $i$-моделей


            function TModelGroup.InternalExec(): boolean;
            var
              AModel: TModel;
            begin
              for AModel in Models do
                begin
                  Result := AModel.Exec();
                  if (not Result) then break;
                end;
            end;

      СУТ должна:
      – просмотреть весь список контролируемых ею $i$-моделей;
      – выбрать из него $i$-модели обладающие наибольшей текущей активностью (arousal) и/или все модели с активностью превышающей установленный порог Clamping и по какому-либо наперед заданному алгоритму распределить между ними дополнительную активность (увеличить их возбужденность), а для оставшихся
      – снизить активность (уменьшить их возбужденность или затормозить);
      – все корректировки возбужденности (дополнительная активация или торможение) перераспределяется (распространяется) по определенному алгоритму внутри каждой из $i$-моделей (для элементов коллекции СУТ – групповых моделей);
      – СУТ выбирает из пула наиболее активных субмоделей набор (или одну наиболее активную) моделей-эффекторов и запускает их на исполнение;
      – затем переходит к новому циклу пересчета или останавливает работу всего М-автомата.
      2. Таким образом, нам необходимо внести следующие изменения в существующие классы
      2.1. В класс TModel добавим виртуализированный метод Perform(…)


            TModel = class(................)
      //.......................................................
            public
      //.......................................................
              function Perform(): boolean; overload; virtual;
      //.......................................................
            end;

      Задача данного метода выполнить текущей $i$-моделью некоторую работу. В частных реализациях М-сетей этот метод должен будет наполнен определенной функциональностью. В случае если работа было выполнена успешно, то она должна вернуть true, в иных случаях – false. По умолчанию метод всегда возвращает – true.


      Замечание 14
      Обращаю внимание читателя на следующий важный момент: метод Exec(…) отвечает за пересчет собственного состояния текущей $i$-модели, а метод Perform() ответственен за выполнение некоторой «внешней» работы – изменение состояния других $i$-моделей или внешней среды.

      2.2. Определим класс очереди исполняемых $i$-моделей TQueuePerformModels (возможно лучшим выбором для его родительского класса была бы очередь с каким-либо предварительно заданным приоритетом)


          TQueuePerformModels = class(TQueue<TModel>)
          public
      //.......................................................
            constructor Create(); overload; override;
            destructor Destroy(); override;
      //.......................................................
          end;
      //.......................................................
            constructor TQueuePerformModels.Create();
            begin
              inherited Create();
              FreeObjects := false;
            end;
            destructor TQueuePerformModels.Destroy();
            begin
              inherited Destroy();
            end;
      //.......................................................

      В классе TActivateInhibiteSystem добавим соответствующее этой очереди поле FQueuePerformModels, инициализацию в конструкторе, метод заполнения очереди EnqueuePerformModels(…), а также, при необходимости, метод доступа к этому полю:


          TActivateInhibiteSystem = class(TModelGroup)
          private
      //.......................................................
              FQueuePerformModels: TQueuePerformModels;
          protected
      //.......................................................
          public
            constructor Create(); overload; override;
            destructor Destroy(); override;
          public
      //.......................................................
          public
            function Perform(): boolean; overload; override;
      //.......................................................
          end;
      //.......................................................
          TActivateInhibiteSystem = class(TModelGroup)
          private
      //.......................................................
              FQueuePerformModels: TQueuePerformModels;
          protected
      //.......................................................
          public
            constructor Create(); overload; override;
            destructor Destroy(); override;
          public
            function Perform(): boolean; overload; override;
      //.......................................................
          end;
      //.......................................................
            constructor TActivateInhibiteSystem.Create();
            begin
              inherited Create();
              Models.FreeObjects := false;
              FClamping := 0.0;
              FQueuePerformModels := TQueuePerformModels.Create();
            end;
            destructor TActivateInhibiteSystem.Destroy();
            begin
              System.SysUtils.FreeAndNil(FQueuePerformModels);
              FClamping := 0.0;
              Models.FreeObjects := false;
              inherited Destroy();
            end;
      //.......................................................
            function TActivateInhibiteSystem.EnqueuePerformModels(): TQueuePerformModels;
            var
              ASubmodel: TSubmodel;
            begin
              Result := FQueuePerformModels;
      // Отсортировали коллекцию моделей.
              OrderBy();
      // Преинициализировали словари.
              Submodels.Reorder();
      // Заполняем очередь
              for ASubmodel in Submodels do
                begin
      // Поскольку мы отсортировали коллекцию, то после первого несоотвествия
      // можно дальше не продолжать проверять оставшиеся элементы.
                  if (not Check(ASubmodel)) then break;
                  Result.Enqueue(ASubmodel);
                end;
            end;
      //.......................................................

      Также перекрываем метод Perform() с учетом особенностей класса СУТ:


      //.......................................................
            procedure TActivateInhibiteSystem.BeforePerform();
            var
              ASubmodel: TSubmodel;
            begin
              for ASubmodel in Submodels do
                begin
                  ASubmodel.Prototype.ChangeArousal(Self);
                end;
            end;
      //.......................................................
            function TActivateInhibiteSystem.Perform(): boolean;
            var
              AModel: TModel;
            begin
              Result := true;
              BeforePerform();
      // Заполняем очередь
              EnqueuePerformModels();
      // Непосредственно исполняем модели из очереди.
              while (not FQueuePerformModels.IsEmpty()) do
                begin
                  if (Accept(FQueuePerformModels.Dequeue())) then continue;
                  Result := false;
                  break;
                end;
      // До конца очистим очередь, если выполнение было прервано.
              while (not FQueuePerformModels.IsEmpty()) do
                FQueuePerformModels.Dequeue();
            end;
      //.......................................................

      2.3. Как видим у нас используются еще три метода Check(…), BeforePerform(…) и Accept(…). Задача метода Check при заполнении очереди – проверить соответствует ли модель критерию наиболее активированной модели или нет. В простейшем случае для него возможна следующая реализация:


      //.......................................................
            function TActivateInhibiteSystem.Check(ASubmodel: TSubmodel): boolean;
            begin
      // В простейшем случае!
              Result := (ASubmodel.Prototype.Arousal >= Clamping);
            end;
      //.......................................................

      Для метода Accept задача стоит несколько более сложная и его полная реализация возможна только для конкретных моделей, а именно в этом метод непосредственно происходит вызов


      //.......................................................
      Result := AModel.Perform();
      //.......................................................

      , а затем по результатам исполнения $i$-модели метод решает, стоит ли продолжать дальнейшее выполнение отобранных моделей или нет. Если метод возвращает true, то выполнение продолжается, в ином случае – false и выполнение отобранных моделей завершается. Приводить ее реализацию не имеет смысла.
      В любом случае метод TActivateInhibiteSystem.Perform завершается удалением всех неисполненных $i$-моделей из очереди.
      Метод BeforePerform(…) необходим для выполнения корректировки уровня возбужденности $i$-моделей


            procedure TActivateInhibiteSystem.BeforePerform();
            var
              ASubmodel: TSubmodel;
            begin
              for ASubmodel in Submodels do
                begin
                  ASubmodel.Prototype.ChangeArousal(Self);
                end;
            end;

      Как видим, не обошлось без добавления в класс TModel виртуализированного метода ChangeArousal(…)


            procedure TModel.ChangeArousal(AActivateInhibiteSystem: TActivateInhibiteSystem);
            begin
            end;

      Этот метод изменяет по какому-либо заданному алгоритму возбужденность $i$-модели. Уже наверно, излишне говорить о том, что он должен быть перекрыт в соответствии с конкретной задачей.


      М-сеть и подсети


      Перейдем рассмотрению структуры и реализации методов М-сети.
      После всего вышенаписанного определение М-сети стало тривиальным:


          TModelsNet = class(TModelGroup)
          private
              FPlatform: TPlatform;
          private
      // Удовлетворенность
              FСontentment: double;
              FAIS: TActivateInhibiteSystem;
          protected
            function GetСontentment(): double; overload; virtual;
            procedure SetСontentment(const AValue: double); overload; virtual;
            function GetPlatform(): TPlatform; overload; override;
            procedure SetPlatform(AValue: TPlatform); overload; override;
          protected
              property Contentment: double read GetСontentment write SetСontentment;
          protected
              property AIS: TActivateInhibiteSystem read FAIS;
              property Platform: TPlatform read GetPlatform write SetPlatform;
          protected
            class function GetClassSubmodels(): TSubmodelsClass; overload; virtual;
            class function GetClassAIS(): TActivationInhibitionSystemClass; overload; virtual;
          public
            constructor Create(); overload; override;
            destructor Destroy(); override;
          public
            function Exec(): boolean; overload; override;
          end;
          TModelsNetClass = class of TModelsNet;

      Не сильно перегружая кодом статью привести полную реализацию ее методов:


            constructor TModelsNet.Create();
            begin
              inherited Create();
              FСontentment := 0.0;
              FAIS := GetClassAIS().Create(Self);
              FAIS.Owner := Self;
              FPlatform := nil;
              AIS.Prepare();
            end;
            destructor TModelsNet.Destroy();
            begin
              FPlatform := nil;
              System.SysUtils.FreeAndNil(FAIS);
              FСontentment := 0.0;
              inherited Destroy();
            end;
            class function TModelsNet.GetClassSubmodels(): TSubmodelsClass;
            begin
              Result := TSubmodels;
            end;
            class function TModelsNet.GetClassAIS(): TActivationInhibitionSystemClass;
            begin
              Result := TActivateInhibiteSystem;
            end;
            function TModelsNet.Exec(): boolean;
            begin
              Result := InternalExec();
      // Здесь может быть добавлен код, выбирающий дальнейшую обработку в зависимости
      // от результатов работы метода InternalExec().
              Result := AIS.Perform();
            end;
            function TModelsNet.GetСontentment(): double;
            begin
              Result := FСontentment;
            end;
            procedure TModelsNet.SetСontentment(const AValue: double);
            begin
      //        FVitality := AValue;
            end;
            function TModelsNet.GetPlatform(): TPlatform;
            begin
              Result := FPlatform;
            end;
            procedure TModelsNet.SetPlatform(AValue: TPlatform);
            var
              ASubmodel: TSubmodel;
            begin
              FPlatform := AValue;
              for ASubmodel in Submodels do
                begin
                  ASubmodel.Prototype.Platform := Self.Platform;
                end;
            end;

      Дополнительного комментария требует только следующий вызов метода Prepare(…):


      //.......................................................
          AIS.Prepare();
      //.......................................................

      Этот вызов потребует в частном случае М-автомата определение специфических классов коллекции моделей и СУТ. В классе СУТ необходимо будет перекрыт метод Prepare(…), который может выглядеть следующим образом


      //.......................................................
          TSpeedyAIS = class(TActivateInhibiteSystem)
      //.......................................................
          public
            procedure Prepare(); overload; overrride; 
      //.......................................................
          end;
      //.......................................................
            procedure TSpeedyAIS.Prepare(); 
            begin
      //.......................................................
              Submodels.Add(TSubmodel.Create(Owner.Automate.FindByName('Отойти')));
              Submodels.Add(TSubmodel.Create(Owner.Automate.FindByName('Подойти')));
      //.......................................................
            end;
      //.......................................................

      Код в приведенном примере реализации загружает в коллекцию моделей, контролируемых СУТ, две модели-эффектора, отвечающих за перемещение автомата вперед и назад.
      Место вызова метода Prepare(…) может быть другим, главное – вызов метода должен быть сделан после создания необходимых моделей в М-автомате или загрузки их из конфигурационного файла, их инициализации, но не позже запуска М-автомата на исполнение. Поэтому вызов этого метода не может выполняться внутри конструкторов и деструкторов!
      По поводу М-сетей стоит сделать замечания концептуального характера:
      1. М-сеть есть групповая сущностная модель, поэтому удаление субмоделей из М-сети должна сопровождаться утилизацией соответствующей модели.
      2. М-сеть может включать в себя другие М-сети (подсети) как групповые субмодели.
      3. Существенное отличие М-сети от других разновидностей групповых моделей состоит в наличии СУТ.
      Большего о М-сети сказать нечего, все сказано до этого…


      М-автомат


      Определение М-автомата


      М-автомат в моей реализации не решает алгоритмических задач – он скорее конструкция, обеспечивающая некоторый технологический каркас для интеграции различных сервисных функций вокруг основной М-сети и взаимодействие с другими компонентами основного приложения.
      В круг решаемых М-автоматом задач входят:
      1. Основное хранение всех используемых моделей и связей между ними.
      2. Загрузка и выгрузка $i$-моделей из (в) конфигурационного (конфигурационный) файла (файл) конкретного М-автомата.
      3. Поддержка главного цикла цикла пересчета и исполнения $i$-моделей
      4. Взаимодействие со средой.
      В соответствии с этим в состав основных компонент автомата должны войти:
      1. коллекция всех $i$-моделей М-автомата;
      2. коллекция соединений всех $i$-моделей (связи);
      3. очередь исполнения $i$-моделей верхнего уровня;
      4. коллекция М-сетей (это может быть и одна М-сеть верхнего уровня, содержащая в качестве субмоделей М-сети более низкого уровня – в этой реализации я остановился на этом варианте);
      5. ссылку на модель среды М-автомата;
      6. генератор числовых идентификаторов отдельных компонент М-автомата;
      В соответствии с определенным перечнем решаемых задач и основных составляющих дадим определение класса М-автомата:


      [XMLROOT('TAutomate')][XMLSerializerMode([soOrderSerialize])]
          TAutomate = class(TDesignate)
          private
      // Генератор идентификаторов i-моделей.
              FSequence: TSequence;
          private
              FReady: boolean;
          private
      // Общее самочувствие "автомата".
              FСontentment: double;
          private
      // Общее хранилище i-моделей.
              FModels: TModels;
              FMNet: TModelsNet;
      // Исходящие связи между i-моделями.
              FConnections: TConnections;
          private
      // Конвейер исполняемых автоматом i-моделей.
              FEngine: TEngineConveyor;
          private
      // Платформа - среда.
              FPlatform: TPlatform;
          private
              FOnBeforeExecute: TOnAutomateExecuteBefore;
              FOnAutomateBeforeAction: TOnActionExecuteBefore;
              FOnAutomateAfterAction: TOnActionExecuteAfter;
              FOnAfterExecute: TOnAutomateExecuteAfter;
          private
            function GetObjectName(): TNameObject; overload; override;
            procedure SetObjectName(const AValue: TNameObject); overload; override;
            function GetObjectNameShort(): TNameShort; overload; override;
            procedure SetObjectNameShort(const AValue: TNameShort); overload; override;
            function GetObjectCode(): TObjectName; overload; override;
            procedure SetObjectCode(const AValue: TObjectName); overload; override;
          protected
            function GetSequence(): TSequence; overload; virtual;
            procedure SetSequence(const AValue: TSequence); overload; virtual;
          protected
            function GetReady(): boolean; overload; virtual;
            procedure SetReady(const AValue: boolean); overload; virtual;
          protected
            function GetСontentment(): double; overload; virtual;
            procedure SetСontentment(const AValue: double); overload; virtual;
            function GetModels(): TModels; overload; virtual;
            procedure SetModels(const AValue: TModels); overload; virtual;
            function GetConnections: TConnections; overload; virtual;
            procedure SetConnections(const AValue: TConnections); overload; virtual;
            function GetEngine(): TEngineConveyor; overload; virtual;
            procedure SetEngine(const AValue: TEngineConveyor); overload; virtual;
            function GetPlatform(): TPlatform; overload; virtual;
            procedure SetPlatform(const AValue: TPlatform); overload; virtual;
          protected
            function GetOnAutomateBeforeExecute(): TOnAutomateExecuteBefore; overload; virtual;
            procedure SetOnAutomateBeforeExecute(const AValue: TOnAutomateExecuteBefore); overload; virtual;
            function GetOnBeforeAction(): TOnActionExecuteBefore; overload; virtual;
            procedure SetOnBeforeAction(const AValue: TOnActionExecuteBefore); overload; virtual;
            function GetOnAfterAction(): TOnActionExecuteAfter; overload; virtual;
            procedure SetOnAfterAction(const AValue: TOnActionExecuteAfter); overload; virtual;
            function GetOnAutomateAfterExecute(): TOnAutomateExecuteAfter; overload; virtual;
            procedure SetOnAutomateAfterExecute(const AValue: TOnAutomateExecuteAfter); overload; virtual;
          protected
            class function GetClassModels(): TModelsClass; overload; virtual;
            class function GetClassConnections(): TConnectionsClass; overload; virtual;
            class function GetClassEngineConveyor(): TEngineConveyorClass; overload; virtual;
            class function GetClassModelNet(): TModelsNetClass; overload; virtual;
          protected
            procedure Binding(); overload; virtual;
            procedure BindSubmodel(ASubmodel: TSubmodel); overload; virtual;
          protected
            function ExecBeforeExecute(): boolean; overload; virtual;
            function ExecBeforeAction(): boolean; overload; virtual;
            procedure ExecAfterAction(var AStatus: boolean); overload; virtual;
            procedure ExecAfterExecute(var AStatus: boolean); overload; virtual;
          public
            constructor Create(); overload; override;
            destructor Destroy(); override;
          public
            procedure Clear(); overload; virtual;
          public
            function FindById(const AModelId: int64): TModel; overload; virtual;
            function FindByName(const AModelName: string): TModel; overload; virtual;
          public
            procedure LoadFromFile(const AFileName: string); overload; virtual;
            procedure SaveToFile(const AFileName: string); overload; virtual;
          public
            function Next(): boolean; overload; virtual;
            function Exec(): boolean; overload; virtual;
            procedure Reset(); overload; virtual;
          public
            function Connect(ASourceId, ATargetId: int64): boolean; overload; virtual;
            function Disconnect(ASourceId, ATargetId: int64): boolean; overload; virtual;
            function ConnectBy(ASourceId, ATargetId: int64): boolean; overload; virtual;
          public
              property Ready: boolean read GetReady write SetReady;
          public
              property Platform: TPlatform read GetPlatform write SetPlatform;
          published
      [XMLEmbedding('Sequence')]
              property Sequence: TSequence read GetSequence write SetSequence;
          published
      [XMLARRAY('Models','Model')]
              property Models: TModels read GetModels write SetModels;
      [XMLARRAY('Connections','Connection')]
              property Connections: TConnections read GetConnections write SetConnections;
      [XMLARRAY('Conveyor','Model')]
              property Engine: TEngineConveyor read GetEngine write SetEngine;
          published
      [XMLAttribute('Vitality')]
              property Contentment: double read GetСontentment write SetСontentment;
          published
              property OnBeforeExecute: TOnAutomateExecuteBefore read GetOnAutomateBeforeExecute write SetOnAutomateBeforeExecute;
              property OnBeforeAction: TOnActionExecuteBefore read GetOnBeforeAction write SetOnBeforeAction;
              property OnAfterAction: TOnActionExecuteAfter read GetOnAfterAction write SetOnAfterAction;
              property OnAfterExecute: TOnAutomateExecuteAfter read GetOnAutomateAfterExecute write SetOnAutomateAfterExecute;
          end;
          TAutomateClass = class of TAutomate;

      Как видим из определения класса М-автомата:
      1. он является наследником некоторого класса TDesignate, содержащего описательную информацию;
      2. содержит поля, соответствующие всем перечисленным выше компонентам, кроме того
      3. добавлено содержательное свойство Сontentment – условно оно соответствует некоторому общему «самочувствию» М-автомата, интегральной характеристике «удовлетворенности» М-автомата своим текущим состоянием;
      4. добавлено техническое свойство Ready, определяющее готовность М-автомата к использованию после завершения его загрузки из конфигурационного файла (или БД).
      5. добавлены события-обработчики для взаимодействия с внешним программным окружением:
      OnBeforeExecute (OnAfterExecute) – внешняя обработка перед (после) цикла пересчета/исполнения для всего М-автомата;
      OnBeforeAction (OnAfterAction) – внешняя обработка перед (после) цикла пересчета/исполнения для отдельной $i$-модели.
      Они могут быть не заданы.
      6. Основными методами пересчета/исполнения для М-автомата являются методы:
      – Next(): boolean – выполнение единичного шага пересчета/исполнения М-автомата:


            function TAutomate.Next(): boolean;
            begin
              if (not ExecBeforeAction()) then exit(false);
              Result := Exec();
              ExecAfterAction(Result);
            end;

      – Exec(): boolean – выполнение полного цикла пересчета/исполнения М-автомата до момента срабатывания заданного критерия его останова, который «индивидуален» для каждой конкретной реализации М-автомата:


            function TAutomate.Exec(): boolean;
            begin
              if (not ExecBeforeExecute()) then exit(false);
              repeat
                Result := Next();
              until (not Result);
              ExecAfterExecute(Result);
            end;

      7. К служебным относятся следующие методы:
      Clear() – очистка всех состояний и коллекций М-автомата;
      FindById(const AModelId: int64) – поиск модели по числовому идентификатору;
      FindByName(const AModelName: string) – поиск модели по имени;
      LoadFromFile(const AFileName: string) – загрузка М-автомата из конфигурационного файла:


            procedure TAutomate.LoadFromFile(const AFileName: string);
            var
              AlterPath: string;
              ANameFile: JOBLIB.FileName.TFileName;
            begin
              ANameFile := AFileName;
              if (TFile.Exists(ANameFile)) then
                begin
                  Ready := false;
                  TSerializer.LoadFromFile(Self, AFileName);
                  Ready := true;
                  Binding();
                  Modified := false;
                  exit;
                end;
              Modified := true;
              TSerializer.SaveToFile(Self, AFileName);
              Modified := false;
            end;

      SaveToFile(const AFileName: string) – сохранение текущей конфигурации М-автомата в файле;
      Reset() – сброс всех параметров и характеристик М-автомата в начальное состояние;
      Binding() – метод связывания отдельных компонент М-автомата после загрузки его конфигурации из файла:


            procedure TAutomate.Binding();
            var
              AModel: TModel;
              ASubmodel: TSubmodel;
              AGroupModels: TModelGroup;
              AConnection: TConnection;
            begin
              FСontentment := 0.0;
      // Связываем групповые модели с субмоделями!
              for AModel in Models do
                begin
                  AModel.Automate := Self;
                  if (not (AModel is TModelGroup)) then continue;
                  AGroupModels := (AModel as TModelGroup);
                  for ASubmodel in AGroupModels.Submodels do
                    begin
                      BindSubmodel(ASubmodel);
                    end;
                end;
      // Устанавливаем связи между моделями!
              for AConnection in Connections do
                begin
                  with AConnection do
                    begin
                      FindById(SourceId).Connect(FindById(TargetId), AConnection);
                    end;
                end;
            end;

      BindSubmodel(ASubmodel: TSubmodel) – метод связывания созданной (загруженной) субмодели с моделью-прототипом (код этого метода мы приводили выше).
      8. Я временно отказался от реализации методов-прототипов, связанных с характеристиками самоорганизации и самообучения М-автомата, поскольку эти методы должны быть достаточно специфичными и относительно которых нужно делать множество дополнительных предположений – это не внесет ясности в излагаемый материал.


      Очередь исполнения


      Несколько слов об очереди исполнения. Назначение этого компонента М-автомата, состоит в
      – хранении $i$-моделей верхнего уровня и
      – их пересчете/исполнении.
      Для этого нам понадобиться следующее определение класса TEngineConveyor:


          TEngineConveyor = class(TModelGroup)
          private
              FCurrModel: int64;
          protected
            procedure SetModesFreePrototype(const AValue: TModesFreePrototype); overload; override;
          public
            constructor Create(); overload; override;
            constructor Create(AOwner: TAutomate); overload; virtual;
            destructor Destroy(); override;
          public
            procedure Reset(); overload; virtual;
            function Next(): boolean; overload; virtual;
            function Perform(): boolean; overload; override;
            function Exec(): boolean; overload; override;
          end;
          TEngineConveyorClass = class of TEngineConveyor;

      В целом, реализация прикладным методов этого класса будет следующей:


            procedure TEngineConveyor.Reset();
            begin
              FCurrModel := 0;
            end;
            function TEngineConveyor.Next(): boolean;
            begin
              if (not Automate.ExecBeforeAction()) then exit(false);
              Result := false;
              if (FCurrModel < Submodels.Count) then
                begin
                  Result := Submodels[FCurrModel].Prototype.Exec();
                  System.Inc(FCurrModel);
                end;
              Automate.ExecAfterAction(Result);
            end;
            function TEngineConveyor.Exec(): boolean;
            begin
              Reset();
              if (not Automate.ExecBeforeExecute()) then exit(false);
              repeat
                Result := Next();
              until (not Result);
              Automate.ExecAfterExecute(Result);
            end;
            function TEngineConveyor.Perform(): boolean;
            begin
            end;

      В этой связи, приведенная выше реализация методов Exec и Next М-автомата потребует от нас другого кода


            function TAutomate.Next(): boolean;
            begin
              Result := Engine.Next();
            end;
            function TAutomate.Exec(): boolean;
            begin
              Result := Engine.Exec();
            end;

      Осталось переопределить единственный метод доступа


            procedure TEngineConveyor.SetModesFreePrototype(const AValue: TModesFreePrototype);
            begin
            end;

      Это требуется в силу необходимости защитить от утилизации модели-прототипы при удалении субмоделей из очереди исполнения (в конструкторе TEngineConveyor мы устанавливаем для этого значение FModesFreePrototype := []).


      Среда (платформа)


      Под средой (платформой) понимается некоторое окружение М-автомата
      – от которой он получает входные значения,
      – в которой он выполняет некоторые действия, изменяющее состояние этого окружения в соответствии со своим назначением.
      Большего, без привязки к конкретной задаче, к своему сожалению, я пока сказать об устройстве внешней среды не могу. И поэтому определение соответствующего класса будет достаточно общим:


          TPlatform = class(TModel)
          private
              FOnBeforeExecute: TOnAutomateExecuteBefore;
              FOnAutomateBeforeAction: TOnActionExecuteBefore;
              FOnAutomateAfterAction: TOnActionExecuteAfter;
              FOnAfterExecute: TOnAutomateExecuteAfter;
          protected
            function GetOnAutomateBeforeExecute(): TOnAutomateExecuteBefore; overload; virtual;
            procedure SetOnAutomateBeforeExecute(const AValue: TOnAutomateExecuteBefore); overload; virtual;
            function GetOnBeforeAction(): TOnActionExecuteBefore; overload; virtual;
            procedure SetOnBeforeAction(const AValue: TOnActionExecuteBefore); overload; virtual;
            function GetOnAfterAction(): TOnActionExecuteAfter; overload; virtual;
            procedure SetOnAfterAction(const AValue: TOnActionExecuteAfter); overload; virtual;
            function GetOnAutomateAfterExecute(): TOnAutomateExecuteAfter; overload; virtual;
            procedure SetOnAutomateAfterExecute(const AValue: TOnAutomateExecuteAfter); overload; virtual;
          protected
            function HandleBeforeExecute(AAutomate: TAutomate): boolean; overload; virtual;
            function HandleAutomateBeforeAction(AAutomate: TAutomate): boolean; overload; virtual;
            function HandleAutomateAfterAction(AAutomate: TAutomate; const AStatus: boolean): boolean; overload; virtual;
            function HandleAfterExecute(AAutomate: TAutomate; const AStatus: boolean): boolean; overload; virtual;
          public
            constructor Create(); overload; override;
            destructor Destroy(); override;
          public
            procedure Prepare(); overload; virtual;
          public
            function Exec(): boolean; overload; override;
          published
              property OnBeforeExecute: TOnAutomateExecuteBefore read GetOnAutomateBeforeExecute write SetOnAutomateBeforeExecute;
              property OnBeforeAction: TOnActionExecuteBefore read GetOnBeforeAction write SetOnBeforeAction;
              property OnAfterAction: TOnActionExecuteAfter read GetOnAfterAction write SetOnAfterAction;
              property OnAfterExecute: TOnAutomateExecuteAfter read GetOnAutomateAfterExecute write SetOnAutomateAfterExecute;
          end;
          TPlatformClass = class of TPlatform;

      1. Как видим в нашем случае мы унаследовали среду (платформу) от М-модели;
      2. снабдили ее возможными связями с внешним программным окружением;
      3. единственным осмысленным методом в данной ситуации может быть только метод Exec:


            function TPlatform.Exec(): boolean;
            begin
              Prepare();
              Result := Automate.Exec();
            end;

      4. Заметим также, что среда может быть реализована как хранилище некоторого набора разнообразных М-автоматов («населена») и в этом случае она должна иметь в своем составе их коллекцию. Но это уже совсем другая история…


      Конфигуратор


      В этой части я не буду полностью описывать устройство приложения, предназначенного для начальной настройки конфигурации М-автомата, не буду приводить снимков форм реализованного приложения, а, тем более описания работы с ним – объем статьи уже и так не маленький. Ограничусь лишь только перечислением основных требований, на которые явно или неявно опирался при его реализации:
      1. Приложение «Конфигуратор», предназначено для первоначальной настройки (конфигурирования) М-автомата.
      2. Конфигуратор должен предоставлять пользователю следующие основные возможности:
      2.1. Создавать каркас нового М-автомата.
      2.2. Создавать и изменять (удалять) в его М-сети набор $i$-моделей различного типа и настраивать имеющиеся у них параметры.
      2.3. Настраивать (создавать, изменять или удалять) необходимые связи между $i$-моделями и инициализировать имеющиеся у них параметры.
      2.4. Настраивать параметры и набор моделей основной СУТ и очереди исполнения М-автомата.
      2.5.1. Сохранять настроенный М-автомат в конфигурационном файле (в БД).
      2.5.2. Загружать настроенный М-автомат из конфигурационного файла (из БД).
      3. Для апробации (проверки) работы М-автомата приложение должно иметь возможность подключения М-автомата к специально созданной тестовой среде (Платформа).
      4. В составе конфигуратора должны быть реализованы компоненты диагностики (мониторинга) пробной работы М-автомата.
      5. Изменение состава (типов) $i$-моделей, СУТ и очереди исполнения должно производиться посредством подключения внешних модулей (плагинов).
      6.1. Тестовая среда (Платформа) может быть реализована либо как подключаемый модуль (DLL и/или BPL), либо в виде автономного приложения.
      6.2. Взаимодействие между $i$-моделями рецепторов и эффекторов может быть реализовано как непосредственное взаимодействие с объектами подключаемого модуля тестовой среды, так и с использованием механизма, основанного на обмене сообщениями (например, на основе ZeroMQ), в зависимости от выбранного варианта реализации тестовой среды.
      7. Возможное будущее расширение функциональности приложения «Конфигуратор» будет связано с необходимостью переноса отдельных компонент М-автомата на другие другие аппаратные платформы.


      Заключение


      Итак, подведем некоторый итог рассмотрения подхода Н.М. Амосова к моделированию психических процессов.
      1. В данной статье раскрыты не все темы, которые рассматриваются в работе [3] команды Н.М. Амосова. За ее рамками остались:
      – эвристические методы в проблеме «искусственный разум»,
      – примеры построения и экспериментальное исследование поведения М-автоматов, в том числе исследование адаптивного поведения (M-автомат МОД).
      2. Основным исходным материалом, как я уже указывал в предисловии, для подготовки статьи стала работа [3], при этом часть рисунков была заимствована из [2, 4]. Однако, для полноценного ознакомления с концепцией Н.М. Амосова, желательно прочитать, уже после изучения работы [3] и в указанном порядке, его работы: [4], [1] и [2].
      3. Желающим добиться углубленного понимания, «прочувствования» подхода настоятельно советую познакомиться с разделами работы, содержащие примеры построения и экспериментального исследования поведения М-автоматов.
      4. При изложении материала [3] автор статьи избегал использования психологических и «нейронных» интерпретаций, присутствующих в оригинальных работах Н.М. Амосова, с целью как можно более лапидарно изложить аппарат М-сетей и автоматов, несмотря на то, что «научно-художественный» стиль мог бы существенно помочь восприятию материала. Не обессудьте!
      5. Еще раз хотелось бы обратить внимание читателей на то, что подход [3] вовсе не про семантические сети или нейросети. Он очень близок к теории функциональных систем (ФС) П.К. Анохина: по своей сути, ансамбли $i$-моделей и есть полный аналог ФС. В определенном смысле концепция Н.М. Амосова более проработанный и прямой путь по программному моделированию ФС, чем иные заявленные, например, в работах Бонгарда М.М. с соавторами [7] или Е.Е. Витяева [8].
      6. Как реализовать $i$-модели и СУТ – процедурно (функционально, алгоритмически) или через нейросеть – вопрос непринципиальный, а в большей степени вопрос наличия возможностей (наличие определенного опыта работы с тем или иным инструментарием, наличие подходящих временных, вычислительных и иных ресурсов) у коллектива разработчиков.
      7. Рассмотрение реализации М-автомата и его основных компонент мною было выполнено во второй части статьи. Эта часть получилась достаточно громоздкой из-за приведенного кода. Но, считаю, что это необходимо, хотя бы из-за того что «голые теоретические положения» как правило не воспринимаются читателем и не вызывают у него живого отклика и понимания. И с другой стороны, правильно было бы в публикации на известном ИТ-ресурсе привести какие-либо коды. Тем более что они у меня есть. Поэтому я добавил к основной части статьи эту часть.
      Выражаю благодарность всем тем читателям, которые дочитали эту статью до самого последнего абзаца, и в особенности тем, кто решит поучаствовать в обсуждении вопросов, поднятых в ней!
      Особую благодарность хотел бы выразить Александру Ершову за поддержку и внимательное отношение к рассматриваемому в статье материалу, а также за внимательное прочтение и критические замечания по основному содержанию статьи.


      Автор статьи и Александр Ершов (Ustas) приглашают всех желающих присоединиться к участию в развитии и решении практических задач в рамках open-source проекта с использованием подхода, предложенного и развивавшегося Николаем Михайловичем Амосовым.

      Литература


      1. Амосов Н.М. Моделирование мышления и психики. – Киев, 1965.
      2. Амосов Н.М. Искусственный разум. – Киев, 1969.
      3. Амосов Н.М., Касаткин А.М., Касаткина Л.М., Талаев С.А. Автоматы и разумное поведение: Опыт моделирования. – Киев, 1973.
      4. Амосов Н.М. Алгоритмы разума – Киев, 1979.
      5. Симонов П.В. Эмоциональный мозг. – М., 1981.
      6. Симонов П.В. Мотивированный мозг. – М., 1987.
      7. Бонгард М.М., Лосев И.С., Смирнов М.С. Проект модели организации поведения – «ЖИВОТНОЕ» — см. например, в сборнике «Моделирование обучения и поведения». М., 1975; стр. 152–171 или «От моделей поведения к искусственному интеллекту». М., 2010; стр. 61-81.
      8. Демин А.В., Витяев Е.Е. Система управления аниматом основанная на теории функциональных систем П.К. Анохина
      9. Рабинович М.И., Мюезинолу М.К. Нелинейная динамика мозга: эмоции и интеллектуальная деятельность — УФН, т. 180 (2010), с. 371–387

Теги:
Хабы:
Всего голосов 13: ↑10 и ↓3+7
Комментарии191

Публикации