Wednesday, November 6, 2013

When will Sagemath Cloud hit 100,000 user accounts?

The Sagemath Cloud is an online environment for computational mathematics. Get an account, log in, and your web-browser transforms into almost everything you need to study algebra, calculus, numerics, statistics, number and game theory, (computational aspects of) physics, chemistry and all other quantitative sciences. It's built around all sorts of tools and utilities offered in a proper Linux environment. All files can be shared with collaborators and edited in real-time without stepping on each other's toes. Oh wait, there is also a LaTeX editor: given you know LaTeX, qualitative sciences are also covered ;-)

But beware, this posting will only talk about the very first: accounts. Below, I'll explain how I managed to create this page, the plot below and how I let it update automatically every hour - all done solely by SMC!

SMC accounts over time


Sagemath Cloud is very public about what is happening on its servers. This stats link gives you the raw data for the current overall load of the machines. What I did over the last weeks was the following: Every hour I've downloaded this stats json file, parsed it, accumulated some interesting numbers, and stored the processed data in a CSV file.

I did all this in SMC, because it allows you to run your own crontab files. Cron periodically goes through all kind of files to figure out if there is a job to do. Just enter a magical line in your own crontab file, and the given command is run whenever you tell it to do so. You do not have to be logged in!

In my case, it's like this:
First, edit your crontab file:
$ crontab -e
Then enter this line:
0 * * * * python $HOME/get.py

The $HOME is important, because you have to specify the full path where your script sits. (By the way, if you have to start something when the SMC server reboots, use the @reboot descriptor)

So, what is get.py doing? It uses the wonderful requests Python library to retrieve and parse the stats url and extracts some data. Then it appends a line to a CSV file.

Two minutes later (the crontab line starts with a "2"), another script is called which processes this CSV file. It reads the columns via pandas, properly parses the dates into time-series, and allows me to do all sorts of analysis, transformations and plots with it. For example, the plot shown above overlays the raw time series plot with OLS fits done by statsmodels for selected time ranges (where it looks "flat"). We can see the growth trends clearly! Even more important, so far the growth increases and hence we are watching the exponential growth phase as part of the beginning of the usual logistic growth.

The other plots on this statistics page show aggregated time-series data. For example, the plot for the number of concurrent connections is also increasing and reassures that SMC is scaling well.
Concurrent connections to SMC
On this statistics page, there are also a few "dynamic" fields in the HTML content. This is done by jinja2 in such a way, that the template "stats.tmpl" contains the HTML code and "mustache"-style variables. Jinja2 renders this template with some variables and that's it.

import jinja2 as j2
env = j2.Environment(loader=j2.FileSystemLoader("."))
stats = env.get_template("stats.tmpl")
data = {
        'date' : "%s UTC" % datetime.utcnow(),
        'recent_data' : totals.ix[-24:].to_html()
}
with open("stats.html", "wb") as output:
    output.write(stats.render(**data))

The last step of the script is to actually publish the files to the webserver. That's rather straight forward. First and only once, create ssh keys via ssh-keygen without a password and then use ssh-copy-id -i ~/.ssh/id_dsa.pub name@server to copy over your keys. Subsequent ssh connections will be established without any questions asked, because the remote server knows your identity. I'm using scp to copy the files: scp *.png stats.html name@remote-server:~/target/dir/

Last but not least, when will SMC hit the 100,000 user mark? In less than 6 days the 10,000 mark should be crossed and I hope the trend continues into the upward direction.

Monday, May 27, 2013

Sage announces 3 GSoC projects

Sage is pleased to announce three Google Summer of Code projects for 2013. They focus on speed improvements of symbolic functions, simplifying the distribution and installation procedure on Debian/Linux and ubiquitous accessibility of Sage on the Android platform.


Mathematical Functions Library

Eviatar Bach –  University of British Columbia in Vancouver, Canada
(Mentor: Flavia Stan, Backup: Burcin Erocal)


Sage interfaces with multiple third-party libraries, such as MPFR, GSL, GP/PARI, mpmath, and Maxima, for numerical evaluation of special functions. There are significant discrepancies between these backends in the performance for numerical approximations of the same expression. An initial benchmark reveals, for example, that calculating spherical_bessel_J(1, 5.2) with SciPy is over 100 times faster than with Maxima.

The project has the following goals:
  1. develop a benchmark framework to determine which backend should be used by default to evaluate a special function over a specific domain,
  2. create symbolic wrappers for all the special functions that can be evaluated numerically by a package included in Sage,
  3. create a data structure for generalized hypergeometric functions and extend the symbolic wrappers to obtain representations in terms of generalized hypergeometric functions when possible,
  4. implement closure properties for holonomic functions as a next step to improve the symbolic processing of special functions in Sage.


Overall improvement of the Sage Android application

Rasmi Elasmar
(Mentor: Volker Braun, Backup: Harald Schilly)


Although there are already some existing efforts, Sage is still not easily accessible from the Android platform. The Sage Cell client/server infrastructure is an already existing step towards running Sage on a server and communicating back the results. The aim of this proposal is to fix, improve and update the Sage Android application to include new features and functionality, as well as an improved interface for simpler and improved usability. Android's new "Holo" style, sharing of calculations and results, and much more waits to be realized on Android for Sage.


Get Sage ready for Linux distributions

Felix Salfelder – Goethe Universität, Frankfurt, Germany
(Mentor: Tobias Hansen, Julien Puydt, Jeroen Demeyer & John Palmieri )

The aim of this project is to detach the build process of Sage ("the software") from Sage ("the distribution"). The goal is a build system that works within the context of Sage as well as for any GNU/Linux distribution that ships the dependencies for Sage. Distributions that already ship Sage packages or plan to do so are Fedora and Debian. This project is an important step towards making Sage packages in GNU/Linux distributions feasible.



Sage warmly welcomes all three new students and wishes them all the best to learn something new and make an impact in Sage's future developments!

Monday, April 8, 2013

Sage part of Google's Summer of Code 2013

Good News, like last year Sage is once again part of Google's Summer of Code. This means, until April 22 at 19:00 UTC students can submit their applications and mentors will review them and do the matching.

Please share this with prospective students or think about being a mentor this year!

links:

Tuesday, March 19, 2013

Sage 5.8

Sage 5.8 has been released, download it here.

Also, the documentation page has been updated to reflect the new thematic tutorials. I highly recommend to browse through all those topics to learn more about Sage's ever increasing scope.

Tuesday, December 18, 2012

Git, Mercurial, Subversion trends

In case you have to convince someone on which horse to bet:


Lenovo x220 Trackpoint and Ubuntu

Although I'm using my Lenovo x220 laptop for more than a year, I discovered the settings for the track-point yesterday (random searches and forums, don't remember). I was never really thinking about it, but the track-point was a bit slow and stiff. The configuration settings of the usual UI configuration menu had no effect at all (yes, certainly a bug), either.

So, to reveal the magic, here it is. I've added a udev configuration file here:

$ cat /etc/udev/rules.d/trackpoint.rules

SUBSYSTEM=="serio", DRIVERS=="psmouse", WAIT_FOR="/sys/devices/platform/i8042/serio1/serio2/sensitivity", ATTR{sensitivity}="240", ATTR{speed}="95", ATTR{press_to_select}="1", ATTR{inertia}="6"



This is just one line for the settings of sensitivity, speed and intertia. You can also play around with them manually, e.g.

$ echo "5" | sudo tee /sys/devices/platform/i8042/serio1/serio2/inertia

Friday, November 30, 2012

Mathematica 9's R integration vs. Rpy2


Some notes about "What's new in Mathematica 9: Builtin R Integration"
To understand what's going on, please open the example: Hierarchical Clustering.

This posting is basically about how to integrate R via Rpy2 in Python and hence also in Sage.

First thing you should notice is the clash of two parallel worlds. MMA's "RSet" command converts and sets the variable y in the scope of R. That's nice, but wouldn't it be much easier, to just have a local variable for that? Second of all, the function definitions inside strings - filled with escaped quotes - is also not that great. Isn't there are better quoting available?

What I want to do is to accomplish something similar with Rpy2. So yes, there are also equivalents to this RSet function, and you can also evaluate arbitrary R code inside strings (Python has triple-quoting to avoid escaping quotes). But that's not everything. You can import R packages [from rpy2.robjects.packages import importr] and bind them to local variables and have, for example, tab-completion on them. R Functions can be referenced directly, data-sets can be exchanged with implicit converters (e.g. numpy's ndarray -> R's Matrix), etc.

You can see the final output and code here: https://gist.github.com/4176508

Besides the usual import/from stuff common in Python, I start by creating a random matrix with Numpy:


mdata = np.random.randn(10, 5)

Enable autmatic conversion between Numpy and R:


from rpy2.robjects import numpy2ri
numpy2ri.activate()



Print the matrix using R's print:

rprint = robj.globalenv.get("print")
rprint(mdata)

            [,1]        [,2]        [,3]         [,4]        [,5]
 [1,]  2.1844065 -1.05401295  1.18316261 -0.356338229  1.55031790
 [2,]  1.2476473  0.89507075 -0.14576584  0.006899727  0.01350773
 [3,] -1.6416017 -0.28180113  0.02784612  1.199042583  0.01272994
 [4,]  1.8645490  0.30993270  0.18107913  0.131505590  0.57083588
 [5,]  1.2397427  0.73639680 -0.42883124 -0.436741492  0.43644592
 [6,] -0.1980955 -0.04463804 -0.11217381  1.768415923 -1.82884840
 [7,]  0.2943145  2.12648235  0.21068166  1.718289719  0.15711455
 [8,]  0.2453526  0.64922040  1.80518277  0.086208024  1.18789962
 [9,]  0.8229507  0.56227084 -1.72153433 -1.511514201  1.04610492
[10,]  0.1430900  0.03371198 -0.58992825  1.023002088 -1.47153121


... and just for fun, R'summary, printed with Python's print function:

print r.summary(mdata)

         V1                V2                 V3                 V4       
 Min.   :-1.6416   Min.   :-1.05401   Min.   :-1.72153   Min.   :-1.5115
 1st Qu.: 0.1687   1st Qu.:-0.02505   1st Qu.:-0.35806   1st Qu.:-0.2655
 Median : 0.5586   Median : 0.43610   Median :-0.04216   Median : 0.1089
 Mean   : 0.6202   Mean   : 0.39326   Mean   : 0.04097   Mean   : 0.3629
 3rd Qu.: 1.2457   3rd Qu.: 0.71460   3rd Qu.: 0.20328   3rd Qu.: 1.1550
 Max.   : 2.1844   Max.   : 2.12648   Max.   : 1.80518   Max.   : 1.7684
       V5        
 Min.   :-1.82885
 1st Qu.: 0.01292
 Median : 0.29678
 Mean   : 0.16746
 3rd Qu.: 0.92729
 Max.   : 1.55032  


... now applying the labels. This time, I show how to execute R code directly, hence injecting the variable "y" in R's global namespace, too:

from rpy2 import robjects as robj
robj.globalenv['y'] = mdata

r("""
dimnames(y) <- b="b">
list(paste("g", 1:10, sep=""),
                    paste("t", 1:5,  sep=""))
y
""")


Compare this to the way you have to do this in MMA9!

--- EDIT
Below is a way how the same is accomplished in Python. The paste command is replaced by Python's list comprehension, the list command is from R and mdata is converted to an R object to be able to do slot assignments.


mdata = numpy2ri.numpy2ri(mdata)
import rpy2.rinterface as ri
descr = ri.baseenv["list"](
         ri.StrSexpVector(['g%s'%_ for _ in range(10)]),
         ri.StrSexpVector(['t%s'%_ for _ in range(5)]))
mdata.do_slot_assign("dimnames", descr)


The first example is certainly more pleaseant, but once you would create some neat aliases for the R functions (e.g. list = ri.baseenv["list"]; strv = ri.StrSexpVector; ...), working in Python shouldn't be hard, too.
--- END EDIT

Calculating the correlation and distance matrix. Notice, that dots in R's functions, like the as.dist(), are converted to underscores in Python. Those dots are just like normal letters in the identifier name, nothing further.

stats = importr("stats")

corrm = r.cor(r.t(mdata), method="spearman")
# no idea how to do 1-matrix automagically
robj.globalenv['corrm'] = corrm
distm = stats.as_dist(r("1-corrm"))


rprint(corrm)
rprint(distm)


      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]  1.0 -0.3  0.1  0.5 -0.2  0.9  0.8  0.6  0.3   0.2
 [2,] -0.3  1.0  0.6  0.3  0.7 -0.4  0.0  0.1  0.2  -0.5
 [3,]  0.1  0.6  1.0 -0.1 -0.1 -0.3 -0.1  0.7  0.6  -0.9
 [4,]  0.5  0.3 -0.1  1.0  0.7  0.6  0.7 -0.1 -0.3   0.3
 [5,] -0.2  0.7 -0.1  0.7  1.0  0.0  0.3 -0.4 -0.3   0.2
 [6,]  0.9 -0.4 -0.3  0.6  0.0  1.0  0.9  0.3  0.1   0.6
 [7,]  0.8  0.0 -0.1  0.7  0.3  0.9  1.0  0.4  0.3   0.5
 [8,]  0.6  0.1  0.7 -0.1 -0.4  0.3  0.4  1.0  0.9  -0.4
 [9,]  0.3  0.2  0.6 -0.3 -0.3  0.1  0.3  0.9  1.0  -0.3
[10,]  0.2 -0.5 -0.9  0.3  0.2  0.6  0.5 -0.4 -0.3   1.0



 

     1   2   3   4   5   6   7   8   9
2  1.3                               
3  0.9 0.4                           
4  0.5 0.7 1.1                       
5  1.2 0.3 1.1 0.3                   
6  0.1 1.4 1.3 0.4 1.0               
7  0.2 1.0 1.1 0.3 0.7 0.1           
8  0.4 0.9 0.3 1.1 1.4 0.7 0.6       
9  0.7 0.8 0.4 1.3 1.3 0.9 0.7 0.1   
10 0.8 1.5 1.9 0.7 0.8 0.4 0.5 1.4 1.3



The clustering happens below (note: I had to execute the "NULL" in the r context, because it seems that Python's "None" isn't converted)

hr = stats.hclust(distm, method = "complete", members = r("NULL"))

Plotting is straightforward too. To plot to a device besides X11, one has to be a bit more specific. The documentation is full of examples. Notice, the only special part is the mfrow=r.c(1,2)

Looking at Wolfram's blogpost, I really don't want to understand what MMA's "getRPlot[...]" calling "mathematicaRPlotWrapper" does.

grdevices = importr('grDevices')
grdevices.png(file="mma9rpy2.png", width=512, height=300)
try:
  r.par(mfrow = r.c(1,2))
  r.plot(hr, hang = 0.1)
  r.plot(hr, hang = -0.1)
finally:
  grdevices.dev_off()

grdevices.png(file="mma9rpy2-2.png", width = 512, height = 512)
try:
  r.heatmap(mdata)
finally:
  grdevices.dev_off()


Final results:


... and a heatmap plot:

Final note, yes I know there is some fuzz at the bottom of the image with the dendrogram. I don't know why. Maybe someone can fix this ... and yes, that's possible, because all of this is fully open-sourced :-) 

td;dr: https://gist.github.com/4176508