Giter VIP home page Giter VIP logo

mraggi / discreture Goto Github PK

View Code? Open in Web Editor NEW
67.0 8.0 7.0 5.46 MB

A modern C++ library for efficiently and easily iterating through common combinatorial objects, such as combinations, permutations, partitions and more.

License: MIT License

C++ 82.24% CMake 3.76% Shell 0.77% TeX 11.69% Meson 0.74% Sage 0.80%
combinatorial-objects combinatorics stl-containers combinations combinatorial-search partition permutation motzkin

discreture's Introduction

Build Status Coverage Status license Codacy Badge

Discreture

This is a modern C++14 library designed to facilitate combinatorial research by providing fast and easy iterators to a few combinatorial objects, such as combinations, permutations, partitions, and others. The idea is to have discreture's lazy containers interface resemble the STL containers as much as possible, by providing the standard ways of iterating over them.

In addition, many of the algorithms described in the standard library work as-is in these containers, as if the containers were marked as const.

This library is provided as a header-only library and has been tested on Linux. Other operating systems might work. Let me know if you find any issues!

Quick preview:

#include <iostream>
#include <discreture.hpp>

using discreture::operator<<;

int main()
{
    for (auto&& x : discreture::combinations(5,3))
        std::cout << x << std::endl;
    
    return 0;
}

The above code would produce the following output:

0 1 2
0 1 3
0 2 3
1 2 3
0 1 4
0 2 4
1 2 4
0 3 4
1 3 4
2 3 4

You need to compile with the -std=c++14 flag: g++ -std=c++14 -O3 main.cpp

Installation

Discreture is a header-only library and its only dependency is boost. Programs using it need to be compiled with c++14 or later. Tested with gcc 5.4.0 and clang 3.9.0 and up. Other compilers might work too.

To use, simply make sure your programs have access to the .hpp files (all files inside "include" dir). Just copy them to your project's include folders or tell your compiler where to look.

Install with package manager

For easy integration, Discreture can be installed through VCPKG. Simply run vcpkg install discreture.

Pre-requisites for compiling

Nothing needs to be compiled. But if you wish to build examples, benchmarks and tests, these are the pre-requisites.

  • A C++ compiler (i.e. gcc or clang)
  • boost (header files. Specifically: iterator_facade)
  • cmake or meson
  • git (only for downloading the directory. You can also download it directly from gitlab/github)
  • Google's Test Framework (for building unit tests only).

Starting from v1.9 we also support the much faster meson build system instead of cmake, but shall continue to maintain support for cmake.

Installing pre-requisites in Ubuntu Linux and derivatives

sudo apt-get install libboost-all-dev git build-essential cmake

Installing pre-requisites in Arch Linux and derivatives

sudo pacman -S boost git gcc cmake

Installing pre-requisites in mac OS

First, make sure HomeBrew is installed. Then in a terminal do:

brew install gcc cmake git boost

Install discreture

To do a system-wide install, do the standard cmake/make dance:

git clone --recursive https://gitlab.com/miguelraggi/discreture.git
cd discreture
mkdir build
cd build
cmake ..
make
sudo make install

This will install the necessary headers under /usr/local/ by default. If you wish to install to some other directory, replace the cmake .. above by something like cmake .. -DCMAKE_INSTALL_PREFIX=/usr/.

There are three options: BUILD_EXAMPLES, BUILD_TESTS and BUILD_BENCHMARKS. To use discreture you don't need to compile anything, but if you wish to, you can compile examples, tests and benchmarks by replacing the cmake .. part by:

cmake .. -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON -DBUILD_BENCHMARKS=ON

By default, nothing is built, as discreture is a header-only library.

Trying the examples

After compiling the examples (with cmake .. -DBUILD_EXAMPLES=ON), try for example running:

./combinations 5 3

This will output all combinations of size 3 from the set {0,1,2,3,4}.

Or try for example

./combinations "abcde" 3

will output all subsets of size 3 of "abcde".

There are many other example programs there. Play with them.

How to start using the library

To use the library, after compiling, just add #include <discreture.hpp> to your project and make sure you are compiling in c++14 mode (or later). With the GCC compiler this can be done by compiling like this: g++ -std=c++14 myfile.cpp. You can include only part of the library, say, combinations, by adding #include <Discreture/Combinations.hpp> for example.

Combinatorial Objects

Within this library, one can construct a few combinatorial objects, such as:

  • Combinations: Subsets of a specific size of either a given set or {0,1,...,n-1}:
    • Example: {0,3,4}, {0,1,5} in combinations(6,3)
    • Example2: {'a','b','c'}, {'a','c','d'} in combinations("abcdef"s,3)
  • Permutations: A permutation of a collection is a reordering of all the elements of C.
    • Example: [0,1,2], [2,0,1] in permutations(3)
    • Example2 : ['a','b','c'], ['c','a','b'] in permutations("abc"s)
  • Partitions: Numbers that add up to a given number.
    • Example: {6,4,1}, {3,3,3,1,1} in partitions(11)
  • Set Partitions: Partitions of {0,...,n-1} into disjoint sets.
    • Example: { {0,2}, {1,3} } in set_partitions(4)
  • Multisets: How many to take of each index?
    • Example: {2,1,3}, {0,1,1} in multisets([3,1,3])
  • Dyck Paths: From (0,0) to (2n,0) but y is never negative and always goes either up or down.
    • Example: [1,1,-1,1,-1,-1] in dyck_paths(3). Note no partial sum is less than 0.
  • Motzkin Paths: Like dyck paths but allowing 0's.
    • Example: [1,0,-1,1,1,-1,0,-1] in motzkin_paths(9)
  • Integer Intervals: A (lazy) closed-open interval of integers.
    • Example: integer_interval(4,8) = {4,5,6,7}
  • Arithmetic Progression: A (lazy) set of the form {a,a+d,a+2d,...,a+kd}.
    • Example: {1,4,7} in arithmetic_progression(1,8,3)

All follow the same design principle: The templated class is called SomethingOrOther<...>, with CamelCase notation, and there is either a function or a typedef for the simplest template parameters. However, most of the time you'll be using the small_case_notation version, which either is a typedef or a function with sensible parameters.

For example, partitions is a typedef of Partitions<int, vector<int>>, but combinations is a function with two versions, depending on the arguments. It returns either an object of type Combinations<T, vector<T>> or IndexedViewContainer</*some template parameters*/>, depending on which arguments are passed. Note that there is currently no support for detecting repeats, so combinations("aabc"s,2) has ab two times. If you need this functionality, let me know and I'll do my best to implement it quickly.

Some tests show that on different machines different types produce faster code, so even if you don't need numbers bigger than 127 it might be a good idea to use int or long rather than char.

Basic usage

auto X = discreture::combinations(30,10); //all subsets of size 10 of {0,1,2,...,29}
for (auto&& x : X) 
{ 
    // x is of type const vector<int>&, so anything that works with 
    // const vector references also works on x, such as indexing, iterating, etc. x[3], etc.
}

Reverse iterators are defined too.

discreture::Combinations<short> X(30,10);
for (auto it = X.rbegin(); it != X.rend(); ++it) 
{ 
    auto& x = *it;
}

But of course there is a simpler way:

auto X = permutations(10);
for (auto&& x : reversed(permutations)) 
{ 
    // do stuff to x.
}

Combinations, Permutations and Multisets are random-access containers so something like this works too:

auto X = discreture::combinations(30,10);
auto comb = X[10000]; //produces the 10,000-th combination.

Warning: Please note that it is much slower if one plans to actually iterate over all of them à la

for (int i = 0; i < X.size(); ++i)
{ 
    // use X[i] 
}

so don't do that.

However, iterator arithmetic is implemented, so one could even do binary search on X with the following code:

#include <algorithm>
// ...
auto X = discreture::combinations(30,10);
std::partition_point(X.begin(), X.end(), predicate);

where predicate is a unary predicate that takes a const combinations::combination& as an argument and returns true or false, in a way that for all the first combinations it returns true and the last ones return false.

This is also useful to use many processors at once. See tutorial_parallel.cpp under "examples" on how to do this.

Tutorial

Here is a quick mini-tutorial. See the examples for more on usage. Check the files under examples for a more complete tutorial on how to use the library. Maybe start with the file called tutorial.cpp and then read the others in any order.

Combinations example

After installing, let's start by creating a file called "combinations.cpp" and adding the following content:

#include <iostream>
#include <string>
#include <discreture.hpp> // just include everything

using namespace std::string_literals;
using discreture::operator<<;

int main()
{
    for (auto&& x : discreture::combinations("abcde"s,3))
    {
        std::cout << x << std::endl;
    }
    return 0;
}

This prints out: a b c a b d a c d b c d a b e a c e b c e a d e b d e c d e

Partition example

For example, suppose you wanted to see all ways to add up to 20 with at most 6 numbers so that all numbers are squares. You can do:

#include <iostream>
#include <Discreture/Partitions.hpp>
#include <Discreture/VectorHelpers.hpp>

using discreture::operator<<;

bool is_perfect_square(int n) 
{
    if (n < 0)
        return false;
    int r = round(sqrt(n));
    return n == r*r;
}

int main()
{
    auto X = discreture::partitions(20,1,6);
    for (auto&& x : X)
    {
        if (std::all_of(x.begin(), x.end(), is_perfect_square))
            std::cout << x << std::endl;
    }
    
    return 0;
}

Then compile with the command g++ -O2 -std=C++14 main.cpp -o out and run ./out. It should produce the following output:

9 4 4 1 1 1
16 1 1 1 1
4 4 4 4 4
9 9 1 1
16 4

Combinations find_if and find_all

Combinations is the most mature part of the library, and some tree-prunning (backtracking) functions to find a specific combination have been implemented:

#include <iostream>
#include <vector>
#include <discreture.hpp>

using discreture::operator<<;

int main()
{
    auto X = discreture::combinations(10,3);
    
    // T will be an iterable object whose elements are the combinations that satisfy the predicate specified by the lambda function.
    // In this case, the lambda checks that the next to last element divides the last element.
    // The elements of T will therefore be the combinations for which every element is a divisor of the next element.
    auto T = X.find_all([](const auto& comb) -> bool
    {
        int k = comb.size();
        
        if (k <= 1) return true;
        
        if (comb[k-2] == 0) return false;
    
        return (comb[k-1]%comb[k-2] == 0);
    });
    for (auto&& t : T)
        std::cout << t << std::endl;
}

Prints out:

1 2 4
1 2 6
1 2 8
1 3 6
1 3 9
2 4 8

These are all combinations for which every element is a divisor of the next element. This is not merely a filter: only combinations which satisfy the partial predicate (given by a lambda function) are further explored, in a branch-and-cut way.

Getting that last drop of speed

By default, Combinations<T>::combination (and many others) is a typedef of std::vector<T>, which allocates memory on the free store. If you really need the utmost performance (although tests show meager gains at best), this may be changed to any random access container with the same interface as vector. A good choice is boost::containter::static_vector<T,K> (or even boost::containter::small_vector<T,K>), where K is the biggest size you'll need.

Some sane defaults for K have been set in combinations_stack, permutations_stack, dyck_paths_stack, which are just typedef's of basic_combinations<int,boost::containter::static_vector<int,K>> and so on.

So for example, the following code iterates over all combinations of size 3 of {0,1,...,6} in a slightly faster way than discreture::combinations.

#include <Discreture/Combinations.hpp>

int main()
{
    for (auto&& x : discreture::combinations_stack(7,3))
    {
        //do stuff with x
    }
}

This only works if combination size (e.g. 3) is less than 32. If for some reason you need combination sizes bigger than 32, just use something like this:

#include <Discreture/Combinations.hpp>

int main()
{
    using my_fast_big_combinations = discreture::basic_combinations<int,boost::containter::static_vector<int,50>>;
    for (auto& x : my_fast_big_combinations(52,50))
    {
        //do stuff with x
    }
}

Each of permutations, dyck_paths, etc. has its corresponding "stack memory" version: permutations_stack, dyck_paths_stack, etc. with their own custom set limits. If you are going to need monstrous objects (like permutations of size 17 or more (why?!)) and for some reason can't allocate a few more bytes (again: why?!), just typedef as in the previous example (or use regular old fashioned permutations).

combinations::for_each and multisets::for_each

For combinations and multisets in particular, there is one last possible speedup: use for_each, like in the following example.

#include <Discreture/Combinations.hpp>

void f(const discreture::combinations_stack::combination& x)
{
    // Do stuff to x
}

int main()
{
    discreture::combinations_stack X(34,17);
    X.for_each(f);
}

This code applies f to every element of X, and it's about 30% faster (see benchmarks) as doing iteration, up to size 17. More than that and for_each falls back on manual iteration. Of course, f can be a lambda or a functor too.

Benchmarks.

Combinations benchmarks

Two different libraries were tested: GNU Scientific Library (GSL) and euler314's library.

The GNU Scientific Library is a well-known and mature library. For more information, check their website.

Euler314's library (unnamed as far as I know) can be found here and provides similar functionality to discreture, although discreture provides many more features.

Iterating over all combinations of size n/2 over a set of size n took the following time (lower is better):

discreture::combinations vs GSL combinations

The GSL code used was the following:

gsl_combination * c;

c = gsl_combination_calloc (n, n/2);
do
{
    DoNotOptimize(*c);
}
while (gsl_combination_next (c) == GSL_SUCCESS);
gsl_combination_free (c);    

The same code using euler314's library:

auto end = combination_iterator<int>();

for (auto it = combination_iterator<int>(n, n/2); it != end; ++it)
{
    DoNotOptimize(*it);
}

Compare to the following (beautiful) code, using discreture:

for (auto&& x : combinations(n,n/2))
{
    DoNotOptimize(x);
}

Or the for_each variant of discreture:

auto X = combinations(n,n/2);
X.for_each([](const combinations::combination& x)
{
    DoNotOptimize(x);
});

Note 1: GSL and euler314 iterates in the same order as lex_combinations. Not really sure why euler314's (yellow) is a tiny bit faster than lex_combinations. The code does essentially the same thing (although it was written independently).

Note 2: DoNotOptimize(x) is just a way to tell the compiler to not optimize away the empty loop. Taken from google benchmarking tools.

If you'd like to see other benchmarks, let me know.

Discreture vs Sagemath

This comparison isn't very fair (C++ vs python). On the same system, iterating over all (24 choose 12) combinations, sage takes 12.2 seconds. Discreture takes approximately 0.005 seconds. No point in graphing that.

Benchmarks

The following benchmarks were done on a i7-5820K CPU @ 3.30GHz, using Manjaro Linux with gcc 8.1.1.

The important column is speed. Higher is better. It means "how many (combinations/permutations/etc) were generated in one second" (basically, # processed / Time). Note the exponents.

discreture::benchmarks

Noteworthy: for_each can be really fast if using "stack" version for combinations (i.e. combinations_stack). Standard iteration was slower with _stack version on both combinations and combinations tree reverse for some unknown reason.

Run your own benchmarks (with colors!) by building with cmake -DBUILD_BENCHMARKS=ON and running

./discreture_benchmarks

Parallel benchmarks

Random access containers (currently: combinations, lex combinations, permutations and multisets) can be used easily in a multithreaded environment. Here are some benchmarks.

discreture::benchmarks_parallel

Run your own benchmarks with

./parallel_benchmarks

By default, this runs on all available CPUs. Optionally specify the number of threads like so:

./parallel_benchmarks 4

Acknowledgements

  • Manuel Alejandro Romo de Vivar (manolo) for his work on dyck paths, motzkin paths, and his contribution to partition numbers.

  • Juho Lauri for suggestions on improving lexicographic combinations and many interesting discussions on combinations.

  • César Benjamín García for suggesting the name "discreture".

  • Samuel Lelièvre for his help installing in macOS.

  • You: for reading this.

Contributing

First: If you use discreture for your own purposes, let us know!

Optimizations, suggestions, feature requests, etc. are very welcome too.

Otherwise, you can contribute in many ways:

  • Provide bug reports if you encounter them, or even request some features if you'd find them helpful. We'll do our best to provide them.
  • Help us package for the various distros (maybe even other OS's).
  • If you are a developer (or aspiring developer) looking to get your feet wet, here is the current status of the project.
Container Forward Iteration? Reverse Iteration? Random Access?
Combinations ✓✓
Permutations
Multisets
Dyck Paths
Motzkin Paths
Partitions
Set Partitions

Just let us know which algorithm you'd like to implement for something that is missing a checkmark ✓.

Or choose another combinatorial object of your liking.

For example, two (likely) easy ones to implement would be "non-decreasing sequences" (also known as combinations with repetition) and compositions, which are probably pretty easy to implement if you wish to get your hands wet.

We'll even help you set up all the boilerplate code. You just need to say what next means for your combinatorial object.

Finally, partitions, set partitions and motzkin paths are both pretty slow right now (well, relatively: only at a few tens of millions of objects generated per second, as opposed to hundreds or even thousands of millions per second, as in the case of combinations for_each). Maybe you have an idea to make them faster.

discreture's People

Contributors

chiumichael avatar kspinka avatar mattpaletta avatar mraggi avatar remz1337 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

discreture's Issues

find_all and for_each

Is it possible to get a lazy iterator back from find_all or pass a predicate to for_each. I would like to be able to bail out of the iteration if a condition is met which in the case of large N results in significant time savings.

combination in parallel with std for_each

Hello,

I have to iterate over 5 billions+ combinations so I would like to do it in parallel using the STL for_each loop with parallel execution, but I get an error saying Parallel algorithms require forward iterators or stronger

auto comb = discreture::combinations(_population, 2);

std::for_each(std::execution::par, comb.begin(), comb.end(), [&](auto i) {
	DoStuff();
});

Anyway I could make it work with STL for_each?

Thanks

Port to R package

This is a feature request.

It would be nice to be able to link to this library as a dependency in R. Also wouldn't be too hard; just make an R package with the header impl. added to inst folder.

Examples of package that do this:

Linking to Boost w/ R can be handled via the BH package.

possible repeat combination is the same item value is repeated in the container

std::vector<int> test = {1,1,2,3,4,5};
auto count = 0;
for (auto&& x : discreture::combinations(test, 3))
    std::cout << ++count << " -- "<< x << '\n';

1 -- 1 1 2
2 -- 1 1 3
3 -- 1 2 3
4 -- 1 2 3
5 -- 1 1 4
6 -- 1 2 4
7 -- 1 2 4
8 -- 1 3 4
9 -- 1 3 4
10 -- 2 3 4
11 -- 1 1 5
12 -- 1 2 5
13 -- 1 2 5
14 -- 1 3 5
15 -- 1 3 5
16 -- 2 3 5
17 -- 1 4 5
18 -- 1 4 5
19 -- 2 4 5
20 -- 3 4 5

Feature request: Product of 2 sets

Hello,

I'm wondering if you could have a look at implementing product combinations?

Let's say we have 2 sets, { A, B } and { X, Y }, product gives this:
A X
A Y
B X
B Y

(Like in the cppitertools package, but I would like to use your divide_work function to parallelize the work)
Thanks!

Combinations avoiding patterns

Thank you for the great library.

Given your experiment, is there (or is it possible to implement) any algorithm or container interface that enumerates all combinations in a pseudo-random order (or at least avoiding patterns) without resampling?

Of course, apart from the impracticable alternative of keeping all possible combinations in memory and shuffling them.

This can be very useful for hypothesis testing and generate-and-test algorithms.

For instance, if we had these combinations:

$c_1$ $c_2$ $c_3$
0 0 0
0 0 1
0 0 2
0 1 0
0 1 1
0 1 2
0 2 0
0 2 1
0 2 2
1 0 0
1 0 1
1 0 2
1 1 0
1 1 1
1 1 2
1 2 0
1 2 1
1 2 2
2 0 0
2 0 1
2 0 2
2 1 0
2 1 1
2 1 2
2 2 0
2 2 1
2 2 2

I'm wondering if there would be a container interface that would return these combinations in random order (or at least an order that avoids patterns) but still being a container interface (without keeping all combinations in memory and shuffling a list of indexes).

In statistics, we could use that for randomized experiment control. In generate-and-test heuristics, this would be a systematic way of enumerating the search space without getting stuck in a region of the objective function.

Thank you.

Accessing IndexedViewContainer as a LazyContainer<SOMECLASS>

What would be the best way to write something like

for (auto&& x : discreture::combinations(_N, 3)) {
myclass = converter(x);
}

or even better

for (auto&& x : mywrapper(_N, 3)) {
myclass = x;
}

where mywrapper could be something like:

class mywrapper {
private:
decltype(..... discreture::lex_combinations...) lc;
public:
size_type size() const { return lc.size();

but I just find hard to write the signature of

converter(x);

thanks

Visual Studio little issue

Hi mraggi, I just tested some of your code examples on Visual Studio 2017, worked like a charm.
You just forgot to type the "typename" keyword before "decltype(d)::param_type;" in the FloatType random_real() function, in the Probability.hpp header. Thank you for your Excellent code work!
``

Installation instructions for macOS

Installing Discreture under macOS

Prerequisites

The prerequisites are:

  • Apple's Developer tools

    • Check if already installed. The result should look like:

      $ xcode-select --version
      xcode-select version 2339.
      
    • If not installed, install it:

      $ xcode-select --install
      

We also need the GNU C++ compiler (the one offered by Apple might not work),
the CMake build tools, and the Boost library.

  • We get those from Homebrew.

    • Check if Homebrew is already installed

      $ brew --version
      Homebrew 1.4.3
      Homebrew/homebrew-core (git revision 68ad7; last commit 2018-01-08)
      
    • If not, install it (see https://brew.sh), then run

      $ brew install gcc
      $ brew install cmake
      $ brew install boost
      
  • Once this is done, check your tools.

    • Check version of g++, the result should look like:

      $ g++-7 --version
      g++-7 (Homebrew GCC 7.2.0) 7.2.0
      Copyright (C) 2017 Free Software Foundation, Inc.
      This is free software; see the source for copying conditions.  There is NO
      warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      
    • Check version of cmake, the result should look like:

      $ cmake --version
      cmake version 3.9.1
      
    • Check that boost's facade iterator is present:

      $ ls /usr/local/include/boost/iterator | grep facade
      iterator_facade.hpp
      

Building Discreture

Decide where to perform the build. In the example below we do this in
the user's home folder. To install elsewhere, replace the first cd
by cd /path/to/elsewhere, replacing /path/to/elsewhere/ by the path
to the folder of your choice.

We change directory to the place we want, we clone the Discreture
repository from GitHub; this creates a folder called discreture.
We change to that folder, create a folder called build, change to
that folder, set the CXX environment variable to g++-7 to use
the GNU C++ compiler we installed via Homebrew, run CMake then Make.

$ cd
$ git clone https://github.com/mraggi/discreture
$ cd discreture
$ mkdir build
$ cd build
$ export CXX=g++-7
$ cmake .. -DBUILD_EXAMPLES=ON -DBUILD_BENCHMARKS=ON
$ make

Once this is done, the list of files in the folder build should look like:

$ ls
CMakeCache.txt            combinations              discreture_benchmark      number_range
CMakeFiles                combinations_reverse      dyck                      partitions
Makefile                  combinations_tree         motzkin                   permutations
cmake_install.cmake       combinations_tree_reverse multisets                 setpartitions

We can try out discreture's combinations enumerator:

$ ./combinations 4 3  
0 1 2
0 1 3
0 2 3
1 2 3

and run the benchmarks

$ ./discreture_benchmark

Completing the installation

Finally, we complete the installation by running

$ sudo make install

This might require typing an admin password.
As a result, the Discreture headers will be copied to
the appropriate location (by default, /usr/local/include).

port to vcpkg

Just to let you know I'm trying to port this to vcpkg package manager. The PR is here:
microsoft/vcpkg#15967

Once it's approved, it would be nice to include a line or 2 about it in the installation section of the readme :)

Thanks

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.