Дмитрий Шуклин  
Научные исследования и Программная архитектура  
 
[English] ГлавнаяПродуктыПубликацииРесурсыНовости
 
Главная
Партнеры
Продукты
Статьи
Форум
Фотоальбом
 

Изучаем Cerebrum 1.0

 

Занятие «Objects-01»

 

Желательно знакомство с занятием «Streams-01»

 

В этом упражнении мы сделаем реализацию синхронизированного линейного дерева для хранения словоформ. Это дерево представляет собой автоматную грамматику (http://www.shuklin.com/ai/ht/ru/ai00002f.aspx, http://www.shuklin.com/ai/ht/ru/ai00008f.aspx) Цель данного упражнения - рассмотреть работу с объектами в СООБЗ Cerebrum а не реализовать грамматику. Поэтому пример будет несколько упрощен. Для синтеза сети будет применена рекурсия что наглядно продемонстрирует возможности СООБЗ однако в реальном сценарии может быть неоптимальным решением.

 

СУООБЗ Cerebrum это многоуровневая система состоящая из различных модулей. Чтобы применять ООБЗ в своей разработке - необходимо определиться, какой именно уровень будет наиболее удобен в разрабатываемом приложении.

Cerebrum.Runtime.dll - это ядро системы. В нем расположены самые низкоуровневые операции. Его можно рассматривать как усовершенствованный менеджер памяти и файловой системы. Например, в нем находится сборщик мусора, управляющий persistent объектами. На основе этого компонента можно реализовать различные архитектуры баз данных и знаний, однако, так как функции, реализованные в нем, достаточно низкого уровня и не определяют архитектуру приложения, использовать этот модуль независимо от остальных будет неудобно - потребуется продублировать код уже доступный в других модулях. Поэтому его удобно использовать в комплекте с несколькими вышестоящими модулями.

Cerebrum.Integrator.dll - это реализация БД конфигурации. Ядро требует метаинформацию о классах .NET управляемых системой. В нем находятся реализации дескрипторов такой метаинформации, например AttributeDescriptor, TableDescriptor, TypeDescriptor и.т.п. Этот модуль не накладывает ограничений на остальную архитектуру программы. Если необходимо использовать Cerebrum в web сайте через ASP.NET то следует ограничится использованием этого модуля, так как архитектура Windows.Forms приложения отличается от Web.

Cerebrum.Windows.Forms - это ядро приложения для Windows.Forms. Если предполагается использовать Cerebrum в своем приложении, то потребуется включить в проект эту и 2 ранее перечисленные DLL .

Cerebrum.DesktopClient.exe - это законченное приложение для просмотра и изменения БД конфигурации. Так же оно разработано с учетом поддержки add-ins. Существует возможность разработать адд-инс и включить его как составную часть Cerebrum.DesktopClient.exe. Выбор между включением Cerebrum в независимое приложение и включением приложения в Cerebrum зависит от целей разработки. На данный момент опубликовано 3 таких add-ins это Elex, Data Explorer или он же Data Manager и пример Streams

Надо учесть что Cerebrum.Runtime.dll и Cerebrum.Integrator.dll не предоставляют нейронную сеть. Это система управления объектно-ориентированной базой. Причем база не реляционная а сетевая. Таблицы реализуются как особый вид сети. Если требуется разработать нейронную сеть, то возможно воспользоваться этими модулями чтобы создать собственную модель нейронной сети. Следует определить классы для своих нейронов и разместить экземпляры этих классов в ООБД. Так как классы имеют методы – возможно реализовать поведение нейрона внутри метода своего класса. Таким образом разработчик не ограничен использованием моей модели нейрона – разработчик может сам реализовать любую модель, а Cerebrum возьмет на себя функции управления памятью и сохранения нейронов. Cerebrum создавался для поддержки задач которые решаются синтезом структуры НС. Синтез более похож на обычное программирование, однако в результатом проектирования и синтеза сети является нейронная сеть, а не программа. Мало того, возможно использования компиляторов нейронной сети - например Elex и есть такой компилятор, который синтезирует структуру нейронной сети на основе описания базы знаний в файле .exp Это можно считать обучением. Основное отличие синтеза от классических алгоритмов обучения – пользователь получает требуемую структуру нейронной сети с первого раза, без длительного итерационного процесса настройки связей. Так же в Cerebrum встроена поддержка коллекций объектов, что позволяет реализовать связи с другими нейронами как коллекции указателей на эти нейроны. На данный момент в одной БД может находится до 2 млрд объектов - разумеется при достаточной дисковой памяти. Необходимые в данный момент экземпляры объектов будут автоматически загружены в память, ненужные будут сброшены на диск. Это происходит автоматически.

Как в РБД эмулируемой на Cerebrum, так и в Elex нет одного нейрона, который выполняет все операции. Мало того, в Elex нейроны находятся в одной и той же БД со строками эмулируемой РБД. в Cerebrum все является объектами и нейроны тоже. Когда производилось подключение Cerebrum.Samples.Streams01.SampleAppUiService к системе то просто подключался listener событий оболочки DesktopClient к событиям интерфейса, который сконфигурировал через EmergencyConfigurator.

Рассмотрим эту задачу включения Cerebrum в свою разработку, а не написание аддина для Cerebrum.DesktopClient более подробно.

Следует воспользоваться версией Cerebrum не ранее 2005.11.16 она значительно расширена как раз для применения как часть в независимом приложении. В качестве основы для своего приложения следует взять исходные тексты Cerebrum.DesktopClient. Я рекомендую удалить PrimaryWindow и полностью заменить его собственной реализацией – жестко реализовать в главном окне все необходимые элементы управления без использования таблиц конфигурации Ui*. Так же следует удалить поддержку tryicon - в большинстве приложений это только мешает. В этом случае разработчику не понадобится БД конфигурации интерфейса пользователя Ui* Разработчику просто потребуется унаследовать собственный класс приложения от класса Cerebrum.Windows.Forms.Application и создать по его запросу экземпляр собственного главного окна. Главное окно, так же как и PrimaryWindow в примере нужно будет унаследовать от VisualComponent. Тогда после вызова метода этого окна SetConnector возможно использовать master database. Ее использование будет возможно через this.DomainContext.

Оригинальную версию DesktopClient имеет смысл использовать как интерфейс к БД конфигурации – для этого нужно будет зарегистрировать свои persistent классы чтобы фабрика могла их создавать при вызове DomainContext.CreateConnector

После того как приложение будет готово существует возможность экспортировать БД конфигурации в XML и поставлять его со своим приложением. DesktopClient останется просто инструментом разработчика и распространять его будет не нужно. Это позволит разработанному приложению создавать столько экземпляров БД на основе заранее заданной структуры сколько будет необходимо.

 

Создадим новое решение sln в среде Microsoft Visual Studio 2003. Cerebrum.Samples.Objects-01.sln

 

Уже существующую форму переименовываем в PrimaryWindow. Затем добавляем класс Application.

 

В класс Application переносим статическую функцию Main из PrimaryWindow. Переопределяем функцию CreatePrimaryWindow и CreateContextService. Заменяем майн на

 

static void Main()
{
    Application app = new Application();
    Cerebrum.Windows.Forms.Application.Instance = app;
    app.Initialize();
    app.Show();
    System.Windows.Forms.Application.Run(app.PrimaryWindow);
    app.Shutdown();
    app.Dispose();
}

Итого имеем

using System;

 

namespace Cerebrum.Samples.Objects01

{

      /// <summary>

      /// Summary description for Application.

      /// </summary>

      public class Application : Cerebrum.Windows.Forms.Application

      {

            public Application()

            {

                  //

                  // TODO: Add constructor logic here

                  //

            }

 

            protected override System.Windows.Forms.Form CreatePrimaryWindow()

            {

                  return new PrimaryWindow();

            }

 

            protected override Cerebrum.Windows.Forms.IContextService CreateContextService()

            {

                  string primaryDirectory = m_MasterContext!=null?(m_MasterContext.SystemDirectory):System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location);

                  string databaseFileName = System.IO.Path.Combine(primaryDirectory, "Cerebrum.Database.Master.bin");

                  string activityFileName = System.IO.Path.Combine(primaryDirectory, "Cerebrum.Database.Master.log");

                  string mergeFileName = System.IO.Path.Combine(primaryDirectory, "Cerebrum.Database.Master.xdb");

 

                  return new Cerebrum.Integrator.SimpleContextService(this, databaseFileName, activityFileName, 4, mergeFileName, false);

            }

 

            /// <summary>

            /// The main entry point for the application.

            /// </summary>

            [STAThread]

            static void Main()

            {

                  Application app = new Application();

                  Cerebrum.Windows.Forms.Application.Instance = app;

                  app.Initialize();

                  app.Show();

                  System.Windows.Forms.Application.Run(app.PrimaryWindow);

                  app.Shutdown();

                  app.Dispose();

            }

      }

}

 

Это минимальная реализация Application.

 

Теперь чтобы убедится, что все работает, добавим на форму DataGrid, а в событие Form_Load и Form_Closed следующий код:

 

 

 

Cerebrum.Data.TableView view;

 

private void PrimaryWindow_Load(object sender, System.EventArgs e)

{

      using(Cerebrum.Runtime.IConnector connector = this.DomainContext.GetTable("Types"))

      {

            view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView;

            dataGrid1.DataSource = view;

      }

}

 

private void PrimaryWindow_Closed(object sender, System.EventArgs e)

{

      view.Dispose();

}

 

В Close вызывать деструктор не обязательно, но крайне желательно - это позволит сработать встроенной системе самодиагностики мемори ликов - если будешь пользоваться Debug версией ядра.

 

Для нейрона мы создадим базовый класс Cerebrum.Samples.Objects01.NeuronBase у этого класса будет два атрибута - список зависимых нейронов (DependentObjectsList) и список нейронов от которых зависит текущий нейрон (PrecedentObjectsList). Предусмотрим интерфейс INeuronLinks для манипулирования этими списками.

 

public interface INeuronLinks

{

      void AddPrecedent(Cerebrum.Runtime.NativeHandle h);

      void AddDependent(Cerebrum.Runtime.NativeHandle h);

 

      void DelPrecedent(Cerebrum.Runtime.NativeHandle h);

      void DelDependent(Cerebrum.Runtime.NativeHandle h);

}

 

Реализуем этот интерфейс в классе NeuronBase

 

От базового класса мы унаследуем нейрон, распознающий символ входной символьной последовательности SymbolNeuron : NeuronBase. Этот нейрон будет реализовывать интерфейс ILineralTreeNode В этом интерфейсе нам понадобится два метода - метод для обучения зависимых нейронов GenerateDependents и метод для получения строк символов, соответствующих нейрону RestoreStrings а так же свойство, возвращающее символ, распознаваемый нейроном Symbol.

 

public interface ILineralTreeNode

{

      Cerebrum.Runtime.NativeHandle[] GenerateDependents(string text);

      string [] RestoreStrings();

      char Symbol {get;set;}

}

 

Обяжем нейрон SymbolNeuron реализовать интерфейс ILineralTreeNodeEx содержащий дополнительный метод FindAllLeafs  позволяющий обнаружить все листовые узлы синхронизированного линейного дерева.

public interface ILineralTreeNodeEx : INeuronLinks, ILineralTreeNode

{

      void FindAllLeafs(System.Collections.ArrayList leafs);

}

 

Таким образом, объект SymbolNeuron будет обладать 3мя атрибутами - DependentObjectsList, PrecedentObjectsList и Symbol

 

Создадим в проекте папку Specialized и в ней класс Concepts Этот класс не будет иметь экземпляров - только статические поля. Поэтому сделаем для него статический конструктор, а конструктор экземпляра объявим как private.

 

Создадив в этом классе несколько полей соответсвующие аттрбутам нейрона

Cerebrum.Runtime.NativeHandle DependentObjectsListAttribute;

Cerebrum.Runtime.NativeHandle PrecedentObjectsListAttribute;

Cerebrum.Runtime.NativeHandle SymbolAttribute;

Cerebrum.Runtime.NativeHandle SymbolNeuronType;

 

В статический конструктор добавим код для их инициализации

static Concepts()

{

      using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Attributes"))

      {

            using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView)

            {

                  System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"];

                  DependentObjectsListAttribute = FindHandle(view, descriptor, "DependentObjectsList");

                  PrecedentObjectsListAttribute = FindHandle(view, descriptor, "PrecedentObjectsList");

                  SymbolAttribute = FindHandle(view, descriptor, "Symbol");

            }

      }

      using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Types"))

      {

            using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView)

            {

                  System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"];

                  SymbolNeuronType = FindHandle(view, descriptor, "SymbolNeuron");

            }

      }

}

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

 

private static Cerebrum.Runtime.NativeHandle FindHandle(Cerebrum.Data.TableView view, System.ComponentModel.PropertyDescriptor prp, string value)

{

      int index = view.Find(prp, value);

      if (index >= 0)

      {

            return view[index].ObjectHandle;

      }

      return Cerebrum.Runtime.NativeHandle.Null;

}

 

Итак в результате

 

using System;

 

namespace Cerebrum.Samples.Objects01.Specialized

{

      /// <summary>

      /// Summary description for Concepts.

      /// </summary>

      public class Concepts

      {

            static Concepts()

            {

                  using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Attributes"))

                  {

                        using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView)

                        {

                             System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"];

                             DependentObjectsListAttribute = FindHandle(view, descriptor, "DependentObjectsList");

                             PrecedentObjectsListAttribute = FindHandle(view, descriptor, "PrecedentObjectsList");

                             SymbolAttribute = FindHandle(view, descriptor, "Symbol");

                        }

                  }

                  using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Types"))

                  {

                        using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView)

                        {

                             System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"];

                             SymbolNeuronType = FindHandle(view, descriptor, "SymbolNeuron");

                        }

                  }

            }

 

            private Concepts()

            {

                  //

                  // TODO: Add constructor logic here

                  //

            }

 

            private static Cerebrum.Runtime.NativeHandle FindHandle(Cerebrum.Data.TableView view, System.ComponentModel.PropertyDescriptor prp, string value)

            {

                  int index = view.Find(prp, value);

                  if (index >= 0)

                  {

                        return view[index].ObjectHandle;

                  }

                  return Cerebrum.Runtime.NativeHandle.Null;

            }

 

            public static readonly Cerebrum.Runtime.NativeHandle DependentObjectsListAttribute;

            public static readonly Cerebrum.Runtime.NativeHandle PrecedentObjectsListAttribute;

            public static readonly Cerebrum.Runtime.NativeHandle SymbolAttribute;

            public static readonly Cerebrum.Runtime.NativeHandle SymbolNeuronType;

 

      }

}

 

Теперь создаем класс NeuronBase, унаследованный от Cerebrum.Integrator.GenericComponent

 

В него добавляем две функции, соответсвующие его аттрибутам и переопределяем функцию SetConnector

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

 

Итого

using System;

 

namespace Cerebrum.Samples.Objects01

{

      /// <summary>

      /// Summary description for NeuronBase.

      /// </summary>

      public class NeuronBase : Cerebrum.Integrator.GenericComponent

      {

            public NeuronBase()

            {

                  //

                  // TODO: Add constructor logic here

                  //

            }

 

            public Cerebrum.Runtime.NativeVector GetPrecedentObjectsListVector()

            {

                  return this.GetAttributeContainer(Cerebrum.Samples.Objects01.Specialized.Concepts.PrecedentObjectsListAttribute) as Cerebrum.Runtime.NativeVector;

            }

 

            public Cerebrum.Runtime.NativeVector GetDependentObjectsListVector()

            {

                  return this.GetAttributeContainer(Cerebrum.Samples.Objects01.Specialized.Concepts.DependentObjectsListAttribute) as Cerebrum.Runtime.NativeVector;

            }

 

            public void AddPrecedent(Cerebrum.Runtime.NativeHandle h)

            {

                  using(Cerebrum.Runtime.NativeVector v = GetPrecedentObjectsListVector())

                  {

                        v.SetMap(h, new Cerebrum.Runtime.NativeHandle(1));

                  }

            }

 

            public void AddDependent(Cerebrum.Runtime.NativeHandle h)

            {

                  using(Cerebrum.Runtime.NativeVector v = GetDependentObjectsListVector())

                  {

                        v.SetMap(h, new Cerebrum.Runtime.NativeHandle(1));

                  }

            }

 

            public void DelPrecedent(Cerebrum.Runtime.NativeHandle h)

            {

                  using(Cerebrum.Runtime.NativeVector v = GetPrecedentObjectsListVector())

                  {

                        v.SetMap(h, Cerebrum.Runtime.NativeHandle.Null);

                  }

            }

 

            public void DelDependent(Cerebrum.Runtime.NativeHandle h)

            {

                  using(Cerebrum.Runtime.NativeVector v = GetDependentObjectsListVector())

                  {

                        v.SetMap(h, Cerebrum.Runtime.NativeHandle.Null);

                  }

            }

 

            protected override void SetConnector(Cerebrum.Runtime.SerializeDirection direction, Cerebrum.Runtime.IConnector connector)

            {

                  base.SetConnector (direction, connector);

                  switch(direction)

                  {

                        case Cerebrum.Runtime.SerializeDirection.Init:

                        {

                             using(Cerebrum.Runtime.NativeWarden warden = (m_Connector.GetContainer() as Cerebrum.Runtime.NativeWarden))

                             {

                                   warden.Newobj(Cerebrum.Samples.Objects01.Specialized.Concepts.PrecedentObjectsListAttribute, Cerebrum.Runtime.KernelObjectClass.Vector);

                                   warden.Newobj(Cerebrum.Samples.Objects01.Specialized.Concepts.DependentObjectsListAttribute, Cerebrum.Runtime.KernelObjectClass.Vector);

                             }

                             break;

                        }

                  }

            }

      }

}

 

Теперь создаем класс SymbolNeuron, наследуем его от NeuronBase и ILineralTreeNodeEx

В этом классе реализуем свойство Symbol и перекрываем метод SetConnector

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

 

Реализуем методы интерфейса ILineralTreeNode

 

 

#region ILineralTreeNode Members

 

public Cerebrum.Runtime.NativeHandle[] GenerateDependents(string text)

{

      //проверяем входные параметры

      if(text==null || text.Length<1) return null;

     

      //вырезаем первый символ из строки

      char symbol = text[0];

     

      //если первый символ совпадает с тем что разпознает этот нейрон - это наша строка

      if(this.Symbol!=symbol) return null;

 

      //получаем остаток строки

      string tail = text.Substring(1);

 

      if(tail.Length>0)

      {

            //пытаемся найти нейроны, распознающие остаток строки

            using(Cerebrum.Runtime.NativeVector vector = this.GetDependentObjectsListVector())

            {

                  foreach(System.Collections.DictionaryEntry de in vector)

                  {

                        Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key;

                        using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h))

                        {

                             ILineralTreeNode node = connector.Component as ILineralTreeNode;

                             if(node!=null)

                             {

                                   //рекурсивный вызов этой же функции но уже у другого нейрона

                                   Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail);

                                   if(hs!=null)

                                   {

                                         Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1];

                                         hs.CopyTo(hs2, 1);

                                         hs2[0] = this.PlasmaHandle;

                                         return hs2;

                                   }

                             }

                        }

                  }

            }

            //если мы здесь, то найти ничего не удалось - создаем то чего нехватает (обучаем сеть).

            using(Cerebrum.Runtime.NativeSector sector = this.DomainContext.GetSector())

            {

                  // создаем ID для нового нейрона

                  Cerebrum.Runtime.NativeHandle h0 = sector.NextSequence();

                  // создаем новый экземпляр нейрона

                  using(Cerebrum.Runtime.IConnector connector = sector.CreateConnector(h0, Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolNeuronType))

                  {

                        // а здесь мы приводим напрямую к себе, так как мы создавали именно себя и можем быть уверены что приведение будет успешным

                        // это надо чтоб добраться до функции AddPrecedent которая не описана в интерфейсе ILineralTreeNode

                        ILineralTreeNodeEx node = connector.Component as ILineralTreeNodeEx;

                        if(node!=null)

                        {

                             //задаем нейрону его символ для распознавания

                             node.Symbol = tail[0];

                             node.AddPrecedent(this.PlasmaHandle);

                             this.AddDependent(connector.PlasmaHandle);

 

                             //рекурсивный вызов этой же функции но уже у нового нейрона

                             Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail);

                             if(hs!=null)

                             {

                                   Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1];

                                   hs.CopyTo(hs2, 1);

                                   hs2[0] = this.PlasmaHandle;

                                   return hs2;

                             }

                        }

                  }

            }

      }

      // мы были последними в списке - дальше распознавать нечего, возвращаем себя

      return new Cerebrum.Runtime.NativeHandle[] {this.PlasmaHandle};

}

 

public string[] RestoreStrings()

{

      //опрашиваем всех предшественников и получаем строки, которые они распознают

      System.Collections.ArrayList result = new System.Collections.ArrayList();

      using(Cerebrum.Runtime.NativeVector vector = this.GetPrecedentObjectsListVector())

      {

            foreach(System.Collections.DictionaryEntry de in vector)

            {

                  Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key;

                  using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h))

                  {

                        ILineralTreeNode node = connector.Component as ILineralTreeNode;

                        if(node!=null)

                        {

                             //рекурсивный вызов этой же функции но уже у другого нейрона

                             string [] heads = node.RestoreStrings();

                             //обычно в heads может быть только одна строка - так как обычно предшественник в дереве только один

                             //на случай поддержки многозначности и нескольких путей распространения волны возбуждения предусматриваем

                             //более сложный вариант со многими предшественниками

                             foreach(string head in heads)

                             {

                                   result.Add(head + this.Symbol.ToString());

                             }

                        }

                  }

            }

      }

      // это корневой узел без предшественников - добавляем себя в начало строки

      if(result.Count<1)

      {

            result.Add(this.Symbol.ToString());

      }

      return (string[]) result.ToArray(typeof(string));

}

 

#endregion

 

Итого

 

using System;

 

namespace Cerebrum.Samples.Objects01

{

      /// <summary>

      /// Summary description for SymbolNeuron.

      /// </summary>

      public class SymbolNeuron : NeuronBase, ILineralTreeNodeEx

      {

            public SymbolNeuron()

            {

                  //

                  // TODO: Add constructor logic here

                  //

            }

 

           

            public char Symbol

            {

                  get

                  {

                        return Convert.ToChar(GetAttributeComponent(Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolAttribute));

                  }

                  set

                  {

                        SetAttributeComponent(Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolAttribute, Convert.ToInt32(value));

                  }

            }

 

            protected override void SetConnector(Cerebrum.Runtime.SerializeDirection direction, Cerebrum.Runtime.IConnector connector)

            {

                  base.SetConnector (direction, connector);

                  switch(direction)

                  {

                        case Cerebrum.Runtime.SerializeDirection.Init:

                        {

                             using(Cerebrum.Runtime.NativeWarden warden = (m_Connector.GetContainer() as Cerebrum.Runtime.NativeWarden))

                             {

                                   warden.Newobj(Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolAttribute, Cerebrum.Runtime.KernelObjectClass.Scalar);

                             }

                             break;

                        }

                  }

            }

 

            #region ILineralTreeNode Members

 

            public Cerebrum.Runtime.NativeHandle[] GenerateDependents(string text)

            {

                  //проверяем входные параметры

                  if(text==null || text.Length<1) return null;

                 

                  //вырезаем первый символ из строки

                  char symbol = text[0];

                 

                  //если первый символ совпадает с тем что разпознает этот нейрон - это наша строка

                  if(this.Symbol!=symbol) return null;

 

                  //получаем остаток строки

                  string tail = text.Substring(1);

 

                  if(tail.Length>0)

                  {

                        //пытаемся найти нейроны, распознающие остаток строки

                        using(Cerebrum.Runtime.NativeVector vector = this.GetDependentObjectsListVector())

                        {

                             foreach(System.Collections.DictionaryEntry de in vector)

                             {

                                   Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key;

                                   using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h))

                                   {

                                         ILineralTreeNode node = connector.Component as ILineralTreeNode;

                                         if(node!=null)

                                         {

                                               //рекурсивный вызов этой же функции но уже у другого нейрона

                                               Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail);

                                               if(hs!=null)

                                               {

                                                     Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1];

                                                     hs.CopyTo(hs2, 1);

                                                     hs2[0] = this.PlasmaHandle;

                                                     return hs2;

                                               }

                                         }

                                   }

                             }

                        }

                        //если мы здесь, то найти ничего не удалось - создаем то чего нехватает (обучаем сеть).

                        using(Cerebrum.Runtime.NativeSector sector = this.DomainContext.GetSector())

                        {

                             // создаем ID для нового нейрона

                             Cerebrum.Runtime.NativeHandle h0 = sector.NextSequence();

                             // создаем новый экземпляр нейрона

                             using(Cerebrum.Runtime.IConnector connector = sector.CreateConnector(h0, Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolNeuronType))

                             {

                                   // а здесь мы приводим напрямую к себе, так как мы создавали именно себя и можем быть уверены что приведение будет успешным

                                   // это надо чтоб добраться до функции AddPrecedent которая не описана в интерфейсе ILineralTreeNode

                                   ILineralTreeNodeEx node = connector.Component as ILineralTreeNodeEx;

                                   if(node!=null)

                                   {

                                         //задаем нейрону его символ для распознавания

                                         node.Symbol = tail[0];

                                         node.AddPrecedent(this.PlasmaHandle);

                                         this.AddDependent(connector.PlasmaHandle);

 

                                         //рекурсивный вызов этой же функции но уже у нового нейрона

                                         Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail);

                                         if(hs!=null)

                                         {

                                               Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1];

                                               hs.CopyTo(hs2, 1);

                                               hs2[0] = this.PlasmaHandle;

                                               return hs2;

                                         }

                                   }

                             }

                        }

                  }

                  // мы были последними в списке - дальше распознавать нечего, возвращаем себя

                  return new Cerebrum.Runtime.NativeHandle[] {this.PlasmaHandle};

            }

 

            public string[] RestoreStrings()

            {

                  //опрашиваем всех предшественников и получаем строки, которые они распознают

                  System.Collections.ArrayList result = new System.Collections.ArrayList();

                  using(Cerebrum.Runtime.NativeVector vector = this.GetPrecedentObjectsListVector())

                  {

                        foreach(System.Collections.DictionaryEntry de in vector)

                        {

                             Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key;

                             using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h))

                             {

                                   ILineralTreeNode node = connector.Component as ILineralTreeNode;

                                   if(node!=null)

                                    {

                                         //рекурсивный вызов этой же функции но уже у другого нейрона

                                         string [] heads = node.RestoreStrings();

                                         //обычно в heads может быть только одна строка - так как обычно предшественник в дереве только один

                                         //на случай поддержки многозначности и нескольких путей распространения волны возбуждения предусматриваем

                                         //более сложный вариант со многими предшественниками

                                         foreach(string head in heads)

                                         {

                                               result.Add(head + this.Symbol.ToString());

                                         }

                                    }

                             }

                        }

                  }

                  // это корневой узел без предшественников - добавляем себя в начало строки

                  if(result.Count<1)

                  {

                        result.Add(this.Symbol.ToString());

                  }