Anirban166 / testComplexity

Asymptotic complexity testing framework
https://anirban166.github.io/testComplexity/
Other
36 stars 2 forks source link
complexity-tester r-package

Build status (Travis CI) Code Coverage (covr/codecov) Codacy Badge The R project for statistical computing GSoC project GitHub License

Abstract Objectives Installation Functions Usage Plotting Benchmarking Testing Resources

Abstract

R package developers currently use ad-hoc tests of asymptotic computational complexity via empirical timings of functions and visual diagnostic plots. However, there is no framework in R for systematically testing the empirical computational complexity of functions, which tends to be a problem because such a testing framework could be essential for identifying big speed gains in R code as well. In response to this, testComplexity provides a suite of functions that will be useful for testing and thereby improving the speed of various algorithms/functions in R.

Objectives

Since algorithms are used in every sphere of research, this package potentially caters to all sorts of R-users, following different fields of study. At its current state, it has been tested on algorithms pertaining to changepoint detection, sorting, constrained optimal segmentation/partitioning, plus a few common ones from base R such as substring and gregexpr.

Installation

Use devtools or remotes to fetch the package from this repository:

if(!require(devtools)) install.packages("devtools")
devtools::install_github("Anirban166/testComplexity")
if(!require(remotes)) install.packages("remotes")
remotes::install_github("Anirban166/testComplexity")

Functional Flow

__________________ R Files _______________________________________ Additional Details _____________________________
testComplexity                              @ returns              @ type                    @ commit-branch(es) 
├──> asymptoticTimings                    : data.frame             timings quantifier        master
│    ├──> asymptoticTimeComplexityClass   :   ├──> string          ↑ complexity classifier   master
│    └──> plotTimings                     :   └──> ggplot object   ↑ plotter                 master/Plotfunc
│
├──> asymptoticMemoryUsage                : data.frame             memory-usage quantifier   Memtest
│    ├──> asymptoticMemoryComplexityClass :   ├──> string          ↑ complexity classifier   Memtest
│    └──> plotMemoryUsage                 :   └──> ggplot object   ↑ plotter                 Memtest/Plotfunc
│
├──> asymptoticComplexityClass            : string                 complexity classifier     Generalizedcomplexity
│    └──> asymptoticComplexityClassifier  :   ↑ string             ↑ complexity classifier   Generalizedcomplexity
│
├──> expect_complexity_class              : -/-                    test function             Testfunc
│    └──> expect_time_complexity          : -/-                    ↑  test function          Testfunc
│         ├──> expect_linear_time         : -/-                    ↑↑ test function          Testfunc
│         ├──> expect_loglinear_time      : -/-                    ↑↑ test function          Testfunc
│         └──> expect_quadratic_time      : -/-                    ↑↑ test function          Testfunc
│
└──> testthat                                                                                
     ├──> testsfortestComplexity                                   unit-tester               All branches
     ├──> testsforConstrainedchangepointmodelalgos                 unit-tester               Testfunc
     └──> testsforRegularfunctions                                 unit-tester               Testfunc
____________________________________________________________________________________________________________________

Usage

To get started, please check the general vignette which highlights all the features, enlists the different types of function categories existent in the package and describes the functionality offered by the underlying user-oriented functions via a set of textual elucidations with one example taken to be discussed throughout for each of them.

For a quick overview of the main functionality (obtaining quantified benchmarks and subsequently computing the time/memory complexity class), please check the examples below.

df.bubble.time <- asymptoticTimings(bubble.sort(sample(1:100, N, replace = TRUE)), data.sizes = 10^seq(1, 3, by = 0.5)) data.table(df.bubble.time) Timings Data sizes 1: 91902 10 2: 39402 10 3: 34701 10 4: 33101 10 5: 33201 10


496: 64490501 1000 497: 59799101 1000 498: 63452200 1000 499: 62807201 1000 500: 59757102 1000

df.bubble.memory <- asymptoticMemoryUsage(bubble.sort(sample(1:100, N, replace = TRUE)), data.sizes = 10^seq(1, 3, by = 0.1)) data.table(df.bubble.memory) Memory usage Data sizes 1: 87800 10.00000 2: 2552 12.58925 3: 2552 15.84893 4: 2552 19.95262 5: 2552 25.11886


17: 7472 398.10717 18: 8720 501.18723 19: 10256 630.95734 20: 12224 794.32823 21: 14696 1000.00000

```r
# Example 2 | Testing PeakSegPDPA, an algorithm for constrained changepoint detection: (expected log-linear time and memory complexity)
data.vec <- rpois(N, 1)
df.PDPA.time <- asymptoticTimings(PeakSegOptimal::PeakSegPDPA(count.vec = data.vec, max.segments = 3L), data.sizes = 10^seq(1, 4, by = 0.1))
data.table(df.PDPA.time)
       Timings Data sizes
  1:    248701         10
  2:    120302         10
  3:    125701         10
  4:    133301         10
  5:    146500         10
 ---                     
696: 405597501      10000
697: 408335001      10000
698: 338544401      10000
699: 404081901      10000
700: 399575501      10000

df.PDPA.memory <- asymptoticMemoryUsage(PeakSegOptimal::PeakSegPDPA(count.vec = data.vec, max.segments = 3L), data.sizes = 10^seq(1, 4, by = 0.1))
data.table(df.PDPA.memory)
    Memory usage Data sizes
 1:         6256   10.00000
 2:         7024   12.58925
 3:         7432   15.84893
 4:         8560   19.95262
 5:         9496   25.11886
 --- 
25:       447792 2511.88643
26:       562336 3162.27766
27:       706512 3981.07171
28:       887792 5011.87234
29:      1116240 6309.57344

Plotting

For obtaining a visual description of the trend followed between runtimes/memory-usage vs data sizes so as to diagnose the complexity result(s) (the traditional method, for visual verification say), simple plots can be crafted. They are roughly grouped into:

df.substring$expr = "substring" df.PeakSegPDPA$expr = "PeakSegPDPA" df.cDPA$expr = "cDPA" df.gregexpr$expr = "gregexpr" df.fpop$expr = "fpop" df.opart$expr = "opart"

plot.df <- rbind(df.substring, df.PeakSegPDPA, df.cDPA, df.gregexpr, df.fpop, df.opart) ggplot(plot.df, aes(x = Data sizes, y = Timings)) + geom_point(aes(color = expr)) + geom_line(aes(color = expr)) + labs(x = "Data sizes", y = "Runtime (in nanoseconds)") + scale_x_log10() + scale_y_log10() + ggtitle("Timings comparison plot", subtitle = "Linear vs Log-linear vs Quadratic complexities") + ggthemes::theme_pander()

<img width = "100%" src = "Images/cp2.png"> <br>

Feel free to include more functions and increase the number of data sizes for a more comprehensive outlook: <br>

<img width = "100%" src = "Images/cp.png"> <br>
- **Generalized Linear Model based Plots** <br>
`ggfortify`, an extension of `ggplot2`, can be used to produce diagnostic plots for generalized linear models with the same formulae as used in the complexity classification functions: <br>
```r
library(ggfortify)
df <- asymptoticTimings(PeakSegDP::cDPA(rpois(N, 1), rep(1, length(rpois(N, 1))), 3L), data.sizes = 10^seq(1, 4 by = 0.1))
glm.plot.obj <- glm(Timings~`Data sizes`, data = df)
ggplot2::autoplot(stats::glm(glm.plot.obj)) + ggthemes::theme_gdocs()


Benchmarking

Among a few options,

Testing

Source Package Function Article Link
base gregexpr Quadratic to linear transition for substring and gregexpr
base substring Quadratic to linear transition for substring and gregexpr
fpop Fpop fpop::Fpop(), a log-linear time segmentation algorithm
gfpop gfpop gfpop::gfpop(), a log-linear time algorithm for constrained changepoint detection
opart gaussian opart::gaussian(), a quadratic time optimal partioning algorithm
PeakSegDP cDPA PeakSegDP::cDPA, a quadratic time constrained dynamic programming algorithm
changepoint cpt.mean PELT and SegNeigh algorithms for changepoint::cpt.mean()
PeakSegOptimal PeakSegPDPA PeakSegOptimal::PeakSegPDPA, a log-linear time algorithm for constrained changepoint detection

A complexity-wise ordered list with functional instances for the aforementioned set of functions can be found here.

Resources

In addition to the readme content, the web version includes quick reference to functions plus vignettes for some use-cases. For blog posts, please check the links below:




Google Summer of Code Project Link Email GitHub Link LinkedIn Link