Coder Social home page Coder Social logo

phryxia / auto-differentiator Goto Github PK

View Code? Open in Web Editor NEW
0.0 0.0 0.0 942 KB

Implement simple mathematical parser and provide some utilities like differentiation.

Home Page: https://parser-heaven.netlify.app/

HTML 0.28% TypeScript 97.70% CSS 1.35% JavaScript 0.67%
boolean-algebra math parser typescript

auto-differentiator's Introduction

Introduction

FE Developer always chaising fun stuffs. Followings are my interests

  • First order logic and SAT solver
  • Mathematical optimization
  • Functional programming
  • Heavy type inference using TypeScript like this one

I'm the original designer and author of the project dxf-json (Not dxf-parser). Currently I'm participating in that project as sub-maintainer.

I love writing small snippets which was targeted for mathematical things. If you're interested, please visit gist page.

Useful Snippets

내가 자주 쓰는 것들 모음집

Utilities

React

Algorithm & Data Structures

Math

auto-differentiator's People

Contributors

phryxia avatar

Watchers

 avatar  avatar  avatar

auto-differentiator's Issues

The uncomfortable question: Domain

At first I just want to know whether two silly expressions are same or not. But as I study more and more, it was not silly problem.

In most of the case, implicit domain assumption works. For example when we compare x and (x^0.5)^2, we assume their start domain is same and it's the smallest one(In this example, x >= 0). Since I defined that valid optimization never shrinks the domain, it was enough.

But following questions were arised.

ln(x) * ln(y^2) vs ln(x^2) * ln(y)

If we apply the same approach of previous things, they should be same since our assumption starts from the smallest domain. But how should I do that? Pulling the exponent out shrinks the domain theoretically, therefore it's not valid. But if we know the start domain explicitly (like (x > 0, y > 0), we may pull them out.

Even we ignore the case of different domain, same domain can cause problem.

x = (x^2)^0.5 if x >= 0 otherwise they are different

There is no way to decide whether some transformation shrinks the total domain or not with implicit assumption.

To handle this, we need following things to prepare.

  • Represent the set using equality / inequality.
  • Decide whether a set is subset of others or not.

Optimization of logarithm

Log has two input- one with the base and the other with the inner value. Even if the base is just e, there are some hard cases.

Basic case: base is e

Log has some weird properties. Summation of logs is same with inner-value-multiplied single log. And multiplication to the log is same with power to inner value.

Actually summation is simple. Summation gather the nodes at the same depth and makes them more optimizable. Summation gathering reduce the problem of log into problem of multiplication.

log((x + 1)/2) + log(2*(x - 1)) => log(x^2 - 1)

Note that log-summation may expand the valid domain. log(A) + log(B)'s domain is A > 0, B > 0 while log(A * B)'s domain is A * B > 0 which is bigger than the first one.

Multiplication makes me think more. Assume x+y > 0, a + b> 0.

2 * log(x + y) * log(a + b) = log(x^2 + 2*x*y + y^2) * log(a + b) = log(x + y) * log(a^2 + 2*a*b + b^2)

Therefore putting the coefficient into exponent seems to be a bad idea. Than we have to factorize inner values. Since this is not general factorization, we can count the used variables and the coefficients and the exponent of them.

But there is one more problem. Pulling out the exponent shrinks the domain.

log(x^2) ≠ 2 * log(x)

This makes one more important question.

Should we somehow implement handling 'domain'? How should we compare the equivalency with out user input about domain?

Things to check for hyper trigonometry

Equivalence Test

Hyper trigonometric functions are actually can be expressed without them.

Consider the sinh function which has popular three equivalent forms.

image

Determining the equivalence of hyper trigonometric functions is exactly same problem to that of primary operation (+, -, *, / and ^) By assuming primary operation equivalence determination works ideally, all we have to do is just compare the equivalence of basic form. (For sinh, it can be one of the expressions in above image)

Showing intermediate process while doing differentiation

This idea had been came from one of my friends. This will helps many students all around the world.

Note that this is more than library, because it is level of services. Therefore I'll not include this into library code. Even though, it's really helpful so I'd like to implement it on the demo page.

But we have to define "What is the intermediate process" first. For example, consider x^2 + 2*x + 1. It's outcome should be 2*x + 2. What should be intermediate process of this derivation?

Doing very simple one can be very annoying if I include super low-level process like using definition of diffentiation. (See this)

So I'd like to think "what is implicit process". Differentiating trivial things are easy like x, sin(x) or x^n. Consider more complex case like sin(ln(x))^2. It's derivative is 2 * sin(ln(x)) * cos(ln(x)) / x. In this case, it's not clear what term was came from where. It looks like magic if you're not familiar with differentiation.

But if we wrap internal expression like this:

We have to compute the derivative of E(x) = sin(ln(x))^2
Let f(x) := sin(ln(x))
Then E(x) = f(x)^2
Using chain rule and knowledge of differentiation of polynomial ensures that, E'(x) = 2 * f(x) * f'(x) ... (1)

Now we have to compute the derivative of f(x) = sin(ln(x))
Let g(x) := ln(x)
Then f(x) = sin(g(x))
Using chain rule and knowledge of trigonometry, f'(x) = cos(g(x)) * g'(x) ... (2)

Now we have to compute the derivative of g(x) = ln(x)
Using knowledge of logarithm, g'(x) = 1/x

Plug g'(x) to (2) so that f'(x) = cos(ln(x)) / x.
Plug f'(x) to (1) so that E'(x) = 2 * sin(ln(x)) * cos(ln(x)) / x.

By deriving them recursively, it's very clear to comprehend. In this example, we can define intermediate process as "children derivations of given node", We can think of this in pseudocode like this:

function showIntermediateProcess(expression):
  show local objective (`We have to compute the derivative of ${expression}`)
  replace child with some function name
  show local derivation using replaced expression
  showIntermediateProcess(child)
  plug result into replaced expression

It's just very simplified idea. More polishing would be needed for further cases. We may hide some duplicated calculation (ex: e^x^2 in sin(e^x^2) + cos(e^x^2)).

Separate the implementation of differentiation of Power

Currently power differentiation assumes both base and exponent are always related to given variable. But this assumption is mathematically illegal.

After defining the expression x^x, it indicates that the function domain is only positive real numbers. Non integer exponent of negative number is not defined. In this assumption, I can perform implicit differentiation using logarithm.

But how about simple polynomial like x^n? Current implementation gives me this:

x^n * (0 * ln(x) + n * 1 / x)

Note that original domain is all real number. So you cannot use above method to this even simplified results are same. This can be more toxic when examining equivalence. x/x is different from 1 as the first one exclude zero from its domain but this power implementation spoils everything.

It's easy to extract the used variable list from any arbitrary expression. By doing it to both lvalue and rvalue, we can branch into three cases.

f(x) ^ g(x)

Current implementation already handle this well. It's result is

f(x) ^ g(x) * (g'(x) * ln(f(x)) + g(x) * f'(x) / f(x))

It's valid since it's domain condition is f(x) > 0.

f(x) ^ c

c * f'(x) * f(x) ^ (c - 1)

It's domain condition differs whether c is integer or not. If c is not integer, domain condition would be f(x) > 0 while the other is x can be any real number

c ^ f(x)

ln(c) * f'(x) * c ^ f(x)

It's domain condition is x is any real number. It doesn't make sense when c is negative.

Any plan for domain determination?

Well I'm interested a little bit, but that might be extremely challenging. Just handling one variable like x is simple. All you have to do is making interval into class, and do the 1D set operations on them.

But what if we have more than one variable? How should I represent any euclidean space in the computer?

One possible idea is return the set of predicates which are expressions themselves. For example, consider this expression:

(x+y) ^ (sin(x) * cos(y))

It's hard to express their domain explicitly. We may need lots of words like inifinite repetition... But I can give you following predicates:

x + y > 0 or (sin(x) * cos(y)) is integer

If someone need to examine any point is valid or not, they can put their value and compare it to zero. On the edge of the set it might be the problem- but why someone tries to evaluate open set's border in the fuzzy floating number world?

But I'm not sure this might be helpful for expected user of this program. So the priority of this topic should be low.

What is mathematical expression?

I was writing #7 and by mistake, I lost everything because of my stupid action: I tried to add Github issue Labels without saving the content. Github threw me another page... But I start to feel- something is missing. It's like I have no strict theoretical foundation of discussing my topics.

So... this is really philosophical question. I learned mathematics for more than 10 years, and I don't have any idea how to express it. Also I graduated the computer science and engineering at my university, I haven't imagined that structural language links with pure mathematics.

In this issue I'll define the model of mathematical entity.

Definition of Mathematical Expression

Let Σ+ be a set of any strings except length of zero. Note that Σ+ is countable. Then there exist a function σ which maps Σ+ into N.

For the further simplicity, we define proj(x, s) as σ(s)-th element of x where xR^∞.

Set of mathematical entities E is a set of triples (s, X, f) where sΣ*, X ⊆ R^∞, f is any function between X and R. Following describes E. We call each triple as mathematical entity, s as its mathematical expression, X as its domain and f as its functional representation.

Number

If s represents a single real number r

then (s, R^∞, f) ∈ E

where f(x) = r.

Grammar

Following EBNF is recognized as number. It's simple number grammar that almost every programming languages support.

number   = [sign], positive
positive = integer, [".", [integer], [exp]]
         | ".", integer, [exp]
exp      = "e", [sign], integer
integer  = digit
         | integer, digit
digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
sign     = "+" | "-"

But there is a problem. Because of the existence of unary operation (see Unary and binary operators), it's impossible to determine whether +, - is sign or unary operator using LL parser.

Therefore we're gonna use positive only and after parsing everything, we convert the parsed number into negative one if there is - unary operator.

Variable

If s represents a single word

then (s, R^∞, f) ∈ E

where f(x) = proj(x, s)

To explain this simple, it is just variable itself. If your variable name is babo then f(x) = babo. This is one of the entry points to manipulate entire E.

Grammar

Ideally, I want to support any human friendly language for variable name, but to simplify the first implementation, let's limit them into small alphabet only.

variable = alphabet | variable, alphabet
alphabet = "a" | "b" | ... | "z"

Now it's time to generate the entire set using them.

Function

Suppose

  • there are some entities, say (s1, X1, f1), (s2, X2, f2), ..., (sn, Xn, fn)
  • function g: D → R is defined for D ⊆ R^n

If s represents a function call of g

then (s = g(s1, s2, ..., sn), X1 X2 ∩ ... ∩ Xn, f) ∈ E

where f is defined as g(f1(x), f2(x), ..., fn(x)).

This is a super powerful generative rule. It uses entire mathematical expressions recursively.

Each functional representation might be constant number, variable or composition of some other functions.

Typical example is sin(x). Note that f(x) is different from f(y). Note that in real implementation, each function name has its own behavior. Many predefined functions will be included.

Currently we only support for built-in functions. User defined function need more than simple expression. It'll create a new programming language, and I don't have any plan to do that.

Grammar

function call      = name, "(", list of variables, ")"
list of parameters = expr 
                   | list of parameters, ",", expr
name               = alphabet
                   | name, alphabet
alphabet           = "a" | "b" | ... | "z"

Parenthesis

If (s, X, f) ∈ E

then ((s), X, f) ∈ E

This describes mathematical expression wrapped with parenthesis. Their functional representation is same.

Grammar

wrapped expr = "(", expr, ")"

Unary and binary operators

Suppose

  • (s1, X1, f1), (s2, X2, f2) ∈ E
  • $ is some unary operator
  • # is some binary operator

Then

  • ($ s1, X1, f(x) = $ f1(x)) ∈ E

  • (s1 # s2, X1X2, f(x) = f1(x) # f2(x)) ∈ E

Following unary operators will be supported.

  • + : positive. (actually it does nothing)
  • - : negative

Following binary operators will be supported.

  • + : addition
  • - : subtraction
  • * : multiplication
  • / : division
  • ^ : exponential

Associative Rule

Suppose

  • (s3, X3, f3) ∈ E

If # is left-first operator, any entity having its string representation as s1 # s2 # s3 is same as the one having (s1 # s2) # s3. Otherwise, it is same as the one having s1 # (s2 # s3).

Every supported binary operators are left-first except ^.

Order

Supported operators obeys mathematical ordering rules.

binary + = binary - < * = / < ^ < unary + or unary -

Grammar

additive       = multiplicative
               | additive, ("+" | "-"), multiplicative
multiplicative = exponential
               | multiplicative, ("*" | "/"), exponential
exponential    = unary
               | unary, "^", exponential
unary          = ["+" | "-"], expr

Full grammar

There is a problem. Since this project is focused on self-implementation, I can only use LL(0) parser. But using above raw grammars is not possible with LL(0) parser.

Actual implementation uses separated lexer which makes tokens of number, string, operators and some other symbols. And doing some tricks like while-loop, following modified grammar can be parsed using LL(0) parser.

expr           = multiplicative
               | additive, ("+" | "-"), multiplicative
               
multiplicative = exponential
               | multiplicative, ("*" | "/"), exponential
               
exponential    = unary
               | unary, "^", exponential
               
unary          = ["+" | "-"], leaf

leaf = number
     | identifier, ["(", list of parameters, ")"]
     | "(", expr, ")"

list of parameters = expr 
                   | list of parameters, ",", expr

// Below uses lexer
number   = integer, [".", [integer], [exp]]
         | ".", integer, [exp]
exp      = "e", [sign], integer
integer  = digit
         | integer, digit
digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

identifier = alphabet
           | identifier, alphabet
alphabet   = "a" | "b" | ... | "z"

Is it even possible to determine whether two arbitrary expression is equivalent?

Before you proceed, please see the model definitions in #8 .

Definition of mathematical equivalence

Let <s, X, f>, <t, X, g> ∈ E.

Two entities are mathematically equivalent on X

iff ∀xX f(x) = g(x)

We're going to use the notation simply fg if they are equivalent. It's important to match each domain, otherwise it doesn't make any sense.

Note that following property is very essential for solving this problem.

  • fgf - g0

Determining mathematical equivalence

If the domain is finite than we can put every values and check whether they are same or not. But in most of cases, the domain is inifinite set.

Another approach is using preserving transformation. For example, f + g is equivalent to g + f then we can use this property to find the path between two expressions. But this is also intractable since the number of preserving transformations is unknown, and even if is known, the state space is inifinite so it cannot be guaranteed to determine in finite time.

Reducing the space

Note that E can be partitioned by grouping the equivalent entities. If there is an algorithm to find representative entity of each partition, then determining equivalence is easy.

We can make the entire problem into smaller one by reducing the space. For example, there are n! ways to express the sum of n different variables, but by sorting them we can reduce the set of every expression summing n different variables into one unique expression.

g0: Every child nodes must be optimized before optimize parent

But it doesn't have to be optimized in every-depth. What it means is this: Consider the small additive tree where x, y, z is not non additive node.

  +
 / \
x   +
   / \
  y   z

Old implementation optimized this like below.

optimize x
optimize y
optimize z
in optimize (y+z)
  try to merge y, z
in optimize x+(y+z)
  try to merge x, y
  try to merge x, z
  try to merge y, z

It was really terrible. Merging n variable requires n*(n-1)/2 comparisons and each comparison uses equivalency comparison, and again it calls optimize recursively for internal terms. I don't want to even estimate its complexity.

Just forget about the old one. If we use same strategy, such tree having total n leaf cause following complexity:

T(n) = n*lg(n) + T(n - 1)
     = n*lg(n) + (n - 1)*lg(n - 1) + T(n - 2)
     = ...
     = n*lg(n) + n*lg(n - 1) + n*lg(n - 2) + ... + n*lg(1)
       - (lg(n - 1) + 2*lg(n - 2) + 3*lg(n - 3) + ... )
     < n*lg(n!) - n*(n+1)/2*lg(n)
     = O(n^2 * lg(n))

Very terrible. New implementation will optimizes them in one batch, so that the complexity can be reduced into O(n * lg(n)). (still equivalent comparison occurs, but this time they are already optimized well)

optimize x
optimize y
optimize z
sort x, y, z
try to merge x, y
try to merge y, z

g1: Every pure-constant-only expressions should be computed

2 * (1 + 3) = 8

g2: In multiplicative and additive expression, child should be ordered by their text rendered form. But constant is exception, and it should be always be first.

a * y * x => a * x * y
x + 1 + e^2 => 1 + e^2 + x

g3: Valid domain of the optimized equation must be equal or large than original domain

Consider this.

x * (x + 1)^0.4

Valid domain of this is x > -1. But if we optimize above equation into below form, it's domain is changed to x > 0.

(x^3.5 + x^2.5)^0.4

This is much smaller than original one. Therefore you shouldn't do this.

Exponential

Every power expressions should be one of these:

  • (has variable) ^ (only constant)
  • (only constant) ^ (has variable)

Keep inside of the exponent minimal if it is possible. This is because power has many equivalent form so that structural complexity increases.

ex) 2^(x+1) => 2*2^x, (x+y)^(2*e) => (x^2+2*x*y+y^2)^e

e0: Nested exponent should be transformed into multiplication

(f ^ g) ^ h = f ^ (g * h)

New child g * h should be reduced using multiplication rules.

e1: If base has variable and exponent has variable, always put base into exponent using log.

f ^ g = e ^ (ln(f) * g)

New child ln(f) * g should be reduced using multiplication rules.

This is because of undecidability of reducing below type.

e ^ (ln(x) * ln(y)) = x ^ ln(y) = y ^ ln(x)

This assures (base is constant and exponent is not constant) or (base is not constant and exponent is constant)

This doesn't matter when one of them is constant.

e ^ (ln(x) * ln(2))
2^ln(x) => x^ln(2) by e5

e ^ (0.1 * ln(x))

x ^ 0.1

e2: If base is non constant additive and exponent is integer constant, spread using binomial theorem.

(f + g) ^ n = f ^ n + n * f ^ (n + (-1)) * g + ...

New children should be reduced using exponential rules.

If there are more than two terms, apply multinomial theorem instead.

e3: If base is multiplicative, spread them.

This assures every base of exponential to be atomic.

(2 * e) ^ h = 2 ^ h * e ^ h

New children should be reduced.

Note that

e4: If base is pure constant and exponent is additive and there is constant term, split constant.

2 ^ (f + 3) = 8 * 2 ^ f

e5: If base is constant and exponent is pure log (or coefficient multiplied), pull down the content into base.

a ^ ln(f) = f ^ ln(a)
a ^ (c * ln(f)) = (f ^ c) ^ ln(a)
e ^ ln(f) = f
e ^ (c * ln(f)) = f ^ c

In the second case and fourth case, new child should be reduced.

Multiplication

m0: Remove / operator

Every expressions using binary division can be replaced by exponentiation.

f / g = f * g ^ (-1)

New child g ^ (-1) should be reduced using exponential rules.

m1: Always spread expression using distributive property

Every power of integer of additive expression should be spread using binomial theorem. Every multiplication of composited expressions should be spread .

(f + g) * h = f * h + g * h

This is because there are many ways to express with factored form, but there is only one (if sorted) additive form. Note that c is guaranteed to be "non integer pure constant" or named constant.

New children should be reduced.

m2: Always group exponent of multiplicative using exponential property

Every multiplication of same base should be grouped using exponential property.

f ^ g * f ^ h = f ^ (g + h)

This is because we want all bases to be unique in multiplication.

New child g + h should be reduced using additive rules, and then f ^ (g + h) should be reduced using exponential rules.

m3: Always group constants

m4: Remove identity

1 * f = f

m5: Remove zero

0 * f = 0

Addition

a0: Remove - operator

Every expressions using binary subtraction can be replaced by multiplication of minus one.

f - g = f + (-1) * g

Since there property is similar to addition, we can reduce the complexity of implementation.

(-1) * g should be reduced using multiplication rules.

a1: Always group coefficient of additive

Every addition of same chunk should be grouped. Note that a and b must be constant. Otherwise it violates previous rule.

a * f + b * f = (a + b) * f

a2: Remove identity

f + 0 = f

m1 optimization rule violates mathematical logic.

m1 optimization rule says I have to expand any expression using distribution rule.

x * (y + z) -> x * y + x * z

But there is a problem. Should I really expand the expression even if it has real exponent? Look at this example.

x * (x + 1)^0.4

The biggest domain possible is x > -1. But things get weird when I put left x into right one. Expression below has domain of x > 0.

(x^(1+1/0.4) + x)^0.4

Most of the optimizations may changes possible domain into bigger one. Actually that's not a problem due to following reasons.

  1. Optimization exists for equivalence test
  2. When it comes to equivalence test, we assume that initial two expression's domain is same.
  3. Even optimized expression has larger domain than original, it doesn't matter because all we have to do is just limit them.

But being much smaller domain is different. It is logically nonsense.

The domain shrinks when I put something into power of real exponent.

Even it changes the value

There are some cases that domain doesn't shrink and still make problems. Look at this one.

x * (x + 1)^0.5 -> (x^3 + x^2)^0.5

If I put the left one into right things, total function's range is changed into only positive number. This is trivial since the definition of rational exponent is actually depend on equation. (x + 1)^0.5 is a positive solution of z^2 = x + 1 while (x^3 + x^2)^0.5 is a solution of z^2 = x^3 + x^2. They are just totally different functions.

Is it possible to detect whether merging two factors does matter or not?

In some pretty cases, merging doesn't produce any problem. ((x+1)^0.5 * x^0.3 = (x^(1+0.3/0.5)+x^(0.3/0.5))^0.5) But tracking this is almost impossible. Even with one variable, it's hard to manipulate its domain. Then using more than one variable will be a disaster.

Conclusion

m1 will be modified to support distributive laws only if it's not real power. And also I have to add one more condition: never optimize if the domain may shrink.

Preparing test code for math package

Math feature should be consistent.

Progression

  • renderToText
    • Constant
    • Variable
    • NamedConstant
    • Add
    • Sub
    • Mul
    • Div
    • Power
    • Log
    • trigonometry
    • hyper trigonometry
  • Parser
  • evaluate
    • Constant
    • Variable
    • NamedConstant
    • Add
    • Sub
    • Mul
    • Div
    • Power
    • Log
    • trigonometry
    • hyper trigonometry
  • differentiate
    • Constant
    • Variable
    • NamedConstant
    • Add
    • Sub
    • Mul
    • Div
    • Power
    • Log
    • trigonometry
    • hyper trigonometry
  • optimize
    • Constant
    • Variable
    • NamedConstant
    • Add
    • Sub
    • Mul
    • Div
    • Power
    • Log
    • trigonometry
    • hyper trigonometry
  • isEquivalent
    • Constant
    • Variable
    • NamedConstant
    • Add
    • Sub
    • Mul
    • Div
    • Power
    • Log
    • trigonometry
    • hyper trigonometry

Optimization fails with x^x after differentiation

image

This caused by two possibilities.

  • renderToText misplaced the variable at the base and the inner expression.
  • multiplicative optimization misplaced the variable at the base and the inner expression

Optimization of power node

Power node is far trickier than I thought. It has too many alternative form which are not trivial.

e^(ln(x) * ln(y)) = x^ln(y) = y^ln(x)

Storing expression into linear form like term of factor is much easier to handle. Just sorting the array of expression can reduce n! possibilities into one single form. Power expression is not linear structure. We have to think many possible combinations.

So I'd like to reduce every power related expressions into linear form as possible.

Constants in the exponent should be pulled out if it is possible. Constant inside exponent is hard to handle. The reason of doing this is same with expanding equations. You know that it's hard to factorize rather than expansion.

2^(2*x + 3) = 8 * 4^x

There are 4 possible power forms.

1. constant ^ constant

Very easy case. We can merge them into one single Constant.

2. non constant ^ constant

constant is integer

In this case we can remove exponent by expansion. It's best to delegate its optimizational responsibility to its parent, and linear structure is always good. (ex: (x+1)^2 => x^2+2*x+1)

constant is not integer

This is stable form. There is no way to simplify it. (ex: (e+1)^0.5)

3. constant ^ non constant

This is a little bit tricky. Look at this case. Assume that a is named constant.

a^ln(pi) = pi^ln(a) = e^(ln(a) * ln(pi))

Another pitfall case is this:

16^(x/4) = 4^(x/2) = 2^x = 0.5^(2*x) = ...

Note that any base b can be replaced by e^ln(b). This makes expression more ugly, but all we have to do is just comparing exponent. We don't have to think infinite possible combination of base-exponent relation.

There is an exception: constant term in non constant exponent. (ex: 2^(ln(x) * 2 + 1)) Any constant term can be pulled out to outer coefficient, which responsibility goes to its parent. This is possible because the base is constant.

4. 'non constant^non constant`

This is same to (3) except constant pulling out is not possible.

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.