а не замутить ли нам недосимулятр?

Публичный форум для http://www.nedopc.org/nedopc

Moderator: Shaos

bar
Senior
Posts: 185
Joined: 07 Aug 2006 10:18

Post by bar »

Shaos wrote:Ну кратчайший путь от любого слоя до входа схемы может составлять 0 если вход схемы подключается к какому-то входу компонента в слое напрямую
Да, точно. Только не ноль, а один: я входы схемы тоже считаю вершинами графа.
Значит не кратчайший, а длиннейший путь. Но без циклов.
Shaos wrote:P.S. А зачем отдельно выделять такую абстракцию как слои?
Так понятнее, с чем приходится иметь дело. Я не говорю, что процессору от этого будет проще, но мозгу проще от этого по-любому. И, при этом, "мозговые" абстракции вовсе не обязательно навязывать процессору.
User avatar
Shaos
Admin
Posts: 24008
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Кстати мой подход с учётом координаты X имеет изъяны - например классическая схема триггера с ходу не заработает - придётся разнести элементы по горизонтали, чтобы обратная связь была только одна, а не две. Так что наверное я возьму на вооружение совет bara - при обходе метить блоки и если пришли к уже отмеченному, то это цикл и его надо разорвать - другой вопрос что надо аккуратнее выбирать место разрыва, чтобы как можно меньше сигналов задерживалось на 1 такт...
Я тут за главного - если что шлите мыло на me собака shaos точка net
bar
Senior
Posts: 185
Joined: 07 Aug 2006 10:18

Post by bar »

Shaos wrote:Кстати мой подход с учётом координаты X имеет изъяны - например классическая схема триггера с ходу не заработает - придётся разнести элементы по горизонтали, чтобы обратная связь была только одна, а не две. Так что наверное я возьму на вооружение совет bara - при обходе метить блоки и если пришли к уже отмеченному, то это цикл и его надо разорвать - другой вопрос что надо аккуратнее выбирать место разрыва, чтобы как можно меньше сигналов задерживалось на 1 такт...
А я наконец допёр, почему этот мой подход с одним битом не будет работать. Как раз в случае триггера. Точнее в случае наличия обратной связи.

У меня мысль-то была примерно такая. Поделить всё множество элементов на три непересекающихся подмножества:
1. обработанные на текущей итерации
2. находящиеся на расстоянии 1 от обработанных
3. все остальные

И перенос элемента из 2 в один (как мне казалось) возможен лишь в одной ситуации: если все элементы на расстоянии -1 от данного уже обработаны. (я не знаю, насколько нормально использовать отрицательные расстояния в орграфах, но напрашивается). Дык вот неверно это. В случае если мы пытаемся обработать rs-триггер нам придётся помечать один из И-НЕ как помеченный, несмотря на то, что его состояние зависит от ещё не помеченного второго И-НЕ.

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

Нужен именно твой подход, только вместо "координаты X", надо использовать расстояние в графе. Которое вычислять единожды перед началом симуляции. Допустим организовав обход графа "в ширину". Этот обход в ширину можно было бы использовать и для собственно итерации, но я не вижу хорошего способа реализовать is_nearer_to_scheme_input(ELEMENT a, ELEMENT b). Конечно можно заменить один бит на один трит, и метить "насовсем" пройденные элементы значением 1, совсем не пройденные значением -1, и иметь множество помеченное значением нуль -- это элементы находящиеся на текущем уровне. Но для этого, по факту, придётся в каждый элемент заглядывать дважды. Один раз, чтобы пометить нулём, второй раз, чтобы вызывать его метод step и пометить единичкой. Вероятно при этом не удастся найти способа обойтись без предварительного обхода всех элеменотв с развешиванием на них метки -1. Хотя не, можно, просто на чётных итерациях надо будет помножать эти значения на -1.
User avatar
Shaos
Admin
Posts: 24008
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

По-моему ты черезчур усложняешь...

Кстати в случае триггера оба элемента будут равноправны - и от входов они будут на одинаковом "расстоянии" - как быть в этом случае?

P.S. В случае использования координаты X триггер с двумя И-НЕ, расположенных друг на другом будет просто самовозбуждаться, превращаясь в генератор...
Я тут за главного - если что шлите мыло на me собака shaos точка net
b2m
Devil
Posts: 907
Joined: 26 May 2003 06:57

Post by b2m »

А нельзя что-ли сначала рассчитать все выходы, и только потом в самом конце итерации переписать значения выходов на входы в соответствии с соединениями?
При этом можно даже монтажное И/ИЛИ учесть :)
Страничка эмулятора наших компьютеров
http://bashkiria-2m.narod.ru/
User avatar
Shaos
Admin
Posts: 24008
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

b2m wrote:А нельзя что-ли сначала рассчитать все выходы, и только потом в самом конце итерации переписать значения выходов на входы в соответствии с соединениями?
При этом можно даже монтажное И/ИЛИ учесть :)
Тогда у тебя задержка будет не на обратных связях, а вообще на каждом элементе...

P.S. Хотя наверное это более правдоподобно и элементы сортировать ненадо в соответствии с удалённостью от входов
Я тут за главного - если что шлите мыло на me собака shaos точка net
bar
Senior
Posts: 185
Joined: 07 Aug 2006 10:18

Post by bar »

Shaos wrote:По-моему ты черезчур усложняешь...
Мне показалось наоборот. Вплоть до того, что элементы каждого слоя можно выписать в отдельный массив и обходить каждый массив в произвольном порядке. [s]Или не в порядке, а параллельно, засунув в GPU[/s]
Shaos wrote:Кстати в случае триггера оба элемента будут равноправны - и от входов они будут на одинаковом "расстоянии" - как быть в этом случае?
Мне казалось что поф... Но я взял таки ручку с бумажкой и вижу что не поф. Ну и как интересно будет правильным разрулить ситуацию? В случае триггера можно обойти эти И-НЕ два раза, каждый раз пересчитывая. В общей ситуации, надо думать, придётся детектить циклы и бегать по этим циклам, разглядывая переключения элементов. И бегать до тех пор, пока либо ситуация стабилизируется, то есть, пробежав круг, мы ничего не изменим. Либо до тех пор, пока не выясним, что состояние элементов меняется циклически (самовозбуждение?). Да, и если математически (читай теоретически) множество состояний группы элементов будет конечным множеством, то практически оно может оказаться достаточно большим, чтобы считать его бесконечностью. А это значит, что цикличность изменения состояния элементов мы можем и не узнать никогда. То есть бегать по кругу -- не вариант в общей ситуации.
Shaos wrote:P.S. В случае использования координаты X триггер с двумя И-НЕ, расположенных друг на другом будет просто самовозбуждаться, превращаясь в генератор...
Я наблюдал, кстати, подобное в cedar. Если rs-триггер перевести в запрещённое состояние, то есть на оба входа сунуть ему 1, ему становится хорошо, и он самовозбуждается. Правда для этого надо чтобы там одновременно появились бы единички на входах. Если значения менять по-одному, то ничего не выходит.
Надо будет погонять его под отладкой. Вопрос каким образом cedar достигает подобного выходит достаточно интересным, тем более что период колебаний триггера у него неустойчивый. Странный период, он зависит от того, какой длины шаг симуляции по времени, и ещё от фазы луны. Причём бывает, что фаза луны меняется в процессе этих самых колебаний.

И если когда я увидел это в первый раз, я не особо парился на этот счёт, то теперь мне стало интересно. То ли cedar учитывает скорость распространения сигнала по схеме, то ли он гоняет по циклам примерно рандомное количество раз, обрывая эту беготню по кругу используя какие-то не очень подходящие критерии в качестве детекта зацикленности. Вариант со временем выглядит более правдоподобным -- а зачем оно ещё может быть надо?
bar
Senior
Posts: 185
Joined: 07 Aug 2006 10:18

Post by bar »

bar wrote:
Shaos wrote:По-моему ты черезчур усложняешь...
Мне показалось наоборот. Вплоть до того, что элементы каждого слоя можно выписать в отдельный массив и обходить каждый массив в произвольном порядке. [s]Или не в порядке, а параллельно, засунув в GPU[/s]
Добавлю.

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

Кстати замечу, что b2m-подход, будет выдавать несколько иные результаты нежели то, что мы обсуждали до сих пор. У него входной сигнал дойдёт до слоя i за i итераций. Если все выходы схемы расположены на одном и том же уровне, то надо думать, что в результате-то (если не забыть дать b2m-подходу несколько итераций на разогрев), в результате получится одно и то же. [add]Подумав ещё... Нихрена. Если как раз с тем триггером решить ситуацию таким образом, чтобы значения с выходов элементов текущего слоя не влияли бы, до тех пор пока мы не закончим с текущим слоем, чтобы вместо них использовались бы значения с предыдущей итерации, тогда да, а так нет[/add]
Но если выходы в разных слоях, что немало вероятно, то результаты будут различны. И рождается вопрос: какой вариант правильнее?
b2m
Devil
Posts: 907
Joined: 26 May 2003 06:57

Post by b2m »

Правильнее учитывать время от изменения на входе логического элемента до изменения на выходе. В "моём" подходе оно будет усреднённым для всех логических элементов и равно времени одной итерации.
Страничка эмулятора наших компьютеров
http://bashkiria-2m.narod.ru/
bar
Senior
Posts: 185
Joined: 07 Aug 2006 10:18

Post by bar »

b2m wrote:Правильнее учитывать время от изменения на входе логического элемента до изменения на выходе.
Угумс. Я тоже так теперь думаю. Без этого времени не удастся без грязных хаков решить проблему обратной связи в общем виде.
bar
Senior
Posts: 185
Joined: 07 Aug 2006 10:18

Post by bar »

Мастер по C++. А вот скажи мне, как бы так в C++ обойти отсутствие generic'ов? Вот есть у меня N классов. Все они производные от одного и того же класса. Даже скажу подробнее, чтобы было понятнее. Это геометрические примитивы. Типа точка, отрезок, прямая, окружность, и чёртегознает ещё мне в голову придёт добавить в этот список потом.
И хочу я расстояние между ними вычислять. В C++ есть перегрузка функций -- это почти то что мне надо, но выбор реализации функции в ней выбирается статически, что полностью обесценивает перегрузку и делает её совершенно бесполезной.

Как-то незаметно я привык к generic'ам из common lisp'а, к тому что это просто, и когда здесь писал перегруженные функции distance, подумал, что ежели потребуется в рантайме выбирать нужную функцию, то я как-нибудь выкручусь, ведь в C++ есть rtti. Ан не тут-то было.

Я поискал в гугле насчёт generic'ов и C++, но там вообще вынос мозга. Пишут что в C# есть generic'и, но там синтаксис как у темплитов, и это такой вынос мозга... Нашёл какую-то статейку, где вроде про дженерики в C++, но там хрен поймёшь: в C++ или в C#. Тоже синтаксис я тя пну и source кода не видно. Дальше я не вникал.

Более конкретно, вот есть у меня две переменные:

Code: Select all

primitive *p1, *p2;
И хочется как-то избавится от необходимости в таком коде:

Code: Select all

if(typeid(*p1) == typeid(point) && typeid(*p2) == typeid(point)) {
    return point_point_distance(p1, p2);
} else if(typeid(*p1) == typeid(point) && typeid(*p2) == typeid(line_segment)) {
    return point_segment_distance(p1, p2);
} else if...
Понятно тут можно оптимизировать и уйти от N*N if'ов, сделать N+N, а учитывая коммутативность ещё поделить количество if'ов примерно на 2. Но дело в том, что... по-моему всё равно уродство. Да и добавляя новый класс придётся лезть в эти if'ы, дописывать чего-то, причём так, чтобы не пропустить ни одного места, где можно пропустить. Можно конечно кодогенератор написать, но опять же, попахивает идиотизмом: специальный кодогенератор ради какой-то мелочи. Да и как этот кодогенератор можно научить выудить из заголовка список классов производных от primitive? Утонешь в регекспах.

Приходит в голову другой вариант -- напихать в каждый класс virtual методов, которые будут измерять расстояние от this до другого примитива. Это конечно позволит избавиться от большинства typeid'ов, а можно даже ото всех, но это как-то совсем черезжопу. Примерно так:

Code: Select all

class point {
    virtual float distance(primitive *p) {
        return p->distance(this);
    }
    virtual float distance(point *p);
    virtual float distance(line_segment *ls);
};
class line_segment {
    virtual float distance(primitive *p) {
        return p->distance(this);
    }
    virtual float distance(point *p) {
        /* используем коммутативность: */
        return p->distance(this);
    }
    virtual float distance(line_segment *ls);
};
Если у нас N разных примитивов, то придётся N+1 виртуальную функцию засовывать в каждый класс.

Можно сделать двумерный массив указателей на те реализации перегруженной distance. Немного повозиться в рантайме с разруливанием коммутативности, или явно создать по перегруженной функции на каждую пару типов. Самый многообещающий вариант, но, блин, C++ ленив приделывать типам целочисленные идентификаторы, и придётся вместо двумерного массива делать двумерный map, или отдельно преобразовывать строковые идентификаторы типов в небольшие целые числа.

Вот на твой взгляд, о Мастер C++, какой из этих путей менее уродлив? Или лучше пойти C'шным путём? То есть завести целочисленное поле type в каждом классе, и потом свести реализацию generic_distance, к:

Code: Select all

return generic_fn_table[p1->type][p2->type](p1, p2);
Чего-то мне кажется, что C'шный подход выйдет наиболее лаконичным и наименее сбивающим с толку читателя кода.
b2m
Devil
Posts: 907
Joined: 26 May 2003 06:57

Post by b2m »

bar wrote:Если у нас N разных примитивов, то придётся N+1 виртуальную функцию засовывать в каждый класс.
То есть всего у нас будет (не считая функций с параметром указателем на базовый класс) N*N функций, половина из которых будут использовать комутативный закон. Это то-же самое, что двумерный массив N*N указателей на процедуры. Здесь же ты избавишься от необходимости преобразовывать аргументы указатели к указателям на нужные пару классов.

Кроме того, все объекты можно привести к небольшому числу геометрических фигур: линия, треугольник, прямоугольник, овал, группа фигур. Т.е. параметрами будут указатели не на реальный объект, а на его геометрическую фигуру (которые можно реализовать как промежуточные базовые классы, и всю эту канитель реализовывать только в них).
Страничка эмулятора наших компьютеров
http://bashkiria-2m.narod.ru/
User avatar
Shaos
Admin
Posts: 24008
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Generics=Templates?
Я тут за главного - если что шлите мыло на me собака shaos точка net
bar
Senior
Posts: 185
Joined: 07 Aug 2006 10:18

Post by bar »

Shaos wrote:Generics=Templates?
Templates всю работу свою выполняют во время компиляции. Как я понимаю generics в понимании C# это ровно то же самое, но в рантайме. То есть подстановка конкретных типов выполняется во время выполнения.

В лисповском же понимании, от которого я собственно и шёл, если это понимание в терминах C++ выражать: generic -- это перегруженная функция, конкретная реализация которой выбирается не во время компиляции, а в рантайме.

То есть допустим если я напишу в C++:

Code: Select all

class primitive {...};
class point : public primitive { ... };
class circle : public primitive { ... };

float distance(point *p1, point *p2) {
  ...
}
float distance(circle *c1, circle *c2) {
  ...
}
То вот такое не скомпиляется:

Code: Select all

primitive *p1, *p2;
p1 = new point;
p2 = new point;
distance(p1, p2);
Потому что C++ во время компиляции видит что типы p1 и p2 -- это указатели на primitive, при этом нету distance перегруженной на (primitive*, primitive*), и C++ не найдёт какую именно функцию надо вызывать. А если бы и нашёл, то нашёл бы во время компиляции. Но ситуация такова, что на этапе компиляции неизвестно какие именно примитивы надо будет засовывать в distance во время выполнения. Ну, допустим, код:

Code: Select all

vector<primitive*> path1, path2;
/* каким-то образом заполняем path1 и path2 _разными_ примитивами */
for(p1 = path.begin(); p1 != path.end(); p1 ++) {
    for(p2 = path.begin(); p2 != path.end(); p2 ++) {
        distance(p1, p2);
    }
}
[add] Добавлю насчёт перегрузки distance для (primitive*, primitive*). Такой перегрузки "по логике ситуации" быть не должно. Поскольку primitive -- абстрактный класс, то сосчитать расстояние от него до куда бы то ни было мы не можем. Такая реализация должна быть (по-логике) чем-то типа virtual функции объявленной с "= 0;" в конце.
User avatar
Shaos
Admin
Posts: 24008
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Вот Александреску в 11 главе писал как сделать то, что ты хочешь:

http://books.google.fr/books?id=aJ1av7U ... &q&f=false

P.S. Лучше (и понятнее) писать на сях, тегируя объекты - код будет сильно прозрачней...
Я тут за главного - если что шлите мыло на me собака shaos точка net