GithubHelp home page GithubHelp logo

astro-informatics / s2wav Goto Github PK

View Code? Open in Web Editor NEW
12.0 3.0 0.0 11.93 MB

Differentiable and accelerated wavelet transform on the sphere with JAX

Home Page: https://astro-informatics.github.io/s2wav/

License: MIT License

Jupyter Notebook 10.51% Python 89.49%
dictionary differentiable-programming jax sphere spherical-harmonics wavelet-transform

s2wav's Introduction

image image image image image All Contributors Open In Colab

Differentiable and accelerated wavelet transform on the sphere

S2WAV is a python package for computing wavelet transforms on the sphere and rotation group, both in JAX and PyTorch. It leverages autodiff to provide differentiable transforms, which are also deployable on modern hardware accelerators (e.g. GPUs and TPUs), and can be mapped across multiple accelerators.

More specifically, S2WAV provides support for scale-discretised wavelet transforms on the sphere and rotation group (for both real and complex signals), with support for adjoints where needed, and comes with a variety of different optimisations (e.g. precompute or not, multi-resolution algorithms) that one may select depending on available resources and desired angular resolution $L$. S2WAV is a sister package of S2FFT, both of which are part of the SAX project, which aims to provide comprehensive support for differentiable transforms on the sphere and rotation group.

Tip

As of version 1.0.0 S2WAV also provides partial frontend support for PyTorch. In future this will be expanded to full support. Also note that this release also provides JAX support for existing C spherical harmonic libraries, specifically SSHT. This works be wrapping python bindings with custom JAX frontends. Note that currently this C to JAX interoperability is limited to CPU.

Wavelet Transform โšก

S2WAV is an updated implementation of the scale-discretised wavelet transform on the sphere, which builds upon the papers of Leistedt et al 2013 and McEwen et al 2017. This wavelet transform is designed to have excellent localisation and uncorrelation properties, and has been successfully adopted for various applications e.g. scattering transforms on the sphere McEwen et al 2022. The wavelet dictionary is constructed by tiling the harmonic line with infinitely differentiable Cauchy-Schwartz functions, which can straightforwardly be performed in an efficient multiresolution manner, as in the Euclidean case. This is what the directional wavelet filters look like in pixel space.

Installation ๐Ÿ’ป

The Python dependencies for the S2WAV package are listed in the file requirements/requirements-core.txt and will be automatically installed into the active python environment by pip when running

pip install s2wav     

This will install the core functionality which includes JAX support (including PyTorch support).

Alternatively, the S2WAV package may be installed directly from GitHub by cloning this repository and then running

pip install .

from the root directory.

Unit tests can then be executed to ensure the installation was successful by first installing the test requirements and then running pytest

pip install -r requirements/requirements-tests.txt
pytest tests/  

Documentation for the released version is available here. To build the documentation locally run

pip install -r requirements/requirements-docs.txt
cd docs 
make html
open _build/html/index.html

Usage ๐Ÿš€

To import and use S2WAV is as simple follows:

# Compute wavelet coefficients
f_wav, f_scal = s2wav.analysis(f, L, N)

# Map back to signal on the sphere 
f = s2wav.synthesis(f_wav, f_scal, L, N)

Note

However we strongly recommend that the multiresolution argument is set to true, as this will accelerate the transform by a factor of the total number of wavelet scales, which can be around an order of magnitude.

C JAX Frontends for SSHT ๐Ÿ’ก

S2WAV also provides JAX support for SSHT, which is a highly optimised C library which implements the underlying spherical harmonic transforms. This works by wrapping python bindings with custom JAX frontends. Note that this C to JAX interoperability is currently limited to CPU.

For example, one may call these alternate backends for the spherical wavelet transform by:

# Compute wavelet coefficients using SSHT C library backend
f_wav, f_scal = s2wav.analysis(f, L, N, use_c_backend=True)

# Map back to signal on the sphere using SSHT C library backend
f = s2wav.synthesis(f_wav, f_scal, L, N, use_c_backend=True)

These JAX frontends supports out of the box reverse mode automatic differentiation, and under the hood is simply linking to the C packages you are familiar with. In this way S2fft enhances existing packages with gradient functionality for modern scientific computing or machine learning applications!

For further details on usage see the associated notebooks.

Contributors โœจ

We strongly encourage contributions from any interested developers; a simple example would be adding support for new wavelet filters e.g. spherical needlets Chan et al 2016 or spherical ridgelets McEwen & Price 2020! Thanks goes to these wonderful people (emoji key):

Matt Price
Matt Price

๐Ÿ’ป ๐Ÿ‘€ ๐Ÿ“– ๐ŸŽจ
Jason McEwen
Jason McEwen

๐Ÿ‘€ ๐ŸŽจ
Alicja Polanska
Alicja Polanska

๐Ÿ’ป ๐Ÿ‘€
Jessica Whitney
Jessica Whitney

๐Ÿ’ป ๐Ÿ‘€

Attribution

A BibTeX entry for S2WAV is:

@article{price:s2wav, 
   author = {Matthew A. Price and Alicja Polanska and Jessica Whitney and Jason D. McEwen},
    title = {"Differentiable and accelerated directional wavelet transform on the sphere and ball"},
   eprint = {arXiv:2402.01282},
     year = {2024}
}

we also request that you cite the following paper

@article{price:s2fft, 
   author      = "Matthew A. Price and Jason D. McEwen",
   title       = "Differentiable and accelerated spherical harmonic and Wigner transforms",
   journal     = "Journal of Computational Physics, submitted",
   year        = "2023",
   eprint      = "arXiv:2311.14670"        
}

in which the core underlying algorithms for the spherical harmonic and Wigner transforms are developed.

License ๐Ÿ“

Copyright 2024 Matthew Price, Jessica Whtiney, Alicja Polanska, Jason McEwen and contributors.

S2WAV is free software made available under the MIT License. For details see the LICENSE file.

s2wav's People

Contributors

alicjapolanska avatar allcontributors[bot] avatar cosmomatt avatar jasonmcewen avatar jesswhitney avatar

Stargazers

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

Watchers

 avatar  avatar  avatar

s2wav's Issues

Readme

Once the project has progressed sufficiently, overhaul the GitHub readme, the (hidden) .pip readme, and the top level of the documentation. Update all badges, ensuring that they link to the correct urls, and update the package description and contributors.

Difficulty:
-Very Low

Synthesis Wavelet Transform

Translate of the synthesis wavelet transform from s2let (c) to s2wav (base python).

Difficulty:

  • High

Background:

  • The wavelet transform can be performed as either analysis or synthesis, both with corresponding adjoint operators. Here we consider the synthesis transform which should map from wavelet coefficients to a signal on the sphere (thereby "synthesising", or re-creating , the signal).

S2let File to translate:

  • can be found here ignore the function on this line as it is no longer necessary.

S2Wav File location:

  • should be put in the main package directory here

Notes:

  • These functions will require some helper functions from ssht and so3 You should be able to access the necessary functions from the python versions of these packages for the time being. Look here for SSHT and here for so3 functions.

Analysis Wavelet Transform

Translate of the analysis wavelet transform from s2let (c) to s2wav (base python).

Difficulty:

  • High

Background:

  • The wavelet transform can be performed as either analysis or synthesis, both with corresponding adjoint operators. Here we consider the analysis transform which should map from a signal on the sphere to wavelet coefficients.

S2let File to translate:

  • can be found here ignore the function on this line as it is no longer necessary.

S2Wav File location:

  • should be put in the main package directory here

Notes:

  • These functions will require some helper functions from ssht and so3 You should be able to access the necessary functions from the python versions of these packages for the time being. Look here for SSHT and here for so3 functions.

Logging

Using the logging module already included, ensure logging is included to important functions within the package (where appropriate)

Difficulty:
-Very Low

Generating Filters

Translate the core functions which generate the wavelet filters from s2let (c) to s2wav (base python).

Difficulty:

  • Medium

Background:

  • The wavelet transform works by "tiling" the harmonic domain with sets of filters, which can be designed for particular applications. Using filter functions (which must already be translated) we can essentially iteratively generate a vector of filters.

S2let File to translate:

  • can be found here but don't include lines 41-110.

S2Wav File location:

  • should be put in the main package directory here

Notes:

  • Remember that in python we don't need to allocate memory, so we can just start with standard numpy arrays of the correct dimension and ignore commands like "calloc" etc.

Support different sampling schemes

Currently we support mw sampling but we should make sure we support mwss, dh and healpix too. I suspect these are all straightforward to support, with much of the heavy lifting being done externally to this package.

Wavelets in real space

Hi, I was just wondering if there is a built-in implementation to transform the harmonic coefficients of the wavelets back into real space to visualize the wavelets on the sphere as depicted in the ReadMe.

Dimension helper Functions

Translate functions which you can call to check dimensions of vectors from s2let (c) to s2wav (base python).

Difficulty:

  • Low

Background:

  • The wavelet transform (and the spherical harmonic transform) are based on sampling theorems which have a variety of different solutions. Given a slightly different sampling, or a different configuration of the wavelets, the dimensionality of vectors involved in the transform will change in a (hopefully quite straightforward!) way.

S2let Files to translate:

  • all functions in here
  • functions from line 41 to 110 in here

S2Wav File location:

  • should be put in the main package directory here

Notes:

  • Some of these functions call-back to ssht functions (e.g. ssht_sampling_mw_ss_nphi) which can be found here.

Automatic JAX Filters

Currently both the NumPy and JAX wavelet transforms will not automatically generate wavelet filters if non are passed. However, the docstrings imply that filters is an optional argument, which is not the case.

We should either:

  • Clarify the docstrings and force users to provide filters.
  • Add a catch to automatically generate filters within the transforms.

Add JAX Synthesis transform

  • Synthesis JAX transform
  • Unit test for transform

This is a good issue to pick up as a first jump into JAX. I would recommend first reading this introduction for JAX. A few hints would be:

  • Don't recompute the filters every pass, these can be computed offline once and then passed as an argument (as their memory footprint is relatively small).
  • JAX has direct equivalents to almost all numpy functions (just import jax.numpy as jnp and switch np to jnp)
  • The way you index into arrays is more formal.
    e.g. rather than x[y] = z you must run x = x.at[y].set(z) etc.

Also remember to consider static arguments (ones that don't change between evaluations i.e. L, this can help when JITing things.

Move from 1D to Tensors

Currently we're working with flattened 1D arrays (as is the norm for C programming), but we should switch to multi-dimensional arrays (and later Tensors) for python.

`filters_axisym` less stable at high `L` than `pys2let.axisym_wav_l`

Consider the following

import numpy as np
import pys2let
import s2wav

L, j_min, B = 1024, 2, 3
old_kappa = pys2let.axisym_wav_l(B, L, j_min)[1]
new_kappa = s2wav.filter_factory.filters.filters_axisym(L, J_min=j_min, lam=B)[0][j_min:].T
np.testing.assert_equal(old_kappa.shape, new_kappa.shape)
np.testing.assert_equal((old_kappa == np.inf).sum(), 0)
np.testing.assert_equal((new_kappa == np.inf).sum(), 2)  # infinite values

The new method of calculating kappa results in some infinite values, which should be close to 1 or in practical terms equal to 1, and not infinity. This will cause issues when making tiling plots, as scipy.interpolate.pchip cannot handle infinite values, e.g. https://github.com/astro-informatics/sleplet/blob/f61b2af612d0c6a3a462770dbdffb8ba463a3a40/examples/arbitrary/south_america/tiling_south_america.py#L16-L46

Basic Math Functions

Translate basic math tiling functions from s2let (c) to s2wav (base python).

Difficulty:

  • Low (This is a nice first issue to pick up).

Background:

  • The wavelet transform works by "tiling" the harmonic domain with sets of filters, which can be designed for particular applications. To compute the tiling filters we need to evaluate the functions which generate those filters, and so it is useful to have those functions stored and easily accessed by more complicated parts of the package.

S2let File to translate:

S2Wav File location:

  • should be put in the main package directory here

Notes:

  • There are also a few extra functions in the s2let file that are useful, but everything below this line can be ignored for the time being!

Parameters Dictionary (Utils)

Add a utility function to take parameters and store them in a dictionary to reduce floating arguments

Difficulty:

  • Low

Background:

  • In S2let we used structures to store all the parameters we need to specify the transform we want to do. We need a similar interface for S2Wav to handle parameters. A good way to do this is with Python dictionaries.

S2Let File:

  • The parameters we should include can be found in the s2let parameters structure here.
  • This is the type of interface you may want to consider.

S2Wav File location:

  • should be put in the test directory here

Notes:

  • There isn't a canonical approach for storing parameters in Python, however a dictionary is a good way. Feel free to search around and if you find a better way then great, just make sure we're consistent throughout!

Paper Review

As a preliminary exercise it would be worthwhile reading through the original ssht paper to understand the underlying spherical harmonic transforms, and the original s2let paper to understand roughly how the wavelet transform operates on the sphere.

As always feel free to throw any questions my way, these can be a little tricky to understand at first.

Analysis Adjoint Wavelet Transform

Translate the adjoint of the analysis wavelet transform from s2let (c) to s2wav (base python).

Difficulty:

  • High

Background:

  • The wavelet transform can be performed as either analysis or synthesis, both with corresponding adjoint operators. Here we consider the adjoint of the analysis transform which should map from wavelet coefficients to a signal on the sphere (kind of).

S2let File to translate:

  • can be found here ignore the function on this line as it is no longer necessary.

S2Wav File location:

  • should be put in the main package directory here

Notes:

  • These functions will require some helper functions from ssht and so3 You should be able to access the necessary functions from the python versions of these packages for the time being. Look here for SSHT and here for so3 functions.

Docstrings

Ensure all docstrings (the summary provided at the top of each function, describing what inputs/outputs the functions should expect and briefly what the function does) are up to date and in line with best practices -- see this guide.

Difficulty:

  • Very Low

Add JAX analysis transform

  • Analysis JAX transform
  • Unit test for transform

This is a good issue to pick up as a first jump into JAX. I would recommend first reading this introduction for JAX. A few hints would be:

  • Don't recompute the filters every pass, these can be computed offline once and then passed as an argument (as their memory footprint is relatively small).
  • JAX has direct equivalents to almost all numpy functions (just import jax.numpy as jnp and switch np to jnp)
  • The way you index into arrays is more formal.
    e.g. rather than x[y] = z you must run x = x.at[y].set(z) etc.

Also remember to consider static arguments (ones that don't change between evaluations i.e. L, this can help when JITing things.

Support upsampling symmetry

See section 3 of this paper . In the code you'll see this coming up with terms like this, where for a given j the bandlimit will be different. Notice also that this shouldn't just be an upper limit but also a lower limit, i.e. a given wavelet scale j will only have non-zero harmonic coefficients between some restricted range L_0j to L_j. This part of the code update should be relatively straightforward to implement in numpy, but may be more difficult when we come to write JAX versions.

Flesh out unit tests

Make sure we're testing where appropriate. Should try and get code coverage > 90% (roughy rule of thumb).

Code Review

As a preliminary exercise it would be worthwhile working through the demo (example) scripts provided by ssht (found here) and then reading through the examples (sadly only in C) provided by s2let (e.g. here).

As always feel free to throw any questions my way, these can be a little tricky to understand at first.

Create a logo

Every good package should have an eye catching logo! Currently we just have a placeholder.

Documentation

Configure auto-docs (requires google style documentation to be done first!), and add details where necessary to the documentation.

Difficulty:

  • Low (but can be time consuming)

Synthesis Adjoint Wavelet Transform

Translate adjoint of the synthesis wavelet transform from s2let (c) to s2wav (base python).

Difficulty:

  • High

Background:

  • The wavelet transform can be performed as either analysis or synthesis, both with corresponding adjoint operators. Here we consider the adjoint of the synthesis transform which should map from a signal on the sphere to wavelet coefficients.

S2let File to translate:

  • can be found here ignore the function on this line as it is no longer necessary.

S2Wav File location:

  • should be put in the main package directory here

Notes:

  • These functions will require some helper functions from ssht and so3 You should be able to access the necessary functions from the python versions of these packages for the time being. Look here for SSHT and here for so3 functions.

Support reality symmetry

Exploits the conjugate symmetry, i.e. f_{l, -m} = (-1)^m f^*_{l, m}, to avoid computing the coefficients for negative m, as they are just conjugate to the positive values. Consequently this reduces both the number of computations and the amount of memory required by ~ a factor of 2. See this line in S2FFT where we generate an explicitly real signal, from which hopefully its clear what is meant by conjugate symmetry

Move dependency from SSHT/SO3 to S2FFT

Currently we call SSHT and SO3 to perform the harmonic and Wigner transforms where required (see e.g. here). We should switch this out to S2FFT functionality when available.

Testing metric functions

Add testing metrics to be used for testing package functions

Difficulty:

  • Medium

Metric list:

  • Mean squared error, this can be used to evaluate round-trip error e.g. x -> x' = inverse( forward( x ) )
  • Dot test, this tests whether two transforms are adjoint to one another (see here for a discussion).

S2Wav File location:

  • should be put in the test directory here

Notes:

  • These metrics should be available to all tests but not shipped as part of the package. The best way to do this is by using Pytest's fixtures api.

`s2wav` is slow for filter construction at moderate `L`

Not sure if I'm missing something or the library is designed only for massive GPUs. I'm running on an M1 MacBook Pro with top specs. This is specifically for s2wav.filter_factory.filters.filters_axisym..., but I've noticed similar for other functions (& s2fft).

The following works fine for me from L=16 to L=2048. It is fine, but feels a little slow.

import pys2let
import s2wav
import time

B = 3
J_MIN = 2
ELL_MIN = 4
ELL_MAX = 11

for ell in range(ELL_MIN, ELL_MAX + 1):
    L = 2**ell
    t0 = time.time()
    pys2let.axisym_wav_l(B, L, J_MIN)
    t1 = time.time()
    s2wav.filter_factory.filters.filters_axisym(L, J_MIN, B)
    t2 = time.time()
    s2wav.filter_factory.filters.filters_axisym_vectorised(L, J_MIN, B)
    t3 = time.time()
    s2wav.filter_factory.filters.filters_axisym_jax(L, J_MIN, B)
    t4 = time.time()
    print(
        f"L={L:>4} | pys2let={t1-t0:.0e}s | s2wav={t2-t1:.0e}s | "
        f"s2wav_vec={t3-t2:.0e}s | s2wav_jax={t4-t3:.0e}s",
    )
# L=  16 | pys2let=7e-05s | s2wav=7e-03s | s2wav_vec=7e-03s | s2wav_jax=9e-02s
# L=  32 | pys2let=9e-05s | s2wav=1e-02s | s2wav_vec=1e-02s | s2wav_jax=2e-01s
# L=  64 | pys2let=2e-04s | s2wav=3e-02s | s2wav_vec=3e-02s | s2wav_jax=4e-01s
# L= 128 | pys2let=4e-04s | s2wav=5e-02s | s2wav_vec=5e-02s | s2wav_jax=8e-01s
# L= 256 | pys2let=7e-04s | s2wav=1e-01s | s2wav_vec=1e-01s | s2wav_jax=2e+00s
# L= 512 | pys2let=1e-03s | s2wav=2e-01s | s2wav_vec=2e-01s | s2wav_jax=5e+00s
# L=1024 | pys2let=3e-03s | s2wav=4e-01s | s2wav_vec=4e-01s | s2wav_jax=2e+01s
# L=2048 | pys2let=7e-03s | s2wav=8e-01s | s2wav_vec=8e-01s | s2wav_jax=5e+01s

Now if we ramp up to L=4096, it returns something like 1e-02s, and s2wav hangs (and at higher L just never finishes).

import pys2let
import s2wav
import time

B = 3
J_MIN = 2
L = 4096

t0 = time.time()
pys2let.axisym_wav_l(B, L, J_MIN)
t1 = time.time()
print(f"{t1 - t0:.0e}")

wavelet transform demo error

Hello,

I want to try the s2wav lib according to astro-informatics/s2let#50 (comment). When I try the code demo, I found an error occurs in s2wav/transforms/jax_wavelets.py", line 260. It seams like that the filters is needful to perform wavelet transform(but it should be optional ). Did I do anything wrong?

Here is my test code:

import s2wav
import numpy as np
L = 128
N = 1
f = np.ones((L, 2*L-1))
f_wav, f_scal = s2wav.analysis(f, L, N)
f = s2wav.synthesis(f_wav, f_scal, L, N)

Here is the error:

Traceback (most recent call last):
  File "test_wav.py", line 7, in <module>
    f_wav, f_scal = s2wav.analysis(f, L, N)
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/traceback_util.py", line 166, in reraise_with_filtered_traceback
    return fun(*args, **kwargs)
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/pjit.py", line 250, in cache_miss
    outs, out_flat, out_tree, args_flat, jaxpr = _python_pjit_helper(
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/pjit.py", line 158, in _python_pjit_helper
    args_flat, _, params, in_tree, out_tree, _, jaxpr = infer_params_fn(
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/api.py", line 300, in infer_params
    return pjit.common_infer_params(pjit_info_args, *args, **kwargs)
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/pjit.py", line 499, in common_infer_params
    jaxpr, consts, canonicalized_out_shardings_flat = _pjit_jaxpr(
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/pjit.py", line 961, in _pjit_jaxpr
    jaxpr, final_consts, out_type = _create_pjit_jaxpr(
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/linear_util.py", line 345, in memoized_fun
    ans = call(fun, *args)
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/pjit.py", line 914, in _create_pjit_jaxpr
    jaxpr, global_out_avals, consts = pe.trace_to_jaxpr_dynamic(
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/profiler.py", line 314, in wrapper
    return func(*args, **kwargs)
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/interpreters/partial_eval.py", line 2155, in trace_to_jaxpr_dynamic
    jaxpr, out_avals, consts = trace_to_subjaxpr_dynamic(
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/interpreters/partial_eval.py", line 2177, in trace_to_subjaxpr_dynamic
    ans = fun.call_wrapped(*in_tracers_)
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/jax/_src/linear_util.py", line 188, in call_wrapped
    ans = self.f(*args, **dict(self.params, **kwargs))
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/s2wav/transforms/jax_wavelets.py", line 260, in analysis
    jnp.conj(filters[0]),
jax._src.traceback_util.UnfilteredStackTrace: TypeError: 'NoneType' object is not subscriptable

The stack trace below excludes JAX-internal frames.
The preceding is the original exception that occurred, unmodified.

--------------------

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test_wav.py", line 7, in <module>
    f_wav, f_scal = s2wav.analysis(f, L, N)
  File "/home/gaowenxuan/anaconda3/envs/py38/lib/python3.8/site-packages/s2wav/transforms/jax_wavelets.py", line 260, in analysis
    jnp.conj(filters[0]),
TypeError: 'NoneType' object is not subscriptable

I have used the pytest to test s2wav and I pass the test.

========================================================== test session starts ===========================================================
platform linux -- Python 3.8.16, pytest-7.3.1, pluggy-1.0.0
rootdir: /home/gaowenxuan/Code/s2wav
configfile: pytest.ini
collected 288 items                                                                                                                      

tests/test_filters.py ........................................                                                                     [ 13%]
tests/test_gradients.py ........                                                                                                   [ 16%]
tests/test_wavelets.py ..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss                            [ 44%]
tests/test_wavelets_base.py ..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss.. [ 79%]
ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss..ss                                                                         [100%]

============================================== 168 passed, 120 skipped in 447.66s (0:07:27) ==============================================

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.