Git Product home page Git Product logo

fury's Introduction

FURY GitHub tag Join the chat at https://gitter.im/szaghi/FURY

dedicated to W. Van Snyder[rg]

License License License License

Status Build Status Build Status Coverage Status

FURY, Fortran Units (environment) for Reliable phYsical math

A KISS pure Fortran Library for improving reliability of physical math computations by taking into account units of measure:

  • FURY is a pure Fortran (KISS) library for improving reliability of physical math computations;
  • FURY is a pure Fortran (KISS) library for symbolic algebra on units symbols;
  • FURY is Fortran 2003+ standard compliant;
  • FURY is OOP designed;
  • FURY is a Free, Open Source Project.

A taste of FURY

use, intrinsic :: iso_fortran_env, only : real64
use fury
type(uom)   :: meter
type(uom)   :: second
type(qreal) :: distance_to_arrival
type(qreal) :: time_to_arrival
type(qreal) :: mean_velocity

meter = uom('m = meter = metre [length] {meter}')
second = uom('s = sec = second [time] {second}')

distance_to_arrival = qreal(100._real64, meter)
time_to_arrival = qreal(9.58_real64, second)

mean_velocity = distance_to_arrival / time_to_arrival

print "(A)", 'Bolt''s record speed: '//mean_velocity%stringify(with_dimensions=.true.)
! print
! Bolt's record speed: +0.104384133611691E+002 m.s-1 [length.time-1]

Issues

GitHub issues Ready in backlog In Progress Open bugs

Compiler Support

Compiler Compiler Compiler Compiler Compiler Compiler


What is FURY? | Main features | Copyrights | Download | Compilation | Documentation | References


What is FURY?

FURY is a poor-by-blow of reliability concept

  • reliability is expression of the consistency-of-a-set-of-measures
  • reliability measures successful probability, i.e. 1 - probability-of-failure
  • reliability (absence) played a role on the Mars Climate Orbiter disastermco

ergo reliability counts

FURY project started to respond to two questions:

  1. is a units-consistency-check facility (UCCF) a desirable feature for a programming language?
    • is useful? or
    • is dangerous?
  2. how it can be implemented in Fortran?
    • is feasible?

These questions are generated by an inspiring Fortran Language Google Group discussion started by W. Van Snyder[rg]. They are really controversial: someone want UCCF to be a built-in feature in Fortran, others claim that such a feature is not useful at all or even dangerous just because it could add more space for errors than it could reduce (thus reducing the reliability).

Starting from an agnostic point of view, the FURY case study conveys us to think that a UCCF is a desirable feature for the next Fortran standards.

Moreover, the large experience borrowed from other programming languages, see references, proves that UCCF like features can be feasible in modern programming languages and they are of proved usefulness. As a consequence, FURY started to begin a fully featured, self-contained project that is presently usable for production-applications, even if the first comprehensive testing phase (of pre v1.0.0 bug-fix one) is still not completed.

FURY is the result of the tentative to respond the above questions:

FURY is a pure Fortran library providing a new set of types (the quantities) having a built-in units-consistency-check facility that is able to improve the reliability of physical computations. Moreover, FURY can perform symbolic math on units symbols.

FURY can do reliable math computations, preserving quantities dimensionality, checking consistency and propagating multiplicative scaling factors. For example, if you want to know how fast is Bolt you can do it with FURY, in a reliable way πŸ˜„

use, intrinsic :: iso_fortran_env, only : real64
use fury
type(uom)   :: meter
type(uom)   :: second
type(qreal) :: distance_to_arrival
type(qreal) :: time_to_arrival
type(qreal) :: mean_velocity

meter = uom('m = meter = metre [length] {meter}')
second = uom('s = sec = second [time] {second}')

distance_to_arrival = qreal(100._real64, meter)
time_to_arrival = qreal(9.58_real64, second)

mean_velocity = distance_to_arrival / time_to_arrival

print "(A)", 'Bolt''s record speed: '//mean_velocity%stringify(with_dimensions=.true.)
! print
! Bolt's record speed: +0.104384133611691E+002 m.s-1 [length.time-1]

FURY is based on a powerful parser of unit-of-measure (UOM) definitions: once a (set of) UOM is defined its scaling factor, symbol and dimensions are consistently checked, preserved and propagated in all computations. For set of UOMs with defined conversion-formulas FURY can check the consistency of all conversions. Eventually, if a quantity has a defined UOM, any tentative to assign it to another quantity with a different UOM will raise an error as well as for all inconsistent computations.

Go to Top

Main features

FURY is inspired by the python great module pint, thus many features are taken from it. Here the main features are listed.

  • User-friendly classes to add units of measure to numbers;
  • errors trapping for invalid computations/assignments;
  • unit parsing: prefixed forms of units are recognized without explicitly defining them, i.e. as the prefix kilo and the unit meter are defined, FURY understands kilometer;
  • effortless conversion even for complex derived UOM;
  • standalone unit definitions: units definitions are loaded from simple and easy to edit text file;
  • advanced string parsing:
    • symbolic algebra on units symbols;
    • Buckingham Pi Theorem;
  • replicate all the useful features of pint;
  • Test Driven Developed (TDD);
  • collaborative developed;
  • well documented;
  • free!

Any feature request is welcome.

Go to Top

Copyrights

FURY is an open source project, it is distributed under a multi-licensing system:

Anyone is interest to use, to develop or to contribute to FURY is welcome, feel free to select the license that best matches your soul!

More details can be found on wiki.

Go to Top

Download

FURY home is at https://github.com/szaghi/FURY. To download all the source files you can:

Go to Top

Compilation

FURY is a modern Fortran project thus a modern Fortran compiler is need to compile the project.

The library is modular, namely it exploits Fortran modules. As a consequence, there is compilation-cascade hierarchy to build the library. To correctly build the library the following approaches are supported

The FoBiS building support is the most complete, as it is the one used for the developing FURY.

Build by means of FoBiS

A fobos file is provided to build the library by means of the Fortran Building System FoBiS.

Build all tests

Type

FoBiS.py build

After (a successful) building a directory ./exe is created containing all the compiled tests that constitute the FURY regression-tests-suite, e.g.

β†’ FoBiS.py build
Builder options
Directories
  Building directory: "exe"
  Compiled-objects .o   directory: "exe/obj"
  Compiled-objects .mod directory: "exe/mod"
Compiler options
  Vendor: "gnu"
  Compiler command: "gfortran"
  Module directory switch: "-J"
  Compiling flags: "-c -frealloc-lhs -std=f2008 -fall-intrinsics -O2 -Dr16p"
  Linking flags: "-O2"
  Preprocessing flags: "-Dr16p"
  Coverage: False
  Profile: False
PreForM.py used: False
PreForM.py output directory: None
PreForM.py extensions processed: []

Building src/tests/basic_use.f90
Compiling src/lib/penf.F90 serially
Compiling src/lib/string_t.F90 serially
Compiling src/lib/stringifor.F90 serially
Compiling ...
Linking exe/basic_use
Target src/tests/basic_use.f90 has been successfully built
...

β†’ tree -L 1 exe/
exe/
β”œβ”€β”€ basic_use
...

Build by means of GNU Make

To be implemented.

Build by means of CMake

To be implemented.

Go to Top


Documentation

Besides this README file the FURY documentation is contained into its own wiki. Detailed documentation of the API is contained into the GitHub Pages that can also be created locally by means of ford tool.

The design

FURY is designed upon a simple, yet powerful UOM grammar, for example the definition of the Pascal unit could be one the followings:

  • 'Pa'
  • 'Pa [pressure]'
  • 'Pa [pressure] {pascal}'
  • 'kg.m-1.s-2'
  • 'kg.m-1.s-2 = Pa'
  • ...
  • 'kg [mass].m-1 [length-1].s-2 [time-2] (Pa[pressure]) {pascal}'

where the first is the most basic definition defining only the main symbol while the others are definitions with increasing complexity where we are defining dimensions, symbol aliases, name, definition with respect other units... The flexibility left to the end user is really high. More in details, the UOM grammar is composed as following:

  • 'kg [mass].m-1 = metre-1 = meter-1 [length-1].s-2 = seconds-2 = sec-2 [time-2] (Pa[pressure]) {pascal}'

where

  • the terms [...] define the dimensions of each unit and are optional (the white spaces are ignored); moreover the exponents of dimensions can be omitted: in this case they are inferred from the symbol reference exponents; in the case they are explicitly written they must match the corresponding symbol ones or an error is raised;
  • the term (...) defines a main unit alias that is optional and must come always after unit reference definition;
  • the term {...} defines the unit name that is optional and must be always the last term.

Reference units used for the definition of another one are separated by the . symbol, thus the Pascal definition is kg/(m * s^2) is defined as kg.m-1.s-2 to mimic the rules of The Unified Code for Units of Measure: each reference unit is intended multiplied the subsequent with the proper exponent with sign. The FURY parser is designed on this grammar, thus each reference unit is tokenized with the . separator.

Symbol aliases are used also for defining units conversion formulas, e.g.

'km = 1000 * m [length].h-1 = 3600 s-1 [time-1] (km/h[speed]) {km/h}'

This is the base, more concise conversion formula grammar, but it could be unsafe if the conversion implies real-valued scaling factor, as it often happens. In this case, a more verbose but more safe grammar should be used, e.g.

'km< = 1000._real64 * m > [length].h-1< = 3600._real64 s-1> [time-1] (km/h[speed]) {km/h}'

where the conversion formulas are protected by the <> symbols. Defining a UOM with fury is simple as

use fury
type(uom) :: pascal
pascal = uom('kg [mass].m-1 [length-1].s-2 [time-2] (Pa[pressure]) {pascal}')

Having a unit so defined, it is allowed to perform your reliable computations, namely all the symbolic computations necessary when dealing with units, e.g. computing the force from a pressure on a surface

use fury
type(uom) :: meter
type(uom) :: pascal
type(uom) :: newton

meter = uom(m [length] (meter[length]) {meter}')
pascal = uom('kg [mass].m-1 [length-1].s-2 [time-2] (Pa[pressure]) {pascal}')

newton = pascal * meter**2
print "(A)", ' newton: '//newton%stringify(with_dimensions=.true.)
! output
! newton: kg.m.s-2 [mass.length.time-2]

For make easier the life of snappy coders a far complete SI system of predefined units is provided, as well an abstract class for defining new units systems.

UOM grammar for reference unit

We indicate a UOM to be a reference unit when it is defined by only main symbol having exponent 1 and multiplicative factor 1. With this categorization the base units of SI are all reference units for us. Moreover, the user can define his/her own reference units, e.g. apple [fruit] {not a pc} is a valid UOM reference definition that can be used to define other (complex) units like GNU< = 1.e20 * apple> [salad_of_fruits] {HPC}. To define a reference UOM you need to specify just the main symbol, but other data can be optionally provided to cover more complex purposes. For example, defining a reference unit you can provide aliases defining conversion formulas to other UOM, you can provide the dimensions or UOM name useful to query systems of units.

required alias formula(s), optional optional optional optional
optional = optional if == 1 Hz optional if == 1 optional
grammar_element main_symbol < = multiplicative_factor symbol symbol_exponent > [dimensions] (alias[dimensions]) {unit_name}
example kHz < = 1000. * Hz 1 > [time-1] (kilohertz[frequency]) {kilohertz}

Examples

  • kHz, only the main symbol is strictly required;
  • kHz = 1000 * Hz, one alias (for conversion formula);
  • kHz = 1000 * Hz = 1000 * s-1, two aliases (for conversion formulas);
  • kHz< = 1000. * Hz>, one alias protected (due to real factor);
  • kHz< = 1000. * Hz = 1000. * s-1 >, two aliases protected;
  • kHz [time-1], dimensions specification;
  • kHz [time-1] (kilohertz[frequency]), a main alias specification, useful for units like pressure:
    • kg.m-1.s-2 (Pa[pressure]) {pascal}
  • kHz [time-1] (kilohertz[frequency]) {my kilohertz}, unit name specification, useful for queries in units systems usage;
  • kHz< = 1000. * Hz = 1000. * s-1 > [frequency] (kilohertz[frequency]) {kilohertz}

UOM grammar for general complex unit

In the case you want to define a UOM with a respect a set of reference UOM, you can do using the . operator that mimics the definition of The Unified Code for Units of Measure group.

required at least 2 UOM definitions optional optional
grammar_element UOMreference . otherUOMreference (alias[dimensions]) {unit_name}
example m [length] . s-1 [time-1] (m/s [speed]) {speed SI}

Examples

  • kg [mass].m [length].s-2 [time-2] (N[force]) {newton}
  • kg [mass].m2 [length2].s-3 [time-3].A-2 [current-2]{ohm}
  • kg [mass].m-1 [length-1].s-2 [time-2] (Pa[pressure]) {pascal}

It worth to note that the user can always define a complex unit as a reference one, let us consider for example two possible definitions of the pressure:

  1. kg [mass].m-1 [length-1].s-2 [time-2] (Pa[pressure]) {pascal_complex}
  2. Pa [pressure] {pascal_simple}

Both are valid, but the first one provides much more informations thus resulting into a more powerful symbolic math. For example consider the following example where the above 2 definitions are used to define the Newton unit:

use fury
type(uom) :: meter
type(uom) :: pascal_complex
type(uom) :: pascal_simple
type(uom) :: newton

meter = uom('m [length] (meter[length]) {meter}')
pascal_complex = uom('kg [mass].m-1 [length-1].s-2 [time-2] (Pa[pressure]) {pascal}')
pascal_simple = uom('Pa[pressure] {pascal}')

newton = pascal_complex * meter**2
print "(A)", ' newton: '//newton%stringify(with_dimensions=.true.)
! output
! newton: kg.m.s-2 [mass.length.time-2]

call newton%unset
newton = pascal_simple * meter**2
print "(A)", ' newton: '//newton%stringify(with_dimensions=.true.)
! output
! newton: Pa.m2 [pressure.length2]

Both results are correct, but the first is more informative if a consistent set of reference units has been defined, as it happens for the SI system.

Physical Quantities

FURY's quantities are built up upon the above UOM: a quantity is essentially a number (real or integer of any kind) with attached a UOM. When a quantity has a defined UOM all UOM's check are pushed to the quantity's magnitude computations, thus improving the math computations reliability.

Unfortunately, we have not find a simple way to attach UOM to standard Fortran types, thus the end user must do reliable computations on a new set of types, namely qreal and qinteger (or their kinded version).

Let us assume to consider the summation of 2 length, the first expressed in meters and the second in kilometers. With FURY this can be implemented as:

use fury
type(system_si) :: SI        ! SI system.
type(qreal)     :: q         ! A physical quantity.
type(qreal)     :: q1        ! A physical quantity.
type(qreal)     :: q2        ! A physical quantity.
type(uom)       :: kilometre ! The kilometre unit based on the metre unit.

! initialize SI units system
call SI%initialize

! define a new unit, i.e. the kilometre, that must
! be compatible with SI's metre
kilometre = uom('km = 1000 * m')

! define 2 quantities
q1 = qreal(magnitude=1._R_P, unit=SI%unit('metre'))
q2 = qreal(magnitude=1._R_P, unit=kilometre)

! try to blindly to sum them
q = q1 + q2
! error: cannot convert from "metre [length]" to "km"

! a more conscious sum
q = q1 + q2%to(SI%unit('metre'))
! no error is raised
print "(A)", '1 m + 1 km = '//q%stringify(format='(F6.1)')
! output "1 m + 1 km = 1001.0 m"

The minimal tutorial

To use FURY the minimal steps necessary are:

1. use FURY module
use fury
2. define FURY quantities
type(qreal)    :: q1 ! A real physical quantity.
type(qinteger) :: q2 ! An integer physical quantity.
3. (optional) define new FURY units
type(uom) :: centimetre ! The centimetre unit based on the metre unit.
type(uom) :: kilometre  ! The kilometre unit based on the metre unit.
type(uom) :: hour       ! The hour unit based on the second unit.

centimetre = uom('cm = 0.01 * m')
kilometre = uom('km = 1000.0 * m')
hour = uom('h = 3600.0 * s')
4. (optional) initialize FURY units system(s)
type(system_si) :: SI ! SI system

call SI%initialize

Note that currently on the SI system is provided.

5. assign unit (and optionally magnitude) to FURY quantities
q1 = qreal(magnitude=1._R_P, unit=kilometre)
q2 = qinteger(magnitude=3, unit=hour)
6. do your physical maths

Now you can do your reliable physical computations :party:

Go to Top

References

A non comprehensive references list of UCCF related contents.

Built-in in language
Libraries

MCO disaster

The Mars Climate Orbiter (MCO) Mishap Investigation Board (MIB) has determined that the root cause for the loss of the MCO spacecraft was the failure to use metric units in the coding of a ground software file, β€œSmall Forces” used in trajectory models, specifically:

thruster performance data in English units instead of metric units was used in the software application code titled SM_FORCES (small forces).

The units mismatching generates an erroneous trajectory that was underestimated by a factor of 4.45, which is the required conversion factor from force in pounds to Newtons.

if the software application SM_FORCES had been developed with a units-consistency-check facility, $300 million of spacecraft could be saved.

Go to Top

fury's People

Contributors

giacrossi avatar szaghi 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fury's Issues

Add non pure-multiplicative conversions

For example support temperature-like conversions with offset, e.g.

  • celsius = 273.15 + 1 * K
  • fahrenheit = 255.372222 + 1 * K
  • american_wire_gauge = 8.62491 * log(1/mm) + 18.2019 * mm
  • dBm = 10 * log(mW)
  • mW = 10**(dBm / 10)

supported conversions

  • multiplicative-like
    • pure multiplicative: y = f * x
    • fixed_offset + multiplication: y = offset + f * x
  • non multiplicative
    • generic f(x) user provided

how to implement generic f(x) user-provided alias-conversion formula

The current alias-conversion formula is implemented into the most base uom_symbol class that looks like

type :: uom_symbol
    integer :: exponent
    real :: factor
    real :: offset
    type(string) :: symbol
endtype uom_symbol

As a matter of fact, currently only multiplicative-like conversion are supported

direct: f(x) = offset + factor * x
inverse: f(x) = (x - offset) / factor

This should/could be generalized by a procedure pointer attribute customizable by user, e.g.

type :: uom_symbol
    integer :: exponent
    real :: factor
    real :: offset
    type(string) :: symbol
    procedure (generic_conversion_interface), pointer, nopass :: generic_conversion => null()
endtype uom_symbol

interface
    function generic_conversion_interface(x, inverse)
        real, intent (in) :: x
        logical, intent(in), optional :: inverse
        real :: generic_conversion_interface
    end function generic_conversion_interface
end interface

The uom_symbol gramar must be modified to take into account this new scenario, e.g.

grammar: @user A

that means that the symbol A has a conversion(s) formula supplied by user.

The problem is how the user can supply these formula, uom_symbol being the most basic (almost hidden to user) class...

Add (real) examples

Add examples like "FURY within FFT" computations... is it viable to "wrap" procedures like Pint does?

Implement uom%simplify

Implement a methid to simplify uom references, e.g.

type(uom) :: u1
type(uom) :: u2

u1 = uom('m.s-1.kg.m-2')
u2 = u1%simplify()
print*, u2%stringify()
! Output
! m-1.s.1.kg

Publication

In order to promote Van Snyder's proposal, it could be of some help to prepare a journal publication to submit to a Software Eng. related journal. To this aim, many issues must addressed

  • FURY reliability study
  • FURY API complexity review
  • FURY overhead quantification
  • array syntax tests must be succesfully completed
  • coupled usage with widely used numerical libraries must be proved
  • usage in complex physical scenario (like the Glen's context) must be proved

References

[1], Automated computation and consistency checking of physical dimensions and units in scientific programs, Petty, 2001.

Error propagation

If a physical quantity has an associated estimation of error, a more reliable computation should automatically propagate the errors. Possible pseudo code

type(qreal) :: q1
type(qreal) :: q2
type(qreal) :: q3

q1 = qreal(magnitude=1., unit=si%unit('second'), error=0.1)
q2 = qreal(magnitude=3., unit=si%unit('second'), error=0.2)

q3 = q1 + q2

print*, q3%stringify(with_error=.true.)
! output
! 4.0 +- 0.5 s

References

[1]rosettacode
[2]Measurements.jl
[3]uncertainties.py

Implement contexts

Like Pint module teaches, as well as the comments of experts like Glen, context concept is very useful. Remember to think to it.

Refactor grammars

Preliminary tests for non pure-multiplicative conversions pointed out that grammars definitions are messy. A cleanup is mandatory.

uom symbol grammar

Let us consider only multiplicative-like (pure or not) conversions of the form

Y = offset + f * X^p

For such a scenario, all the data to be stored are already in current uom_symbol class

type :: uom_symbol
  real :: offset
  real :: factor
  type(string) :: symbol
  integer :: exponent
end type

The grammar could be untouched

uom_symbol = "offset + f * X^p"

uom reference grammar

A uom reference:

  • has at least 1 uom symbol
    • optionally has other uom symbol aliases that could be also used for conversions
    • optionally has one dimensions

For example:

  • m is both a uom symbol and a uom reference
  • degC = 273.15 + K is a uom reference defining Celsius degree and the alias symbol 273.15 + K could be used for conversions

For such a scenario, all the data to be stored are already in current uom_referencel class

type :: uom_reference
  type(uom_symbol), allocatable :: aliases(:)
  type(uom_symbol), allocatable :: dimensions
end type

The grammar could be untouched

uom_reference = "Y< = offsetx + fx * X^px = offsetz + fz * Z^pz = ...> [dimensionsY]"
# that is
uom_reference = "uom_symbolY< = uom_symbolX = uom_symbolZ = ...> [dimensionsY]"

uom grammar

Hinc sunt leones

A uom is composed by an arbitrary number of uom references, e.g.

  • kg.m.s-2 is definition of Newton
  • kg< = 0.0 + 1000.0 * g>.m< = 0.0 + 100.0 * cm = 0.0 + 1000.0 * mm>.s-2 is also a definition of Newton

The grammar could be

uom = "Y< = offsetx + fx * X^px = offsetz + fz * Z^pz = ...> [dimensionsY].A< = offsetb + fb * B^pb = offsetc + fc * Z^pc = ...> [dimensionsA]"
# that is
uom = "uom_referenceY.uom_referenceA (alias[dimensions_alias])"

For example

  • m is both a uom symbol, uom reference and a complete uom
  • kg<=1000 * g>[mass].m<=100 * cm>[length].s-2[time-2](N[force]) is a complete uom

These grammars sound robust enough. What is messy are the definitions of uom comparison tests.

equality comparison

uom_symbol

s1 == s2 if

  • s1%offset == s2%offset
  • s1%factor == s2%factor
  • s1%symbol == s2%symbol
  • s1%exponent == s2%exponent

uom_reference

r1 == r2 if r1%aliases(1)==r2%aliases(1)
i.e. only the first alias is the main symbol to be considered for equality

uom

u1 == u2 if all(u1%references==u2%references)
i.e. only if all references are equal and both units have the same references number

compatibility comparison

uom_symbol

s1.compatible.s2 if

  • s1%offset == s2%offset
  • s1%factor == s2%factor
  • s1%symbol == s2%symbol

i.e. the exponents are not considered

uom_reference

r1.compatible.r2 if r1%aliases(1).compatible.r2%aliases(1)
i.e. only the first alias is the main symbol to be considered for compatibility

uom

u1.compatible.u2 if all(u1%references.compatible.u2%references)
i.e. only if all references are compatible and both units have the same references number

usage of aliases

Aliases must be used only for conversion purposes and only when the user explicitly invoke a %to like method.

convertibility comparison

uom_symbol

s1.covertible.s2 if
s1%exponent == s2%exponent
s1%symbol == s2%symbol
i.e. only symbol x^n isconsidered

uom_reference

r1.covertible.r2 if ...

  • r1%aliases(1).convertible.one-of-r2%aliases(:) => direct conversion, otherwise
  • one-of-r1%aliases(2:).convertible.r2%aliase(1) => inverse conversion

where

direct conversion: R1^p1 = offset2+ f2 * R2^p2
inverse conversion: R1^p1 = (R2^p2 - offset1)/f1

uom

u1.convertible.u2 if
one-of-u1%references(:).convertible.one-of-u2%references(:)

Benchmarks!

Compuations with dimensional quantities have an overhead, please quantify it!

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.