Giter VIP home page Giter VIP logo

qir-alliance / pyqir Goto Github PK

View Code? Open in Web Editor NEW
54.0 54.0 24.0 2.18 MB

PyQIR is a set of APIs for generating, parsing, and evaluating Quantum Intermediate Representation (QIR).

Home Page: https://qir-alliance.github.io/pyqir

License: MIT License

PowerShell 23.73% Python 24.73% Rust 39.81% LLVM 5.91% Handlebars 3.14% CMake 1.44% C 0.52% C++ 0.54% Batchfile 0.11% Shell 0.06%
llvm qir quantum-computing

pyqir's People

Contributors

airwoodix avatar ausbin avatar bamarsha avatar bettinaheim avatar bmhowe23 avatar cqc-melf avatar georgios-ts avatar idavis avatar laurentajdnik avatar qartik avatar scottcarda-ms avatar sezna avatar swernli avatar wingcode 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

pyqir's Issues

Add suggested citation metadata to README.md and/or conceptual docs

For use from academic papers, it would be nice if there was an official suggestion as to what BibTeX source to use in citing PyQIR packages.

Examples in other Python packages for scientific computing use:

Support external functions in generator

PyQIR Generator should support adding external function declarations to a module and emitting LLVM call instructions for them using the builder. The initial version of the feature will probably support only external functions with a void return type. That will enable custom quantum instruction sets and state-changing utility functions.

A draft PR with an initial implementation is available: #54

There are a few different ideas for how the API can be designed:

1. Declare function before calling

SimpleModule could have a method like add_external_function(name, types) that returns a wrapped LLVM function value. Builder could have a method like call(function, args) that takes the function value and a list of arguments.

module = SimpleModule(...)
my_function = module.add_external_function("my_function", Qubit)
module.builder.call(my_function, module.qubits[0])

Pros:

  • Simple and explicit API with minimal magic under the hood
  • Close to LLVM and easy to evolve into a general LLVM call API (e.g. with return types)
  • Good support for mypy and code completion

Cons:

  • Verbose

2. Automatically declare function on first call

There would be no explicit method to add a function - it would happen automatically the first time call(name, args) is used with a new function name. The type of the external function would be inferred from the type of the arguments.

module = SimpleModule(...)
module.builder.call("my_function", module.qubits[0])

Pros:

  • Simpler for currently supported cases (statically known function value with void return type)

Cons:

  • Return type can't easily be inferred in the future
  • Very dynamic; typos in function name string or issues in inferred argument types could lead to hard-to-debug errors. Because an LLVM function definition is implicily added the first time call is used, the first call will always succeed but subsequent calls may fail if the argument types are different.

3. Automatically declare function on first call with sugar

A sugared version of option 2 would be to define __getattr__ on Builder that would handle unknown method names. For example, builder.my_external_function(args) would be converted to builder.call("my_external_function", args).

module = SimpleModule(...)
module.builder.my_function(module.qubits[0])

Pros:

  • Very concise

Cons:

  • Does not work as well with Python tooling (type checking, code completion)
  • Function names can clash with other method names on Builder

Future evolution

We should consider how the chosen API will evolve into a more general LLVM call instruction API. In particular, the options above all have the call method on Builder, but Builder is not specific to the basic QIR profile. So if we choose a simplified call API, it may stick around alongside the more general API.

Some features we should consider for future evolution:

  • Arguments may be any LLVM value (e.g. referring to a previous instruction result), not just Python values that are converted into LLVM constants.
  • Functions could have a non-void return type.
  • The function value may not be statically known (but this may require a different API for QIR callables).

LLVM objects should implement `__repr__`

When printing out pyqir_parser objects at the console or in a notebook, the default object.__repr__ implementation is used, making it hard to understand output without further processing:

image

It would be good if parser objects supported __repr__ to provide more useful diagnostic output in such contexts.

ANTLR 4.10-generated parsers incompatible with previous runtimes

ANTLR released a new version, 4.10, on 11 April 2022. This release breaks the augmented transition network (ATN) serialized state machine in the generated code from earlier versions. The 4.10 Release notes explain that they broke the serializer format and the generated code needs to be regenerated with the 4.10 tooling and the python dep needs to be set to 4.10+

Choose a Python module/package hierarchy

#43 updates the PyQIR packages to use a shared namespace package pyqir (pyqir.generator, pyqir.jit, etc.). It also adds additional hierarchy to the generator modules, so that e.g. SimpleModule must be imported from pyqir.generator.module, instead of simply from pyqir.generator.

We should take another look at these two things to make sure we're following Python conventions, and are choosing the right level of organization:

  1. Do we want to use namespace packages (pyqir.generator) or independent modules (pyqir_generator or pyqirgenerator)?
  2. Do we want to expose a nested module structure within each package (from pyqir.generator.module import SimpleModule), a flat one (from pyqir.generator import SimpleModule), or both?

Tasks

  • #58
  • Plan for namespace packages

pyqir-evaluator should gracefully fail when bitcode contains unknown external functions

Describe the bug

When an LLVM file contains declarations of external functions that are unknown, the evaluator crashes without displaying an error message or raising an Exception.

Issue related to #121.

To Reproduce

Use an LLVM file that contains unknown external functions, such as:

%String = type opaque

declare %String* @__quantum__rt__bool_to_string(i1)

define void @main() #1 {
entry:
  call %String* @__quantum__rt__bool_to_string(i1 1)
  ret void
}

attributes #1 = { "EntryPoint" }

This evaluation does not reach the end and throws no Exception:

from pyqir.evaluator import GateLogger, NonadaptiveEvaluator

print("Start")

try:
    NonadaptiveEvaluator().eval("CrashMe.ll", GateLogger())
except Exception as e:
    print("An exception was raised.")

print("End")

Expected behavior

The evaluator should either:

  • Handle them in a transparent manner (if possible)
  • Display an error message ("Unsupported function...")
  • Raise an exception

Support for `void` with `QirRetTerminator`

I've got a QIR instruction:

  ret void

But when I try to parse that I'm getting an exception

~/miniconda3/envs/aq/lib/python3.8/site-packages/pyqir/parser/_parser.py in operand(self)
    416         """
    417         if not hasattr(self, "_operand"):
--> 418             self._operand = QirOperand(self.term.ret_operand)
    419         return self._operand
    420 

~/miniconda3/envs/aq/lib/python3.8/site-packages/pyqir/parser/_parser.py in __new__(cls, op)
    233     """
    234     def __new__(cls, op: PyQirOperand):
--> 235         if op.is_local:
    236             return super().__new__(QirLocalOperand)
    237         elif op.is_constant:

AttributeError: 'NoneType' object has no attribute 'is_local'

I can work around this with a try/except:

            try:
                operand = ter.operand
            except AttributeError:
                operand = None

Add mypy to CI

We have type annotations and mypy stub files (.pyi) that aren't checked during CI, so they can drift out of sync with the implementation. We should run mypy during CI to catch these issues. This should be done as a build task run as part of the checks tasks.

Add examples to CI

It would be good to add the examples to the CI to ensure that they are working correctly.

Create v0.4.2a1 Release

This issue captures the mini "release process" for GitHub releases
(no need to edit):

  • Create a PR to update the change log and the version numbers in the installing.md with the new tag for the release
  • Download the artifacts from the CI of that PR (Actions · qir-alliance/pyqir (github.com))
  • Create a new release (New release · qir-alliance/pyqir (github.com)) of these artifacts with the new tag, where the title should be the new tag, and the description should contain the section in the change log that was previously captured under "Unreleased". Set the pre-release tag.
  • Merge the release PR (the one from the first task)

Add musllinux wheel distribution

Alpine (musl) based distribution is not currently supported. To achieve this, several components must be updated:

  • Create Alpine OCI base image (maturin, compiler toolchain, etc)
  • Get LLVM building correctly in this environment
  • Get PyQIR building correctly in this environment
  • Update build to create musllinux wheels
  • Update build to test musllinux wheels

Wheel should likely follow PEP 656 for naming via Maturin with musl 1.2 for the compatibility.

unwrap() calls can propagate to Python PanicExceptions

In some cases, calls to unwrap can result in PanicExceptions propagating out to Python callers, but without actionable error messages to help users solve possible issues.

For example:

>>> import pyqir_generator as pqg
>>> builder.add_quantum_register("qr", 3)
>>> builder.add_classical_register("cr0", 1) # ← typo here!
>>> builder.h("qr0")
>>> builder.m("qr0", "cr0")
>>> builder.get_ir_string()
---------------------------------------------------------------------------
PanicException                            Traceback (most recent call last)
~\...\1016496886.py in <module>
----> 1 builder.get_ir_string()

~\...\lib\site-packages\pyqir_generator\builder.py in get_ir_string(self)
    235         Returns the modeled circuit as an LLVM IR module (human readable) string.
    236         """
--> 237         return self.pyqir.get_ir_string()
    238 
    239     def get_bitcode_base64_string(self):

PanicException: called `Option::unwrap()` on a `None` value

In this example, the typo in the call to add_classical_register is not easy to find, as the eventual error message does not mention that register name was incorrect.

QirBlock does not have a stable hash

In the definition for the QirBlock class, when you ask for the list of instructions it constructs a new list each time. This makes the hash unstable then when iterating through the functions of a block at different times. It seems like the QirBlock should either be unhashable or have it pass on the hash from the block.

return [QirInstr(i) for i in self.block.instructions]

Support control flow and classical computation in PyQIR Generator

Currently, there is no support for emitting QIR that contains branching based on measurement, or any other more complex classical computations. This issue should be broken up into multiple issues to more closely capture the work, and details will be added at that time.

When this issue is updated or resolved, please update the section on current limitations in the pyqir-generator readme.

Tasks

  • Branching based on measurement: #32
  • Branching based on boolean: #145
  • Classical computation

Cargo git dependency causes slow clone of llvm repo

If one uses qirlib as a cargo dependency using the git specification,
e.g. in Cargo.toml

qirlib = {path ="https://github.com/qir-alliance/pyqir"}

Cargo attempts to clone llvm-project as a submodule, which is a very large repository, and does not appear to be required for the dependency.

I don't have a good suggested fix, nor do I know if this actually requires fixing, just something I thought was worth being aware of.

Currently my workaround is to clone the repository myself without submodules and have a local path dependency.

Parser should allow construction from in-memory bitcode

Currently, the pyqir_parser only supports initializing the parser from a string that contains the path to an on-disk bitcode file. It would be helpful to support constructing a parser instance from an in-memory bitcode file, as that would open up more scenarios where code is already actively operating on bitcode, such as receiving it as a network stream.

Note that this may require updates to the underlying llvm_ir module that the parser depends on.

Create v0.3.1a1 Release

This issue captures the mini "release process" for GitHub releases
(no need to edit):

  • Create a PR to update the change log and the version numbers in the installing.md with the new tag for the release
  • Download the artifacts from the CI of that PR (Actions · qir-alliance/pyqir (github.com))
  • Merge the release PR (the one from the first task)
  • Create a new release (New release · qir-alliance/pyqir (github.com)) of these artifacts with the new tag, where the title should be the new tag, and the description should contain the section in the change log that was previously captured under "Unreleased". Set the pre-release tag.

Sample for using pyqir_generator to compile a subset of Python to QIR

Filing an issue to capture a question/request that has come up in discussions:

This repo currently contains an example that shows how to use the pyqir_generator to generate QIR for a mock language. Conceptually, a program is written in a language, the language specific compiler is then used to create the AST representing that program, and usually tools for traversing that AST (and AST walker) and perform custom logic on each AST node are available as part of the compiler infrastructure. The idea is then to use such a walker to invoke the pyqir_generator on each node to emit QIR representing the program.
While this is shown for a custom language in one of the examples, there also exist tools that create an AST for a Python program. The request hence was to add an example to show how such a tool could be used to compile parts of Python itself to QIR.

Minimize QIR profile and update the used QIS

PyQIR Generator creates QIR modules with an entry point that loops through an array of measurement results, converts them to strings, and prints them to standard output. However, not all hardware backends support arrays, strings, or console output. It should be possible to generate modules without the built-in measurement result output code.

Upgrade to LLVM 13

  • Add LLVM build automation
  • Migrate tests/bitcode/ir
  • Update docs
  • Update ENV vars for LLVM_SYS across dev container, code, docs, etc

[pyqir-generator] Support for identity gate

A very useful gate to have as part of basic gateset is the identity gate. On hardware, it can be used to construct an intentional time delay, and in software, it can be useful for debugging purposes.

Qiskit, for instance, supports the identity gate as part of the basic gateset and cannot be optimized or unrolled:
https://qiskit.org/documentation/stubs/qiskit.circuit.library.IGate.html#igate

Currently, the pyqir BasicQisBuilder does not support an identity gate; instead, one could use two subsequent X gates. However, these could be optimized out at later stages; it would be good to have a dedicated Identity gate for the aforementioned purposes.

Avoid installing Python build dependencies into a non-virtual environment

In eng/psakefile.ps1, we install tox using python -m pip install tox. We run this command without creating a virtual environment ourselves, so it installs into whatever environment the build script is run from. We should avoid automatically installing things into the user's environment, e.g. by creating our own virtual environment first, or by requiring tox to already be installed.

Adding static allocation support

Summary

The code generation should support static and dynamic:

  • qubit allocation
  • result allocation

The static qubit allocation is the default but is not expressed in the Python APIs. At the moment, only dynamic result allocation is used. This has the side effect that any code which uses dynamic result allocation will not give a target result id during evaluation as the result is returned from the measurement call.

Requirements

Generator

  • Add use_static_qubit_alloc(bool) to SimpleModule
  • Add use_static_result_alloc(bool) to SimpleModule

The underlying SemanticModel defaults to true for use_static_qubit_alloc and false for use_static_result_alloc. This is only to avoid a breaking QIR generation change. The initial work will leave the defaults as they are now. When we bump the version number, we will change the default behavior to static result allocation.

Evaluator

  • Add mz to the GateSet and GateLogger
  • Open question on whether the GageLogger should log m or mz in the output.

We only know at the semantic model that mz was used if we have a result target id.

Qirlib

Generation:

  • Rename static_alloc to use_static_qubit_alloc: bool on SemanticModel
  • Add use_static_qubit_alloc: bool on CodeGenerator
  • Add use_static_result_alloc: bool to SimpleModule
  • Add use_static_result_alloc: bool to CodeGenerator
  • Add support for emitting mz: declare void @__quantum__qis__mz__body(%Qubit*, %Result*)
  • Add generator support for emitting mz during static result allocation and m during dynamic
  • Add support for emitting read_result: declare i1 @__quantum__qis__read_result__body(%Result*)
  • Add generator support for emitting different code for result based branching
    • read_result during static result allocation
    • result_equal and get_one for dynamic as it works right now
  • When static result generation is enabled, create int ids for the results
  • Add requiredResults metadata with the entrypoint attribute to match the requiredQubits emission.

JIT:

  • Add mz to GateSet
  • Add extern mz decl
  • Add static result cache used by extern mz decl to record results. This will read from the result stream and insert into the cache based on result id.
  • Add extern read_result decl: will return the value in the cache for the supplied result id. If the cache doesn't contain that id, it will return false (read without measurement)
  • Add reset_static_result_cache for JIT to clear static cache before module executions.
  • Map the extern decls for mz and read_result to the module if the module declares those extern functions

Testing

The pyqir-tests project will need to run the end-to-end tests with all combinations of qubit/result static/dynamic combination.

More code generation tests will need to be written to verify QIR emission across all combinations of qubit/result static/dynamic.

Implementation

PR #103

Consider providing the core llvm lib as a Rust crate

We've heard from people using the Rust core of PyQIR directly. I am hence filing this issue for the purpose of discussion and to gather interest in having a Rust package. So far, the Rust API were not necessarily intended to be used directly, and would need to be examined carefully if we were to provide the Rust infrastructure as its own crate.

pyqir_parser exposing too many things on import

I was working with the parser package, and saw that when you import it, it lists a lot of things that probably shouldn't.
Things like List and Optional, the underlying Rust interface (things prefixed by "Py"), and the parser/pyqir_parser also related to internals that probably shouldn't be user facing.

The example below was generated from using this conda env, and locally downloading and installing the windows wheels from the v0.1.0a1 pre-release on GH.

import pyqir_parser as pqp
dir(pqp)

['List',
 'Optional',
 'PyQirBasicBlock',
 'PyQirConstant',
 'PyQirFunction',
 'PyQirInstruction',
 'PyQirModule',
 'PyQirOperand',
 'PyQirParameter',
 'PyQirTerminator',
 'PyQirType',
 'QirAShrInstr',
 'QirAddInstr',
 'QirAndInstr',
 'QirArrayType',
 'QirBlock',
 'QirBrTerminator',
 'QirCallInstr',
 'QirCondBrTerminator',
 'QirConstant',
 'QirDoubleConstant',
 'QirDoubleType',
 'QirFAddInstr',
 'QirFCmpInstr',
 'QirFDivInstr',
 'QirFMulInstr',
 'QirFNegInstr',
 'QirFRemInstr',
 'QirFSubInstr',
 'QirFunction',
 'QirICmpInstr',
 'QirInstr',
 'QirIntConstant',
 'QirIntegerType',
 'QirLShrInstr',
 'QirLocalOperand',
 'QirModule',
 'QirMulInstr',
 'QirNamedStructType',
 'QirNullConstant',
 'QirOpInstr',
 'QirOperand',
 'QirOrInstr',
 'QirParameter',
 'QirPhiInstr',
 'QirPointerType',
 'QirQirCallInstr',
 'QirQisCallInstr',
 'QirQubitConstant',
 'QirQubitType',
 'QirResultConstant',
 'QirResultType',
 'QirRetTerminator',
 'QirRtCallInstr',
 'QirSDivInstr',
 'QirSRemInstr',
 'QirShlInstr',
 'QirStructType',
 'QirSubInstr',
 'QirSwitchTerminator',
 'QirTerminator',
 'QirType',
 'QirUDivInstr',
 'QirURemInstr',
 'QirUnreachableTerminator',
 'QirVoidType',
 'QirXorInstr',
 'Tuple',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__path__',
 '__spec__',
 'module_from_bitcode',
 'parser',
 'pyqir_parser']

Rust API for qirlib uses String to represent OS paths

The Rust API for qirlib uses the &str type to represent OS-specific paths, such as the emit_ir method, but these slices are immediately converted into Path values.

pub fn emit_ir(&self, file_path: &str) -> Result<(), String> {
    let ir_path = Path::new(file_path);
    if let Err(llvmstr) = self.module.print_to_file(ir_path) {
        return Err(llvmstr.to_string());
    }
    Ok(())
}

These methods should be generalized to allow any type that supports Into<Path>, so that APIs which return Path values can be used with the trivial Into<T> for T impl but without breaking existing code that relies on passing &str slices.

Rename qir_jit to qir_evaluation

Some discussions around comprehensive naming of the packages have come up. Something like qir_evaluation rather than qir_jit may be more approachable.

Create v0.3.2a1 Release

This issue captures the mini "release process" for GitHub releases
(no need to edit):

  • Create a PR to update the change log and the version numbers in the installing.md with the new tag for the release
  • Merge the release PR (the one from the first task)
  • Download the artifacts from the CI of that PR (Actions · qir-alliance/pyqir (github.com))
  • Create a new release (New release · qir-alliance/pyqir (github.com)) of these artifacts with the new tag, where the title should be the new tag, and the description should contain the section in the change log that was previously captured under "Unreleased". Set the pre-release tag.

Evaluator: Can't evaluate QIR file

Using NonadaptiveEvaluator.eval crashes the process when given a QIR .ll file and fails with an encoding exception when given a bitcode version of the same file. Nothing custom was done to generate the QIR (straight from the Q# compiler) or bitcode (just passed directly to llvm::WriteBitcodeToFile). The code itself is very basic - just one of the many Q# examples floating around. pyqir was also installed from pip, not a local version.

Bitcode issue is likely due to us using LLVM 13 which I believe is a different version than the one you're using, so may not be an issue per-say, but attaching it if you want to look at it further. There might be a way to strip version/encoding from the bitcode, but haven't spent too much time digging as-of yet.

Just mention if you need any additional info.

QIR and bitcode files.

Versions/System Info

py-evaluator: 0.4.0-alpha.1
OS: Windows 10 Pro, 21H1
Python: 3.8
Rust: 1.60
Q# SDK: 0.24.208024

Publish to PyPI

  • Clean up individual project READMEs
  • Add GH page links to READMEs for API docs
  • Update Version numbers
  • Fix #27
  • Fix module index in docs

Create v0.3.0a1 Release

  • Create a PR to update the change log and the version numbers in the installing.md with the new tag for the release
  • Download the artifacts from the CI of that PR (Actions · qir-alliance/pyqir (github.com))
  • Create a new release (New release · qir-alliance/pyqir (github.com)) of these artifacts with the new tag, where the title should be the new tag, and the description should contain the section in the change log that was previously captured under "Unreleased". Set the pre-release tag.
  • Merge the release PR (the one from the first task)

Create v0.4.1a1 Release

  • Create a PR to update the change log and the version numbers in the installing.md with the new tag for the release
  • Merge the release PR (the one from the first task)
  • Download the artifacts from the CI of that PR (Actions · qir-alliance/pyqir (github.com))
  • Create a new release (New release · qir-alliance/pyqir (github.com)) of these artifacts with the new tag, where the title should be the new tag, and the description should contain the section in the change log that was previously captured under "Unreleased". Set the pre-release tag.

Update minimum supported Python version to 3.7

Python 3.6 is EOL via PEP 494 as of Dec 2021.

  • Configure smoke test images to use 3.7 - This is problematic as the package sources don't ship 3.7, or if they do, they don't have the dist/setup utils for 3.7 needed.
  • Change ABI feature compatibility to 3.7
  • Update PyO3
  • Update Maturin
  • Update documentation

Create v0.2.0a1 Release

Mini "release process":

  • Create a PR to update the change log and the version numbers in the installing.md with the new tag for the release
  • Download the artifacts from the CI of that PR (Actions · qir-alliance/pyqir (github.com))
  • Create a new release (New release · qir-alliance/pyqir (github.com)) of these artifacts with the new tag, where the title should be the new tag, and the description should contain the section in the change log that was previously captured under "Unreleased". Set the pre-release tag.
  • Merge the release PR (the one from the first task)

Create v0.4.0a1 Release

  • Create a PR to update the change log and the version numbers in the installing.md with the new tag for the release
  • Merge the release PR (the one from the first task)
  • Download the artifacts from the CI of that PR (Actions · qir-alliance/pyqir (github.com))
  • Create a new release (New release · qir-alliance/pyqir (github.com)) of these artifacts with the new tag, where the title should be the new tag, and the description should contain the section in the change log that was previously captured under "Unreleased". Set the pre-release tag.

Publish binaries for Apple silicon

Is your feature request related to a problem? Please describe.
It would be useful for our CI to use wheels for M1. Since we're running tests for that platform.

Clean up qirlib generator API to match current Python API

The qirlib generator API contains some older design choices that don't match the new pyqir-generator API (#32), and should be reconsidered:

  • Instructions use string identifiers for results and qubits, but the new Python API abstracts away the string. It would be simpler and cheaper to use numeric identifiers.
  • QuantumRegister represents a single qubit, but ClassicalRegister represents a sequence of results, which is asymmetric. The Python API treats them symmetrically.

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.