Giter Site home page Giter Site logo

raox's Introduction

Rao X

Summary

This project is an implementation of RDO modelling language in Eclipse, using xtext.

Preparing

  • Install Ubuntu Desktop or any other linux distribution

  • Install Java 8

    IMPORTANT Latest version of openjdk at the moment of writing this (openjdk-8-jdk 8u66) crushes when usingSWT_AWT bridge. Oracle jdk distributions should be used instead. For debian-based linux distributions:

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer

Check java version:

java -version

If output is different than (except version numbers):

java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

set it manually

sudo update-alternatives --config java
cd ~/Downloads
gunzip -c eclipse-dsl-luna-SR2-linux-gtk-x86_64.tar.gz  | tar xvf -
cd eclipse
./eclipse
  • Git clone rdo-xtext repository
ssh-add ~/.ssh/github.openssh.private.key
git clone [email protected]:aurusov/rdo-xtext.git

Installing

Setting up the workspace for Eclipse

  • File > Import > General > Existing Projects into Workspace``> Select root directory > /home/USERNAME/git/rdo-xtext > Finish
  • Wait for the workspace to build and get tons of errors
  • ru.bmstu.rk9.rao/src/ru.bmstu.rk9.rao/Rao.xtext > Run As > Generate Xtext Artifacts > Proceed
  • *ATTENTION* It is recommended to use the ANTLR 3... press y
0    [main] INFO  lipse.emf.mwe.utils.StandaloneSetup  - Registering platform uri '/home/drobus/git/rdo-xtext'
637  [main] INFO  lipse.emf.mwe.utils.StandaloneSetup  - Adding generated EPackage 'org.eclipse.xtext.common.types.TypesPackage'
644  [main] INFO  ipse.emf.mwe.utils.DirectoryCleaner  - Cleaning /home/drobus/git/rdo-xtext/ru.bmstu.rk9.rao/../ru.bmstu.rk9.rao/src-gen
652  [main] INFO  ipse.emf.mwe.utils.DirectoryCleaner  - Cleaning /home/drobus/git/rdo-xtext/ru.bmstu.rk9.rao/../ru.bmstu.rk9.rao/model
652  [main] INFO  ipse.emf.mwe.utils.DirectoryCleaner  - Cleaning /home/drobus/git/rdo-xtext/ru.bmstu.rk9.rao/../ru.bmstu.rk9.rao.ui/src-gen
653  [main] INFO  ipse.emf.mwe.utils.DirectoryCleaner  - Cleaning /home/drobus/git/rdo-xtext/ru.bmstu.rk9.rao/../ru.bmstu.rk9.rao.tests/src-gen
5720 [main] INFO  clipse.emf.mwe.utils.GenModelHelper  - Registered GenModel 'http://www.bmstu.ru/rk9/rao/Rao' from 'platform:/resource/ru.bmstu.rk9.rao/model/generated/Rao.genmodel'
27489 [main] INFO  text.generator.junit.Junit4Fragment  - generating Junit4 Test support classes
27496 [main] INFO  text.generator.junit.Junit4Fragment  - generating Compare Framework infrastructure
27550 [main] INFO  .emf.mwe2.runtime.workflow.Workflow  - Done.

[!] If your output differs from the one above by a lot of errors mentioning RULE_ANY_OTHER, you should run the generation process again and again until the bulid is succesfull. This is Xtext/Antlr bug caused by complex rules supporting unicode identifiers in grammar, sorry for the inconvenience

  • Run > Run Configurations... > Eclipse Application > New > Name =
  • runtime-EclipseXtext
    > Location =
  • ${workspace_loc}/../runtime-EclipseXtext
    > Run
  • Ignore this if you use Java version 8 or later. Eclipse Platform may freeze during its launch. This happens due to the unsufficient permgen size available to Eclipse. To prevent that, add -XX:MaxPermSize=256M to VM arguments in Run Configuration.
  • And that's it.

Running

  • Window > Open Perspective > Other... > Rao
  • File > New > Project... > Rao > Rao Project > Next> > Project name: >
  • set project name
  • choose model template
  • Finish
  • Models examples

raox's People

Contributors

bogachev-pa avatar k-alexandrovsky avatar aurusov avatar lekaitow avatar kstrizhov avatar alexchernov avatar asquad avatar

Watchers

 avatar  avatar

raox's Issues

Вывод трассировки в реальном времени

@aurusov
@k-alexandrovsky

Есть несколько вопросов, касательного того, как это должно работать.

  1. Должна ли трассировка выводиться в риал-тайме в режиме максимальной скорости симуляции?
  2. Если нет, то как поступать, если пользователь начинает переключаться между режимами в процессе прогона?
  3. Скролл должен следовать за последней записью, т.е. постоянно листаться вниз? А в режиме максимальной скорости?

Далее по реализации.
@k-alexandrovsky
4. Для вывода в реальном времени окошку трассировки нужно два вида нотификаций:
А. Модель запущена - в этом случае от берет у трассировщика traceList и устанавливает его как свой инпут.
Б. Трассировщик добавил новую запись. В этом случае окно трассировщика обновляется только itemCount - это нужно для ленивого вывода строк.
По-моему, с текущим интерфейсов Subscriberа это реализовать нельзя.
5. Хочется делать нотификацию каждый раз, когда база данных добавляет что-то в allEntries. Я для этого завернуть все вызовы allEntries.add(entry) в функцию addEntry. И в ней хочу вызывать Simulator.notifyChange("EntryAdded"). Но этот метод приватный. Изменить ему видимость или я всё неправильно делаю?
6. К началу симуляции, а также к моменту создания трассировщика в базе данных уже есть entry в списке. Я не могу понять, в какой момент мне надо инициализировать трассировщика, чтобы он точно создался до добавления первой записи в базу данных. Иначе приходится костылить трассировщику методы типа считай, что уже есть в базе данных в момент создания.

Реализовать возможности трассировки старого РДО

  1. Уметь трассировать:
    a. Системные данные [done]
    b. Данные ресурсов [done]
    c. Данные паттернов [done]
    d. Данные точек принятия решений [done]
    e. Данные результатов [done]
  2. Раскрашивать трассировку по типам. [done]
  3. Вывод трассировки в реальном времени. [done]

Exception'ы в rdolib

@k-alexandrovsky
Вчера на консультации зашла речь о том, что нам нужны собственные exception'ы в библиотеке.
Причем сделать их иерархично, в духе Internal -> Database и т.п.

Я правильно понимаю, что ничего похожего у нас сейчас нет?

Оптимизация окошка с деревом всех сериализуемых объектов

После прогона модели с ~10000 клиентами, при попытке раскрыть в дереве список клиентов, окошко подвисает на 10-15 секунд. (На винде ещё хуже - даже запуск модели невозможен.)
Когда число клиентов достигает 28000, то возможен только Force quit.

Cравнить производительность окон трассировки нового и старого РДО

@aurusov

В старом РДО измеряю скорость таким образом:

bool LogView::scrollVertically(int inc)
{
    if (!m_SM_Y.applyInc(inc))
    {
        return false;
    }

    auto before = std::chrono::high_resolution_clock::now();

    m_strings.setCursor(m_SM_Y.position, m_SM_Y.posMax);
    getVertScrollBar().setValue(m_SM_Y.position);
    update();

    auto after = std::chrono::high_resolution_clock::now();
    std::cout << "diff = " << std::chrono::duration_cast<std::chrono::nanoseconds>(
            after - before).count() << std::endl;

    return true;
}

Чтобы спамить фейковые записи делаю:

void RDOTrace::writeTraceEnd(const LPRDORuntime& pRuntime)
{
    if (isNull()) return;

    unsigned count = 1000;
    for (unsigned i = 0; i < count; i++)
    {
        getOStream() << "ES " << pRuntime->getCurrentTime()
          << " 2" << std::endl << getEOL();
    }
}

Примерно на миллионе записей РДО подвисает секунд на 5, но потом оживает и всё-таки их выводит.

Для count = 1000 при пролистывании всего окна сверху вниз выводятся примерно 15 строк такого порядка (это всё наносекунды):

diff = 3468
diff = 3443
diff = 6509
diff = 8021

При count = 10 000

diff = 42338
diff = 20728
diff = 23233
diff = 19614

При count = 100 000

diff = 146759
diff = 124691
diff = 167342
diff = 113972

При count = 1 000 000

diff = 1187479
diff = 1042001
diff = 1147465
diff = 1116977

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

Теперь в новом РДО. Измеряю скорость в ContentProvider'е. Сравнение кажется адекватным, этот метод обновляет содержание строки.

    @Override
    public void updateElement(int index)
    {
        long startTime = System.nanoTime();
        if (traceList != null && index < traceList.size())
            RDOTraceView.viewer.replace(traceList.get(index), index);
        long endTime = System.nanoTime();
        System.out.println("diff = " + (endTime - startTime));
    }

1000 записей:

diff = 33184
diff = 33755
diff = 69732
diff = 43266

10 000 записей:

diff = 35633
diff = 37142
diff = 92097
diff = 62327

100 000 записей:

diff = 39375
diff = 39586
diff = 89736
diff = 57518

1 000 000 записей:

diff = 81969
diff = 48528
diff = 46327
diff = 44978

Кроме как константной, я эту сложность назвать никак не могу.
Можно было бы подумать, что метод RDOTraceView.viewer.replace() не вызывает перерисовку.
Но, во-первых, я покопался в исходниках swt, куда он ведет, и там в итоге довольно объемный метод doUpdateItem(), в котором вызывается что-то в духе columnViewer.refresh(cellToUpdate);, что ведет к labelProvider.update(cell);, а labelProvider это как раз тот, кто говорит, что надо вывести в данной строке таблицы (т.е. какой текст и какую картинку).
Во-вторых, я пробовал измерять время между вызовами updateElement(), и в этом случае я получаю все времена примерно на порядок больше, но зависимость всё еще сохранятся линейной.

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

Edit: только сейчас понял, что собирал старое РДО в дебаге (но запускал Run, а не Debug). Не знаю, насколько сильно это могло повлиять на измерения. Чуть позже перемеряю, но сложность это вроде не должно изменить.

Edit2: что-то я не пойму, как мне собрать РДО в релизе. Собираю под ubuntu в Netbeans.
В CMake файлах вообще не вижу ничего, связанного с дебагом, что бы не касалось MSVC.
По флагам оптимизации грепается только build/CMakeCache.txt, т.е. уже сгенеренный файл, и там как будто -O2 -g -DNDEBUG для Release with Debug Info builds (вообще странно сочетание флагов) и -O3 -DNDEBUG для Release builds. Только где задается, какие именно флаги используются при сборке, я не пойму.

Отображать несколько файлов в окне RDOTraceConfigView

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

Имплементация новой грамматики языка

Начало обсуждения здесь: LeKaitoW#27

В шапке буду периодически обновлять код модели в текущей грамматике.

enum Orig { TYPE1, TYPE2 }

type Foo {
    int bar;
    String str = "hello";
}

type EnumTypeTest {
     Orig testEnum;
}
enum Client_type {Type1, Type2}
enum Barb_states {Free, Busy}
enum Client_state {Entered, Started}

type BarberShops {
     double people_in_queue;
}

type Clients {
    Client_type client_type;
    Client_state state = Client_state.Entered;
}

type Barbers {
    Barb_states state_of_barber = Barb_states.Free;
    int people_served;
    int duration_min;
    int duration_max;
    Client_type client_type_of_client;
}

resource BarberShop = BarberShops.create(0.0);
resource Barber_1 = Barbers.create(*, 0, 20, 40, Client_type.Type1);
resource Barber_2 = Barbers.create(*, 0, 25, 70, Client_type.Type2);
resource Barber_3 = Barbers.create(*, 0, 30, 60, Client_type.Type2);
resource foo = Foo.create(5, *);
EnumTypeTest.create(Orig.TYPE1);
EnumTypeTest.create(Orig.TYPE2);

constant int x = 40;

event Pat_coming_of_client_test (int a, double b, Orig c) {
    resource _client = Clients.create(Тип_клиента.next(), *);
    _client.state = Client_state.Entered;
    Pat_coming_of_client_test.plan( currentTime + Интервал_прихода.next(), 1, 2.0, Orig.TYPE1 );
    BarberShop.people_in_queue += 1;
}

operation Pat_of_serving_of_client(int a, double b, Orig test_enum_param) {
    relevant _BarberShop = BarberShop.select(_BarberShop.people_in_queue > 0);
    relevant _Client = Clients.select(_Client.state == Client_state.Entered);
    relevant _Barber = Barbers.select(_Barber.state_of_barber == Barb_states.Free
            and _Barber.client_type_of_client == _Client.client_type)
                .withMin( _Barber.people_served);

    set duration() {
        return Длительность_обслуживания.next( _Barber.duration_min, _Barber.duration_max );
    }
    set begin() {
        _BarberShop.people_in_queue--;
        _Client.state = Client_state.Started;
        _Barber.state_of_barber = Barb_states.Busy;
    }
    set end() {
        _Barber.state_of_barber = Barb_states.Free;
        _Barber.people_served += a;
        _Client.erase();
    }
}

rule Change_string_rule() {
    relevant _Foo = Foo.select(_Foo.bar == 5);
    set execute() {
        _Foo.bar++;
        _Foo.str = "heyheyyyy";
    }
}

dpt model {
    set init() {
        setCondition(any);
        setPriority(1);
    }
    activity Serving_of_client(5) checks Pat_of_serving_of_client(15, 15.0, Orig.TYPE1);
    activity ChangeString(5) checks Change_string_rule();
}

set init() {
    Pat_coming_of_client_test.plan(currentTime + 30, 1, 2.0, Orig.TYPE1);
}

set terminateCondition() {
    return currentTime >= 60 * 24 * 10 * testFun(1, 2.0) * testTable(1) * testList(1);
}

result Busyness_of_barber_1 = watchState(Barber_1.state_of_barber == Barb_states.Busy);
result Busyness_of_barber_2 = watchState(Barber_2.state_of_barber == Barb_states.Busy);
result Busyness_of_barber_3 = watchState(Barber_3.state_of_barber == Barb_states.Busy);
result Served_by_barber_1 = getValue(Barber_1.people_served);
result Served_by_barber_2 = getValue(Barber_2.people_served);
result Served_by_barber_3 = getValue(Barber_3.people_served);

sequence Интервал_прихода = double exponential(123456789, 1/30.0);

sequence Длительность_обслуживания = double uniform(123456789);

sequence Тип_клиента = Client_type histogram(123456789) {
    Client_type.Type1 1.0
    Client_type.Type2 5.0
}

int testFun(int test1, double test2) {
    return 1;
}

int table testTable (int[1 .. 2] a) {
    1 1
    2 2
}

int list testList (int[1 .. 2] a) {
    1 = 1
}

Реорганизовать файлы проектов

@aurusov
@k-alexandrovsky
@LeKaitoW
@kstrizhov

Сейчас:

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

Я думаю, нужно как минимум вынести всю прослойку в отдельный проект, и, может быть, внутри проектов как-то разбить все более логично по пакетам.

Как минимум что должно быть по проектам:

  • rdo - без изменений
  • rdo.lib - только классы, которые необходимы рантайму. Database в их числе.
  • rdo.ui.helpers - как назвать? Сделать отдельным проектом или пакетом внутри проекта ui? Туда отправятся всякие трейсеры и прочая прослойка между базой данной и уи. Т.е. все классы, что предоставляют информацию о ходе моделирования для уи.
  • rdo.ui - без изменений

Если есть какие мнения на этот счет, высказывайте.

Цитируя начало обсуждения:

Что значит не хочется. Если нода - это часть базы, то текущее решение выглядит ошибочным, потому что не отражает эту мысль. Видимо неясно выразился. Я не предлагаю сделать Node частью класса Database. Предлагаю, чтобы все классы базы жили в ПАПКЕ database.

ru.bmstu.rk9.rdo.lib.database.Query
ru.bmstu.rk9.rdo.lib.database.Node
ru.bmstu.rk9.rdo.lib.database.Subscriber

и т.д.

Исправить схему добавления записей об изменении ресурсов в базу данных

Начало обсуждения тут: #25 (comment)

Вкратце:

  • мы не умеем детектить изменения нерелевантных ресурсов, и, например, писать по ним трассироку и делать графики
  • мы можем лишний раз записать в трассироку релевантный ресурс, которые НЕ поменялся в паттерне

Ленивый трассировщик

@aurusov
@k-alexandrovsky
Нужно проверить, приведет ли ленивая имплементация трассировщика (конвертация базы данных в строки на ходу при прокрутке) к увеличению производительности. Наверное, надо будет все равно вести какой-то небольшой кэш в последними распаршенными строками.

Сразу несколько проблем:

  1. Некоторые записи в базе данных не являются самодостаточными в смысле их конвертации в трассировочную строку. То есть нужно знать информацию из каких-то предыдущих записей, чтобы их распарсить.
    Для основного трассировщика это выражается только в отсутствии номера текущей точки принятия решений в записях dpt search (кроме SB) - но это легко поправить (и, я думаю, надо это сделать в любом случае).
    А вот для легаси-трассировщика всё намного сложнее, потому что он для совместимости добавляет фейковые записи и всё такое. Либо придется его серьезно рефакторить, либо как-то умудриться работать с ним в старом режиме.
  2. При поиске по трассировке придется парсить все записи заново.
  3. Реализовать всякие фишки вроде фильтров будет сложнее.
  4. Что тогда передавать в ContentProvider RDOTraceView? Получается, что сырой список записей из библиотеки. Мне это кажется не совсем идеологически верным, но других способов не вижу.

Навести порядок в способах хранения структуры модели

Структура модели хранится сейчас с частичным дублированием в трех местах:

  • в базе данных в формате JSON: link
  • в симуляторе как объект класса ModelStructureCache: link - изначально класс был создан для более быстрого доступа к структуре модели из трассировщика.
  • небольшие части так же дублируются в индексах базы данных: link

Необходимо навести здесь порядок и хранить всю структуру в одном месте или, как минимум, четко разделить, на каком этапе используются обращения к структуре в формате JSON, а на каком к кэшированной структуре.

Начало обсуждения тут:
LeKaitoW#37 (comment)

Связана с:

Tracer: проверять traceConfig при формировании вывода

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

Унифицировать схему нотификации гуи об изменениях в базе данных

Связана с #27

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

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

Выбор формата трассировки

@aurusov
@k-alexandrovsky
@AlexChernov
@kstrizhov

Опрос по поводу наиболее читаемого и приятного для глаза формата трассировки.
Ниже для наглядности будут скрины с различными вариантами (пока не затрагивающими dpt), напишите, что вам нравится/не нравится и почему.
Если есть что добавить от себя, говорите, я попробую и выложу соответствующий скрин.

Основные предметы обсуждения:
А. Круглые или фигурные скобки вокруг параметров ресурса.
Б. Если фигурные, надо ли писать знак равенства.
В. Круглые или фигурные скобки вокруг релевантных ресурсов паттерна.
Г. Дополнительные пробелы/табы между "хедером" и основным содержанием.
Д. Обрубать ли параметрам типа real ".0", если они целые?

Так что вот различные несколько наиболее приятных, на мой взгляд, глазу вариантов (модель приблизительно соответсвует шаблонной модели с временными ресурсам в старом РДО).

  1. Всё фигурными скобками с двойным пробелом перед параметрами.
    2
  2. Всё круглыми без двойного пробела.
    3
  3. Ресурсы фигурными со знаком равенства, результаты просто со знаком равенства, паттерны в круглых. Идея такая, что ресурс по смыслу близок к структуре, паттерн к функции, а результат просто переменная.
    1

Первые два варианта более единообразные, но третий лучше отражает смысл данных. Я лично за него.

Использовать сторонние библиотеки как jar вместо копипасты исходного кода

@aurusov
@k-alexandrovsky

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

Планы на дипломный проект

@aurusov

Собственно, что я планирую сделать за лето:

  1. Доделать документацию (в частности оформление - встроить в эклипс и т.п.)
  2. Порефакторить весь код. Туча тасок в моем форке.
  3. Настроить систему тестирования.

Какие я вижу задачи на осенний семестр:

  1. Прореживание графиков (вроде обсуждали).
  2. Есть идея запилить своп базы данных на диск, чтобы мы не умирали по памяти при моделировании. Что думаете на этот счет?
  3. Нужно провести переход на иксбейз.

Трассировка

Копипаст требований из старой таски:

Требования

  1. [suspended] Несколько окон трассировки на экран
  2. [suspended] Прореживание по различным критериям
  3. [done] Спроектировать читаемый вид трассировки
  4. Научиться выводить трассировку по графу в виде графа
    Выполняется @kstrizhov.
  5. [done] Трассировка должна динамически дополняться
  6. [done] Поддержка текстового поиска, в том числе по регулярному выражению

Выбрасывать исключения вместо возврата null'ов в случае внутренних ошибок

Начало обсуждения тут:
LeKaitoW#37 (comment)

Мне кажется, лучше и проще всего будет во всем местах, где return null означает внутреннюю ошибку в системе, писать

throw new RuntimeException("<описание внутренней ошибки>")

а все проверки

if (returnValue != null)

в методах, куда этот null мог вернуться, убрать. Тогда в случае внутренней ошибки ее будет максимально легко отловить и понять.

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

@k-alexandrovsky
Кирилл, что думаешь по этому поводу?

Обратить внимание:
LeKaitoW#59 (comment)

Legacy-Tracer

Связана с:

@aurusov
@k-alexandrovsky

Необходимо уметь выводить трассировку в формате, эквивалентном формату старого РДО.

Тут есть три опции:

  1. LegacyTracer - отдельный модуль. Работает непосредственно с базой данный и её байтбуфером и не зависит от трассировщика.
    [+] Неважно, в каком формате будет сохранять строки основной трассировщик (модульность - наше всё)
    [+] По сути, он практически написан, за небольшими исключениями это текущий основной трассировщик.
    [-] Предположительно будет копипаста и, наверняка, немало.
  2. LegacyTracer - отдельный модуль. Работает со строковым выводом трассировщика.
    [+] Если изменения не будут колоссальными, то модуль будет небольшим и достаточно простым.
    [-] Модуль зависит от основного трассировщика и его придется менять, если изменится вывод последнего.
    [-] В какой-то момент изменения в трассировке могут оказаться такими, что нам будет недостаточно JSON-структуры модель для конвертации. Тогда нам придется обращаться к байт-буферу и это полное фиаско.
  3. LegacyTracer - это особенный режим работы основного трассировщика.
    [+] Вероятно, добавит меньше всего кода.
    [+] Практически не будет копипасты.
    [-] Код трассировщика будет замусорен легаси-кодом.

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.