<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3332270588565635319</id><updated>2011-11-17T04:41:01.868+08:00</updated><category term='math'/><category term='blas'/><category term='wrapper'/><category term='data mining'/><category term='c/c++'/><category term='java'/><category term='weka'/><category term='sparse matrix'/><category term='fortran'/><category term='WekaSharp'/><category term='videos'/><category term='Sho'/><category term='F#'/><category term='logistric regression'/><category term='svd'/><category term='binary-search-tree'/><category term='GUI'/><category term='c#'/><category term='set'/><category term='tutorials'/><category term='pinvoke'/><category term='matrix'/><category term='linear algebra'/><category term='generic type'/><category term='Microsoft Solver Foundation'/><category term='optimization'/><category term='Flickr'/><category term='eigenface'/><category term='face recognition'/><category term='design'/><category term='Crawler'/><category term='performance'/><category term='PGM'/><category term='math-provider'/><category term='async'/><category term='numeric'/><category term='sort'/><category term='lapack'/><title type='text'>F# and Data Mining</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>32</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-8293635244735593829</id><published>2011-10-31T19:41:00.001+08:00</published><updated>2011-11-06T12:05:49.570+08:00</updated><title type='text'>Any numerical computing environment on Java platform?</title><content type='html'>&lt;p&gt;Recently &lt;a href="http://tomasp.net/"&gt;Tomas Petricek&lt;/a&gt; and I have co-written a book chapter, &lt;a href="http://msdn.microsoft.com/en-us/library/hh273075.aspx"&gt;Numerical Computing in F#.&lt;/a&gt; It is a free online book chapter for his Manning book: &lt;a href="http://www.amazon.com/gp/product/1933988924/ref=as_li_tf_il?ie=UTF8&amp;amp;tag=httptomasnet-20&amp;amp;linkCode=as2&amp;amp;camp=217145&amp;amp;creative=399369&amp;amp;creativeASIN=1933988924"&gt;Real World Functional Programming&lt;/a&gt;. This book chapter is a survey on numerical computing for .Net languages, specially for F#. I will write another article on this book chapter and present some materials that were cut down from final version on MSDN. &lt;/p&gt;  &lt;p&gt;But today, I am looking for things on our competitor’s side -- numerical computing environments for Java platform. I am just wondering what Java guys are doing for numerical computing.  &lt;/p&gt;  &lt;p&gt;Looking at the mature numerical systems such as Matlab, Mathematica and S-plus/R, we can summarize three general elements of such a system: &lt;/p&gt;  &lt;p&gt;1. &lt;strong&gt;Math library&lt;/strong&gt;. Math library is a must for serious numerical computing. It is hard to build well-tested numerical procedures. This kind of low level stuff, e.g. linear algebra routines and FFT, is usually available as a reusable software component. &lt;/p&gt;  &lt;p&gt;2. &lt;strong&gt;Interactive/Console&lt;/strong&gt;. The Read-eval-print loop (REPL) is very important for exploratory analysis because it gives a quick feedback on some small fragments of code. &lt;/p&gt;  &lt;p&gt;3. &lt;strong&gt;Plot/chart library&lt;/strong&gt;. A plot explains what’s going on clearly and quickly! &lt;/p&gt;  &lt;p&gt;I did some web search and found quite a few blog articles; I want to mention &lt;a href="http://blog.mikiobraun.de/2010/04/machine-learning-jvm-jruby-scala.html"&gt;articles&lt;/a&gt; by &lt;a href="http://user.cs.tu-berlin.de/%7Emikio/"&gt;Mikio L. Braun&lt;/a&gt;. He also maintains his &lt;a href="http://jblas.org/"&gt;jblas&lt;/a&gt; library, which is a JNI wrapper to ATLAS, the highly-optimized BLAS library. &lt;/p&gt;  &lt;p&gt;I am aware &lt;a href="http://incanter.org/"&gt;Incanter&lt;/a&gt;, which is a Clojure project aiming to be R in Java platform. But I don't think rewriting basic things, e.g. PCA and linear regression, for Java Platform is a good idea since a stable implementation for these algorithms usually take years because there is just too much details in numerical algorithms.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;This time, I found a new library, &lt;a href="http://code.google.com/p/scalalab/w/list"&gt;ScalaLab&lt;/a&gt;, which is a on-going project for numerical computing in Scala.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;Let’s first talk the library a little bit. For matrix and linear algebra, there are also two quite stable Java libraries: &lt;a href="http://acs.lbl.gov/software/colt/"&gt;colt&lt;/a&gt; and &lt;a href="http://math.nist.gov/javanumerics/jama/"&gt;JAMA&lt;/a&gt;. The linear algebra functionality of the two libraries should be intact. But the performance is quite bad compared to native code, even several times slower to pure .Net code. [The comparison to .Net code is my personal experience. ] But one should not care too much about performance as the bottleneck of a numerical project differs from projects to projects. A working system is the most important thing, various methods could help to improve the performance. Btw, the performance difference is usually not noticeable when we work on small or sample datasets. I use R quite often recently, the matrix library associated with its Windows version is from Netlib, a normal native performance one. I didn’t have any performance complain over months. &lt;/p&gt;  &lt;p&gt;For &lt;strong&gt;REPL&lt;/strong&gt;, basically it is a language issue. After reading through online, I think I will like something built on Scala, such as the ScalaLab environment. Among the mature/quite-mature JVM languages, Scala is most similar to F#. The advantage of Scala to F# is that Scala is a component language, which means Scala has better OO features for library designers and software architects. However, on the small/low level of programming (e.g. a small function), F# feels much more functional and pure than Scala. This, I think, is partially because F# “IS” an ML, while Scala borrows some syntax from Java and C#. The other reason is that OO is at the heart of Scala – everything is an object. Scala’s OO model is greatly influenced by Smalltalk’s. I haven’t learned any Smalltalk. But after some learning in Scala, I can appreciate why Alan Kay, the inventor of Smalltalk, says, “Actually I made up the term "object-oriented", and I can tell you I did not have C++ in mind. “. &lt;strong&gt;So even functional programming in Scala is OO-emulated, while F# has a functional base first and then the implementation using .Net is transparent to its syntax.&lt;/strong&gt; Ok… the above comparison is just off the topic. Anyway, Scala can serve as a good static script language with a REPL. &lt;/p&gt;  &lt;p&gt;For the plotting library, &lt;a href="http://www.jfree.org/jfreechart/"&gt;JFreeChart&lt;/a&gt; seems to be the standard. It is also the plotting library used in Incanter [For this part, I think Incanter really does a great job!]. I am not sure whether there are some advanced or commercial plotting libraries for Java. But I know that .Net has tons of that! Anyway, JFreeChart is ok for daily use. &lt;/p&gt;  &lt;p&gt;The last thing, which I haven’t listed above, is &lt;strong&gt;parallel computing&lt;/strong&gt;! Java platform, or more specific, the Java Virtual Machine, proves to be one of the most stable multithreaded systems ever built. For example, Hadoop is written in Java. Although people in Google said that Hadoop is way slow compared to their C++ MapReduce. A lot of big companies are using it. Two months ago, I read the following nice paper by Martin Odersky and &lt;a href="http://ogun.stanford.edu/%7Ekunle/"&gt;Kunle Olukotun&lt;/a&gt;: &lt;/p&gt;  &lt;p&gt;&lt;a href="http://infoscience.epfl.ch/record/148814/files/paper.pdf"&gt;Language Virtualization for Heterogeneous Parallel Computing&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;It is just published and already has 28 citations on Google Scholar. The idea is to build a DSL, which serves a middle ware, to write high level programs, e.g. equations involving some matrix operations. Underlying the DSL, there are complicated optimizations going on to transform it and make it parallel using heterogeneous technologies, e.g. GPU and multi-core. So the programmer does not need to know anything about parallel computing and his program is executed in parallel utilizing the multi-cores! The DSL is an internal one built in Scala, another example showing the great expressiveness of Scala. Since such idea is not new, the novel part of the paper is to demonstrate how Scala perfectly fits the requirements of such a paradigm.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;I don’t quite have a conclusion for this post. Just some random thoughts on numerical computing in Java and an advertisement for &lt;a href="http://msdn.microsoft.com/en-us/library/hh273075.aspx"&gt;my articles on MSDN&lt;/a&gt;&lt;img style="border-style: none;" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://lh3.ggpht.com/-7THghsLWtA8/Tq6JWFm4jnI/AAAAAAAABLA/MHfwX-weZ8I/wlEmoticon-smile%25255B2%25255D.png?imgmax=800" /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-8293635244735593829?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/8293635244735593829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/10/any-numerical-computing-environment-on.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8293635244735593829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8293635244735593829'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/10/any-numerical-computing-environment-on.html' title='Any numerical computing environment on Java platform?'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-7THghsLWtA8/Tq6JWFm4jnI/AAAAAAAABLA/MHfwX-weZ8I/s72-c/wlEmoticon-smile%25255B2%25255D.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-6805660794912595793</id><published>2011-09-14T23:04:00.001+08:00</published><updated>2011-09-15T07:13:32.230+08:00</updated><title type='text'>A functional red black tree with dynamic order statistics in F#</title><content type='html'>&lt;p&gt;Yin Zhu&lt;/p&gt;  &lt;p&gt;14 September 2011&lt;/p&gt;  &lt;p&gt;(The code for this article is at &lt;a href="http://fdatamining.codeplex.com/SourceControl/changeset/view/10469#156758"&gt;codeplex&lt;/a&gt;.)&lt;/p&gt;  &lt;p&gt;Binary search trees play an extremely important role in computer science. They were hot research topics back to 1970s and early 1980s. While the plain binary search tree is of little usage, balanced search trees are now being used everywhere. For example, most C++ STL implementations use Red Black Tree for its set and map containers. The F# Core library uses a functional version of AVL tree for Set and Map, which are both persistent/functional containers. &lt;/p&gt;  &lt;p&gt;Special binary search trees also have other functionalities other than implementing standard set or map containers. For example, splay tree has an efficient split operation – splitting a dynamic set into two in O(log n) time: one is less than the given key; the other is greater than or equal to the given key. This operation could be used to optimize network flow algorithms. Details could be found in Tarjan’s classical (also tiny) data structures book published in 1983. &lt;/p&gt;  &lt;p&gt;In this post, I’d like to implement the classical Red Black Tree in F#. I searched on the internet and found that there were some Red Black Tree code snippets in F#, but none of them are complete. Some only implemented the insert operation, which is relatively easy. Some are incorrect. &lt;/p&gt;  &lt;p&gt;My main purpose for implementing the Red Black Tree in F# is to test whether I am still good at implementing complex data structures (I implemented quite a few classical data structures and algorithms during my early undergraduates for ACM/ICPC competitions). But there are new things: 1) the implementation language is F# rather than C++, 2) the implementation should be functional rather than imperative, and 3) the interface design should be general, and allows code reuse. &lt;/p&gt;  &lt;p&gt;One function I always miss from standard set containers is dynamic order statistics – finding the ith smallest element in a dynamic set. The query time should be in O(log n). To accomplish this, we need add a size field to the internal tree node. Adding extra fields into a data structure is called augmenting data structures (Chapter 14, CLRS). Augmenting data structures is useful when a function is not built in a classical data structure but can be added by genuinely modifying the classical data structure. It is a very important skill when solving algorithmic competition problems. &lt;/p&gt;  &lt;p&gt;I divided this article into several sections. I will first introduce the basics of red black trees, and then we move the insertion part, and order statistics, and then the hardest part – deletion. I have a section describing the testing procedures that ensure the correctness of the implementation. At last, I will leave the specifics of Red Black Trees, and cover the tree traversal in general and show some more functional stuff: continuation and a continuation Monad. &lt;/p&gt;  &lt;h3&gt;Red Black Tree Basics&lt;/h3&gt;  &lt;p&gt;A Red Black Tree is a binary search tree with the following extra properties: &lt;/p&gt;  &lt;p&gt;1. The root is black and the leaf nodes are black (note: leaf nodes do not contain keys, only internal nodes contain keys). &lt;/p&gt;  &lt;p&gt;2. A red node’s two children must be black. &lt;/p&gt;  &lt;p&gt;3. For any subtree rooted at x, all the simple paths down to the leaf nodes contain the same number of black nodes. &lt;/p&gt;  &lt;p&gt;With the above properties, the Red Black tree is able to perform queries such as search, insert and delete all in O(log n) time. &lt;/p&gt;  &lt;p&gt;The F# definition of the tree is given below:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;Color =&lt;br /&gt;   Red | Black | DoubleBlack&lt;br /&gt;  &lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;RBTree&amp;lt;'a &lt;span style="color:blue;"&gt;when &lt;/span&gt;'a:comparison&amp;gt; =&lt;br /&gt;   | Node &lt;span style="color:blue;"&gt;of &lt;/span&gt;Color * 'a * int * RBTree&amp;lt;'a&amp;gt; * RBTree&amp;lt;'a&amp;gt;&lt;br /&gt;   | Leaf &lt;span style="color:blue;"&gt;of &lt;/span&gt;Color&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Notice that a node could be Red or Black. For black, there are two cases Black and DoubleBlack. The DoubleBlack color is used only when we are dealing with deletion, and in a valid Red Black tree, there would be of no DoubleBlack node. I will explain DoubleBlack in the delete section. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;A Red Black Tree has two kinds of nodes: internal nodes storing keys and dummy leaf nodes. The internal node has a color field, the key filed, the size of the subtree and left and right children. The leaf node has two possible colors: Black or DoubleBlack. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Let’s write two simple recursive functions working on the tree structure to get us warmed up:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;contains x = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;-&amp;gt; false&lt;br /&gt;   &lt;/span&gt;| Node (_, y, _, a, b) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       if &lt;/span&gt;x = y &lt;span style="color:blue;"&gt;then true&lt;br /&gt;       elif &lt;/span&gt;x &amp;lt; y &lt;span style="color:blue;"&gt;then &lt;/span&gt;contains x a&lt;br /&gt;       &lt;span style="color:blue;"&gt;else &lt;/span&gt;contains x b&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;depth = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;0&lt;br /&gt;   | Node (_, _, _, a, b) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;1 + max (depth a) (depth b)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The first is to test whether a key x is in the tree or not. The second is to calculate the depth of a tree. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Insert&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For insertion, there is a known trick found by Chris Okasaki in &lt;a href="http://dl.acm.org/citation.cfm?id=968583"&gt;this paper&lt;/a&gt; (Red-black trees in a functional setting). Okasaki is also the author of the famous book, Purely Functional Data Structures. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The idea of insertion is: always insert a Red node at some leaf node so that property 3 holds, then fix property 2 up to the root. There are four cases where property 2 could break. For all the four cases, the grand parent is always black, and there will be two red nodes right in the below. Please see the picture on page 3 of the paper. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Then the buttom-up fixing goes up the root. The root may be changed into Red. To hold property 1, simply change the color of the root to Black. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;insert x (t: RBTree&amp;lt;'a&amp;gt;) =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;balance = &lt;span style="color:blue;"&gt;function&lt;br /&gt;       &lt;/span&gt;| Black, z, size, Node (Red, y, _, Node (Red, x, _, a, b), c), d&lt;br /&gt;       | Black, z, size, Node (Red, x, _, a, Node (Red, y, _, b, c)), d&lt;br /&gt;       | Black, x, size, a, Node (Red, z, _, Node (Red, y, _, b, c), d)&lt;br /&gt;       | Black, x, size, a, Node (Red, y, _, b, Node (Red, z, _, c, d))&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;Node (Red, y, size, Node (Black, x, (getSize a b)+1, a, b), Node (Black, z, (getSize c d)+1, c, d))&lt;br /&gt;       | color, b, _, c, d  &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color:green;"&gt;// grandparent is red, does not change&lt;br /&gt;           &lt;/span&gt;Node (color, b, (getSize c d)+1, c, d)&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;let rec &lt;/span&gt;ins = &lt;span style="color:blue;"&gt;function&lt;br /&gt;       &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;Node (Red, x, 1, Leaf(Black), Leaf(Black))&lt;br /&gt;       | Node (color, y, size, a, b) &lt;span style="color:blue;"&gt;as &lt;/span&gt;s  &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           if &lt;/span&gt;x &amp;lt; y &lt;span style="color:blue;"&gt;then &lt;/span&gt;balance(color, y, (getSize a b)+2, ins a, b)&lt;br /&gt;           &lt;span style="color:blue;"&gt;elif &lt;/span&gt;x &amp;gt; y &lt;span style="color:blue;"&gt;then &lt;/span&gt;balance(color, y, (getSize a b)+2, a, ins b)&lt;br /&gt;           &lt;span style="color:blue;"&gt;else &lt;/span&gt;s&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;match &lt;/span&gt;ins t &lt;span style="color:blue;"&gt;with&lt;br /&gt;       &lt;/span&gt;| Node (Red, y, size, a, b)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;Node (Black, y, size, a, b)&lt;br /&gt;       | t &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;t&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Order statistics &lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Let’s have some rest before going to the monstrous delete operation. To get the nth smallest key in the tree, we can add a size field to each node, which is the size of the subtree rooted at the node. Thus the size of Node(_, _, _, left, right) is defined as&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;size = left.size + right.size + 1&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;and, as a boundary condition, a leaf node has size 0. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;With this size field, the nth function is given below:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nth n t =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let rec &lt;/span&gt;nth' n = &lt;span style="color:blue;"&gt;function&lt;br /&gt;       &lt;/span&gt;| Leaf _&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"can't find the nth member"&lt;br /&gt;       &lt;/span&gt;| Node (_, v, size, l, r)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           let &lt;/span&gt;lsize=getSize l (Leaf(Black))&lt;br /&gt;           &lt;span style="color:blue;"&gt;if &lt;/span&gt;lsize+1=n &lt;span style="color:blue;"&gt;then&lt;br /&gt;               &lt;/span&gt;v&lt;br /&gt;           &lt;span style="color:blue;"&gt;elif &lt;/span&gt;lsize+1&amp;gt;n &lt;span style="color:blue;"&gt;then&lt;br /&gt;               &lt;/span&gt;nth' n l&lt;br /&gt;           &lt;span style="color:blue;"&gt;else&lt;br /&gt;               &lt;/span&gt;nth' (n-lsize-1) r&lt;br /&gt;   &lt;span style="color:blue;"&gt;if &lt;/span&gt;n &amp;gt;= size t || n &amp;lt; 0 &lt;span style="color:blue;"&gt;then&lt;br /&gt;       &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"nth out of range"&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:blue;"&gt;else&lt;br /&gt;       &lt;/span&gt;nth' (n+1) t&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Delete&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The idea of delete is simple (if we don’t care about the three properties of Red Black Trees): if the node x to be deleted has less than two children, then simply delete it, and lift its child (if any) to its position; if the node has two children, that means there must a node that is just bigger than x in its right child, replace x with that value, now the problem reduces to delete the smallest node in x’s right child.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;First let’s write the function to get the smallest value in a tree: &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;min = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| Leaf _&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;None&lt;br /&gt;   | Node (_, value, _, Leaf(Black), _)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;Some(value)&lt;br /&gt;   | Node (_, _, _, l, r)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;min l&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Then we continue to code the delete logic:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;        &lt;span style="color:blue;"&gt;let rec &lt;/span&gt;del value = &lt;span style="color:blue;"&gt;function&lt;br /&gt;           &lt;/span&gt;| Leaf(color) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;               &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"RBT delete failed because x is not in the tree"&lt;br /&gt;           &lt;/span&gt;| Node (color, y, _, a, b) &lt;span style="color:blue;"&gt;as &lt;/span&gt;s &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;               if &lt;/span&gt;value &amp;lt; y &lt;span style="color:blue;"&gt;then &lt;/span&gt;balance(color, y, 0, del value a, b)&lt;br /&gt;               &lt;span style="color:blue;"&gt;elif &lt;/span&gt;value &amp;gt; y &lt;span style="color:blue;"&gt;then &lt;/span&gt;balance(color, y, 0, a, del value b)&lt;br /&gt;               &lt;span style="color:blue;"&gt;else&lt;br /&gt;                   match &lt;/span&gt;(a, b) &lt;span style="color:blue;"&gt;with&lt;br /&gt;                   &lt;/span&gt;| (Leaf(Black), Leaf(Black)) &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color:green;"&gt;// without non-leaf children&lt;br /&gt;                       &lt;/span&gt;Leaf(Black++color)&lt;br /&gt;                   | (Leaf(Black), Node (nc, nv, size, nl, nr))&lt;br /&gt;                   | (Node (nc, nv, size, nl, nr), Leaf(Black)) &lt;span style="color:blue;"&gt;-&amp;gt;  &lt;/span&gt;&lt;span style="color:green;"&gt;// with a single child&lt;br /&gt;                       &lt;/span&gt;Node (color++nc, nv, size, nl, nr)&lt;br /&gt;                   | (l, r) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;                       &lt;/span&gt;&lt;span style="color:green;"&gt;//find the successor (smallest element in a right child),&lt;br /&gt;                       //replace the key with the successor's&lt;br /&gt;                       //and delete relevant node&lt;br /&gt;                       &lt;/span&gt;&lt;span style="color:blue;"&gt;match &lt;/span&gt;(min r) &lt;span style="color:blue;"&gt;with&lt;br /&gt;                       &lt;/span&gt;| Some(v) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;                           &lt;/span&gt;balance(color, v, 0, l, del v r)&lt;br /&gt;                       | None &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;                           &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"impossible: can't find the successor"&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The hard part is how to balance the tree after the deletion of the node. If we delete a black node, then Property 3 cannot hold anymore. So we must make rotations and do some rearrangements on colors to make Property 3 to hold again. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Think this problem in another way: after the deletion of a Black node and we connect its single child or leaf node into it, the node becomes darker. Conceptually Property 3 still holds if we think the node has two “Black”s in it. We mark this node as DoubleBlack and the task of balance is to eliminate this DoubleBlack node!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I defined an operator ++ to plus two colors:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;(++) color1 color2 =&lt;br /&gt;   &lt;span style="color:blue;"&gt;match &lt;/span&gt;color1,  color2 &lt;span style="color:blue;"&gt;with&lt;br /&gt;       &lt;/span&gt;| Red, Black&lt;br /&gt;       | Black, Red &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;Black&lt;br /&gt;       | Black, Black &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;DoubleBlack&lt;br /&gt;       | _, _ &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;Red&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Let’s define the invariant for the balance function as follows: no node under pv is DoubleBlack. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Consider pv is the root of the subtree, and its left child is rooted at x, which is a DoubleBlack. Let’s consider all the cases for its right child. (After this is done, then we can work out when x is located as the right similarly.)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Case 1. If pv is black and its right child rv is red. Then do a left-rotation and make the DoubleBlack node x one level down (so that we reduce the problem): &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-yH7E_a_GKhE/TnDDk9GIGlI/AAAAAAAABKY/DkxacWRPtWM/s1600-h/image%25255B7%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" alt="image" src="http://lh3.ggpht.com/-AvkO-1tMpkY/TnDDlfyNqbI/AAAAAAAABKc/Ke5znpagHO4/image_thumb%25255B3%25255D.png?imgmax=800" border="0" height="314" width="418" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;(notations in the picture: nodes in circle are single nodes with colors, blue represents either red or black; a symbol represents a tree rooted at the symbol, for some cases the colors of the symbols are also noted.)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Case 2: If pv is of anycolor, and its right child is black. The three sub cases are shown below:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/--Poh97zcCOU/TnDCWpp8jzI/AAAAAAAABKg/gP2gokX1K04/s1600-h/clip_image004%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image004" alt="clip_image004" src="http://lh3.ggpht.com/-zO3XsnfdObQ/TnDCXDwiCtI/AAAAAAAABKk/y8uuCe4ZvjU/clip_image004_thumb%25255B1%25255D.png?imgmax=800" border="0" height="281" width="373" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-aR6zXeW65e8/TnDCXq6b3-I/AAAAAAAABKo/j-hG_d9PgP4/s1600-h/clip_image006%25255B7%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image006" alt="clip_image006" src="http://lh4.ggpht.com/-tUSjkLrjpl4/TnDCYd0aOII/AAAAAAAABKs/6d3O_VkwNdA/clip_image006_thumb%25255B4%25255D.png?imgmax=800" border="0" height="279" width="371" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-qCx1o70kG54/TnDCY0gejcI/AAAAAAAABKw/A0eqOsY-RL8/s1600-h/clip_image008%25255B7%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image008" alt="clip_image008" src="http://lh5.ggpht.com/-WpGPIOYpspY/TnDCZXAPymI/AAAAAAAABK0/YEtPwByEj8s/clip_image008_thumb%25255B4%25255D.png?imgmax=800" border="0" height="279" width="370" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;And the balance procedure is as follows: &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;balance = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:green;"&gt;// invariant: the two children rooted at node pv have same "black" depth&lt;br /&gt;   // but the root node itself may become a "double" black node&lt;br /&gt;   &lt;/span&gt;| Black, pv, _, x, Node (Red, rv, size, ra, rb) &lt;span style="color:blue;"&gt;when &lt;/span&gt;(isDoubleBlack x)&lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color:green;"&gt;// case 1&lt;br /&gt;       &lt;/span&gt;balance(Black, rv, 0, balance(Red, pv, 0, x, ra), rb) &lt;span style="color:green;"&gt;// do left-rotate and continue balance&lt;br /&gt;   &lt;/span&gt;| pc, pv, _, x, Node (Black, rv, size, ra, rb) &lt;span style="color:blue;"&gt;when &lt;/span&gt;(isDoubleBlack x)&lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color:green;"&gt;// case 2.a 2.b and 2.c&lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;if &lt;/span&gt;isBlack ra &amp;amp;&amp;amp; isBlack rb &lt;span style="color:blue;"&gt;then &lt;/span&gt;&lt;span style="color:green;"&gt;// 2.a: reduce a black on both side&lt;br /&gt;           &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;tempNode=Node (Red, rv, size, ra, rb)&lt;br /&gt;           Node (pc++Black, pv, (getSize x tempNode)+1, blackify x, tempNode) &lt;span style="color:green;"&gt;// reduces the double node to root&lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;elif &lt;/span&gt;isBlack rb &lt;span style="color:blue;"&gt;then &lt;/span&gt;&lt;span style="color:green;"&gt;// 2.b: do a right rotation in the right child, so rb becomes red and recudes to the "else" case&lt;br /&gt;           &lt;/span&gt;&lt;span style="color:blue;"&gt;match &lt;/span&gt;ra &lt;span style="color:blue;"&gt;with&lt;br /&gt;           &lt;/span&gt;| Node (_, rav, _, raa, rbb)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;               let &lt;/span&gt;tempNode1= Node (Red, rv, (getSize rbb rb)+1, rbb, rb)&lt;br /&gt;               &lt;span style="color:blue;"&gt;let &lt;/span&gt;tempNode2=Node (Black, rav, (getSize raa tempNode1)+1, raa, tempNode1)&lt;br /&gt;               balance(pc, pv, 0, x, tempNode2)&lt;br /&gt;           | _&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;               &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"impossible error"&lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;else &lt;/span&gt;&lt;span style="color:green;"&gt;// 3.c&lt;br /&gt;           &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;tempNode=Node (Black, pv, (getSize x ra)+1, blackify x, ra)&lt;br /&gt;           Node (pc, rv, (getSize tempNode rb)+1, tempNode, blackify rb)&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:green;"&gt;// when doubleblack x is on the right&lt;br /&gt;   &lt;/span&gt;| Black, pv, _, Node (Red, lv, _, la, lb), x &lt;span style="color:blue;"&gt;when &lt;/span&gt;(isDoubleBlack x)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;balance(Black, lv, 0, la, balance(Red, pv, 0, lb, x))&lt;br /&gt;   | pc, pv, _, Node (Black, lv, size, la, lb), x &lt;span style="color:blue;"&gt;when &lt;/span&gt;(isDoubleBlack x)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       if &lt;/span&gt;isBlack la &amp;amp;&amp;amp; isBlack lb &lt;span style="color:blue;"&gt;then&lt;br /&gt;           let &lt;/span&gt;tempNode=Node (Red, lv, (getSize la lb)+1, la, lb)&lt;br /&gt;           Node (pc++Black, pv, (getSize tempNode x)+1, tempNode, blackify x)&lt;br /&gt;       &lt;span style="color:blue;"&gt;elif &lt;/span&gt;isBlack la &lt;span style="color:blue;"&gt;then&lt;br /&gt;           match &lt;/span&gt;lb &lt;span style="color:blue;"&gt;with&lt;br /&gt;           &lt;/span&gt;| Node (_, _, lbv, lba, lbb)&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;               let &lt;/span&gt;tempNode1=Node (Red, lv, (getSize la lb)+1, la, lba)&lt;br /&gt;               &lt;span style="color:blue;"&gt;let &lt;/span&gt;tempNode2=Node (Black, lbv, (getSize tempNode1 lbb)+1, tempNode1, lbb)&lt;br /&gt;               balance(pc, pv, 0, tempNode2, x)&lt;br /&gt;           | _&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;               &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"impossible error"&lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;else  &lt;br /&gt;           let &lt;/span&gt;tempNode=Node (Black, pv, (getSize lb x)+1, lb, blackify x)&lt;br /&gt;           Node (pc, lv, (getSize la tempNode)+1, blackify la, tempNode)   &lt;br /&gt;       &lt;br /&gt;   | a, b, _, c, d&lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;Node (a, b, (getSize c d)+1, c, d)  &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Remember that this balance only balances the two children of any tree. Thus the root can still be DoubleDark (similar case in insert). We need a final check on the root:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;match &lt;/span&gt;del x t &lt;span style="color:blue;"&gt;with&lt;br /&gt;&lt;/span&gt;| Node (_, y, size, a, b) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;   &lt;/span&gt;Node (Black, y, size, a, b)&lt;br /&gt;| Leaf _ &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;Leaf(Black)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Validation for the implementation&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;It is important to have some test to verify the correctness of the above implementation. My idea is to do some random insertions and deletions, and then after each insertion or deletion, check whether the tree still holds the three properties. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The validate function is given below:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;validate t =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let rec &lt;/span&gt;validate = &lt;span style="color:blue;"&gt;function &lt;br /&gt;       &lt;/span&gt;| Leaf Black &lt;span style="color:blue;"&gt;-&amp;gt; true&lt;/span&gt;, 0&lt;br /&gt;       | Leaf DoubleBlack &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;printfn &lt;span style="color:maroon;"&gt;"DoubleBlack in a Leaf node"&lt;br /&gt;           &lt;/span&gt;&lt;span style="color:blue;"&gt;false&lt;/span&gt;, 0&lt;br /&gt;       | Leaf Red &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;printfn &lt;span style="color:maroon;"&gt;"Red in a Leaf node"&lt;br /&gt;           &lt;/span&gt;&lt;span style="color:blue;"&gt;false&lt;/span&gt;, 0&lt;br /&gt;       | Node (Black, _, _, a, b) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           let &lt;/span&gt;lr, ln = validate a&lt;br /&gt;           &lt;span style="color:blue;"&gt;let &lt;/span&gt;rr, rn = validate b&lt;br /&gt;           lr &amp;amp;&amp;amp; rr &amp;amp;&amp;amp; (ln = rn), ln + 1&lt;br /&gt;       | Node (Red, _, _, a, b) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           match &lt;/span&gt;a, b &lt;span style="color:blue;"&gt;with&lt;br /&gt;               &lt;/span&gt;| Node (Red, _, _, _, _), _&lt;br /&gt;               | _, Node (Red, _, _, _, _) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;                   let &lt;/span&gt;lr, ln = validate a&lt;br /&gt;                   printfn &lt;span style="color:maroon;"&gt;"Red-Red in a tree"&lt;br /&gt;                   &lt;/span&gt;&lt;span style="color:blue;"&gt;false&lt;/span&gt;, ln&lt;br /&gt;               | _ &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;                   let &lt;/span&gt;lr, ln = validate a&lt;br /&gt;                   &lt;span style="color:blue;"&gt;let &lt;/span&gt;rr, rn = validate b&lt;br /&gt;                   lr &amp;amp;&amp;amp; rr &amp;amp;&amp;amp; (ln = rn), ln&lt;br /&gt;       | Node (DoubleBlack, _, _, a, _) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           let &lt;/span&gt;lr, ln = validate a&lt;br /&gt;           printfn &lt;span style="color:maroon;"&gt;"DoubleBlack in an internal node"&lt;br /&gt;           &lt;/span&gt;&lt;span style="color:blue;"&gt;false&lt;/span&gt;, ln&lt;br /&gt;   &lt;span style="color:blue;"&gt;match &lt;/span&gt;t &lt;span style="color:blue;"&gt;with&lt;br /&gt;   &lt;/span&gt;| Leaf Black &lt;span style="color:blue;"&gt;-&amp;gt; true&lt;/span&gt;, 0&lt;br /&gt;   | Leaf _ &lt;span style="color:blue;"&gt;-&amp;gt; false&lt;/span&gt;, 0&lt;br /&gt;   | Node (Black, _, _, _, _) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;validate t&lt;br /&gt;   | Node (Red, _, _, _, _)&lt;br /&gt;   | Node (DoubleBlack, _, _, _, _) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;printfn &lt;span style="color:maroon;"&gt;"root must be Black"&lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;false&lt;/span&gt;, 0&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I did use the above test case to catch one bug in the implementation, the original code for the final deletion step is:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;match del x t with&lt;br /&gt;&lt;br /&gt;| Node (_, y, size, a, b) -&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    Node (Black, y, size, a, b)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;| t -&amp;gt; t&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This code has a bug that when the tree is deleted to empty, its representation is Leaf (DoubleBlack) rather than Leaf(Black)!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I did a performance test against with F#’s set, which uses AVL tree:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color:green;"&gt;// Fisher-Yates shuffle&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;randperm n =&lt;br /&gt;       &lt;span style="color:blue;"&gt;let &lt;/span&gt;p = Array.init n (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;i)&lt;br /&gt;       &lt;span style="color:blue;"&gt;let &lt;/span&gt;r = &lt;span style="color:blue;"&gt;new &lt;/span&gt;System.Random(1)&lt;br /&gt;       &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=n-1 &lt;span style="color:blue;"&gt;downto &lt;/span&gt;1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;           let &lt;/span&gt;j = r.Next(i+1) &lt;span style="color:green;"&gt;// random number 0 &amp;lt;= j &amp;lt;= i&lt;br /&gt;           &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;tmp = p.[j]&lt;br /&gt;           p.[j] &amp;lt;- p.[i]&lt;br /&gt;           p.[i] &amp;lt;- tmp&lt;br /&gt;       p&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;lst = randperm 1000000 |&amp;gt; Seq.toList&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;rb = RBTree.ofList lst |&amp;gt; ignore&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.Collections&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;av = Set.ofList lst |&amp;gt; ignore&lt;br /&gt;&lt;br /&gt;   &lt;span style="color:green;"&gt;//timing on a AMD E-350 Netbook (1.6 GHz)&lt;br /&gt;   //    Real: 00:00:44.635, CPU: 00:00:44.694, GC gen0: 218, gen1: 61, gen2: 4&lt;br /&gt;   //&lt;br /&gt;   //    val rb : unit = ()&lt;br /&gt;   //&lt;br /&gt;   //    &amp;gt;&lt;br /&gt;   //    Real: 00:00:17.448, CPU: 00:00:18.766, GC gen0: 76, gen1: 20, gen2: 2&lt;br /&gt;   //&lt;br /&gt;   //    val av : unit = ()&lt;br /&gt;   //&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;As we can see, for a large set with one million elements, my implementation costs about two times of that of AVL tree in F# Core library. That was not bad. I shall devote more time into performance and see if we can do more improvement. One optimization is to reduce redundant tree node allocation. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Tree traversal &lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Let’s talk some interesting stuff about tree traversal at last. I did implement some traversal functions:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;map f = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;as &lt;/span&gt;l &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;l&lt;br /&gt;   | Node (color, x, size, l, r) &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;Node (color, f x, size, map f l, map f r)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;iter f = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;()&lt;br /&gt;   | Node (color, x, size, l, r) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;iter f l&lt;br /&gt;       f x&lt;br /&gt;       iter f r&lt;br /&gt;  &lt;br /&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;fold f t acc =&lt;br /&gt;   &lt;span style="color:blue;"&gt;match &lt;/span&gt;t &lt;span style="color:blue;"&gt;with&lt;br /&gt;   &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;acc&lt;br /&gt;   | Node (color, x, size, l, r) &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;fold f l (f x (fold f r acc))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;They are straightforward recursive functions, and not tail-recursive. For a balanced tree like red black tree, tail-recursive is not important because the tree does not go too deep (a tree with 1 to 1000000 in it has a depth of 26). But it is just fun to make tem tail-recursive by using continuations. The standard accumulating variable trick does not work here because we have two recursive calls. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Let’s write the tail-recursive form of map function:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;map2 f t =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let rec &lt;/span&gt;map f t k =&lt;br /&gt;       &lt;span style="color:blue;"&gt;match &lt;/span&gt;t &lt;span style="color:blue;"&gt;with&lt;br /&gt;       &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;as &lt;/span&gt;l &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;k l&lt;br /&gt;       | Node (color, x, size, l, r) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;map f l (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;lmap &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;               &lt;/span&gt;map f r (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;rmap &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;k (Node(color, f x, size, lmap, rmap))))&lt;br /&gt;&lt;br /&gt;   map f t (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;x &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;x)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We can also use a Monad to abstract the continuation pattern out:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;Cont&amp;lt;'a,'r&amp;gt; = ('a &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;'r) &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;'r&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;ContBuilder() =&lt;br /&gt;   &lt;span style="color:blue;"&gt;member &lt;/span&gt;x.Return (a):Cont&amp;lt;'a,'r&amp;gt; = &lt;span style="color:blue;"&gt;fun &lt;/span&gt;k &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;k a&lt;br /&gt;   &lt;span style="color:blue;"&gt;member &lt;/span&gt;x.Bind (c:Cont&amp;lt;'a,'r&amp;gt;, f:'a &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;Cont&amp;lt;'b,_&amp;gt;) =&lt;br /&gt;       (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;k &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;c (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;a &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;f a k))&lt;br /&gt;   &lt;span style="color:blue;"&gt;member &lt;/span&gt;this.Delay(f) = f()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;cont = ContBuilder()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;map3 f t =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let rec &lt;/span&gt;map f = &lt;span style="color:blue;"&gt;function&lt;br /&gt;       &lt;/span&gt;| Leaf _ &lt;span style="color:blue;"&gt;as &lt;/span&gt;t &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;cont { &lt;span style="color:blue;"&gt;return &lt;/span&gt;t }&lt;br /&gt;       | Node (color, x, size, l, r) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;           &lt;/span&gt;cont {&lt;br /&gt;               &lt;span style="color:blue;"&gt;let! &lt;/span&gt;lm = map f l&lt;br /&gt;               &lt;span style="color:blue;"&gt;let! &lt;/span&gt;rm = map f r&lt;br /&gt;               &lt;span style="color:blue;"&gt;return &lt;/span&gt;Node (color, f x, size, lm, rm)&lt;br /&gt;           }&lt;br /&gt;   map f t (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;x &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;x)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;A note on &amp;lt;'a when 'a:comparison&amp;gt;&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In our definition of the Red Black Tree:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;RBTree&amp;lt;'a &lt;span style="color:blue;"&gt;when &lt;/span&gt;'a:comparison&amp;gt; =&lt;br /&gt;   | Node &lt;span style="color:blue;"&gt;of &lt;/span&gt;Color * 'a * int * RBTree&amp;lt;'a&amp;gt; * RBTree&amp;lt;'a&amp;gt;&lt;br /&gt;   | Leaf &lt;span style="color:blue;"&gt;of &lt;/span&gt;Color&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The type signature has an ad hoc construct: when ‘a: comparison. Actually the only other constraint on ‘a is equality. F# only supports these two type constraints. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Type constraints behave like type classes in Haskell. :equality is Eq, :comparison is Ord. However, type classes in Haskell are much flexible. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Details could be found on &lt;a href="http://blogs.msdn.com/b/dsyme/archive/2009/11/08/equality-and-comparison-constraints-in-f-1-9-7.aspx"&gt;this blog article&lt;/a&gt; by Don Syme. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Summary&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In this article, I have implemented a functional Red Black Tree with dynamic order statistics. The tree is complete and has been well tested. Before any performance tuning, the running speed of it is within a small factor (2-3) of the AVL tree in F# Core library. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This article shows many important features of functional programming: 1) pattern matching and algebraic data types help implement complex data structures; 2) recursion is everywhere and the correctness of the program could be proved by inductive reasoning; and finally 3) a light introduction to tree traversal and continuation and Monad in F#. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-6805660794912595793?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/6805660794912595793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/09/functional-red-black-tree-with-dynamic.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/6805660794912595793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/6805660794912595793'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/09/functional-red-black-tree-with-dynamic.html' title='A functional red black tree with dynamic order statistics in F#'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-AvkO-1tMpkY/TnDDlfyNqbI/AAAAAAAABKc/Ke5znpagHO4/s72-c/image_thumb%25255B3%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-7465707022660752766</id><published>2011-08-28T20:30:00.001+08:00</published><updated>2011-08-28T20:30:23.044+08:00</updated><title type='text'>N-queen problem, F#, Scheme and Haskell!</title><content type='html'>&lt;p&gt;I am busy with my research and other F# work, I haven’t written blog articles recently. But I always keep an eye on interesting things on F#. The F# version of “Write Yourself a Scheme in 48 hours” by Loca Bologonese really caught my eyes. Luca took the Haskell version and translated it into F# with the main structure remained the same. This gives me (an F# programmer) a great opportunity to learn Haskell by reading the F# and the Haskell programs side by side! It is really a good learning experience (previously I know a little Haskell).  &lt;p&gt;&amp;nbsp; &lt;h2&gt;N-Queen in F# and Scheme&lt;/h2&gt; &lt;p&gt;The fun part was that I wished to write a functional version of n-queen program in Scheme and see the execution speeds of the F# evaluator and the Haskell evaluator! &lt;p&gt;n-queen problem is one of my favorite puzzles. I learned depth first search/backtracking with this problem. It is a good exercise for how to write a non-trivial recursive program other than factorial or Fibonacci sequence.  &lt;p&gt;As the readers of this blog are probably F# users, I gave an F# version first in case you are not familiar with Scheme. The scheme version of the program uses exactly the same idea and same data structures.  &lt;p&gt;The F# version:&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// check if the two queens attack each other&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;conflict (i1, j1) (i2, j2) = &lt;br /&gt;    &lt;span style="color: blue"&gt;if &lt;/span&gt;i1 = i2 || j1 = j2 || abs(i1-i2) = abs(j1-j2) &lt;span style="color: blue"&gt;then&lt;br /&gt;        true&lt;br /&gt;    else&lt;br /&gt;        false&lt;br /&gt;    &lt;br /&gt;&lt;/span&gt;&lt;span style="color: green"&gt;// check if a new position is valid to a list of other (valid) positions&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;check (newPos:int*int) (posList: (int*int) list) = &lt;br /&gt;    posList&lt;br /&gt;    |&amp;gt; List.exists (&lt;span style="color: blue"&gt;fun &lt;/span&gt;pos &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;conflict newPos pos)&lt;br /&gt;    |&amp;gt; not&lt;br /&gt;&lt;br /&gt;&lt;span style="color: green"&gt;// the backtracking procedure to get all solutions&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let rec &lt;/span&gt;searchQueen i n sols = &lt;br /&gt;    &lt;span style="color: blue"&gt;if &lt;/span&gt;i = n &lt;span style="color: blue"&gt;then &lt;br /&gt;        &lt;/span&gt;sols&lt;br /&gt;    &lt;span style="color: blue"&gt;else&lt;br /&gt;        let &lt;/span&gt;newSols = &lt;br /&gt;            sols&lt;br /&gt;            |&amp;gt; List.map (&lt;span style="color: blue"&gt;fun &lt;/span&gt;sol &lt;span style="color: blue"&gt;-&amp;gt; &lt;br /&gt;                &lt;/span&gt;[0..n-1]&lt;br /&gt;                |&amp;gt; List.filter (&lt;span style="color: blue"&gt;fun &lt;/span&gt;j &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;check (i,j) sol)&lt;br /&gt;                |&amp;gt; List.map (&lt;span style="color: blue"&gt;fun &lt;/span&gt;j &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;(i,j)::sol)&lt;br /&gt;                )&lt;br /&gt;            |&amp;gt; List.concat&lt;br /&gt;        searchQueen (i+1) n newSols&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;allSolutions = searchQueen 0 8 [[]]&lt;br /&gt;&lt;br /&gt;allSolutions |&amp;gt; List.length&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The scheme version:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;;; helper functions&lt;br&gt;(define (accumulate op init seq)&lt;br&gt;&amp;nbsp; (if (null? seq)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; init&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (op (car seq)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (accumulate op init (cdr seq)))))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br&gt;&lt;font face="Courier New"&gt;(define (accumulate op init seq) (if (null? seq) init (op (car seq) (accumulate op init (cdr seq)))))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br&gt;&lt;font face="Courier New"&gt;(define (flatmap proc seq)&lt;br&gt;&amp;nbsp; (accumulate append '() (map proc seq)))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;(define (enumerate-interval low high)&lt;br&gt;&amp;nbsp; (if (&amp;gt; low high)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (cons low (enumerate-interval (+ low 1) high))))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br&gt;&lt;font face="Courier New"&gt;;; data structures and basic functions for board&lt;br&gt;(define (make-queen row col) (list row col))&lt;br&gt;(define (get-row queen) (car queen))&lt;br&gt;(define (get-col queen) (car (cdr queen)))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;(define (same-col? q1 q2) (= (get-col q1) (get-col q2)))&lt;br&gt;(define (same-diag? q1 q2) &lt;br&gt;&amp;nbsp; (= &lt;br&gt;&amp;nbsp;&amp;nbsp; (abs (- (get-row q1) (get-row q2))) &lt;br&gt;&amp;nbsp;&amp;nbsp; (abs (- (get-col q1) (get-col q2)))))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;(define (attacks? q1 q2)&lt;br&gt;&amp;nbsp; (or (same-col? q1 q2) (same-diag? q1 q2)))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;(define (safe? newq qlist)&lt;br&gt;&amp;nbsp; (cond &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ((null? qlist) #t)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ((attacks? newq (car qlist)) #f)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (else (safe? newq (cdr qlist)))))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;(define (safe-board? qlist)&lt;br&gt;&amp;nbsp; (let &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ((newq (car qlist))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (rest (cdr qlist)))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (safe? newq rest)))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br&gt;&lt;font face="Courier New"&gt;;; the depth-first search for queens&lt;br&gt;(define (queens board-size)&lt;br&gt;&amp;nbsp; (define (queen-rows k sols)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (if (= k board-size)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sols&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (queen-rows (+ k 1) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (filter &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (lambda (board) (safe-board? board))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (flatmap &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (lambda (rest-queens)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (map (lambda (new-col)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (cons (list k new-col) rest-queens))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (enumerate-interval 1 board-size)))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sols)))))&lt;br&gt;&amp;nbsp; (queen-rows 0 (list '())))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;&lt;/font&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;(length (queens 8))&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Here are some correspondences between the F# version and the scheme version:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;1. In F#, each queen position is represented as a tuple (int * int), while in Scheme, each queen position is a list with length 2. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;2. In the search procedure, the main logic is: given a list of sub-solutions (sols), expand every one with all possible columns for the new position. This gives a list of lists of lists of positions. There should be a flatten function to transform it into a list of lists of positions. In F#, it is List.concat, in Scheme, it is flatmap. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Run n-queen in the Scheme interpreters &lt;/h2&gt;&lt;br /&gt;&lt;p&gt;The fun part is to run the n-queen program in the F# version interpreter. However, throwing the above scheme program into the interpreter gives errors. (It runs well in CzScheme) &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Here are the limitations that I encountered:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;1. The interpreter only supports one-line definitions. Overcoming this limitation is easy – just write the functions in one-lines. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;2. The interpreter does not support (let). let syntax gives a word for some expression. The easy way is to use the expression directly. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;3. Some built-in functions are not included in the interpreter, e.g. abs and append. Implementing them is quite easy. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;4. The interpreter uses Console.ReadLine() to read from the input, which only supports 256 characters per line by default. The following lines reset the buffer size to allow more characters per line:&lt;/p&gt;&lt;pre&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; let inputBuffer = Array.create 1024 0y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; let inputStream = Console.OpenStandardInput(inputBuffer.Length)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.SetIn(new IO.StreamReader(inputStream))&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;5. Does not support nested-define. Nested-define could be implemented as let + lambda. But I didn’t solve this problem in this post. I just remove nested-defines. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Here is the runnable Scheme program:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;(define (abs x) (if (negative? x) (- 0 x) x))&lt;br&gt;(define (append list1 list2) (if (null? list1) list2 (cons (car list1) (append (cdr list1) list2))))&lt;br&gt;(define (accumulate op init seq) (if (null? seq) init (op (car seq) (accumulate op init (cdr seq)))))&lt;br&gt;(define (flatmap proc seq) (accumulate append (quote ()) (map proc seq)))&lt;br&gt;(define (enumerate-interval low high) (if (&amp;gt; low high)&amp;nbsp;&amp;nbsp;&amp;nbsp; '()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (cons low (enumerate-interval (+ low 1) high))))&lt;br&gt;(define (make-queen row col) (list row col))&lt;br&gt;(define (get-row queen) (car queen))&lt;br&gt;(define (get-col queen) (car (cdr queen)))&lt;br&gt;(define (same-col? q1 q2) (= (get-col q1) (get-col q2)))&lt;br&gt;(define (same-diag? q1 q2)&amp;nbsp;&amp;nbsp; (=&amp;nbsp;&amp;nbsp;&amp;nbsp; (abs (- (get-row q1) (get-row q2)))&amp;nbsp;&amp;nbsp;&amp;nbsp; (abs (- (get-col q1) (get-col q2)))))&lt;br&gt;(define (attacks? q1 q2)&amp;nbsp; (or (same-col? q1 q2) (same-diag? q1 q2)))&lt;br&gt;(define (safe? newq qlist)&amp;nbsp; (if&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (null? qlist) #t&amp;nbsp;&amp;nbsp;&amp;nbsp; (if (attacks? newq (car qlist)) #f&amp;nbsp; (safe? newq (cdr qlist)))))&lt;br&gt;(define (safe-board? qlist)&amp;nbsp; (safe? (car qlist) (cdr qlist)))&lt;br&gt;(define board-size 7)&lt;br&gt;(define (queen-rows k sols) (if (= k board-size)&amp;nbsp;&amp;nbsp; sols (queen-rows (+ k 1)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (filter&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (lambda (board) (safe-board? board))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (flatmap&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (lambda (rest-queens)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (map (lambda (new-col)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (cons (list k new-col) rest-queens))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (enumerate-interval 1 board-size)))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sols))))) &lt;br&gt;(length (queen-rows 0 (list (quote ()))))&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The above program runs OK in both F# and Haskell interpreter at about the same speed. However, when board-size equals 8, F# version gets a Stackoverflow runtime error. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;I think it is enough for a post, I will see if I can solve these problems&lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/-kaoBjdWZpr8/Tlo03RCgQEI/AAAAAAAABJs/A7kwfIXFIW8/wlEmoticon-smile%25255B2%25255D.png?imgmax=800"&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-7465707022660752766?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/7465707022660752766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/08/n-queen-problem-f-scheme-and-haskell.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7465707022660752766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7465707022660752766'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/08/n-queen-problem-f-scheme-and-haskell.html' title='N-queen problem, F#, Scheme and Haskell!'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-kaoBjdWZpr8/Tlo03RCgQEI/AAAAAAAABJs/A7kwfIXFIW8/s72-c/wlEmoticon-smile%25255B2%25255D.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-4707297651338702914</id><published>2011-04-18T11:32:00.001+08:00</published><updated>2011-04-18T17:08:22.991+08:00</updated><title type='text'>Extracting top words from titles and abstractions in MIX11 presentations</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;The videos of MIX11 (Apr. 11-14) conference are available at &lt;a href="http://channel9.msdn.com/Events/MIX/MIX11"&gt;&lt;b&gt;http://channel9.msdn.com/Events/MIX/MIX11&lt;/b&gt;&lt;/a&gt;. To get a sense of what’s going on recently, I wrote an F# script to count the top occurring words in the titles and abstractions of all the presentations. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Title statistics:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&amp;quot;windows&amp;quot; : 41    &lt;br /&gt;&lt;font style="background-color: #ffff00"&gt;&amp;quot;phone&amp;quot; : 26      &lt;br /&gt;&amp;quot;azure&amp;quot; : 14&lt;/font&gt;     &lt;br /&gt;&amp;quot;web&amp;quot; : 14     &lt;br /&gt;&lt;font style="background-color: #ffff00"&gt;&amp;quot;html5&amp;quot; : 11      &lt;br /&gt;&amp;quot;silverlight&amp;quot; : 9&lt;/font&gt;     &lt;br /&gt;&amp;quot;net&amp;quot; : 9     &lt;br /&gt;&lt;font style="background-color: #ffff00"&gt;&amp;quot;7&amp;quot; : 8&lt;/font&gt;     &lt;br /&gt;&amp;quot;applications&amp;quot; : 8     &lt;br /&gt;&amp;quot;data&amp;quot; : 8     &lt;br /&gt;&amp;quot;platform&amp;quot; : 7     &lt;br /&gt;&amp;quot;application&amp;quot; : 7     &lt;br /&gt;&lt;font style="background-color: #ffff00"&gt;&amp;quot;javascript&amp;quot; : 7      &lt;br /&gt;&lt;/font&gt;&amp;quot;new&amp;quot; : 7     &lt;br /&gt;&lt;font style="background-color: #ffff00"&gt;&amp;quot;ux&amp;quot; : 6&lt;/font&gt;     &lt;br /&gt;&amp;quot;what’s&amp;quot; : 6     &lt;br /&gt;&amp;quot;asp&amp;quot; : 6     &lt;br /&gt;&amp;quot;boot&amp;quot; : 5     &lt;br /&gt;&amp;quot;camp&amp;quot; : 5     &lt;br /&gt;&amp;quot;building&amp;quot; : 5&lt;/p&gt;  &lt;p&gt;From the statistics of the words in titles, we can find that windows mobile phone and azure cloud platform are the hottest topics.&amp;#160; HTML5, the next standard of web page technology, has been always been a focus by Microsoft. Silverlight still has its heat. It is a good company to HTML5, I think it will have many applications in in-house web applications; while HTML5 has more support and available in all browsers across platforms. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Abstract statistics:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&amp;quot;web&amp;quot; : 62    &lt;br /&gt;&amp;quot;session&amp;quot; : 55     &lt;br /&gt;&amp;quot;windows&amp;quot; : 49     &lt;br /&gt;&amp;quot;new&amp;quot; : 40     &lt;br /&gt;&amp;quot;applications&amp;quot; : 36     &lt;br /&gt;&amp;quot;phone&amp;quot; : 36     &lt;br /&gt;&amp;quot;learn&amp;quot; : 32     &lt;br /&gt;&amp;quot;we’ll&amp;quot; : 29     &lt;br /&gt;&amp;quot;use&amp;quot; : 25     &lt;br /&gt;&amp;quot;using&amp;quot; : 24     &lt;br /&gt;&amp;quot;silverlight&amp;quot; : 23     &lt;br /&gt;&amp;quot;data&amp;quot; : 23     &lt;br /&gt;&amp;quot;net&amp;quot; : 22     &lt;br /&gt;&amp;quot;javascript&amp;quot; : 21     &lt;br /&gt;&amp;quot;azure&amp;quot; : 21     &lt;br /&gt;&amp;quot;developers&amp;quot; : 20     &lt;br /&gt;&amp;quot;come&amp;quot; : 17     &lt;br /&gt;&amp;quot;microsoft&amp;quot; : 17     &lt;br /&gt;&amp;quot;features&amp;quot; : 17     &lt;br /&gt;&amp;quot;one&amp;quot; : 16&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;and the whole F# program is actually short -- only 40 lines! You have everything there: download webpages, get the titles and titles, stopword removing and word counting, sorting... &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:57039625-f95f-4f39-9248-6d401e9bc8b3" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px"&gt;Code Snippet&lt;/div&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;open&lt;/span&gt; System&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&lt;span style="color:#0000ff"&gt;open&lt;/span&gt; System.Net&lt;/li&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;open&lt;/span&gt; System.Text.RegularExpressions&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; fetchUrlSimple (url:string) = &lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; req = WebRequest.Create(url) &lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; response = req.GetResponse()&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    &lt;span style="color:#0000ff"&gt;use&lt;/span&gt; stream = response.GetResponseStream()&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;use&lt;/span&gt; streamreader = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; System.IO.StreamReader(stream)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    streamreader.ReadToEnd()&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; topKWords (docs:string seq) K = &lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; separator = [|&lt;span style="color:#800000"&gt;&amp;#39; &amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;&amp;#92;r&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;&amp;#92;n&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;,&amp;#39;&lt;/span&gt; ; &lt;span style="color:#800000"&gt;&amp;#39;&amp;#92;t&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;!&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;?&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;&amp;#92;&amp;#39;&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;;&amp;#39;&lt;/span&gt;; &lt;span style="color:#800000"&gt;&amp;#39;/&amp;#39;&lt;/span&gt; |]&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; stopwords = Set(&lt;span style="color:#800000"&gt;&amp;quot;a,able,about,across,after,all,almost,also,am,among,an,and,any,are,as,at,be,because,been,but,by,can,cannot,could,dear,did,do,does,either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,on,only,or,other,our,own,rather,said,say,says,she,should,since,so,some,than,that,the,their,them,then,there,these,they,this,tis,to,too,twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,will,with,would,yet,you,your&amp;quot;&lt;/span&gt;.Split &lt;span style="color:#800000"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;)&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    docs&lt;/li&gt; &lt;li&gt;    |&amp;gt; Seq.map (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; doc &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;        doc.Split(separator, StringSplitOptions.RemoveEmptyEntries)&lt;/li&gt; &lt;li&gt;        |&amp;gt; Seq.map (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; word &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; word.ToLower())&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;        |&amp;gt; Seq.filter (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; word &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; not (stopwords.Contains(word)))&lt;/li&gt; &lt;li&gt;        )&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    |&amp;gt; Seq.concat&lt;/li&gt; &lt;li&gt;    |&amp;gt; Seq.groupBy (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; x&lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt;x)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    |&amp;gt; Seq.map (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; (word, wordSeq) &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; (word, wordSeq |&amp;gt; Seq.length))&lt;/li&gt; &lt;li&gt;    |&amp;gt; Seq.sortBy (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; (_, wordCnt) &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; - wordCnt)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    |&amp;gt; Seq.take K&lt;/li&gt; &lt;li&gt;    |&amp;gt; Seq.toList&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; rawpage = fetchUrlSimple &lt;span style="color:#800000"&gt;@&amp;quot;http://channel9.msdn.com/Events/MIX/MIX11&amp;quot;&lt;/span&gt; &lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; titles, abstracts =&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; page = Regex.Replace(rawpage, &lt;span style="color:#800000"&gt;&amp;quot;&amp;amp;#?[a-z0-9]+;&amp;quot;&lt;/span&gt;, &lt;span style="color:#800000"&gt;&amp;quot; &amp;quot;&lt;/span&gt;)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; titleMatches = Regex.Matches(page, &lt;span style="color:#800000"&gt;&amp;quot;class=&amp;#92;&amp;quot;title&amp;#92;&amp;quot;&amp;gt;(.*?)&amp;lt;/a&amp;gt;&amp;quot;&lt;/span&gt;)&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; abstractMatches = Regex.Matches(page.Replace(&lt;span style="color:#800000"&gt;&amp;quot;&amp;#92;n&amp;quot;&lt;/span&gt;,&lt;span style="color:#800000"&gt;&amp;quot; &amp;quot;&lt;/span&gt;), &lt;span style="color:#800000"&gt;&amp;quot;class=&amp;#92;&amp;quot;description&amp;#92;&amp;quot;&amp;gt;(.*?)&amp;lt;/div&amp;gt;&amp;quot;&lt;/span&gt;)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; matchesToSeq (matches: MatchCollection) = &lt;/li&gt; &lt;li&gt;        seq {&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;            &lt;span style="color:#0000ff"&gt;for&lt;/span&gt; m &lt;span style="color:#0000ff"&gt;in&lt;/span&gt; matches &lt;span style="color:#0000ff"&gt;do&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                &lt;span style="color:#0000ff"&gt;yield&lt;/span&gt; m.Groups.[1].Value&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;        }&lt;/li&gt; &lt;li&gt;    matchesToSeq titleMatches, matchesToSeq abstractMatches&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;topKWords titles 20&lt;/li&gt; &lt;li&gt;topKWords abstracts 20&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;My colleague Defu Lian wrote a C# version for topKWords function using LINQ:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:453489c7-1a03-4561-a953-10201c75ea34" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px"&gt;Code Snippet&lt;/div&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;static&lt;/span&gt; &lt;span style="color:#2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color:#2b91af"&gt;Tuple&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;, &lt;span style="color:#0000ff"&gt;int&lt;/span&gt;&amp;gt;&amp;gt; topKWords(&lt;span style="color:#2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;&amp;gt; docs,&lt;span style="color:#0000ff"&gt;int&lt;/span&gt; K)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;{&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;char&lt;/span&gt;[] separator = { &lt;span style="color:#a31515"&gt;&amp;#39; &amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;&amp;#92;r&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;&amp;#92;n&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;&amp;#92;t&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;!&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;?&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;&amp;#92;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;;&amp;#39;&lt;/span&gt;, &lt;span style="color:#a31515"&gt;&amp;#39;/&amp;#39;&lt;/span&gt; };&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;    &lt;span style="color:#0000ff"&gt;var&lt;/span&gt; stopwords = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; &lt;span style="color:#2b91af"&gt;HashSet&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;&amp;gt;(&lt;span style="color:#a31515"&gt;&amp;quot;a,able,about,across,after,all,almost,also,am,among,an,and,any,are,as,at,be,because,been,but,by,can,cannot,could,dear,did,do,does,either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,on,only,or,other,our,own,rather,said,say,says,she,should,since,so,some,than,that,the,their,them,then,there,these,they,this,tis,to,too,twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,will,with,would,yet,you,your&amp;quot;&lt;/span&gt;.Split(&lt;span style="color:#a31515"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;));&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;return&lt;/span&gt; docs.SelectMany(doc =&amp;gt; doc.Split(separator, &lt;span style="color:#2b91af"&gt;StringSplitOptions&lt;/span&gt;.RemoveEmptyEntries)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;                        .Select(word =&amp;gt; word.ToLower())&lt;/li&gt; &lt;li&gt;                        .Where(word =&amp;gt; !stopwords.Contains(word)))&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;        .GroupBy(a =&amp;gt; a)&lt;/li&gt; &lt;li&gt;        .Select(wordgroup =&amp;gt; &lt;span style="color:#2b91af"&gt;Tuple&lt;/span&gt;.Create&amp;lt;&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;,&lt;span style="color:#0000ff"&gt;int&lt;/span&gt;&amp;gt;(wordgroup.Key, wordgroup.Count()))&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;        .OrderByDescending(w2c =&amp;gt; w2c.Item2)&lt;/li&gt; &lt;li&gt;        .Take(K)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;        .ToList();&lt;/li&gt; &lt;li&gt;}&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-4707297651338702914?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/4707297651338702914/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/04/extracting-top-words-from-titles-and.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/4707297651338702914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/4707297651338702914'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/04/extracting-top-words-from-titles-and.html' title='Extracting top words from titles and abstractions in MIX11 presentations'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-5946581607480077387</id><published>2011-04-10T20:44:00.001+08:00</published><updated>2011-04-10T21:34:56.673+08:00</updated><title type='text'>A Note on F# Quotations</title><content type='html'>&lt;p&gt;I worked on extending the F# ODSL (Optimization Domain Specific Language, &lt;a href="http://archive.msdn.microsoft.com/solverfoundationfs1"&gt;link1&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/b/lengningliu/archive/2009/09/04/optimization-domain-specific-language-in-f-with-units-of-measure.aspx"&gt;link2&lt;/a&gt;) during the weekend. This is the first time I use F# quotations in a non-trivial fashion. I tried some quotation examples in &lt;i&gt;Programming F#&lt;/i&gt; when I was learning the language and read code examples in F# PowerPack (for LINQ integration) and other libraries before.  &lt;p&gt;Only after I actually write some programs in it, I can appreciate this F# feature more and have some of my own thoughts. In this post, I’d like to share these thoughts. I don’t intend to go into detailed F# code, but maybe in the future I will write a separate blog on my work on extending the F# ODSL.  &lt;h2&gt;General idea of F# Quotations&lt;/h2&gt; &lt;p&gt;The great idea of quotation at least traces back to Lisp, where program is also a kind of data – the execution behavior of a piece of program is completely controllable by the user, just treat it as input data and write a custom evaluator for it. The default Lisp evaluator is eval, we can easily write a custom one to change the default behavior [LispEval].  &lt;p&gt;In F#, we can also treat a piece of F# code as data by quoting it:&lt;/p&gt;&lt;pre&gt;&lt;/pre&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;quotedProgramAsData = &lt;br /&gt;    &amp;lt;@&lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;y = 1 + 2 * 30&lt;br /&gt;        20 * y&lt;br /&gt;        &lt;span style="color: green"&gt;// code here:&lt;br /&gt;        // an F# program using a subset of F# language features&lt;br /&gt;    &lt;/span&gt;@&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;When we create the variable quotedProgramAsData, the .Net runtime will not compute the expression in the quotation; instead it generates the following value: &lt;br /&gt;&lt;p&gt;val quotedProgramAsData : Quotations.Expr&amp;lt;int&amp;gt; = &lt;pre class="code"&gt;val quotedProgramAsData : Quotations.Expr&amp;lt;int&amp;gt; =&lt;br /&gt;  Let (y,&lt;br /&gt;     Call (None, Int32 op_Addition[Int32,Int32,Int32](Int32, Int32),&lt;br /&gt;           [Value (1),&lt;br /&gt;            Call (None, Int32 op_Multiply[Int32,Int32,Int32](Int32, Int32),&lt;br /&gt;                  [Value (2), Value (30)])]),&lt;br /&gt;     Call (None, Int32 op_Multiply[Int32,Int32,Int32](Int32, Int32),&lt;br /&gt;           [Value (20), y]))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp; &lt;p&gt;Let’s visualize it in a tree form: &lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TaGmF-Pq9CI/AAAAAAAABHk/AKmQgTMlR8k/s1600-h/clip_image002%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TaGmHqEamUI/AAAAAAAABHo/Ig4z05lSync/clip_image002_thumb%5B1%5D.jpg?imgmax=800" width="228" height="294"&gt;&lt;/a&gt; &lt;br /&gt;&lt;p&gt;F# compiler generates this tree structure by free and the rest is how you deal/evaluate this tree. If the above code is not in quotation, F# compiler will generate code that put the value of the second parameter (1 + 2 * 30) of Let to the first parameter (y), and continue to generate code for the third parameter (20 * y). But because it is in the quotation, only the expression tree is generated without any explicit execution behavior for them. &lt;br /&gt;&lt;p&gt;With different purposes, we can write different evaluators for the quoted F# code. In the following I list some of the application areas. &lt;br /&gt;&lt;h2&gt;Domain specific language (DSL)&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;There are different kinds of domain specific languages. In his book &lt;i&gt;Domain Specific Languages&lt;/i&gt;, Martin Fowler summarizes them into external and internal ones: &lt;br /&gt;&lt;p&gt;&lt;em&gt;DSLs come in two main forms: external and internal. An external DSL is a language that's parsed independently of the host general purpose language: good examples include regular expressions and CSS. External DSLs have a strong tradition in the Unix community. Internal DSLs are a particular form of API in a host general purpose language, often referred to as a &lt;/em&gt;&lt;a href="http://martinfowler.com/bliki/FluentInterface.html"&gt;&lt;em&gt;fluent interface&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. The way mocking libraries, such as JMock, define expectations for tests are good examples of this, as are many of the mechanisms used by Ruby on Rails. Internal DSLs also have a long tradition of usage, particularly in the Lisp community. (&lt;/em&gt;&lt;a href="http://martinfowler.com/books.html#dsl"&gt;&lt;em&gt;http://martinfowler.com/books.html#dsl&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt; &lt;br /&gt;&lt;p&gt;Quotations can be used as a very good language feature for implementing internal domain specific languages. &lt;br /&gt;&lt;p&gt;As stated as before, the quoted program is anyway an F# program. How could it be look like a domain specific one? Remember F# has a rich set of syntax while a domain language takes a small subset of it is usually enough expressive. &lt;br /&gt;&lt;p&gt;Take a look at the following program, which expresses the quadratic optimization for Support Vector Machines:&lt;/p&gt;&lt;pre&gt;&lt;/pre&gt;&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// solve the SVM using ODSL&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;dsl_solver = &lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;index = [ 0..n-1]&lt;br /&gt;    Solver &lt;br /&gt;        &amp;lt;@&lt;br /&gt;            qp()&lt;br /&gt;            &lt;span style="color: blue"&gt;let &lt;/span&gt;alpha = vararray1 (index)&lt;br /&gt;            maximise (sum index (&lt;span style="color: blue"&gt;fun &lt;/span&gt;i &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;alpha.[i]) - &lt;br /&gt;                        (sum index (&lt;span style="color: blue"&gt;fun &lt;/span&gt;i &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;sum index (&lt;span style="color: blue"&gt;fun &lt;/span&gt;j &lt;span style="color: blue"&gt;-&amp;gt; &lt;br /&gt;                            &lt;/span&gt;coef.[i,j] * alpha.[i] * alpha.[j])))) &lt;span style="color: green"&gt;// Eq. (1)&lt;br /&gt;            &lt;/span&gt;where &lt;br /&gt;                [&lt;br /&gt;                    foreach index (&lt;span style="color: blue"&gt;fun &lt;/span&gt;i &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;0.0 &amp;lt;= alpha.[i]) &lt;span style="color: green"&gt;// Eq. (2)&lt;br /&gt;                    &lt;/span&gt;foreach index (&lt;span style="color: blue"&gt;fun &lt;/span&gt;i &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;alpha.[i] &amp;lt;= C) &lt;span style="color: green"&gt;// Eq. (2)&lt;br /&gt;                    &lt;/span&gt;sum index (&lt;span style="color: blue"&gt;fun &lt;/span&gt;i &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;alpha.[i] * y.[i]) = 0. &lt;span style="color: green"&gt;// Eq. (3)&lt;br /&gt;                &lt;/span&gt;]&lt;br /&gt;        @&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;and its optimization formulation: &lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TaGmItP7CNI/AAAAAAAABHs/LGB8D1a_OIE/s1600-h/clip_image004%5B6%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TaGmJhnaFDI/AAAAAAAABHw/3p8UZrVqbto/clip_image004_thumb%5B3%5D.png?imgmax=800" width="466" height="78"&gt;&lt;/a&gt; &lt;br /&gt;&lt;p&gt;Subject to constraints: &lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TaGmKfXthdI/AAAAAAAABH0/1t7Ah1NJ6bA/s1600-h/clip_image006%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TaGmMK3EA-I/AAAAAAAABH4/HfupJKP81ow/clip_image006_thumb%5B1%5D.png?imgmax=800" width="133" height="25"&gt;&lt;/a&gt; &lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TaGmMyQrgOI/AAAAAAAABH8/jyP_wwLcnPg/s1600-h/clip_image008%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TaGmN1NlpNI/AAAAAAAABIA/P8024bbP7O8/clip_image008_thumb%5B1%5D.png?imgmax=800" width="141" height="63"&gt;&lt;/a&gt; &lt;br /&gt;&lt;p&gt;The correspondence between the DSL code and mathematical formulates is very clear. In the future work, we can even vectorize this DSL to eliminate the usage of sum: range -&amp;gt; lambda (int-&amp;gt;value) -&amp;gt; value function, which will make the program and the formulas more similar. &lt;br /&gt;&lt;p&gt;Note that in the above example qp, maximise, sum and where are not magic, but are all F# functions whose behaviors are defined in the DSL. For example, F# function qp() only indicates the following code is a quadratic programming. &lt;br /&gt;&lt;p&gt;Compare this code with the code in &lt;a href="http://fdatamining.blogspot.com/2011/02/support-vector-machines-svms-in-f-using.html"&gt;my SVM post&lt;/a&gt;: it is clearer and thus easier to write. &lt;br /&gt;&lt;p&gt;The restriction of such a DSL inside F# is that the syntax of this DSL should follow that of F# -- it should be a valid program recognizable by the F# lexer and parser. This is why DSL people like Lisp which has so simple and flexible syntax (just brackets), but sometimes it is also boring and confusing when one gets lost in the brackets. &lt;br /&gt;&lt;h2&gt;High performance computing&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;We know that F#’s Seq module and C#’s LINQ share some common features, e.g. chained operations and the laziness. Because these operations are so fundamental, LINQ team has spent enormous time optimizing LINQ; on the other hand, F#’s implementation is quite a standard one without heavy optimization because F# compiler team is not big and they have other important tasks to do. (F#’s Seq module actually does some optimization, e.g. for arrays and lists special routines are called instead of the general ones for IEnumerable&amp;lt;T&amp;gt; objects. But LINQ does more!) &lt;br /&gt;&lt;p&gt;So one idea of speeding up sequence operations in F# is to use LINQ’s equivalent functions. This could be implemented by putting F#’s sequence operations in a quotation and rewrite them in LINQ expression as done in F# PowerPack. Interested readers can read the source code of LINQ for F# in PowerPack. &lt;br /&gt;&lt;p&gt;The above example talks about basic data structures. As a data mining guy myself, let’s move to numerical computing: we can quote a piece of numerical code in F# and translates it into Fortran/C and a JIT Fortran/C compiler compiles the code translated code into native machine code which uses special instruction set in that platform (e.g. &lt;a href="http://en.wikipedia.org/wiki/Vectorization_%28parallel_computing%29"&gt;vectorized instructions&lt;/a&gt; in P4 CPU). Or even we can compile this piece of F# into GPU and utilize the parallel computing there. [Syme_ML06] &lt;br /&gt;&lt;p&gt;But any fancy optimization has some overhead too. For example, the compiling time may cost some time. &lt;br /&gt;&lt;h2&gt;Education: Compiler courses&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;The last application area is education. There are few CS departments teaching functional programming. Some of them will mention it when teaching programming language concepts. F# is a push for FP into industry and main stream. Hope in the near future, schools will open tiny or selected courses for F#. See the recent F# in Education Workshop [Education] for details. &lt;br /&gt;&lt;p&gt;Ok. Let’s focus on compiler courses. Compilers, different from functional programming, are taught in nearly all CS departments. However the projects in compiler courses are a huge pain to students. They are so complicated! And the focus of the first half of the course (lexing and parsing) and the second half (code generation and optimization) are kind of separate. The theories behind the two halves are different. Sometimes, the course instructor focuses too more on lexing and parsing and don’t have enough time for students to work on code generation and optimization, which in my mind are more important in a compiler course. While lexing and parsing will occur in other CS courses too, e.g. computational theory, code generation and optimization will only be in a compiler course. &lt;br /&gt;&lt;p&gt;By using the F# quotations, F# compiler generates the expression tree for free. Just so convenient and straightforward! Considering that the F# syntax you can put in quotation is a capable set of imperative language + functional language, the expression tree for us to do the code generation and optimization is non-trivial. By using tools like .Net Reflector, we can also study how the standard F# compiler generates the same piece of code and learn the tricks there. Students can work on code generation for closures, and tail-recursive-calls – important language features that are not implemented or fully implemented in many main stream languages. &lt;br /&gt;&lt;h2&gt;References&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Some references in the above text: &lt;br /&gt;&lt;p&gt;[LispEval] Christian Queinnec , Lisp in Small Pieces. &lt;br /&gt;&lt;p&gt;[Syme_ML06] Leveraging .NET Meta-programming Components from F#, ML Workshop, 2006. &lt;br /&gt;&lt;p&gt;[Education] F# in Education Workshop, &lt;a href="http://research.microsoft.com/en-us/events/fsharpined/"&gt;http://research.microsoft.com/en-us/events/fsharpined/&lt;/a&gt;. &lt;br /&gt;&lt;p&gt;A very good paper by Leijen and Meijer: &lt;br /&gt;&lt;p&gt;[Leijen&amp;amp;Meijer] Domain specific embedded compilers, &lt;i&gt;Sigplan Notices&lt;/i&gt;, vol. 35, no. 1, pp. 109-122, 2000. &lt;br /&gt;&lt;p&gt;F# quotation extensively uses active patterns for expression tree pattern matching: &lt;br /&gt;&lt;p&gt;[Syme_Active] Extensible Pattern Matching via a Lightweight Language Extension, ICFP 2007. &lt;br /&gt;&lt;p&gt;Here are some relevant blog posts: &lt;br /&gt;&lt;p&gt;[Petricek] F# Overview (IV.) - Language Oriented Programming, &lt;a href="http://tomasp.net/blog/fsharp-iv-lang.aspx"&gt;http://tomasp.net/blog/fsharp-iv-lang.aspx&lt;/a&gt;. &lt;br /&gt;&lt;p&gt;[RubyDSL] Building a DSL in Ruby, &lt;a href="http://jroller.com/rolsen/entry/building_a_dsl_in_ruby"&gt;http://jroller.com/rolsen/entry/building_a_dsl_in_ruby&lt;/a&gt;. &lt;br /&gt;&lt;p&gt;Two nice answers by Petricek &amp;amp; Harrop discussing Quotations (F#, Ocaml and Lisp) and DSL on Stackoverflow:&lt;br /&gt;&lt;p&gt;&lt;a title="http://stackoverflow.com/questions/4317912/confusing-about-f-quotations-and-pattern-matching-in-meta-programming" href="http://stackoverflow.com/questions/4317912/confusing-about-f-quotations-and-pattern-matching-in-meta-programming"&gt;http://stackoverflow.com/questions/4317912/confusing-about-f-quotations-and-pattern-matching-in-meta-programming&lt;/a&gt;. &lt;br /&gt;&lt;h2&gt;Acknowledgement&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;I’d like to thank &lt;a href="http://blogs.msdn.com/b/natbr/"&gt;Nathan Brixius&lt;/a&gt; for discussions on F# ODSL! &lt;br /&gt;&lt;br /&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-5946581607480077387?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/5946581607480077387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/04/note-on-f-quotations.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5946581607480077387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5946581607480077387'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/04/note-on-f-quotations.html' title='A Note on F# Quotations'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_4bFBK2BKrQI/TaGmHqEamUI/AAAAAAAABHo/Ig4z05lSync/s72-c/clip_image002_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-7891944344993188449</id><published>2011-02-24T15:40:00.001+08:00</published><updated>2011-05-24T13:41:40.870+08:00</updated><title type='text'>Support vector machines (SVMs) in F# using Microsoft Solver Foundation</title><content type='html'>&lt;p&gt;Support vector machines are a super star in machine learning and data mining in the past decade. It reheats statistical learning in machine learning community. It is also one of the classifiers that work practically with applications to other disciplines such as bioinformatics.&lt;/p&gt;  &lt;p&gt;In this post, I will give a brief review of the theory of this classifier and show an F# implementation using the quadratic programming (QP) solver in Microsoft Solver Foundation (MSF). &lt;/p&gt;  &lt;p&gt;It may seem dull to start with mathematics. But the math in SVMs is actually quite intuitive and would be quite easy if one has some training in linear algebra and calculus. Also understanding the intuition behind the formulae is more important in the formulation itself. There are quite a lot of good SVM tutorials written by researchers. Thus I only write the important formulas that occur in the implementation. &lt;i&gt;The reader could link the F# code with formulas directly.&lt;/i&gt;&lt;/p&gt;  &lt;p&gt;My note is based on the machine learning course by Prof. Jaakkola [Jaakkola] and the Matlab implementation therein. The course material is highly comprehensive yet accurate. &lt;/p&gt;  &lt;h2&gt;Background review for SVMs&lt;/h2&gt;  &lt;p&gt;Given a binary labeled dataset&lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYKj5K4NsI/AAAAAAAABDQ/22g0tNDLzzI/s1600-h/clip_image002%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image002" alt="clip_image002" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYKlU9twnI/AAAAAAAABDU/7rXs-KX37hY/clip_image002_thumb%5B1%5D.png?imgmax=800" border="0" width="118" height="25" /&gt;&lt;/a&gt;, where &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYKl3U4ewI/AAAAAAAABDY/KHRQUXw9cq8/s1600-h/clip_image004%5B6%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image004" alt="clip_image004" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYKmqW6pdI/AAAAAAAABDc/LPspYwPntsg/clip_image004_thumb%5B1%5D.png?imgmax=800" border="0" width="61" height="26" /&gt;&lt;/a&gt; and &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYKnFWXXYI/AAAAAAAABDg/0r0Ihf8oTjE/s1600-h/clip_image006%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image006" alt="clip_image006" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYKoCOoh4I/AAAAAAAABDk/qii6LJC0154/clip_image006_thumb%5B1%5D.png?imgmax=800" border="0" width="16" height="25" /&gt;&lt;/a&gt; is &lt;i&gt;1&lt;/i&gt; or &lt;i&gt;-1&lt;/i&gt;. Think about the data points &lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYKowYFqZI/AAAAAAAABDo/tB17a0zkuDg/s1600-h/clip_image008%5B8%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image008" alt="clip_image008" src="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYKqecAIdI/AAAAAAAABDs/EoKHuluas2g/clip_image008_thumb%5B1%5D.png?imgmax=800" border="0" width="16" height="25" /&gt;&lt;/a&gt; plotted in a &lt;i&gt;d&lt;/i&gt; dimensional (Euclidean) space, the linear SVM classifier is a hyperplane &lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYKqzyxe1I/AAAAAAAABDw/cCylMvIh5zI/s1600-h/clip_image010%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image010" alt="clip_image010" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYKrnP-DEI/AAAAAAAABD0/Sg-WItR7Zqo/clip_image010_thumb%5B1%5D.png?imgmax=800" border="0" width="70" height="25" /&gt;&lt;/a&gt; in the space and it “best” separates the two kinds of data points. Here “best” means that the hyperplane should have the largest margin, which is the distance from the plane to the sides of labeled points. Intuitively the larger the margin is, the more robust and confident the classifier is. As if the hyperplane shakes a little, it still classifies well for its being far from both sides. &lt;/p&gt;  &lt;p&gt;Let’s take &lt;i&gt;d&lt;/i&gt;=2 as the discussing example: data points are plotted in a 2-D plane&lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYKsFhFNqI/AAAAAAAABD4/dHyns-NI6TA/s1600-h/clip_image012%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image012" alt="clip_image012" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYKtp7pkkI/AAAAAAAABD8/nfk9PnKLM4k/clip_image012_thumb%5B1%5D.png?imgmax=800" border="0" width="110" height="25" /&gt;&lt;/a&gt;. The aim is to draw a line &lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYKuAtxp3I/AAAAAAAABEA/Y_xDa93AIY4/s1600-h/clip_image014%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image014" alt="clip_image014" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYKu910c9I/AAAAAAAABEE/PRLOcskPpc4/clip_image014_thumb%5B1%5D.png?imgmax=800" border="0" width="208" height="25" /&gt;&lt;/a&gt; to separate the two kinds of points such that the margin of the separation is maximized. In the following Figure, the line with maximum margin is shown. After some geometry work, the margin is calculated as &lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYKvvy4wgI/AAAAAAAABEI/udYBPcCx4L4/s1600-h/clip_image016%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image016" alt="clip_image016" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYKxKSj6dI/AAAAAAAABEM/-NN3Rmc4HX8/clip_image016_thumb%5B1%5D.png?imgmax=800" border="0" width="59" height="36" /&gt;&lt;/a&gt;, thus minimizing &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYKxviFvdI/AAAAAAAABEQ/j6exY08r57o/s1600-h/clip_image018%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image018" alt="clip_image018" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYKyakPWuI/AAAAAAAABEU/w3zZ5U_Nufs/clip_image018_thumb%5B1%5D.png?imgmax=800" border="0" width="42" height="25" /&gt;&lt;/a&gt; with the constraints that the two kinds of points are well separated (&lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYKzGACvgI/AAAAAAAABEY/ZlPE_hsDKsc/s1600-h/clip_image020%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image020" alt="clip_image020" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYKz1pnqbI/AAAAAAAABEc/Hn3Ei_DzxZI/clip_image020_thumb%5B1%5D.png?imgmax=800" border="0" width="108" height="28" /&gt;&lt;/a&gt;) gives the max-margin hyperplane. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/2/2a/Svm_max_sep_hyperplane_with_margin.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image022" alt="clip_image022" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYK0vqs6xI/AAAAAAAABEg/4u-hfMkACWA/clip_image022%5B4%5D.png?imgmax=800" border="0" width="223" height="240" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;(Wikipedia picture)&lt;/p&gt;  &lt;p&gt;Two important extensions to the above simplest SVM are:&lt;/p&gt;  &lt;p&gt;1. &lt;b&gt;Allowing imperfect separation&lt;/b&gt;, that is when a hyperplane could not separate the points perfectly, it is allowed to mis-separate some data points. This introduces a slack variable &lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYK1aYEBUI/AAAAAAAABEk/CFJEVNtaWkI/s1600-h/clip_image024%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image024" alt="clip_image024" src="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYK1-rKJ3I/AAAAAAAABEo/xLX7g-TC-B0/clip_image024_thumb%5B1%5D.png?imgmax=800" border="0" width="14" height="25" /&gt;&lt;/a&gt; for each data point &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYK2oFNF7I/AAAAAAAABEs/bUmOTbolgCA/s1600-h/clip_image008%5B1%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image008[1]" alt="clip_image008[1]" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYK3fclXiI/AAAAAAAABEw/xG8I8Lt2-8c/clip_image008%5B1%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="16" height="25" /&gt;&lt;/a&gt;, when the point is on the correct side of the plane, the slack variable is 0, otherwise &lt;i&gt;it is measures the distance it goes from the plane minus 1, if the values goes to below zero set it zero&lt;/i&gt;. (Please read the Note and reference section for more explanation.)&lt;/p&gt;  &lt;p&gt;2. &lt;b&gt;Kernel&lt;/b&gt;. The simplest SVM is a linear classifier in the original space. However, there are situations where linear classifier is not sufficient (even the best), one strategy is to do a non-linear transformation or mapping &lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYK4N5WMOI/AAAAAAAABE0/LtSHSs6T6Fc/s1600-h/clip_image026%5B6%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image026" alt="clip_image026" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYK4w77K-I/AAAAAAAABE4/XQYBndHCEr8/clip_image026_thumb%5B1%5D.png?imgmax=800" border="0" width="33" height="25" /&gt;&lt;/a&gt; that maps a data point &lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYK5jWMFkI/AAAAAAAABE8/nHBQBrPhJ9w/s1600-h/clip_image004%5B1%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image004[1]" alt="clip_image004[1]" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYK6UjXbjI/AAAAAAAABFA/--DUMkR-LqQ/clip_image004%5B1%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="61" height="26" /&gt;&lt;/a&gt;&lt;b&gt; &lt;/b&gt;to another space, so that a linear classifier in that space, which is non-linear in the original &lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYK7PYc1hI/AAAAAAAABFE/KN7F7OgfXG0/s1600-h/clip_image028%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image028" alt="clip_image028" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYK71idW4I/AAAAAAAABFI/jgwDgcDreMw/clip_image028_thumb%5B1%5D.png?imgmax=800" border="0" width="22" height="26" /&gt;&lt;/a&gt; space, does a better separation of the data points. The kernel trick is that we don’t need to define &lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYK8Sx1NMI/AAAAAAAABFM/WniCFdHYMNU/s1600-h/clip_image026%5B1%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image026[1]" alt="clip_image026[1]" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYK9JoLySI/AAAAAAAABFQ/FSJpxCmBnXA/clip_image026%5B1%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="33" height="25" /&gt;&lt;/a&gt; explicitly; only the definition of inner product of that space is required: &lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYK9gVDP7I/AAAAAAAABFU/cj7csklT4y4/s1600-h/clip_image030%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image030" alt="clip_image030" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYK-W3mGpI/AAAAAAAABFY/dv5cwMw9WeY/clip_image030_thumb%5B1%5D.png?imgmax=800" border="0" width="191" height="27" /&gt;&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;With the two extensions, the new maximum margin objective becomes:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYK-1mTC3I/AAAAAAAABFc/Dy2XazPLSos/s1600-h/clip_image032%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image032" alt="clip_image032" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYK_amSeJI/AAAAAAAABFg/LOHyRexgkmg/clip_image032_thumb%5B1%5D.png?imgmax=800" border="0" width="169" height="63" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;subject to &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLAGynZ4I/AAAAAAAABFk/uIgAFKDo9u8/s1600-h/clip_image034%5B6%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image034" alt="clip_image034" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLA0es4oI/AAAAAAAABFo/eG-GRBiEwn4/clip_image034_thumb%5B3%5D.png?imgmax=800" border="0" width="426" height="32" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The dual form:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLBoZtYiI/AAAAAAAABFs/_jZY9xRwPLo/s1600-h/clip_image036%5B5%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image036" alt="clip_image036" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYLCRYVUoI/AAAAAAAABFw/c52nXEuHd-Q/clip_image036_thumb%5B2%5D.png?imgmax=800" border="0" width="311" height="57" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;subject to &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYLDNry1zI/AAAAAAAABF0/WttUNn38UpI/s1600-h/clip_image038%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image038" alt="clip_image038" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLD_0QGtI/AAAAAAAABF4/I1WpQc_rAis/clip_image038_thumb%5B1%5D.png?imgmax=800" border="0" width="133" height="25" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLEggru3I/AAAAAAAABF8/Lp3f_WgYUnc/s1600-h/clip_image040%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image040" alt="clip_image040" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYLFZ_NJ6I/AAAAAAAABGA/AgCrkK1XlRc/clip_image040_thumb%5B1%5D.png?imgmax=800" border="0" width="141" height="63" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The data points with its alpha value greater than 0 are called support vectors. &lt;/p&gt;  &lt;p&gt;With the &lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLGLPBf0I/AAAAAAAABGE/cNpjTKmoPUM/s1600-h/clip_image042%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image042" alt="clip_image042" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYLG3H_F-I/AAAAAAAABGI/4GnQrUQ0Bd0/clip_image042_thumb%5B1%5D.png?imgmax=800" border="0" width="18" height="25" /&gt;&lt;/a&gt;in the dual problem solved, the SVM classification hyperplane is recovered by:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYLHbc7dZI/AAAAAAAABGM/OPPQNgPIiSI/s1600-h/clip_image044%5B6%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image044" alt="clip_image044" src="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLKGhBaqI/AAAAAAAABGQ/n2xyuw192Gc/clip_image044_thumb%5B3%5D.png?imgmax=800" border="0" width="584" height="56" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The threshold parameter &lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLK-G-jfI/AAAAAAAABGU/lAK7M6bYjLc/s1600-h/clip_image046%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image046" alt="clip_image046" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYLMbPJUHI/AAAAAAAABGY/1499roo6lmg/clip_image046_thumb%5B1%5D.png?imgmax=800" border="0" width="25" height="25" /&gt;&lt;/a&gt;could be calculated by substituting back to the support vectors:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYLNPMAYaI/AAAAAAAABGc/dToBJKJ44nk/s1600-h/clip_image048%5B5%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image048" alt="clip_image048" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLN9kc74I/AAAAAAAABGg/Z7QxTr1ujTw/clip_image048_thumb%5B2%5D.png?imgmax=800" border="0" width="442" height="46" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Any &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLOndRbjI/AAAAAAAABGk/Qu6yu321doA/s1600-h/clip_image050%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image050" alt="clip_image050" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLPzXk9FI/AAAAAAAABGo/sA837ITnJWQ/clip_image050_thumb%5B1%5D.png?imgmax=800" border="0" width="51" height="25" /&gt;&lt;/a&gt; and &lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLQtDXujI/AAAAAAAABGs/wDsf_0D2dNA/s1600-h/clip_image052%5B4%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image052" alt="clip_image052" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLRfwCwtI/AAAAAAAABGw/mkXKcDpisq8/clip_image052_thumb%5B1%5D.png?imgmax=800" border="0" width="58" height="25" /&gt;&lt;/a&gt; (So that make sure data point &lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYLSCJ-YCI/AAAAAAAABG0/X1kmI5CVME0/s1600-h/clip_image008%5B2%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image008[2]" alt="clip_image008[2]" src="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLTjG_pwI/AAAAAAAABG4/AFnhYXZQjPQ/clip_image008%5B2%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="16" height="25" /&gt;&lt;/a&gt; is on the margin boundary, not in-between) would give a &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLUF5vG_I/AAAAAAAABG8/Du_jIFpn3fQ/s1600-h/clip_image054%5B6%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image054" alt="clip_image054" src="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLVLYlmmI/AAAAAAAABHA/V3tZR6NO2sA/clip_image054_thumb%5B1%5D.png?imgmax=800" border="0" width="21" height="25" /&gt;&lt;/a&gt;, to stabilize the solution, it is common practice to take the median of off the possible &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLV4BEpxI/AAAAAAAABHE/vvTmyVj9_JA/s1600-h/clip_image054%5B1%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image054[1]" alt="clip_image054[1]" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLWrXFejI/AAAAAAAABHI/DPKTU2FCDUw/clip_image054%5B1%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="21" height="25" /&gt;&lt;/a&gt;. &lt;/p&gt;  &lt;h2&gt;The implementation&lt;/h2&gt;  &lt;p&gt;The solver is actually an Interior Point Solver, which could solve linear and quadratic programming with linear constraints. &lt;/p&gt;  &lt;p&gt;Page 14-17 of MSF-SolverProgrammingPrimer.pdf show an example usage of the QP solver in Microsoft Solver Foundation. But the example is not complete and it does not demonstrate one very subtle part: how to set the coefficients for the quadratic terms.  &lt;/p&gt;  &lt;p&gt;The following code example shows a simple example with &lt;strong&gt;the the comment regarding the coefficient setting&lt;/strong&gt;:&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:9d9ac386-61a4-483b-9f59-ca9838d2e281" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0); font-family: 'Courier New',Courier,Monospace; font-size: 10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(0, 0, 128); color: rgb(255, 255, 255); font-family: Verdana,Tahoma,Arial,sans-serif; font-weight: bold; padding: 2px 5px;"&gt;Example usage of the QP solver&lt;/div&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 500px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2.5em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;#r&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0);"&gt;@"C:\Program Files (x86)\Sho 2.0 for .NET 4\packages\Optimizer\Microsoft.Solver.Foundation.dll"&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;open&lt;/span&gt; Microsoft.SolverFoundation.Common&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;open&lt;/span&gt; Microsoft.SolverFoundation.Solvers&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;open&lt;/span&gt; Microsoft.SolverFoundation.Services&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;// test QP&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;module&lt;/span&gt; TEST = &lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;(* minimize x^2 + y^2 + 3xy + 2x + y *)&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;(* notice that in the InteriorPointSolver,&lt;/span&gt;&lt;/li&gt; &lt;li&gt;   &lt;span style="color: rgb(0, 128, 0);"&gt;the coefficients for xy &amp;amp; yx should be the same (so only set ONCE!)&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;   &lt;span style="color: rgb(0, 128, 0);"&gt;if we set both 3xy and 0yx, the solver takes the later coef. &lt;/span&gt;&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;*)&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; solver = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; InteriorPointSolver()&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; _, goal = solver.AddRow(&lt;span style="color: rgb(128, 0, 0);"&gt;"dual objective value"&lt;/span&gt;)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    solver.AddGoal(goal, 0, &lt;span style="color: rgb(0, 0, 255);"&gt;true&lt;/span&gt;)&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; _, x = solver.AddVariable(&lt;span style="color: rgb(128, 0, 0);"&gt;"x"&lt;/span&gt;)&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; _, y = solver.AddVariable(&lt;span style="color: rgb(128, 0, 0);"&gt;"y"&lt;/span&gt;)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;    solver.SetCoefficient(goal, x, Rational.op_Implicit(2))&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    solver.SetCoefficient(goal, y, Rational.op_Implicit(1))&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;// for terms like x-y (where x != y), set its coef for only one time!&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    solver.SetCoefficient(goal, Rational.op_Implicit(3), x, y)&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;//solver.SetCoefficient(goal, Rational.Zero, y, x)&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    solver.SetCoefficient(goal, Rational.op_Implicit(1), x, x)&lt;/li&gt; &lt;li&gt;    solver.SetCoefficient(goal, Rational.op_Implicit(1), y, y)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;/li&gt; &lt;li&gt;    &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; param = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; InteriorPointSolverParams()&lt;/li&gt; &lt;li&gt;    solver.Solve(param) |&amp;gt; ignore&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;//solver.Result&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; objectiveValue = solver.GetValue(0).ToDouble()&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; x0 = solver.GetValue(1).ToDouble()&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; y0 = solver.GetValue(2).ToDouble()&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    x0*x0 + y0*y0 + 3.0 * x0 * y0 + 2. * x0  + y0&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;//x0*x0 + y0*y0 + 0.0 * x0 * y0 + 2. * x0  + y0&lt;/span&gt;&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;The implementation of SVM is a straightforward translation of equations (1) to (5). The following shows the svm learning (&lt;strong&gt;bulidSvm&lt;/strong&gt;) and testing (&lt;strong&gt;svmClassify&lt;/strong&gt;) functions:&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:b8485512-e347-4e00-b427-fabd91c604b1" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0); font-family: 'Courier New',Courier,Monospace; font-size: 10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(0, 0, 128); color: rgb(255, 255, 255); font-family: Verdana,Tahoma,Arial,sans-serif; font-weight: bold; padding: 2px 5px;"&gt;SVM Implementation&lt;/div&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 500px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 3em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;open&lt;/span&gt; System&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;type&lt;/span&gt; dataset = &lt;/li&gt; &lt;li&gt;    { features: float array array; &lt;span style="color: rgb(0, 128, 0);"&gt;// (instance = float array) array&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;      &lt;span style="color: rgb(0, 0, 255);"&gt;mutable&lt;/span&gt; labels: int array; &lt;span style="color: rgb(0, 128, 0);"&gt;// &lt;/span&gt;&lt;/li&gt; &lt;li&gt;    }&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;with&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;member&lt;/span&gt; x.NumSamples = x.features.Length&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;module&lt;/span&gt; Array = &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; median (a:'a array) = &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; sorted = Array.sort a&lt;/li&gt; &lt;li&gt;        sorted.[sorted.Length / 2]&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;module&lt;/span&gt; Kernel = &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; linear a b = &lt;/li&gt; &lt;li&gt;        Array.fold2 (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; acc p q &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; acc + p * q) 0.0 a b&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; polynomial k a b = &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; dot = linear a b&lt;/li&gt; &lt;li&gt;        Math.Pow(1.0 + dot, k |&amp;gt; float)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; gaussian beta a b = &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; diff = Array.fold2 (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; acc p q &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; acc + (p-q)*(p-q)) 0.0 a b&lt;/li&gt; &lt;li&gt;        exp (-0.5 * beta * diff)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;module&lt;/span&gt; SVM = &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;type&lt;/span&gt; svmmodel = {&lt;/li&gt; &lt;li&gt;        SVs:dataset;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        alpha:float array;&lt;/li&gt; &lt;li&gt;        kernel: float[] &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; float[] &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; float;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        w0:float;&lt;/li&gt; &lt;li&gt;        }&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;with&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;member&lt;/span&gt; x.NumSupporVectors = x.SVs.features.Length&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; buildSVM (ds:dataset) (C:float) (kernel:float[] &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; float[] &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; float) = &lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; n = ds.features.Length&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; C = Rational.op_Implicit(C)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; zero = Rational.Zero&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// create a interior point solver, which solves the QP problem&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; solver = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; InteriorPointSolver()&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// set the objective value / goal&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; _, goal = solver.AddRow(&lt;span style="color: rgb(128, 0, 0);"&gt;"dual objective value"&lt;/span&gt;)&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// false == maximizing the objective value&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// the value of goal is (1)&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        solver.AddGoal(goal, 0, &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt;) |&amp;gt; ignore&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// add the Lagangian variables \alpha_i and set their bounds (0 &amp;lt;= \alpha_i &amp;lt;= C)&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; alpha = Array.create n 0&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; i=0 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; n-1 &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; _, out = solver.AddVariable(&lt;span style="color: rgb(128, 0, 0);"&gt;"alpha_"&lt;/span&gt;+i.ToString())&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            alpha.[i] &amp;lt;- out&lt;/li&gt; &lt;li&gt;            solver.SetBounds(out, zero, C)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// add contraint: \sum_i \alpha_i * y_i = 0&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// equation (2)&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; _, sumConstraint = solver.AddRow(&lt;span style="color: rgb(128, 0, 0);"&gt;"SumConstraint"&lt;/span&gt;)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        solver.SetBounds(sumConstraint, zero, zero);&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; i=0 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; n-1 &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 128, 0);"&gt;// set the coefs for the sum constraint&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            &lt;span style="color: rgb(0, 128, 0);"&gt;// equation (2)&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            solver.SetCoefficient(sumConstraint, alpha.[i], Rational.op_Implicit(ds.labels.[i]))&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 128, 0);"&gt;// add the \alpha_i terms into the objective&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            solver.SetCoefficient(goal, alpha.[i], Rational.One)&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            &lt;span style="color: rgb(0, 128, 0);"&gt;// add the qudratic terms&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; j=0 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; i &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                &lt;span style="color: rgb(0, 128, 0);"&gt;// coef = y_i * y_j * K(x_i, x_j)&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; coef = float(ds.labels.[i] * ds.labels.[j]) * (kernel ds.features.[i] ds.features.[j])&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; i=j &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                    solver.SetCoefficient(goal, Rational.op_Implicit(-0.5 * coef), alpha.[i], alpha.[j])&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;else&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                    solver.SetCoefficient(goal, Rational.op_Implicit(-coef), alpha.[i], alpha.[j])&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// use the default parameters &lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; param = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; InteriorPointSolverParams()&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;/li&gt; &lt;li&gt;        solver.Solve(param) |&amp;gt; ignore&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// get the alpha values out&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; alphaValue = Array.init n (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; i &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; solver.GetValue(i+1))&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;(* print optimization result&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;printfn "goal value = %A" (solver.GetValue(0).ToDouble())&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;for i=1 to n do&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 128, 0);"&gt;printfn "%A" (solver.GetValue(i).ToDouble())&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 128, 0);"&gt;*)&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; alphaNew = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; ResizeArray&amp;lt;Rational&amp;gt;()&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// extract the non-zero alpha values out and their corresponding support vectors&lt;/span&gt;&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; SVs = &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; feats = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; ResizeArray&amp;lt;float[]&amp;gt;()&lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; labs = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; ResizeArray&amp;lt;int&amp;gt;()&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; maxAlpha = Array.max alphaValue&lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; threshold = maxAlpha * Rational.op_Implicit(1e-8)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; i=0 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; n-1 &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; alphaValue.[i] &amp;gt; threshold &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                    feats.Add(ds.features.[i])&lt;/li&gt; &lt;li&gt;                    labs.Add(ds.labels.[i])&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                    alphaNew.Add(alphaValue.[i])&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            { features = feats |&amp;gt; Seq.toArray;&lt;/li&gt; &lt;li&gt;              labels = labs |&amp;gt; Seq.toArray;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            }&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// solve w_0 in the primal form&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; alphaNZ = alphaNew |&amp;gt; Seq.toArray&lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// equation (5)&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; w0 = &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            alphaNZ&lt;/li&gt; &lt;li&gt;            |&amp;gt; Array.mapi (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; i a &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; a = C &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt; &lt;/li&gt; &lt;li&gt;                    None&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;else&lt;/span&gt; &lt;/li&gt; &lt;li&gt;                    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;mutable&lt;/span&gt; tmp = 0.0&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                    &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; j=0 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; SVs.NumSamples-1 &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                        tmp &amp;lt;- tmp + alphaNZ.[j].ToDouble() * (SVs.labels.[j] |&amp;gt; float) * (kernel SVs.features.[i] SVs.features.[j])&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                    Some ((float SVs.labels.[i]) - tmp)&lt;/li&gt; &lt;li&gt;                )&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            |&amp;gt; Array.filter (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; v &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;match&lt;/span&gt; v &lt;span style="color: rgb(0, 0, 255);"&gt;with&lt;/span&gt; None &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt; | _ &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;true&lt;/span&gt;)&lt;/li&gt; &lt;li&gt;            |&amp;gt; Array.map (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; v &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;match&lt;/span&gt; v &lt;span style="color: rgb(0, 0, 255);"&gt;with&lt;/span&gt; Some v &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; v | _ &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; 0.0)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            |&amp;gt; Array.median&lt;/li&gt; &lt;li&gt;        &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// construct an svm record&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        {&lt;/li&gt; &lt;li&gt;            SVs = SVs;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            alpha = alphaNZ |&amp;gt; Array.map (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; v &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; v.ToDouble());&lt;/li&gt; &lt;li&gt;            kernel = kernel;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            w0 = w0;&lt;/li&gt; &lt;li&gt;        }&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;/li&gt; &lt;li&gt; &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; svmClassify (model:svmmodel) (ds:dataset) = &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 128, 0);"&gt;// equation (4)&lt;/span&gt;&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; vals = &lt;/li&gt; &lt;li&gt;            ds.features&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            |&amp;gt; Array.map (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; x &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;mutable&lt;/span&gt; sum = 0.0&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; i=0 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; model.NumSupporVectors-1 &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                    sum &amp;lt;- sum + model.alpha.[i] * (float model.SVs.labels.[i]) * (model.kernel model.SVs.features.[i] x)&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;                sum + model.w0&lt;/li&gt; &lt;li&gt;                )&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; nCorrect = &lt;/li&gt; &lt;li&gt;            Array.map2 (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; value label &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; (value &amp;gt; 0.0) &amp;amp;&amp;amp; (label = 1) || (value &amp;lt; 0.0) &amp;amp;&amp;amp; (label = -1) &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt; 1 &lt;span style="color: rgb(0, 0, 255);"&gt;else&lt;/span&gt; 0) vals ds.labels&lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;            |&amp;gt; Array.sum&lt;/li&gt; &lt;li&gt;    &lt;/li&gt; &lt;li style="background: none repeat scroll 0% 0% rgb(243, 243, 243);"&gt;        (float nCorrect) / (float ds.NumSamples), vals&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;Try on the iris data set we used in the Logistic Regression post:&lt;/p&gt;  &lt;p&gt;let svm = buildSVM iris 10.0 Kernel.linear   &lt;br /&gt;let classifyResult = svmClassify svm iris    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;val svm : svm =   &lt;br /&gt;  {SVs =    &lt;br /&gt;    {features =    &lt;br /&gt;      [|[|1.0; 6.2; 2.2; 4.5; 1.5|]; [|1.0; 5.9; 3.2; 4.8; 1.8|];    &lt;br /&gt;        [|1.0; 6.3; 2.5; 4.9; 1.5|]; [|1.0; 6.7; 3.0; 5.0; 1.7|];    &lt;br /&gt;        [|1.0; 6.0; 2.7; 5.1; 1.6|]; [|1.0; 5.4; 3.0; 4.5; 1.5|];    &lt;br /&gt;        [|1.0; 4.9; 2.5; 4.5; 1.7|]; [|1.0; 6.0; 2.2; 5.0; 1.5|];    &lt;br /&gt;        [|1.0; 6.3; 2.7; 4.9; 1.8|]; [|1.0; 6.2; 2.8; 4.8; 1.8|];    &lt;br /&gt;        [|1.0; 6.1; 3.0; 4.9; 1.8|]; [|1.0; 6.3; 2.8; 5.1; 1.5|];    &lt;br /&gt;        [|1.0; 6.0; 3.0; 4.8; 1.8|]|];    &lt;br /&gt;     labels = [|-1; -1; -1; -1; -1; -1; 1; 1; 1; 1; 1; 1; 1|];};    &lt;br /&gt;   alpha =    &lt;br /&gt;    [|6.72796421; 10.0; 10.0; 10.0; 10.0; 6.475497298; 1.490719712;    &lt;br /&gt;      8.547262902; 3.165478894; 10.0; 10.0; 10.0; 10.0|];    &lt;br /&gt;   kernel = &amp;lt;fun:svm@226-32&amp;gt;;    &lt;br /&gt;   w0 = -13.63716815;}&lt;/p&gt;  &lt;p&gt;&amp;gt;   &lt;br /&gt;Real: 00:00:00.002, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0&lt;/p&gt;  &lt;p&gt;val classifyResult : float * float [] =   &lt;br /&gt;  (0.97,    &lt;br /&gt;   [|-2.787610618; -2.380530973; -1.42477876; -2.929203541; -1.681415929;    &lt;br /&gt;     -1.96460177; -1.24778761; -6.106194692; -2.761061946; -2.973451328;    &lt;br /&gt;….     4.203539824; 1.82300885|])&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;Notes and references&lt;/h2&gt;  &lt;p&gt;The optimization in SVMs could be treated as a general QP problem as shown in our implementation. However, when the number of the data points is big, the QP grows too big to handle by the QP solver. &lt;/p&gt;  &lt;p&gt;As a special QP problem, SVM has a lot of novel solutions proposed in the machine learning research area, e.g. the SMO approach [Platt] in LibSVM implementation and the Ball Vector Machine approach [Tsang] transforming the special QP into a (discrete) computational geometry problem, minimum enclosing ball. For practical usage of SVM, one usually uses these dedicated approaches. &lt;/p&gt;  &lt;p&gt;Besides the maximum margin interpretation, SVMs also have theory roots in functional regularization theory, in which the optimization &lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TWYLXeybLQI/AAAAAAAABHM/rNwPTc2OuNc/s1600-h/clip_image002%5B5%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image002[5]" alt="clip_image002[5]" src="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLYO2ZIXI/AAAAAAAABHQ/8Y_8SOkg4xM/clip_image002%5B5%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="169" height="63" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;is viewed as minimizing the error term &lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/TWYLY-_-_hI/AAAAAAAABHU/i2Jj6NI_iLY/s1600-h/clip_image004%5B7%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image004[7]" alt="clip_image004[7]" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLZmDlpGI/AAAAAAAABHY/zzJUCg3sYCk/clip_image004%5B7%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="55" height="25" /&gt;&lt;/a&gt; while controlling the complexity of the function &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TWYLaazWVuI/AAAAAAAABHc/-Y7mzmY1xwg/s1600-h/clip_image006%5B5%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="clip_image006[5]" alt="clip_image006[5]" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TWYLbI3qC3I/AAAAAAAABHg/rjxWIxlFyo8/clip_image006%5B5%5D_thumb%5B1%5D.png?imgmax=800" border="0" width="42" height="25" /&gt;&lt;/a&gt; and &lt;i&gt;C&lt;/i&gt; is a tradeoff parameter between these two. The reader is referred to the teaching notes and papers by Prof. Poggio [Poggio]. &lt;/p&gt;  &lt;p&gt;[Burges] A Tutorial on Support Vector Machines for Pattern Recognition. &lt;a href="http://research.microsoft.com/en-us/um/people/cburges/papers/svmtutorial.pdf"&gt;http://research.microsoft.com/en-us/um/people/cburges/papers/svmtutorial.pdf&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;[Jaakkola] &lt;a href="http://courses.csail.mit.edu/6.867/"&gt;http://courses.csail.mit.edu/6.867/&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;[Platt] Sequential Minimal Optimization: A Fast Algorithm for Training Support Vector Machines. &lt;a href="ftp://ftp.research.microsoft.com/pub/tr/tr-98-14.pdf"&gt;ftp://ftp.research.microsoft.com/pub/tr/tr-98-14.pdf&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;[Poggio] &lt;a href="http://www.mit.edu/%7E9.520/spring10/"&gt;http://www.mit.edu/~9.520/spring10/&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;[Tsang] &lt;a href="http://www.cs.ust.hk/%7Eivor/cvm.html"&gt;http://www.cs.ust.hk/~ivor/cvm.html&lt;/a&gt;. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-7891944344993188449?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/7891944344993188449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/02/support-vector-machines-svms-in-f-using.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7891944344993188449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7891944344993188449'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/02/support-vector-machines-svms-in-f-using.html' title='Support vector machines (SVMs) in F# using Microsoft Solver Foundation'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_4bFBK2BKrQI/TWYKlU9twnI/AAAAAAAABDU/7rXs-KX37hY/s72-c/clip_image002_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-5367529294709271362</id><published>2011-02-11T16:32:00.001+08:00</published><updated>2011-02-11T16:32:37.230+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sho'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='data mining'/><category scheme='http://www.blogger.com/atom/ns#' term='logistric regression'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='Microsoft Solver Foundation'/><title type='text'>Logistic Regression in F# using Microsoft Solver Foundation</title><content type='html'>&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Logistic_regression"&gt;Logistic regression&lt;/a&gt; is a workhorse in data mining. Like decision trees and SVMs, it is a very standard classifier. If you have a labeled data, logistic regression definitely is one of the classifiers that should tried. &lt;/p&gt;  &lt;p&gt;In this post, I’d like to show how to implement a logistic regression using Microsoft Solver Foundation in F#. Because we use the optimization in Microsoft Solver Foundation, the core part of a logistic regression contains only 20 – 30 lines of code. &lt;/p&gt;  &lt;p&gt;If you are only interested in F# and L-BFGS, but not logistic regression, please only read the second section: &lt;em&gt;L-BFGS solver in Microsoft Solver Foundation&lt;/em&gt;.&lt;/p&gt;  &lt;h2&gt;Background for logistic regression&lt;/h2&gt;  &lt;p&gt;Logistic regression model predicts the probability that a data instance &lt;b&gt;&lt;i&gt;x&lt;/i&gt;&lt;/b&gt; being labeled as &lt;i&gt;y&lt;/i&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TVTz8EVZHyI/AAAAAAAABCQ/neuYkmfu1W4/s1600-h/clip_image002%5B5%5D%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002[5]" border="0" alt="clip_image002[5]" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TVTz9aiURkI/AAAAAAAABCU/g877rwP9-nc/clip_image002%5B5%5D_thumb%5B2%5D.png?imgmax=800" width="349" height="32" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;A data instance is a vector with each dimension representing a value for some feature. For example, the Iris flower data set we are going to use in this post has 4 features: Sepal Length, Sepal Width, Petal Length and Petal Width. Each instance has a label &lt;i&gt;y&lt;/i&gt; indicating the category. The above model only models binary labels. Logistic regression also has multiple categories version, see [Mitchell]. In this post, we focus on the binary version; however multiple versions should be straightforward once we know how to implement the binary one. &lt;/p&gt;  &lt;p&gt;The model contains two parameters weight vector &lt;b&gt;&lt;i&gt;w&lt;/i&gt;&lt;/b&gt; and offset &lt;i&gt;b&lt;/i&gt;, which should be estimated/learned on the training data set&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TVTz-OF2_wI/AAAAAAAABCY/wvWYWAZCBxI/s1600-h/clip_image004%5B3%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004[3]" border="0" alt="clip_image004[3]" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TVTz_a_toCI/AAAAAAAABCc/Igh_7TvMS9A/clip_image004%5B3%5D_thumb%5B1%5D.png?imgmax=800" width="117" height="26" /&gt;&lt;/a&gt;. In our derivations below, we will drop the offset &lt;i&gt;b&lt;/i&gt;, because we can always add one feature (constant 1) to feature vector &lt;b&gt;&lt;i&gt;x’=[x &lt;/i&gt;&lt;/b&gt;&lt;i&gt;1&lt;b&gt;]&lt;/b&gt;&lt;/i&gt; and hide &lt;i&gt;b&lt;/i&gt; into the extended &lt;b&gt;&lt;i&gt;w’=[w &lt;/i&gt;&lt;/b&gt;&lt;i&gt;b&lt;b&gt;]&lt;/b&gt;&lt;/i&gt;. Sometimes, a model without the offset parameter could also perform equally well. Because equation (1) measures the probability, we could solve the model parameters by optimizing the joint probability estimated on dataset &lt;i&gt;X&lt;/i&gt;. &lt;/p&gt;  &lt;p&gt;If we assume that all the instances are independent and identically distributed (i.i.d.). Then the regularized joint probability is &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TVT0AJLy8sI/AAAAAAAABCg/W7zy2lIew6o/s1600-h/clip_image006%5B3%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006[3]" border="0" alt="clip_image006[3]" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TVT0Axhqs_I/AAAAAAAABCk/n_L8YlHKOYw/clip_image006%5B3%5D_thumb%5B1%5D.png?imgmax=800" width="240" height="61" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;where &lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TVT0BvQMqKI/AAAAAAAABCo/7-15_h9j-Ms/s1600-h/clip_image008%5B1%5D%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008[1]" border="0" alt="clip_image008[1]" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TVT0CSN5cJI/AAAAAAAABCs/EbbViS-ylCo/clip_image008%5B1%5D_thumb%5B1%5D.png?imgmax=800" width="130" height="40" /&gt;&lt;/a&gt;&lt;b&gt; &lt;/b&gt;is the regularization term for avoiding over-fitting on the training data. &lt;/p&gt;  &lt;p&gt;The log version of this likelihood is&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TVT0DFQcfkI/AAAAAAAABCw/lfjXzAo5-5A/s1600-h/clip_image010%5B1%5D%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image010[1]" border="0" alt="clip_image010[1]" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TVT0EQbt5II/AAAAAAAABC0/xCAD-9L6xwk/clip_image010%5B1%5D_thumb%5B2%5D.png?imgmax=800" width="345" height="56" /&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;We want to find the model parameter &lt;b&gt;&lt;i&gt;w&lt;/i&gt;&lt;/b&gt; that maximizes this likelihood. P(X) is a concave function with respect to w, so it has a global maximal. &lt;/p&gt;  &lt;p&gt;What left is how to optimize this function. BFGS is a Quasi-Newton method that only needs first order gradient while a Newton family method needs second order explicitly. Microsoft Solver Foundation has a limited memory version of BFGS (L-BFGS). (L-BFGS is more popular than BFGS, sometimes people mention BFGS to mean L-BFGS.) There is a memory parameter &lt;i&gt;m&lt;/i&gt; in L-BFGS, the space complexity of this algorithm is &lt;i&gt;O(md),&lt;/i&gt; where &lt;i&gt;d&lt;/i&gt; is the dimension of the variable space. Say, if you want to optimize a function that has 1,000,000 free variables, and set &lt;i&gt;m&lt;/i&gt; to 10, then you need only a little more than 1M*10*8/1024^2=76M bytes. &lt;/p&gt;  &lt;p&gt;The input for an L-BFGS optimizer is: 1) the function evaluator &lt;i&gt;L(&lt;b&gt;w&lt;/b&gt;)&lt;/i&gt; and 2) the gradient evaluator &lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/TVT0E9Hm2WI/AAAAAAAABC4/UcbG7MPwbjM/s1600-h/clip_image012%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TVT0Fve6BtI/AAAAAAAABC8/xxOW48DiyXc/clip_image012_thumb%5B1%5D.png?imgmax=800" width="37" height="36" /&gt;&lt;/a&gt;:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TVT0GRjd_uI/AAAAAAAABDA/GgAJwc6tq30/s1600-h/clip_image014%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image014" border="0" alt="clip_image014" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TVT0G1vO_jI/AAAAAAAABDE/ahcJJSNabDA/clip_image014_thumb%5B1%5D.png?imgmax=800" width="240" height="44" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The output is the best &lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TVT0HpmQ61I/AAAAAAAABDI/-4kv7pE8lDs/s1600-h/clip_image016%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image016" border="0" alt="clip_image016" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TVT0IAGRnDI/AAAAAAAABDM/c1VTQqII9cI/clip_image016_thumb%5B1%5D.png?imgmax=800" width="18" height="25" /&gt;&lt;/a&gt;that gives the maximum function value&lt;b&gt;. &lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;  &lt;h2&gt;L-BFGS solver in Microsoft Solver Foundation&amp;#160; &lt;/h2&gt;  &lt;p&gt;Microsoft Solver Foundation already implements an L-BFGS solver. The reader is suggested to read pages 24-27 &amp;amp; 72-75 of &lt;b&gt;MSF-SolverProgrammingPrimer.pdf (&lt;/b&gt;on my machine: C:\Program Files\Microsoft Solver Foundation\3.0.1.10599\Documents&lt;b&gt;) &lt;/b&gt;for detailed reference. Because the code for optimizing Rosenbrock function is in C#, here I translated it to F#. Note how I translate between delegate type &lt;b&gt;System.Func&amp;lt;&amp;gt;&lt;/b&gt; and F# functions. &lt;/p&gt;  &lt;p&gt;Btw, I find that using the Microsoft.Solver.Foundatoin.dll in Sho would be more convenient as it packages everything into one DLL. &lt;/p&gt;  &lt;p&gt;&lt;span style="color: blue"&gt;#r &lt;/span&gt;&lt;span style="color: maroon"&gt;@&amp;quot;C:\Program Files (x86)\Sho 2.0 for .NET 4\packages\Optimizer\Microsoft.Solver.Foundation.dll&amp;quot;     &lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;open&lt;/span&gt;Microsoft.SolverFoundation.Common    &lt;br /&gt;&lt;span style="color: blue"&gt;open&lt;/span&gt;Microsoft.SolverFoundation.Solvers    &lt;br /&gt;&lt;span style="color: blue"&gt;open&lt;/span&gt;Microsoft.SolverFoundation.Services    &lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;&lt;span style="color: blue"&gt;let&lt;/span&gt;solverParams = &lt;span style="color: blue"&gt;new&lt;/span&gt;CompactQuasiNewtonSolverParams()    &lt;br /&gt;&lt;span style="color: blue"&gt;let&lt;/span&gt;solver = &lt;span style="color: blue"&gt;new&lt;/span&gt;CompactQuasiNewtonSolver()    &lt;br /&gt;&lt;span style="color: green"&gt;//add variables     &lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let&lt;/span&gt;_, vidVaribaleX = solver.AddVariable(&lt;span style="color: blue"&gt;null&lt;/span&gt;)    &lt;br /&gt;&lt;span style="color: blue"&gt;let&lt;/span&gt;_, vidVaribaleY = solver.AddVariable(&lt;span style="color: blue"&gt;null&lt;/span&gt;)    &lt;br /&gt;    &lt;br /&gt;&lt;span style="color: green"&gt;//add a row and set it as the goal     &lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let&lt;/span&gt;_, vidRow = solver.AddRow(&lt;span style="color: blue"&gt;null&lt;/span&gt;)    &lt;br /&gt;solver.AddGoal(vidRow, 0, &lt;span style="color: blue"&gt;true&lt;/span&gt;)    &lt;br /&gt;    &lt;br /&gt;&lt;span style="color: blue"&gt;let&lt;/span&gt;OriginalRosenbrockFunction =     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;let&lt;/span&gt;f (model:INonlinearModel)&amp;#160; (rowVid:int)&amp;#160; (values:ValuesByIndex)&amp;#160; (newValues:bool) =     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Math.Pow(1. - values.[1], 2.) + 100. * (Math.Pow(values.[2] - (values.[1] * values.[1]), 2.))&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;new&lt;/span&gt;System.Func&amp;lt;INonlinearModel, int, ValuesByIndex, bool, float&amp;gt; (f)    &lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;&lt;span style="color: blue"&gt;let&lt;/span&gt;OriginalRosenbrockGradient =     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;let&lt;/span&gt;f (model:INonlinearModel) (rowVid:int) (values:ValuesByIndex) (newValues:bool) (gradient:ValuesByIndex) =     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; gradient.[1] &amp;lt;- -2. * (1. - values.[1]) - 400. * values.[1] * (values.[2] - (values.[1] * values.[1]))    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; gradient.[2] &amp;lt;- 200. * (values.[2] - (values.[1] * values.[1]))    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;new&lt;/span&gt;System.Action&amp;lt;INonlinearModel, int, ValuesByIndex, bool, ValuesByIndex&amp;gt; (f)    &lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;solver.FunctionEvaluator &amp;lt;- OriginalRosenbrockFunction    &lt;br /&gt;solver.GradientEvaluator &amp;lt;- OriginalRosenbrockGradient    &lt;br /&gt;solver.Solve(solverParams);    &lt;br /&gt;    &lt;br /&gt;Console.WriteLine(solver.ToString());&lt;/p&gt;  &lt;p&gt;and the output:&lt;/p&gt;  &lt;pre class="code"&gt;&amp;gt; &lt;br /&gt;Minimize problem&lt;br /&gt;Dimensions = 2&lt;br /&gt;Variable indexes: &lt;br /&gt;1, 2&lt;br /&gt;Starting point = 0, 0&lt;br /&gt;Solution quality is: LocalOptima&lt;br /&gt;Number of iterations performed: 21&lt;br /&gt;Number of evaluation calls: 27&lt;br /&gt;Finishing point =0.999999997671584, 0.999999995285991&lt;br /&gt;Finishing value =5.74844710320033E-18&lt;br /&gt;&lt;br /&gt;val it : unit = ()&lt;br /&gt;&amp;gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;&amp;#160;&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Implementation for logistic regression&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We first get a sample data set online:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;module &lt;/span&gt;Net = &lt;br /&gt;    &lt;span style="color: blue"&gt;open &lt;/span&gt;System.Net&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;fetchUrlSimple (url:string) = &lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;req = WebRequest.Create(url) &lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;response = req.GetResponse()&lt;br /&gt;        &lt;span style="color: blue"&gt;use &lt;/span&gt;stream = response.GetResponseStream()&lt;br /&gt;        &lt;span style="color: blue"&gt;use &lt;/span&gt;streamreader = &lt;span style="color: blue"&gt;new &lt;/span&gt;System.IO.StreamReader(stream)&lt;br /&gt;        streamreader.ReadToEnd()&lt;br /&gt;     &lt;br /&gt;&lt;span style="color: blue"&gt;type &lt;/span&gt;dataset = &lt;br /&gt;    { features: float array array; &lt;span style="color: green"&gt;// (instance = float array) array&lt;br /&gt;      &lt;/span&gt;&lt;span style="color: blue"&gt;mutable &lt;/span&gt;labels: int array; &lt;span style="color: green"&gt;// &lt;br /&gt;    &lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;iris = &lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;page = Net.fetchUrlSimple &lt;span style="color: maroon"&gt;@&amp;quot;http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data&amp;quot;&lt;br /&gt;    &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;lines = page.Split([|&lt;span style="color: maroon"&gt;'\n'&lt;/span&gt;|], StringSplitOptions.RemoveEmptyEntries)&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;getIrisId (s:string) =     &lt;br /&gt;        &lt;span style="color: blue"&gt;if &lt;/span&gt;s = &lt;span style="color: maroon"&gt;&amp;quot;Iris-setosa&amp;quot; &lt;/span&gt;&lt;span style="color: blue"&gt;then&lt;br /&gt;            &lt;/span&gt;2&lt;br /&gt;        &lt;span style="color: blue"&gt;elif &lt;/span&gt;s = &lt;span style="color: maroon"&gt;&amp;quot;Iris-versicolor&amp;quot; &lt;/span&gt;&lt;span style="color: blue"&gt;then&lt;br /&gt;            &lt;/span&gt;-1&lt;br /&gt;        &lt;span style="color: blue"&gt;else&lt;br /&gt;            &lt;/span&gt;1&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;instances = &lt;br /&gt;        lines&lt;br /&gt;        |&amp;gt; Array.map (&lt;span style="color: blue"&gt;fun &lt;/span&gt;line &lt;span style="color: blue"&gt;-&amp;gt; &lt;br /&gt;            let &lt;/span&gt;s = line.Split &lt;span style="color: maroon"&gt;','&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;features = [| &lt;span style="color: maroon"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;; s.[0]; s.[1]; s.[2]; s.[3] |] |&amp;gt; Array.map float &lt;span style="color: green"&gt;// add a dumy 1.0 into the fatures &lt;br /&gt;            &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;label = getIrisId s.[4]&lt;br /&gt;            features, label&lt;br /&gt;            )&lt;br /&gt;        |&amp;gt; Array.filter (&lt;span style="color: blue"&gt;fun &lt;/span&gt;(_, label) &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;label &amp;lt;&amp;gt; 2)&lt;br /&gt;        &lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;F, L = Array.unzip instances&lt;br /&gt;    {&lt;br /&gt;        features = F;&lt;br /&gt;        labels = L;&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The logistic regression implementation:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;module &lt;/span&gt;LogReg = &lt;br /&gt;    &lt;span style="color: blue"&gt;let mutable &lt;/span&gt;lambda = 0.1&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;dotProduct (x:float array) (g:ValuesByIndex) =&lt;br /&gt;        &lt;span style="color: blue"&gt;let mutable &lt;/span&gt;dot = 0.0&lt;br /&gt;        &lt;span style="color: blue"&gt;for &lt;/span&gt;i=0 &lt;span style="color: blue"&gt;to &lt;/span&gt;x.Length-1 &lt;span style="color: blue"&gt;do&lt;br /&gt;            &lt;/span&gt;dot &amp;lt;- dot + x.[i] * g.[i+1]&lt;br /&gt;        dot&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;sigmoid (x:float array) (y:int) (g:ValuesByIndex) = &lt;br /&gt;        &lt;span style="color: blue"&gt;let mutable &lt;/span&gt;dot = 0.0&lt;br /&gt;        &lt;span style="color: blue"&gt;for &lt;/span&gt;i=0 &lt;span style="color: blue"&gt;to &lt;/span&gt;x.Length-1 &lt;span style="color: blue"&gt;do&lt;br /&gt;            &lt;/span&gt;dot &amp;lt;- dot + x.[i] * g.[i+1]&lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;z = (float y) * dot&lt;br /&gt;        &lt;span style="color: green"&gt;(* code to deal with extream numbers, but with some issues with l(w) calculation&lt;br /&gt;        if z &amp;gt; 30.0 then&lt;br /&gt;            1.0&lt;br /&gt;        elif z &amp;lt; -30.0 then&lt;br /&gt;            0.0&lt;br /&gt;        else&lt;br /&gt;        *)&lt;br /&gt;        &lt;/span&gt;1.0 / (1.0 + exp (- z))&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;sigmoid2 (x:float array) (y:int) (g:float array) = &lt;br /&gt;        &lt;span style="color: blue"&gt;let mutable &lt;/span&gt;dot = 0.0&lt;br /&gt;        &lt;span style="color: blue"&gt;for &lt;/span&gt;i=0 &lt;span style="color: blue"&gt;to &lt;/span&gt;x.Length-1 &lt;span style="color: blue"&gt;do&lt;br /&gt;            &lt;/span&gt;dot &amp;lt;- dot + x.[i] * g.[i]&lt;br /&gt;        1.0 / (1.0 + exp (- (float y) * dot))&lt;br /&gt;&lt;br /&gt;        &lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;logregValue(ds:dataset) = &lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;dim = ds.features.[0].Length&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;f (model:INonlinearModel)  (rowVid:int)  (values:ValuesByIndex)  (newValues:bool) = &lt;br /&gt;            &lt;span style="color: blue"&gt;let mutable &lt;/span&gt;L = 0.0&lt;br /&gt;            &lt;br /&gt;            &lt;span style="color: blue"&gt;for &lt;/span&gt;i=1 &lt;span style="color: blue"&gt;to &lt;/span&gt;dim &lt;span style="color: blue"&gt;do&lt;br /&gt;                &lt;/span&gt;L &amp;lt;- L + values.[i]*values.[i]&lt;br /&gt;            L &amp;lt;- - (L * lambda / 2.0)&lt;br /&gt;            &lt;br /&gt;            &lt;span style="color: blue"&gt;for &lt;/span&gt;i=0 &lt;span style="color: blue"&gt;to &lt;/span&gt;ds.features.Length-1 &lt;span style="color: blue"&gt;do&lt;br /&gt;                &lt;/span&gt;&lt;span style="color: green"&gt;//L &amp;lt;- L - log (1.0 + exp (- (float ds.labels.[i]) * dotProduct ds.features.[i] values ))&lt;br /&gt;                &lt;/span&gt;L &amp;lt;- L + log (sigmoid ds.features.[i] ds.labels.[i] values)&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: green"&gt;// printfn &amp;quot;L = %.10f&amp;quot; L&lt;br /&gt;            &lt;/span&gt;L&lt;br /&gt;            &lt;br /&gt;        &lt;span style="color: blue"&gt;new &lt;/span&gt;System.Func&amp;lt;INonlinearModel, int, ValuesByIndex, bool, float&amp;gt; (f)&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;logregGradient(ds:dataset) = &lt;br /&gt;        &lt;span style="color: green"&gt;// printfn &amp;quot;gradient calculated&amp;quot;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;dim = ds.features.[0].Length&lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;f (model:INonlinearModel) (rowVid:int) (values:ValuesByIndex) (newValues:bool) (gradient:ValuesByIndex) = &lt;br /&gt;            &lt;span style="color: blue"&gt;for &lt;/span&gt;j=1 &lt;span style="color: blue"&gt;to &lt;/span&gt;dim &lt;span style="color: blue"&gt;do &lt;br /&gt;                &lt;/span&gt;gradient.[j] &amp;lt;- -lambda * values.[j]&lt;br /&gt;            &lt;br /&gt;            &lt;span style="color: blue"&gt;for &lt;/span&gt;i=0 &lt;span style="color: blue"&gt;to &lt;/span&gt;ds.features.Length-1 &lt;span style="color: blue"&gt;do&lt;br /&gt;                let &lt;/span&gt;coef = (1.0 - sigmoid ds.features.[i] ds.labels.[i] values) * (float ds.labels.[i])&lt;br /&gt;                &lt;span style="color: blue"&gt;for &lt;/span&gt;j=1 &lt;span style="color: blue"&gt;to &lt;/span&gt;dim &lt;span style="color: blue"&gt;do &lt;br /&gt;                    &lt;/span&gt;gradient.[j] &amp;lt;- gradient.[j] + coef * ds.features.[i].[j-1]&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;new &lt;/span&gt;System.Action&amp;lt;INonlinearModel, int, ValuesByIndex, bool, ValuesByIndex&amp;gt; (f)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;let &lt;/span&gt;makeSolver(ds:dataset) = &lt;br /&gt;&lt;br /&gt;        &lt;span style="color: green"&gt;// set the solver parameters &lt;br /&gt;        &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;solverParams = &lt;span style="color: blue"&gt;new &lt;/span&gt;CompactQuasiNewtonSolverParams()&lt;br /&gt;        &lt;span style="color: green"&gt;//solverParams.IterationLimit &amp;lt;- 11&lt;br /&gt;        //solverParams.Tolerance &amp;lt;- 1e-4&lt;br /&gt;        // the memory parameter m, default 17&lt;br /&gt;        &lt;/span&gt;solverParams.IterationsToRemember &amp;lt;- 10&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: green"&gt;// solver&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;solver = &lt;span style="color: blue"&gt;new &lt;/span&gt;CompactQuasiNewtonSolver()&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;dim = ds.features.[0].Length&lt;br /&gt;        &lt;span style="color: green"&gt;// add variables&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: blue"&gt;for &lt;/span&gt;i=1 &lt;span style="color: blue"&gt;to &lt;/span&gt;dim &lt;span style="color: blue"&gt;do &lt;br /&gt;            &lt;/span&gt;solver.AddVariable(&lt;span style="color: blue"&gt;null&lt;/span&gt;) |&amp;gt; ignore&lt;br /&gt;        &lt;br /&gt;        &lt;span style="color: green"&gt;// add a row and set it as the goal&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;_, vidRow = solver.AddRow(&lt;span style="color: blue"&gt;null&lt;/span&gt;)&lt;br /&gt;        solver.AddGoal(vidRow, 0, &lt;span style="color: blue"&gt;false&lt;/span&gt;) |&amp;gt; ignore&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: green"&gt;// set funcation evaluator and gradient evaluator&lt;br /&gt;        &lt;/span&gt;solver.FunctionEvaluator &amp;lt;- logregValue(ds)&lt;br /&gt;        solver.GradientEvaluator &amp;lt;- logregGradient(ds)&lt;br /&gt;        &lt;br /&gt;        &lt;span style="color: green"&gt;// solve!&lt;br /&gt;        &lt;/span&gt;solver.Solve(solverParams) |&amp;gt; ignore&lt;br /&gt;        &lt;br /&gt;        &lt;span style="color: green"&gt;// get the optimized point (w) in the solver&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;w = Array.create dim 0.0&lt;br /&gt;        &lt;span style="color: blue"&gt;for &lt;/span&gt;i=1 &lt;span style="color: blue"&gt;to &lt;/span&gt;dim &lt;span style="color: blue"&gt;do&lt;br /&gt;            &lt;/span&gt;w.[i-1] &amp;lt;- solver.GetValue(i).ToDouble()&lt;br /&gt;        w&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Try our logistic regression model on the Iris dataset:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;sol = LogReg.makeSolver(iris)&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;nWrongs = ref 0&lt;br /&gt;&lt;span style="color: blue"&gt;for &lt;/span&gt;i=0 &lt;span style="color: blue"&gt;to &lt;/span&gt;iris.features.Length-1 &lt;span style="color: blue"&gt;do&lt;br /&gt;    let &lt;/span&gt;prob = LogReg.sigmoid2 iris.features.[i] iris.labels.[i] sol&lt;br /&gt;    printfn &lt;span style="color: maroon"&gt;&amp;quot;L = %A sig value = %A label = %A&amp;quot; &lt;/span&gt;iris.labels.[i] prob (&lt;span style="color: blue"&gt;if &lt;/span&gt;prob &amp;gt; 0.5 &lt;span style="color: blue"&gt;then &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;correct&amp;quot; &lt;/span&gt;&lt;span style="color: blue"&gt;else &lt;/span&gt;incr nWrongs; &lt;span style="color: maroon"&gt;&amp;quot;wrong&amp;quot;&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Reference&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Tom Mitchell, &lt;a href="http://www.cs.cmu.edu/~tom/mlbook/NBayesLogReg.pdf"&gt;Generative and Discriminative Classifiers: Naive Bayes and Logistic Regression&lt;/a&gt;, new book chapter. This chapter gives a good introduction to logistic regression. For a more theoretic comparison, see: Ng &amp;amp; Jordan: on discriminative vs generative classifiers a comparison of logistic regression and naïve bayes.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;cite&gt;&lt;/cite&gt;Thomas P. Minka, &lt;a href="http://research.microsoft.com/en-us/um/people/minka/papers/logreg/"&gt;A comparison of numerical optimizers for logistic regression&lt;/a&gt;. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160; It is well written, the first page ready gives a concise introduction to logistic regression. The rest of the paper has a lot of discussion on optimization methods. Tom also has a Matlab package. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Galen Andrew and Jianfeng Gao. Scalable training of L1-regularized log-linear models. In &lt;em&gt;Proceedings of the 24th International Conference on Machine Learning (ICML 2007)&lt;/em&gt;, pp. 33-40, 2007.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;L1-regularized logistic regression with an implementation in C++. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;libLBFGS(LBFGS in C): &lt;a title="http://www.chokkan.org/software/liblbfgs/" href="http://www.chokkan.org/software/liblbfgs/"&gt;http://www.chokkan.org/software/liblbfgs/&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Note&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I quite like this post because it combines so many good things together: F#, logistic regression, optimization and playing with sample datasets. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-5367529294709271362?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/5367529294709271362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/02/logistic-regression-in-f-using.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5367529294709271362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5367529294709271362'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/02/logistic-regression-in-f-using.html' title='Logistic Regression in F# using Microsoft Solver Foundation'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_4bFBK2BKrQI/TVTz9aiURkI/AAAAAAAABCU/g877rwP9-nc/s72-c/clip_image002%5B5%5D_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-7800545849608315577</id><published>2011-02-10T15:38:00.001+08:00</published><updated>2011-02-10T16:47:52.407+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sho'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Introducing Sho and using its libraries from F#</title><content type='html'>&lt;p&gt;My friend passed me the link for &lt;a href="http://research.microsoft.com/en-us/projects/sho/default.aspx"&gt;Sho&lt;/a&gt;, a data processing and number crunching tool on .Net platform. It is developed in Microsoft Research. I am quite amazed by this tool and like to share a post here. &lt;/p&gt;  &lt;p&gt;The frontend of Sho is IronPython. Besides all the standard Python core language and its libraries, Sho also integrates with part of Intel MKL library, which supports linear algebra operations. &lt;/p&gt;  &lt;p&gt;There are rumors that there will be a &lt;strong&gt;machine learning/data mining&lt;/strong&gt; library based on Sho in the future. &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h2&gt;Key features of Sho&lt;/h2&gt;  &lt;p&gt;1) IronPython, and its console has some IntelliSense &lt;/p&gt;  &lt;p&gt;2) libraries:&lt;/p&gt;  &lt;p&gt;&amp;#160; * Matrix and linear algebra (Intel MKL©)&lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font style="background-color: #ffff00"&gt;both 32bit &amp;amp; 64bit support + multi-threaded &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160; * Statistics (Sho Library)&lt;/p&gt;  &lt;p&gt;&amp;#160; * Signal processing (Sho Library)&lt;/p&gt;  &lt;p&gt;&amp;#160; * Optimization (Microsoft Solver Foundation) &lt;/p&gt;  &lt;p&gt;3) Interactive visualization &lt;/p&gt;  &lt;p&gt;The official site of Sho has some &lt;a href="http://research.microsoft.com/en-us/projects/sho/screenshots.aspx"&gt;examples&lt;/a&gt; and good &lt;a href="http://research.microsoft.com/en-us/projects/sho/documentation.aspx"&gt;documents&lt;/a&gt; including a &lt;a href="http://research.microsoft.com/en-us/um/redmond/projects/sho/documentation/sho2.0/TheBookofSho.html"&gt;reference book&lt;/a&gt;. &lt;/p&gt;  &lt;h2&gt;IronPython?&lt;/h2&gt;  &lt;p&gt;The language and the open nature make Python a very suitable language for scientific computing. I know that a lot of researchers in data mining area use or occasionally use Python for their research. Three friends of mine are using Python for their research. They are also very good at Matlab. One said to me that he writes his development speed of Python(Scipy/Numpy) and Matlab are about the same. For the running speed, Scipy uses ATLAS + Lapcak, which is about the same as Matlab. &lt;/p&gt;  &lt;p&gt;But I think the real power of Python is that your prototype is your deployment. If you ecosystem uses .Net, your number crunching module in IronPython could be ready be used in the system. While for Matlab, deployment is not that easy. &lt;/p&gt;  &lt;p&gt;I think IronPython + Sho would be as good as Python+SciPy/Numpy. &lt;/p&gt;  &lt;p&gt;I personally don’t quite favor dynamic languages. It is the libraries Sho uses make Sho excellent, F# + Sho libraries rocks too. &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h2&gt;Using the Sho API in F#&lt;/h2&gt;  &lt;p&gt;The following is a sample F# script showing how to use Sho library in F#. &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: green"&gt;// set the env variable SHODIR to the root of Sho's installation folder&lt;br /&gt;&lt;/span&gt;System.Environment.SetEnvironmentVariable(&lt;span style="color: maroon"&gt;&amp;quot;SHODIR&amp;quot;&lt;/span&gt;, &lt;span style="color: maroon"&gt;@&amp;quot;C:\Program Files (x86)\Sho 2.0 for .NET 4&amp;quot;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;#r &lt;/span&gt;&lt;span style="color: maroon"&gt;@&amp;quot;C:\Program Files (x86)\Sho 2.0 for .NET 4\bin\ShoArray.dll&amp;quot;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;#r &lt;/span&gt;&lt;span style="color: maroon"&gt;@&amp;quot;C:\Program Files (x86)\Sho 2.0 for .NET 4\bin\MatrixInterf.dll&amp;quot;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;#time&lt;br /&gt;&lt;br /&gt;open &lt;/span&gt;ShoNS.Array&lt;br /&gt;&lt;span style="color: blue"&gt;open &lt;/span&gt;System&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;rnd = &lt;span style="color: blue"&gt;new &lt;/span&gt;Random(1)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;vec = &lt;span style="color: blue"&gt;new &lt;/span&gt;DoubleArray(3)&lt;br /&gt;vec.FillRandom(rnd)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;arr2 = &lt;span style="color: blue"&gt;new &lt;/span&gt;DoubleArray(3,3)&lt;br /&gt;arr2.FillRandom(rnd)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: green"&gt;// big matrix multiplication&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;bigArr = &lt;span style="color: blue"&gt;new &lt;/span&gt;DoubleArray(1200,20000)&lt;br /&gt;bigArr.FillRandom(rnd)&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;bigArr2 = bigArr.Transpose()&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;bigArrMulti = bigArr * bigArr2&lt;br /&gt;&lt;span style="color: green"&gt;// multi-threaded:&lt;br /&gt;// Real: 00:00:01.745, CPU: 00:00:10.389, GC gen0: 0, gen1: 0, gen2: 0&lt;br /&gt;&lt;br /&gt;// svd&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;svd = &lt;span style="color: blue"&gt;new &lt;/span&gt;SVD(arr2)&lt;br /&gt;printfn &lt;span style="color: maroon"&gt;&amp;quot;U = %A\nD = %A\nV = %A&amp;quot; &lt;/span&gt;svd.D svd.U svd.V&lt;br /&gt;&lt;br /&gt;&lt;span style="color: green"&gt;// lu&lt;br /&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;lu = &lt;span style="color: blue"&gt;new &lt;/span&gt;LU(arr2)&lt;br /&gt;printfn &lt;span style="color: maroon"&gt;&amp;quot;L = %A\nU = %A&amp;quot; &lt;/span&gt;lu.L lu.U&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Notice that Sho linear algebra library is actually licensed from Intel MKL, which is multi-threaded! (Matlab also uses Intel MKL as its basic linear algebra library.)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Basic operations like matrix multiplication is very fast if it uses more cores, as illustrated above. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Note&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Fsi.exe runs in 32bit mode. Thus your cannot create large matrices and other objects in the F# interactive. There are workarounds &lt;a href="http://ig2600.blogspot.com/2010/05/making-fsharp-interpreter-fsi-run-in.html"&gt;here&lt;/a&gt;. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-7800545849608315577?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/7800545849608315577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/02/introducing-sho-and-using-its-libraries.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7800545849608315577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7800545849608315577'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/02/introducing-sho-and-using-its-libraries.html' title='Introducing Sho and using its libraries from F#'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-7564391852672512779</id><published>2011-02-01T11:35:00.001+08:00</published><updated>2011-02-01T12:02:19.801+08:00</updated><title type='text'>How to quickly learn F#</title><content type='html'>&lt;p&gt;I haven’t blogged about F# for sometime now because I am busy with my research work recently. But I nearly use F# everyday to conduct experiments. Also I attracted several of my teammates to get interested in F#, so I write the following guide for my teammates to quickly learn F#. &lt;/p&gt;  &lt;p&gt;There are other “learning outline” pages such as the &lt;a href="http://stackoverflow.com/questions/734525/getting-started-with-f"&gt;wiki page on Stackoverflow&lt;/a&gt;. However the following list provides a path for learning F# and hope to reduce the learning time to be as short as possible. &lt;/p&gt;  &lt;p&gt;BTW, it is the Chinese New Year now. Wish everyone a warm Spring Festival!&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h4&gt;Step 1&lt;/h4&gt;  &lt;p&gt;Before reading books/articles, watch the &lt;a href="http://fdatamining.blogspot.com/2009/12/f-online-videos.html"&gt;three videos&lt;/a&gt; by Luca Bolognese (former F# PM), Luke Hoban (F# PM) and Don Syme (creator of F#).&lt;/p&gt;  &lt;h4&gt;Step 2&lt;/h4&gt;  &lt;p&gt;Read the book by Chris Smith (former F# team member, now at Google): &lt;i&gt;Programming F#.&lt;/i&gt; After reading the book, quickly scan the following two online books:&lt;/p&gt;  &lt;p&gt;· F# Wikibook: &lt;a href="http://en.wikibooks.org/wiki/Programming:F_Sharp"&gt;http://en.wikibooks.org/wiki/Programming:F_Sharp&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;· The F# Survival Guide: &lt;a href="http://www.ctocorner.com/fsharp/book/default.aspx"&gt;http://www.ctocorner.com/fsharp/book/default.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;They basically cover the same content as &lt;i&gt;Programming F#&lt;/i&gt;; reading them gives you a refresh of basic F#. &lt;/p&gt;  &lt;h4&gt;Step 3&lt;/h4&gt;  &lt;p&gt;Try to solve the first 50 problems at Project Euler: &lt;a href="http://projecteuler.net/"&gt;http://projecteuler.net/&lt;/a&gt; and read the solutions at: &lt;a href="http://fsharp-euler.wikispaces.com/"&gt;http://fsharp-euler.wikispaces.com/&lt;/a&gt; if you have difficulty. You can also search the solutions online; there are many blogs on how to solve Project Euler problems using F#. But before reading the solution, try your best and you can learn F# basics very quickly. &lt;/p&gt;  &lt;h4&gt;Step 3 alternative &lt;/h4&gt;  &lt;p&gt;You can skip step 3 (or part of it) if you have experience in building a compiler or interpreter and you are interested in programming languages. The ProgramAsData folder contains the free online book, homeworks (some F# template code is provided) and well-edited slides in the &lt;a href="http://www.itu.dk/courses/BPRD/E2010/"&gt;&lt;em&gt;Program as Data &lt;/em&gt;course by Prof. Peter Sestoft&lt;/a&gt;. &lt;/p&gt;  &lt;h4&gt;Step 4&lt;/h4&gt;  &lt;p&gt;Read some chapters of &lt;i&gt;Expert F# 2.0&lt;/i&gt; by Don Syme, and use the book as a reference when you program a bigger/real-world project in F#. &lt;/p&gt;  &lt;p&gt;[Optional] Read books like &lt;i&gt;CLR&lt;/i&gt; &lt;em&gt;via C# &lt;/em&gt;to know more about the .Net platform. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-7564391852672512779?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/7564391852672512779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2011/01/how-to-quickly-learn-f.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7564391852672512779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7564391852672512779'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2011/01/how-to-quickly-learn-f.html' title='How to quickly learn F#'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-1871561513237291560</id><published>2010-11-22T21:25:00.001+08:00</published><updated>2010-11-22T21:25:29.798+08:00</updated><title type='text'>F# Math Provider RE-available</title><content type='html'>&lt;p&gt;see:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://mathprovider.codeplex.com/" href="http://mathprovider.codeplex.com/"&gt;http://mathprovider.codeplex.com/&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-1871561513237291560?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/1871561513237291560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/11/f-math-provider-re-available.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/1871561513237291560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/1871561513237291560'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/11/f-math-provider-re-available.html' title='F# Math Provider RE-available'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-3027400874798450627</id><published>2010-09-19T17:03:00.001+08:00</published><updated>2010-09-19T17:03:59.229+08:00</updated><title type='text'>WekaSharp: more features</title><content type='html'>&lt;p&gt;1. &lt;a href="http://fdatamining.blogspot.com/2010/08/wekasharp-f-wrapper-for-weka.html"&gt;An F# wrapper for Weka&lt;/a&gt;. The minimal wrapper in F# for Weka. &lt;/p&gt;  &lt;p&gt;2. More features. (this post) This post will contain improvement over the minimal wrapper, e.g. more Dataset processing function, some plot functionality, etc.&lt;/p&gt;  &lt;p&gt;3. &lt;strong&gt;&lt;a href="http://fdatamining.blogspot.com/2010/09/wekasharp-tutorial-for-running-weka-in.html"&gt;Tutorial/Usage examples&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt;. &lt;/strong&gt;This post is for end users, who might have no interested in reading the implementation details, but rather knowing how to use this wrapper to perform data mining tasks in .Net. &lt;/p&gt;  &lt;p&gt;Here lists a set of features that have been added to WekaSharp. &lt;/p&gt;  &lt;h3&gt;Parameters for data mining algorithms. &lt;/h3&gt;  &lt;p&gt;F# features: &lt;strong&gt;named parameters&lt;/strong&gt; and &lt;strong&gt;pattern matching&lt;/strong&gt;. &lt;/p&gt;  &lt;p&gt;Data mining algorithm usually have some parameters to set before applying to the data. E.g. the K-means clustering needs the user to provide the number of clusters, the Support Vector Machine classification algorithm need the user to provide the kernel type of the SVM to use, etc. &lt;/p&gt;  &lt;p&gt;Also different algorithms have different set of parameters. In Weka, algorithm parameters are encoded as a string (similar to the parameter style of command line tools). However, a first time user would not be able to remember what does “-R 100” mean for a specific algorithm. So we’d like to have a&lt;strong&gt; parameter maker&lt;/strong&gt; for each algorithm. F# fully supports this task with its&lt;strong&gt; named parameters&lt;/strong&gt;. &lt;/p&gt;  &lt;p&gt;Let’s see an example. The following code is for making parameters for Logistic Regression:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;type &lt;/span&gt;LogReg() =&lt;br /&gt;    &lt;span style="color: blue"&gt;static member &lt;/span&gt;DefaultPara = &lt;span style="color: maroon"&gt;&amp;quot;-R 1.0E08 -M -1&amp;quot;&lt;br /&gt;    &lt;/span&gt;&lt;span style="color: blue"&gt;static member &lt;/span&gt;MakePara&lt;strong&gt;(?maxIter, ?ridge)&lt;/strong&gt; = &lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;maxIterStr = &lt;br /&gt;            &lt;span style="color: blue"&gt;let &lt;/span&gt;m = &lt;span style="color: blue"&gt;match &lt;/span&gt;maxIter &lt;span style="color: blue"&gt;with&lt;br /&gt;                    &lt;/span&gt;| Some (v) &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;v&lt;br /&gt;                    | None &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;-1&lt;br /&gt;            &lt;span style="color: maroon"&gt;&amp;quot;-M &amp;quot; &lt;/span&gt;+ m.ToString()&lt;br /&gt;        &lt;span style="color: blue"&gt;let &lt;/span&gt;ridgeStr = &lt;br /&gt;            &lt;span style="color: blue"&gt;let &lt;/span&gt;r = &lt;span style="color: blue"&gt;match &lt;/span&gt;ridge &lt;span style="color: blue"&gt;with&lt;br /&gt;                    &lt;/span&gt;| Some(v) &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;v&lt;br /&gt;                    | None &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;1.0E08&lt;br /&gt;            &lt;span style="color: maroon"&gt;&amp;quot;-R &amp;quot; &lt;/span&gt;+ r.ToString()&lt;br /&gt;        maxIterStr + &lt;span style="color: maroon"&gt;&amp;quot; &amp;quot; &lt;/span&gt;+ ridgeStr&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;and this is an example usage of it:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;let logRegPara = LogReg.MakePara (ridge = 1.0)&lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You can see that besides named parameter functionality, we can also only supply part of the parameters. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Another highlight F# feature in this the Parameter module is &lt;strong&gt;pattern matching&lt;/strong&gt;. Sometimes, the parameters could be quite complicated, e.g. the nearest neighbor search algorithm used in KNN. There are 4 search algorithms, each of them is capable for some of the distance metrics. Discriminate unions and pattern matching make the parameter generating task easy:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color: blue"&gt;type &lt;/span&gt;DistanceFunction = Euclidean | Chebyshev | EditDistance | Manhattan&lt;br /&gt;        &lt;span style="color: blue"&gt;with&lt;br /&gt;        static member &lt;/span&gt;GetString(distanceFunction) = &lt;br /&gt;                &lt;span style="color: blue"&gt;let &lt;/span&gt;d = &lt;span style="color: blue"&gt;match &lt;/span&gt;distanceFunction &lt;span style="color: blue"&gt;with&lt;br /&gt;                        &lt;/span&gt;| Some (v) &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;v&lt;br /&gt;                        | None &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;DistanceFunction.Euclidean&lt;br /&gt;                &lt;span style="color: blue"&gt;match &lt;/span&gt;d &lt;span style="color: blue"&gt;with&lt;br /&gt;                &lt;/span&gt;| DistanceFunction.Euclidean &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.EuclideanDistance -R first-last&amp;quot;&lt;br /&gt;                &lt;/span&gt;| DistanceFunction.Chebyshev &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.ChebyshevDistance -R first-last&amp;quot;&lt;br /&gt;                &lt;/span&gt;| DistanceFunction.EditDistance &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.EditDistance -R first-last&amp;quot;&lt;br /&gt;                &lt;/span&gt;| DistanceFunction.Manhattan &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.ManhattanDistance -R first-last&amp;quot;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;            &lt;span style="color: blue"&gt;let &lt;/span&gt;distanceFunctionStr = DistanceFunction.GetString distanceFunction &lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;let &lt;/span&gt;searchAlgorithmStr = &lt;br /&gt;                &lt;span style="color: blue"&gt;let &lt;/span&gt;s = &lt;span style="color: blue"&gt;match &lt;/span&gt;searchAlgorithm &lt;span style="color: blue"&gt;with&lt;br /&gt;                        &lt;/span&gt;| Some (v) &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;v&lt;br /&gt;                        | None &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;NNSearchAlgorithm.LinearSearch&lt;br /&gt;                &lt;span style="color: blue"&gt;match &lt;/span&gt;s &lt;span style="color: blue"&gt;with&lt;br /&gt;                &lt;/span&gt;| NNSearchAlgorithm.LinearSearch &lt;span style="color: blue"&gt;-&amp;gt; &lt;br /&gt;                    &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.neighboursearch.LinearNNSearch&amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot; -A &amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot; &lt;/span&gt;+ distanceFunctionStr + &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot;&lt;br /&gt;                &lt;/span&gt;| NNSearchAlgorithm.BallTree &lt;span style="color: blue"&gt;-&amp;gt;&lt;br /&gt;                    &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.neighboursearch.BallTree&amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot; -A &amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot; &lt;/span&gt;+ distanceFunctionStr + &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot;&lt;br /&gt;                &lt;/span&gt;| NNSearchAlgorithm.CoverTree &lt;span style="color: blue"&gt;-&amp;gt;&lt;br /&gt;                    &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.neighboursearch.CoverTree&amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot; -A &amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot; &lt;/span&gt;+ distanceFunctionStr + &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot;&lt;br /&gt;                &lt;/span&gt;| NNSearchAlgorithm.KDTree &lt;span style="color: blue"&gt;-&amp;gt;&lt;br /&gt;                    &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;weka.core.neighboursearch.KDTree&amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot; -A &amp;quot; &lt;/span&gt;+ &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot; &lt;/span&gt;+ distanceFunctionStr + &lt;span style="color: maroon"&gt;&amp;quot;\&amp;quot;&amp;quot;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;If you are interested, you can browse the Java code(in Weka’s GUI component) for this task and appreciate how succinct F# gets. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;More Dataset manipulating functions&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Weka’s dataset class(&lt;strong&gt;Instances)&lt;/strong&gt; is very hard to use. Normally, a dataset is simply a table, with each row representing an instance and each column representing a feature. The dataset class in Weka is too general to be manipulated easily. Weka provides various &lt;strong&gt;filters &lt;/strong&gt;to manipulate datasets. However, these filters are hard to remember their names and applying a filtering needs several lines of code. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I first write a function that transforms a 2 dimensional array into Weka dataset; it can transform the in-memory data more easily into a dataset. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;from2DArray (d:float[,]) (intAsNominal:bool)  &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The parameter intAsNominal=true means that if a columns are all integers, then treat this column as a nominal column. Otherwise all columns are treated as real ones. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I also wrapper common filters into single functions, that means, applying one filter only uses one line code and they are all put in Dataset module. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The parallel processing &lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The PSeq module (in F# PowerPack) makes writing computing-bound parallel programs effortless. And in data mining, when it comes to parameter tuning or trying different models on different datasets, it would be fast to run these tasks in parallel on a multi-core machine. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The following code shows two functions performing a bulk of classification tasks:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;evalBulkClassify (tasks:ClaEvalTask seq) = &lt;br /&gt;    tasks&lt;br /&gt;    |&amp;gt; Seq.map evalClassify&lt;br /&gt;    |&amp;gt; Seq.toList&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;evalBulkClassifyParallel (task:ClaEvalTask seq) = &lt;br /&gt;    task&lt;br /&gt;    |&amp;gt; PSeq.map evalClassify&lt;br /&gt;    |&amp;gt; PSeq.toList&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The latter one is the parallel version, which simply change the Seq in the sequential version to PSeq. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-3027400874798450627?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/3027400874798450627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/09/wekasharp-more-features.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3027400874798450627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3027400874798450627'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/09/wekasharp-more-features.html' title='WekaSharp: more features'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-4400899817919896984</id><published>2010-09-05T11:06:00.000+08:00</published><updated>2010-09-05T11:24:56.363+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='numeric'/><category scheme='http://www.blogger.com/atom/ns#' term='c#'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Numeric Performance in C, C# and Java</title><content type='html'>By occasion, I find the following draft by Prof. Peter Sestoft:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.itu.dk/~sestoft/papers/numericperformance.pdf"&gt;Numeric Performance in C, C# and Java&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;He implemented several typical numerical programs in C, C# and Java, and tested their running time.&lt;br /&gt;&lt;br /&gt;The conclusion in the paper is that managed code is also performant! Read the paper for the details.&lt;br /&gt;&lt;br /&gt;Prof. Sestoft is an expert in compiler design and compiler writing. The draft also contains the generated byte code with his annotations. Enjoy!&lt;br /&gt;&lt;br /&gt;Just a note:&lt;br /&gt;Although the C code in the paper is native, it does not take the advantage of the modern CPU design. A fully optimized implementation, which is very hard for normal programmers to write, is faster than the native code. This is why Matlab/Numpy's matrix multiplication is faster than your C code. Matlab uses Intel MKL optimized implementation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-4400899817919896984?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/4400899817919896984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/09/numeric-performance-in-c-c-and-java.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/4400899817919896984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/4400899817919896984'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/09/numeric-performance-in-c-c-and-java.html' title='Numeric Performance in C, C# and Java'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-2651143676809524692</id><published>2010-09-03T18:01:00.001+08:00</published><updated>2010-09-03T19:52:11.382+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wrapper'/><category scheme='http://www.blogger.com/atom/ns#' term='weka'/><category scheme='http://www.blogger.com/atom/ns#' term='WekaSharp'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='data mining'/><title type='text'>WekaSharp: Tutorial for using Weka in F#/.Net</title><content type='html'>&lt;h5&gt;&lt;a href="http://fdatamining.blogspot.com/2010/08/wekasharp-f-wrapper-for-weka.html"&gt;&lt;/a&gt;&lt;/h5&gt;  &lt;p&gt;There are 3 posts in this series: &lt;/p&gt;  &lt;p&gt;1. &lt;a href="http://fdatamining.blogspot.com/2010/08/wekasharp-f-wrapper-for-weka.html"&gt;An F# wrapper for Weka&lt;/a&gt;. The minimal wrapper in F# for Weka. &lt;/p&gt;  &lt;p&gt;2. More features. (not available yet) This post will contain improvement over the minimal wrapper, e.g. more Dataset processing function, some plot functionality, etc.&lt;/p&gt;  &lt;p&gt;3. &lt;strong&gt;Tutorial&lt;/strong&gt;/&lt;strong&gt;Usage examples. (this post)&lt;/strong&gt; This post is for end users, who might have no interested in reading the implementation details, but rather knowing how to use this wrapper to perform data mining tasks in .Net. &lt;/p&gt;  &lt;h2&gt;Installation &lt;/h2&gt;  &lt;p&gt;The easiest way is to use &lt;strong&gt;Visual Studio 2010&lt;/strong&gt; with the&lt;strong&gt; latest F# PowerPack&lt;/strong&gt;.  Download the release/source code at &lt;a title="http://wekasharp.codeplex.com/" href="http://wekasharp.codeplex.com/"&gt;http://wekasharp.codeplex.com/&lt;/a&gt; and change the dll references to ones at the &lt;strong&gt;bin\Release&lt;/strong&gt; folder. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Visual Studio 2008&lt;/strong&gt; should also be able open the project file. Only the parallel functionality cannot be used in 2008 as Parallel Sequence is only added in .Net 4.0. &lt;/p&gt;  &lt;p&gt;The support for &lt;strong&gt;Mono&lt;/strong&gt; is not prepared yet. However, it should be easy to figure out. &lt;/p&gt;  &lt;p&gt;As all necessary runtimes are also included in the release, so the user &lt;strong&gt;does not need &lt;/strong&gt;to download Weka or IKVM runtimes. &lt;/p&gt;  &lt;h2&gt;Quick Guide&lt;/h2&gt;  &lt;p&gt;The user is encourage to look at the script code in &lt;strong&gt;Script.fsx&lt;/strong&gt; and run them to have a general feeling of the wrapper. These examples cover typical usage of WekaSharp. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Reading the other two posts&lt;/strong&gt; is also very useful for using WekaSharp, and more importantly, for changing the source code of WekaSharp on your own need. &lt;/p&gt;  &lt;h2&gt;The License &lt;/h2&gt;  &lt;p&gt;As Weka is GPL, I must make WekaSharp under GPL. &lt;/p&gt;  &lt;h2&gt;The Efficiency Concern &lt;/h2&gt;  &lt;p&gt;People may concern how fast is the wrapper compared to the original Weka in Java. I haven’t thoroughly tested this yet. Some casual tests show that they are &lt;strong&gt;just the same&lt;/strong&gt;, at least on Windows platforms. For instance, I run a 3-fold cross validation on the letter dataset using J48 decision trees, both the wrapper and the Weka (run from GUI) use about 20 seconds. It is quite surprising that IKVM’s compiler does so good a job. &lt;/p&gt;  &lt;p&gt;As I will show later, the WekaSharp has some &lt;strong&gt;parallel constructs&lt;/strong&gt;, which enable us to utilize multi-core more conveniently. &lt;/p&gt;  &lt;p&gt;I’ve provided a module to do profiling. The design is similar to that of Matlab, but better:) You can use &lt;strong&gt;Profile.tic()&lt;/strong&gt; and &lt;strong&gt;Profile.toc(“str”)&lt;/strong&gt; pair to time the execution of F# code. Or You can get multiple timers by using &lt;strong&gt;Profile.getWatch().&lt;/strong&gt; &lt;/p&gt;  &lt;h2&gt;The Dataset and IO&lt;/h2&gt;  &lt;p&gt;The &lt;strong&gt;Dataset&lt;/strong&gt; module contains functions to manipulate datasets. &lt;strong&gt;Most of the functions are pure&lt;/strong&gt;, i.e., they don’t change the input dataset and create a new dataset after processing. &lt;/p&gt;  &lt;p&gt;Current 4 types of data files are supported: Weka’s ARFF format, LibSvm, Csv and SvmLight. Both loading and saving are supported, i.e. there are 8 functions.&lt;/p&gt;  &lt;p&gt;The following code shows how to load an ARFF dataset and set the last column as the label:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;sonar =&lt;br /&gt;   &lt;span style="color:maroon;"&gt;@"D:\temp\datasets-UCI\UCI\sonar.arff"&lt;br /&gt;   &lt;/span&gt;|&amp;gt; Dataset.&lt;strong&gt;readArff&lt;/strong&gt;&lt;br /&gt;   |&amp;gt; Dataset.&lt;strong&gt;setClassIndexWithLastAttribute&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;You can also do the two steps in one function:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;iris =&lt;br /&gt;   &lt;span style="color:maroon;"&gt;@"C:\Program Files\Weka-3.6\data\iris.arff"&lt;br /&gt;   &lt;/span&gt;|&amp;gt; Dataset.&lt;strong&gt;readArffLastAttributeAsLabel&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Parameters&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Data mining algorithms usually have parameters, sometimes very complicated parameters. F# provides very convenient syntax construct for optional parameters. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;E.g. the full set parameters of an SVM has the SVM type, the kernel type, the C value, and the parameters for the kernel, etc. Maybe you just need to set the C value: Parameter.SVM.MakePara(&lt;strong&gt;C = c&lt;/strong&gt;). &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here &lt;strong&gt;Parameter&lt;/strong&gt; is a module for parameters setting. For each data mining algorithm, there are two members: &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;* &lt;strong&gt;DefaultPara&lt;/strong&gt;. The default parameter string, which is the same as ones used in Weka GUI. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;* &lt;strong&gt;MakePara&lt;/strong&gt;. A function to make different parameters. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here are several more complicated examples of .MakePara method for different algorithms:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;Parameter.SVM.MakePara(kernelType = Parameter.SVMKernelType.LinearKernel, C = 10.0)&lt;br /&gt;Parameter.KMeans.MakePara(K=5, maxIter=10, seed=10)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;Parameter.KNN.MakePara(distanceFunction = Parameter.Manhattan, K = 3)&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;and you can also use the &lt;strong&gt;IDE support&lt;/strong&gt; to find which parameters are supported in a data mining algorithm:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TIDHXHQ6CjI/AAAAAAAABBU/JDEWMZzGqoE/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border: 0px none; display: inline;" title="image" alt="image" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TIDHXnIvJhI/AAAAAAAABBY/G2VFopilZpo/image_thumb%5B2%5D.png?imgmax=800" width="704" border="0" height="69" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Classification and its evaluation &lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;WekaSharp supports most of the common classification algorithms: &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;ClassifierType =&lt;br /&gt;   J48 | LogReg | NeuralNet | KNN | NaiveBayes | SVM | LibLinear | LinReg | AdaBoost | Bagging&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There are three types of classification tasks:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;ClaEvalTask =&lt;br /&gt;   | &lt;strong&gt;CrossValidation&lt;/strong&gt; &lt;span style="color:blue;"&gt;of &lt;/span&gt;int * Dataset * ClassifierType * parameters&lt;br /&gt;   | &lt;strong&gt;RandomSplit&lt;/strong&gt; &lt;span style="color:blue;"&gt;of &lt;/span&gt;float * Dataset * ClassifierType * parameters&lt;br /&gt;   | &lt;strong&gt;TrainTest&lt;/strong&gt; &lt;span style="color:blue;"&gt;of &lt;/span&gt;Dataset * Dataset * ClassifierType * parameters&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To run this task, you need the &lt;strong&gt;evalClassify&lt;/strong&gt; method in &lt;strong&gt;Eval&lt;/strong&gt; module. The following code shows a complete example using J48 as the classifier:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;(* playing decision trees on Iris dataset *)&lt;br /&gt;// load the dataset&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;iris =&lt;br /&gt;   &lt;span style="color:maroon;"&gt;@"C:\Program Files\Weka-3.6\data\iris.arff"&lt;br /&gt;   &lt;/span&gt;|&amp;gt; Dataset.readArff&lt;br /&gt;   |&amp;gt; Dataset.setClassIndexWithLastAttribute&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// describe 3 kinds of classification tasks&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;j48Tt = TrainTest(iris, iris, ClassifierType.J48, Parameter.J48.DefaultPara)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;j48Cv = CrossValidation(5, iris, ClassifierType.J48, Parameter.J48.DefaultPara)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;j48Rs = RandomSplit(0.7, iris, ClassifierType.J48, Parameter.J48.DefaultPara)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// perform the task and get result&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;ttAccuracy = j48Tt |&amp;gt; Eval.evalClassify |&amp;gt; Eval.getAccuracy&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;cvAccuracy = j48Cv |&amp;gt; Eval.evalClassify |&amp;gt; Eval.getAccuracy&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;rsAccuracy = j48Rs |&amp;gt; Eval.evalClassify |&amp;gt; Eval.getAccuracy&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The evalClassify function returns a result object, you can use “.” to that object in the IDE to find out various types of results available. In the above, we use predefined functions to get the accuracy from it. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Clustering and its evaluation&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Performing clustering is very similar to classification. You can define two types of clustering:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;CluEvalTask =&lt;br /&gt;   | ClusterWithLabel &lt;span style="color:blue;"&gt;of &lt;/span&gt;Dataset * ClustererType * parameters&lt;br /&gt;   | DefaultCluster &lt;span style="color:blue;"&gt;of &lt;/span&gt;Dataset * ClustererType * parameters&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;ClusterWithLabel means that you will need to use the label of the dataset to do evaluation. DefaultCluster does not require the dataset has label (actually, it does now allow datasets to have labels either), so the result will only contain the clustering assignments, but not accuracy, etc. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The following code shows a complete clustering example:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;irisLabeled =&lt;br /&gt;   &lt;span style="color:maroon;"&gt;@"C:\Program Files\Weka-3.6\data\iris.arff"&lt;br /&gt;   &lt;/span&gt;|&amp;gt; Dataset.readArffLastAttributeAsLabel&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;kmeansTask = ClusterWithLabel(irisLabeled, ClustererType.KMeans, Parameter.KMeans.MakePara(K=3))&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;emTask = ClusterWithLabel(irisLabeled, ClustererType.EM, Parameter.EM.MakePara(K=3))&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;dbscanTask = ClusterWithLabel(irisLabeled, ClustererType.DBScan, Parameter.DBScan.DefaultPara)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;kmeansResult = Eval.evalClustering kmeansTask |&amp;gt; Eval.getClusterSummary&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;emResult = Eval.evalClustering emTask |&amp;gt; Eval.getClusterSummary&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;dbscanResult = Eval.evalClustering dbscanTask |&amp;gt; Eval.getClusterSummary&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Bulk &amp;amp; parallel processing tasks&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Sometimes, you need to run multiple tasks. E.g. you need to run the same task multiple times to see the mean and variance of the result, or you need to try different parameters for an algorithm, or you simply have different data mining tasks to run. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The following example shows how to create a bulk of tasks and run them:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// load the data set&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;sonar =&lt;br /&gt;   &lt;span style="color:maroon;"&gt;@"D:\temp\datasets-UCI\UCI\sonar.arff"&lt;br /&gt;   &lt;/span&gt;|&amp;gt; Dataset.readArff&lt;br /&gt;   |&amp;gt; Dataset.setClassIndexWithLastAttribute&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// set different parameters&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Cs = [0.01; 0.1; 1.; 10.; 50.; 100.; 500.; 1000.; 2000.; 5000. ]&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// make the tasks with the parameter set&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;tasks =&lt;br /&gt;   Cs&lt;br /&gt;   |&amp;gt; List.map (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;c &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;Parameter.SVM.MakePara(C = c))&lt;br /&gt;   |&amp;gt; List.map (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;p &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;CrossValidation(3, sonar, ClassifierType.SVM, p))&lt;br /&gt;  &lt;br /&gt;Profile.tic()&lt;br /&gt;&lt;span style="color:green;"&gt;// the accuracy result&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;results =&lt;br /&gt;   tasks&lt;br /&gt;   |&amp;gt; Eval.&lt;strong&gt;evalBulkClassify&lt;/strong&gt;&lt;br /&gt;   |&amp;gt; List.map Eval.getAccuracy&lt;br /&gt;Profile.toc(&lt;span style="color:maroon;"&gt;"sequential time: "&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here I created different SVM tasks for different C values, run them and get the accuracy as a list. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;F# provides very easy syntax to perform multiple tasks at the same time. Thus I provide &lt;strong&gt;evalBulkClassifyParallel&lt;/strong&gt; method:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;Profile.tic()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;resultsParallel =&lt;br /&gt;   tasks&lt;br /&gt;   |&amp;gt; Eval.evalBulkClassifyParallel&lt;br /&gt;   |&amp;gt; List.map Eval.getAccuracy&lt;br /&gt;Profile.toc(&lt;span style="color:maroon;"&gt;"parallel (PSeq) time: "&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// sequential time: : 9767.804800 ms&lt;br /&gt;// parallel (PSeq) time: : 6154.715500 ms&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;As the profiling shows, on a two-core machine, &lt;strong&gt;parallel executing the tasks does boost the speed&lt;/strong&gt;. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Plotting &lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;is not finished, but it is still quite usable. To plot the different accuracies for different Cs in the SVM, we can use:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// do the plot&lt;br /&gt;&lt;/span&gt;lc.column(y = results, xname = &lt;span style="color:maroon;"&gt;"differnet C"&lt;/span&gt;, yname = &lt;span style="color:maroon;"&gt;"Accuracy"&lt;/span&gt;, title = &lt;span style="color:maroon;"&gt;"SVM on iris"&lt;/span&gt;,&lt;br /&gt;   isValueShownAsLabel = &lt;span style="color:blue;"&gt;true &lt;/span&gt;) |&amp;gt; display&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Making Datasets from Memory &lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;All the above examples use data from data files. In practice, we might want to convert the data in memory, e.g. in an array into a Weka dataset. The following examples shows how to create dataset from F# arrays:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;(* create dataset from F# arrays *)&lt;br /&gt;&lt;br /&gt;// make the data array&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;data = [| 0.; 0.;&lt;br /&gt;             1.; 1.;&lt;br /&gt;             0.; 1.;&lt;br /&gt;             1.; 0.; |]&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;xorArray = Array2D.init 4 2 (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;data.[i*2 + j])&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// make weka dataset from array&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;xor0 = Dataset.from2DArray xorArray &lt;span style="color:blue;"&gt;false&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;// add labels&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;xor = xor0 |&amp;gt; Dataset.addClassLabels [&lt;span style="color:maroon;"&gt;"T"&lt;/span&gt;; &lt;span style="color:maroon;"&gt;"T"&lt;/span&gt;; &lt;span style="color:maroon;"&gt;"F"&lt;/span&gt;; &lt;span style="color:maroon;"&gt;"F"&lt;/span&gt;]&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// make svm tasks&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;rbfTask = TrainTest(xor, xor, ClassifierType.SVM, Parameter.SVM.DefaultPara)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;linearTask = TrainTest(xor, xor, ClassifierType.SVM, Parameter.SVM.MakePara(kernelType = Parameter.SVMKernelType.LinearKernel, C = 10.0) )&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// rbf svm gets 100% accuracy&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;rbfAccuracy = rbfTask |&amp;gt; Eval.evalClassify |&amp;gt; Eval.getAccuracy&lt;br /&gt;&lt;span style="color:green;"&gt;// linear svm does not work on XOR data set&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;linearAccuracy = linearTask |&amp;gt; Eval.evalClassify |&amp;gt; Eval.getAccuracy&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;As a user of WekaSharp, I already benefit from it as I can &lt;strong&gt;process the data and run data mining algorithms over it both in F#.&lt;/strong&gt; Originally, I needed to write my data into ARFF file, call Weka, parse the output to get the result. And the F# solution is far more &lt;strong&gt;declarative&lt;/strong&gt;. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Another benefit is that we can &lt;strong&gt;write extensions &lt;/strong&gt;to Weka in F#!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-2651143676809524692?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/2651143676809524692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/09/wekasharp-tutorial-for-running-weka-in.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/2651143676809524692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/2651143676809524692'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/09/wekasharp-tutorial-for-running-weka-in.html' title='WekaSharp: Tutorial for using Weka in F#/.Net'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_4bFBK2BKrQI/TIDHXnIvJhI/AAAAAAAABBY/G2VFopilZpo/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-8464697886879476938</id><published>2010-08-23T20:00:00.001+08:00</published><updated>2010-09-03T18:03:49.799+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wrapper'/><category scheme='http://www.blogger.com/atom/ns#' term='weka'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='data mining'/><title type='text'>WekaSharp: An F# wrapper for Weka</title><content type='html'>&lt;p&gt;There are 3 posts in this series: &lt;/p&gt;  &lt;p&gt;1. An F# wrapper for Weka. (this post) The minimal wrapper in F# for Weka. &lt;/p&gt;  &lt;p&gt;2. More features. This post will contain improvement over the minimal wrapper, e.g. more Dataset processing function, some plot functionality, etc.&lt;/p&gt;  &lt;p&gt;3. &lt;a href="http://fdatamining.blogspot.com/2010/09/wekasharp-tutorial-for-running-weka-in.html"&gt;Tutorial/Usage examples&lt;/a&gt;. This post is for end users, who might have no interested in reading the implementation details, but rather knowing how to use this wrapper to perform data mining tasks in .Net. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://wekasharp.codeplex.com/"&gt;WekaSharp&lt;/a&gt; is available at CodePlex:&lt;a title="http://wekasharp.codeplex.com/" href="http://wekasharp.codeplex.com/"&gt;http://wekasharp.codeplex.com/&lt;/a&gt;. &lt;/p&gt;    &lt;p&gt;Weka is one of the most widely used data mining software package. It contains over 50 data mining algorithms, a good GUI support and well written documents. Because of its good features, a lot data mining courses use it as an illustrative software. &lt;/p&gt;  &lt;p&gt;Its GPL license also allows it to be used freely in academic. &lt;/p&gt;  &lt;p&gt;However, Weka GUI enables us to perform data loading, filtering and classifier building in an interactive way, sometimes programming is still needed. For instance, GUI has poor support for parameter selection and processing 100 data sets. In these tasks, scripting is a better way. However, Java language is &lt;strong&gt;not declarative&lt;/strong&gt; and it &lt;strong&gt;does not support interactive shell &lt;/strong&gt;either. &lt;/p&gt;  &lt;p&gt;F# well supports these two features, also data processing in F# in general is far better than Java. So F# and Weka are good combination. &lt;/p&gt;    &lt;h2&gt;Compile Weka library to a .Net dll&lt;/h2&gt;  &lt;p&gt;As everybody knows, Weka is written in Java. To use it within .Net framework seamlessly, we need to compile it into a .Net assembly.  Fortunately, IKVM project is made for such purpose. &lt;/p&gt;  &lt;p&gt;There is an online &lt;a href="http://weka.wikispaces.com/IKVM+with+Weka+tutorial"&gt;tutorial&lt;/a&gt; for compiling weka.jar to weka.dll. However, this tutorial does not mention how to use external jar libraries in Weka, e.g. libsvm.jar. Because Java’ class loading mechanism is different from .Net’s. We cannot simply compile different jars into separate dlls. The simplest way is to compile weka.jar and libsvm.jar to a single dll:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&amp;gt;ikvmc -sharedclassloader -target:library weka.jar libsvm.jar&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Notice that there is a &lt;a href="http://sourceforge.net/projects/wekadotnet/files/"&gt;WekaDotNet&lt;/a&gt; project at sourceforge. The project is compiled using Visual J#, which is already deprecated in Visual Studio family. Using IKVM to compile jar into .Net is an easier and more stable way.&lt;/p&gt;  &lt;h2&gt;The overall structure of the wrapper&lt;/h2&gt;  &lt;p&gt;Based on the Weka class hierarchy, I’ve defined several separate modules for different kinds of functionality:&lt;/p&gt;  &lt;p&gt;* &lt;strong&gt;Common&lt;/strong&gt; – contains all common types over the whole wrapper. E.g. the union type for various classifiers. &lt;/p&gt;  &lt;p&gt;* &lt;strong&gt;Dataset&lt;/strong&gt; – contains IO routines to read/save various datasets and functions (e.g. split) to manipulate/preprocess datasets.&lt;/p&gt;  &lt;p&gt;* &lt;strong&gt;Parameter&lt;/strong&gt; – contains functions to make parameters for the data mining algorithms, all the default parameters are also provided. &lt;/p&gt;  &lt;p&gt;* &lt;strong&gt;Classify&lt;/strong&gt; – contains standard classifiers. &lt;/p&gt;  &lt;p&gt;* &lt;strong&gt;Cluster&lt;/strong&gt; – contains standard clustering algorithms. &lt;/p&gt;  &lt;p&gt;* &lt;strong&gt;Eval&lt;/strong&gt; – contains functions to perform different kinds of classification/clustering tasks and output their result (e.g. accuracy/F1 measure) out. &lt;/p&gt;  &lt;p&gt;In the following sections, I will present each module in details. &lt;/p&gt;  &lt;h2&gt;Common module&lt;/h2&gt;  &lt;p&gt;In this module, I try to write all types, including classifier types, clustering algorithm types and some evaluation-task types. This module is marked as &lt;strong&gt;AutoOpen&lt;/strong&gt;, i.e., the types in the modules are easily accessible from other modules. &lt;/p&gt;  &lt;pre class="code"&gt;[&amp;lt;AutoOpen&amp;gt;]&lt;br /&gt;&lt;span style="color:blue;"&gt;module &lt;/span&gt;Common =&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;NYI() = failwith &lt;span style="color:maroon;"&gt;"Not yet implemented!"&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;Dataset = core.Instances&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;parameters = string&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;DatafileType =&lt;br /&gt;   Arff | LibSVM | Csv | Svmlight&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;ClassifierType =&lt;br /&gt;   J48 | LogReg | NeuralNet | KNN | NaiveBayes | SVM | LibLinear&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;ClaEvalTask =&lt;br /&gt;   | CrossValidation &lt;span style="color:blue;"&gt;of &lt;/span&gt;int * Dataset * ClassifierType * parameters&lt;br /&gt;   | RandomSplit &lt;span style="color:blue;"&gt;of &lt;/span&gt;float * Dataset * ClassifierType * parameters&lt;br /&gt;   | TrainTest &lt;span style="color:blue;"&gt;of &lt;/span&gt;Dataset * Dataset * ClassifierType * parameters&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;ClustererType =&lt;br /&gt;   KMeans | EM | DBScan&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I’ve redefined Weka Instances class as Dataset, and use a string for parameters.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Dataset module&lt;/h2&gt;This module includes functions to read/save 4 kinds of data files: Weka’s ARFF, libsvm, Csv and SvmLight. It also has a function randomSplit to randomly split a dataset with a given ratio.&lt;br /&gt;&lt;br /&gt;This module currently is far from complete. For instance, we can only load datasets from 4 kinds of disk files, and no way to build a dataset using in-memory data, e.g. an array of vectors. We also only provide one data preprocessing step – random split, many other common preprocessing steps are needed.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I’d like to use another post to finish these. In this post, delivering the whole working wrapper (although incomplete) is more important. &lt;/p&gt;Here is some of the implementation:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color:blue;"&gt;let &lt;/span&gt;readDataFile (ftype: DatafileType) (fname:string) =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;loader =&lt;br /&gt;       &lt;span style="color:blue;"&gt;match &lt;/span&gt;ftype &lt;span style="color:blue;"&gt;with&lt;br /&gt;           &lt;/span&gt;| Arff &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;converters.ArffLoader() :&amp;gt; AbstractFileLoader&lt;br /&gt;           | LibSVM &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;converters.LibSVMLoader() :&amp;gt; AbstractFileLoader&lt;br /&gt;           | Csv &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;converters.CSVLoader() :&amp;gt; AbstractFileLoader&lt;br /&gt;           | Svmlight &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;converters.SVMLightLoader() :&amp;gt; AbstractFileLoader&lt;br /&gt;   &lt;span style="color:blue;"&gt;try&lt;br /&gt;       &lt;/span&gt;loader.setFile(&lt;span style="color:blue;"&gt;new &lt;/span&gt;java.io.File(fname))&lt;br /&gt;       loader.getDataSet()&lt;br /&gt;   &lt;span style="color:blue;"&gt;with&lt;br /&gt;       &lt;/span&gt;| _ &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"dataset loading error"&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;its input is a datafiletype and the file name. Based on this function, 4 concrete functions are defined:&lt;br /&gt;&lt;/p&gt;&lt;span style="color:blue;"&gt;        let &lt;/span&gt;readArff = readDataFile DatafileType.Arff&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;    let &lt;/span&gt;readLibsvm = readDataFile DatafileType.LibSVM&lt;br /&gt;&lt;span style="color:blue;"&gt;    let &lt;/span&gt;readCsv = readDataFile DatafileType.Csv&lt;br /&gt;&lt;span style="color:blue;"&gt;    let &lt;/span&gt;readSvmlight = readDataFile DatafileType.Svmlight&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The data saving functions have similar implementation.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Parameter module &lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This module contains functions that create parameters for different data mining algorithms. As Weka uses a space-separated string as parameters, it would be unclear to the first-time user that what does “-R 1.0 –M -1” mean for a logistic regression.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I have provided a default parameter string for each algorithm.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Classify module&lt;/h2&gt;In this module, I have wrapped most commonly used classification algorithms: C4.5 decision tree (named J48 in Weka), NaiveBayes, Logistic Regression, Multilayer perception neural net, Support Vector Machines (SVM).&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The getClassifier function accepts a classifier type and the an option string, and returns a classifier instance:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getClassifier (ctype:ClassifierType) (option:string)  =&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;classifier =&lt;br /&gt;   &lt;span style="color:blue;"&gt;match &lt;/span&gt;ctype &lt;span style="color:blue;"&gt;with&lt;br /&gt;       &lt;/span&gt;| J48 &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;classifiers.trees.J48() :&amp;gt; classifiers.Classifier&lt;br /&gt;       | NaiveBayes &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;classifiers.bayes.NaiveBayes() :&amp;gt; classifiers.Classifier&lt;br /&gt;       &lt;span style="color:green;"&gt;//| KNN -&amp;gt;new classifiers.lazy.IBk() :&amp;gt; weka.classifiers.Classifier&lt;br /&gt;       &lt;/span&gt;| LogReg &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;classifiers.functions.Logistic() :&amp;gt; classifiers.Classifier&lt;br /&gt;       | NeuralNet &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;classifiers.functions.MultilayerPerceptron() :&amp;gt; classifiers.Classifier&lt;br /&gt;       | SVM &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;classifiers.functions.LibSVM() :&amp;gt; classifiers.Classifier&lt;br /&gt;       | LibLinear &lt;span style="color:blue;"&gt;-&amp;gt; new &lt;/span&gt;classifiers.functions.LibLINEAR() :&amp;gt; classifiers.Classifier&lt;br /&gt;       | _ &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"not supported"&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;classifier.setOptions(core.Utils.splitOptions option)&lt;br /&gt;classifier&lt;/pre&gt;and buildClassifier takes one more parameter – the dataset to build a trained classifier:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;buildClassifier (ctype:ClassifierType) (option:string) (ds:Dataset) =&lt;br /&gt;checkDataset ds&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;classifier = getClassifier ctype option&lt;br /&gt;classifier.buildClassifier(ds)&lt;br /&gt;classifier&lt;/pre&gt;Handy functions like getJ48, getSVM are also defined:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getJ48 (option:string) = getClassifier ClassifierType.J48 option&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getSVM (option:string) = getClassifier ClassifierType.SVM option&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There are several issues: &lt;/p&gt;1. KNN (Weka class: IBk) is not supported as its namespace in Weka is core.classifiers.lazy. But lazy is a keyword in F#, thus cannot appear in a namespace.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;2. The IO wrapper for classifiers are not provided yet.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Cluster module&lt;br /&gt;&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Similar to Classify module, there are getClusterer and buildClusterer in Cluster module. There are also shortcuts to concrete clustering algorithms such as getKmeans and buildKmeans.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Eval module&lt;/h2&gt;This module contains evaluation methods for classification and clustering. For classification, I’ve defined a task discrete union:&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;ClaEvalTask =&lt;br /&gt;&lt;pre class="code"&gt;    | CrossValidation &lt;span style="color:blue;"&gt;of &lt;/span&gt;int * Dataset * ClassifierType * parameters&lt;br /&gt;| RandomSplit &lt;span style="color:blue;"&gt;of &lt;/span&gt;float * Dataset * ClassifierType * parameters&lt;br /&gt;| TrainTest &lt;span style="color:blue;"&gt;of &lt;/span&gt;Dataset * Dataset * ClassifierType * parameters&lt;/pre&gt;classifyEval function is to do such a task:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;classifyEval (task:ClaEvalTask) =&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;rnd = &lt;span style="color:blue;"&gt;new &lt;/span&gt;java.util.Random(System.DateTime.Now.Ticks)&lt;br /&gt;&lt;span style="color:blue;"&gt;match &lt;/span&gt;task &lt;span style="color:blue;"&gt;with&lt;br /&gt;&lt;/span&gt;| TrainTest (dsTrain, dsTest, ctype, para) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;   &lt;/span&gt;Classify.checkDataset dsTrain&lt;br /&gt;   Classify.checkDataset dsTest&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;eval = &lt;span style="color:blue;"&gt;new &lt;/span&gt;classifiers.Evaluation(dsTrain)&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;cl = Classify.buildClassifier ctype para dsTrain&lt;br /&gt;   eval.evaluateModel(cl, dsTest) |&amp;gt; ignore&lt;br /&gt;   eval&lt;br /&gt;| CrossValidation(cv, ds, ctype, para) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;   &lt;/span&gt;Classify.checkDataset ds&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;eval = &lt;span style="color:blue;"&gt;new &lt;/span&gt;classifiers.Evaluation(ds)&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;cl = Classify.getClassifier ctype para&lt;br /&gt;   eval.crossValidateModel(cl, ds, cv, rnd, Array.empty)&lt;br /&gt;   eval&lt;br /&gt;| RandomSplit(ratio, ds, ctype, para) &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;   &lt;/span&gt;Classify.checkDataset ds&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;train, test = Dataset.randomSplit ratio ds&lt;br /&gt;   classifyEval (TrainTest(train, test, ctype, para))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;the result of this function is an evaluation result object. We can use its properties to get various evaluation metrics, or use the shortcut functions in the Eval module:&lt;/p&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getAccuracy (eval:classifiers.Evaluation) = eval.pctCorrect()&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getPrecison (eval:classifiers.Evaluation) (classIdx:int) = eval.precision(classIdx)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getRecall (eval:classifiers.Evaluation) (classIdx:int) = eval.recall(classIdx)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getF1 (eval:classifiers.Evaluation) (classIdx:int) = eval.fMeasure(classIdx)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getAUC (eval:classifiers.Evaluation) (classIdx:int) = eval.areaUnderROC(classIdx)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;getClassifySummary (eval:classifiers.Evaluation) = eval.toSummaryString()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;Two features of the wrapper are&lt;strong&gt;&lt;br /&gt;&lt;br /&gt;1) a declarative wrapper to Weka&lt;/strong&gt;. See the following code to see how declarative the wrapper is:&lt;br /&gt;&lt;br /&gt;// load the dataset&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;dataset =&lt;br /&gt;&lt;span style="color:maroon;"&gt;@"C:\Program Files\Weka-3.6\data\iris.arff"&lt;br /&gt;&lt;/span&gt;|&amp;gt; Dataset.readArff&lt;br /&gt;|&amp;gt; Dataset.setClassIndexWithLastAttribute&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// describe the cross validation task using SVM as the classifier&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;classifyTask = CrossValidation(5, dataset, ClassifierType.SVM, Parameter.SVM.defPara)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// perform the cross validation task and then get the average accuracy&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;cvAccuracy =&lt;br /&gt;classifyTask&lt;br /&gt;|&amp;gt; Eval.classifyEval&lt;br /&gt;|&amp;gt; Eval.getAccuracy&lt;/pre&gt;The code is understandable even to those who do not know F#.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;2)&lt;/strong&gt; &lt;strong&gt;interactively perform data mining algorithms&lt;/strong&gt;. &lt;/p&gt;F# interactive is well integrated in Visual Studio. It is very stable and convenient to use. The powerful VS IDE provides good Intellisense for F#.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-8464697886879476938?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/8464697886879476938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/08/wekasharp-f-wrapper-for-weka.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8464697886879476938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8464697886879476938'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/08/wekasharp-f-wrapper-for-weka.html' title='WekaSharp: An F# wrapper for Weka'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-5098273681270158508</id><published>2010-08-14T21:25:00.001+08:00</published><updated>2010-08-14T21:25:06.140+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='math-provider'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Reading F# Projects, Part III: The F# math providers.</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;We’ve covered the usage of Math providers in the matrix series. In this post, the whole source code is decomposed. &lt;/p&gt;  &lt;p&gt;As I find it time consuming to covert One Note notes into a blog format. Please see the attached pdf file. &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h3&gt;&amp;#160;&lt;a href="http://www.cse.ust.hk/~yinz/note-math-providers.pdf"&gt;PDF&lt;/a&gt;. &lt;/h3&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TGaZKksAGVI/AAAAAAAABBE/usBRTf-oUDo/s1600-h/math-providers.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="math-providers" border="0" alt="math-providers" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TGaZMGQA_II/AAAAAAAABBI/4i70ZOLOzsI/math-providers_thumb.png?imgmax=800" width="636" height="391" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-5098273681270158508?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/5098273681270158508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/08/reading-f-projects-part-iii-f-math.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5098273681270158508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5098273681270158508'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/08/reading-f-projects-part-iii-f-math.html' title='Reading F# Projects, Part III: The F# math providers.'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_4bFBK2BKrQI/TGaZMGQA_II/AAAAAAAABBI/4i70ZOLOzsI/s72-c/math-providers_thumb.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-8756714001535533192</id><published>2010-08-14T20:49:00.001+08:00</published><updated>2010-08-14T20:53:04.106+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='set'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='binary-search-tree'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><title type='text'>Reading F# Projects, Part II: F# Set</title><content type='html'>&lt;p&gt;The design patterns in a data structure project like F# Set is simple. However, understanding it really needs time as data structures involve heavy generic programming and the type system in F# (or any other static FP language) is very complex. &lt;/p&gt;  &lt;p&gt;In this post, we will explore two aspects of F# Set:&lt;/p&gt;  &lt;p&gt;1. &lt;strong&gt;The general implementation structure for a generic data structure.&lt;/strong&gt; After knowing the pattern, we could write other data structures in F# and these data structures are generic enough. &lt;/p&gt;  &lt;p&gt;2. Know something about&lt;strong&gt; generics in F# and type inference&lt;/strong&gt;. We will come to this part in later readings. &lt;/p&gt;  &lt;p&gt;In F# source code, the immutable set implementation (set.fs) is a good one for us to start for data structures. Why not more fundamental data structures like F# sequences? Because those ones are more involved in .Net platform and F# Set is nearly pure F# code. Also because:&lt;/p&gt;  &lt;h2&gt;I love trees!&lt;/h2&gt;  &lt;p&gt;F# Set and Map are implemented using balanced binary search tress, to be exact, AVL tress. Balanced BSTs support insert, delete and find operations all in O(log n). So it is widely used for implementing dynamic sets. &lt;/p&gt;  &lt;p&gt;In general, trees are the most beautiful data structures and have so many variants. They are also efficient, e.g. R tree and friends are the fundamental data structures used in every database. Quadtrees are used for computational geometry in theory and spatial database in practice. &lt;/p&gt;  &lt;p&gt;The F# Set is implemented by an immutable AVL tree. Here immutable means that every node, once created, its values could be be changed. In CS literature, researchers use “persistent” to name immutable data structures. If you do 100 insertions to an empty tree, you can have 101 version of trees, where common nodes are shared to reduce the space cost. It is proved that in such a tree, inserting operation still costs O(log n) time, and O(log n) space for duplicating some nodes in the tree (usually along the insertion path). &lt;/p&gt;  &lt;p&gt;As this post is not intended to be a tutorial on data structures, I won’t go into details for the tree implementation. We mainly study how an industry-strength data structure is implemented in F#. &lt;/p&gt;  &lt;h2&gt;The main structure &lt;/h2&gt;  &lt;p&gt;set.fs contains about 840 lines of code. The code skeleton is shown below:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;namespace &lt;/span&gt;Microsoft.FSharp.Collections&lt;br /&gt;   &lt;span style="color:blue;"&gt;type &lt;/span&gt;SetTree&amp;lt;'T&amp;gt; &lt;span style="color:blue;"&gt;when &lt;/span&gt;'T : comparison =&lt;br /&gt;       | SetEmpty                                          &lt;span style="color:green;"&gt;// height = 0  &lt;br /&gt;       &lt;/span&gt;| SetNode &lt;span style="color:blue;"&gt;of &lt;/span&gt;'T * SetTree&amp;lt;'T&amp;gt; *  SetTree&amp;lt;'T&amp;gt; * int    &lt;span style="color:green;"&gt;// height = int&lt;br /&gt;       &lt;/span&gt;| SetOne  &lt;span style="color:blue;"&gt;of &lt;/span&gt;'T                                     &lt;span style="color:green;"&gt;// height = 1  &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:blue;"&gt;module internal &lt;/span&gt;SetTree =&lt;br /&gt;       &lt;span style="color:green;"&gt;// functions:&lt;br /&gt;       // indert, delete, find, etc.&lt;br /&gt;       // split, rebalance, etc.&lt;br /&gt;      &lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;SetIterator&amp;lt;'T&amp;gt; &lt;span style="color:blue;"&gt;when &lt;/span&gt;'T : comparison  =&lt;br /&gt;           { &lt;span style="color:blue;"&gt;mutable &lt;/span&gt;stack: SetTree&amp;lt;'T&amp;gt; list;  &lt;span style="color:green;"&gt;// invariant: always collapseLHS result&lt;br /&gt;             &lt;/span&gt;&lt;span style="color:blue;"&gt;mutable &lt;/span&gt;started : bool           &lt;span style="color:green;"&gt;// true when MoveNext has been called  &lt;br /&gt;           &lt;/span&gt;}&lt;br /&gt;       &lt;span style="color:blue;"&gt;type &lt;/span&gt;Set&amp;lt;'T &lt;span style="color:blue;"&gt;when &lt;/span&gt;'T : comparison &amp;gt;(comparer:IComparer&amp;lt;'T&amp;gt;, tree: SetTree&amp;lt;'T&amp;gt;) =&lt;br /&gt;           &lt;span style="color:green;"&gt;//member functions:&lt;br /&gt;           //  Add, Remove, Contains, etc.&lt;br /&gt;           //operators:&lt;br /&gt;           //  - + Intersection, etc.&lt;br /&gt;           //overrides:&lt;br /&gt;           //  GetHashCode, Equals, ToString&lt;br /&gt;           //implement interfaces:&lt;br /&gt;           //  IComparable&lt;br /&gt;           //  ICollection&amp;lt;'T&amp;gt;&lt;br /&gt;           //  IEnumerable&amp;lt;'T&amp;gt;&lt;br /&gt;           //  IEnumerable&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:blue;"&gt;module &lt;/span&gt;Set =&lt;br /&gt;       &lt;span style="color:green;"&gt;// set module functions:&lt;br /&gt;       // add, isEmpty, etc.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Under Microsoft.FSharp.Collections namespace, there are three things: SetTree&amp;lt;’T&amp;gt; type, Set module and SetTree module.&lt;br /&gt;&lt;/p&gt;SetTree is a type constructor, the parameter is a comparable type ‘T.&lt;br /&gt;&lt;p&gt;Most of the code is in SetTree internal module:&lt;br /&gt;&lt;/p&gt;    the first part contains the code to manipulate the tree structure, e.g. inserting a node, deleting a node, etc.&lt;br /&gt;&lt;br /&gt;    Type SetIterator&amp;lt;’T&amp;gt; and related functions are for implementing IEnumerable interface.&lt;br /&gt;    Set&amp;lt;’T&amp;gt; (comparer, tree) is the implementation for Set.&lt;br /&gt;&lt;br /&gt;Set module contains the common function to manipulate a set.&lt;br /&gt;&lt;p&gt;In a summary, A container, like Set, usually follows this pattern:&lt;br /&gt;&lt;/p&gt;&lt;strong&gt;1.&lt;/strong&gt; An implementation of the underlying data structure, here it is AVL tree.&lt;br /&gt;&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; A module for manipulating the data structures; members functions also can do this. Usually we keep functions in both places.&lt;br /&gt;&lt;/p&gt;&lt;strong&gt;3.&lt;/strong&gt; Implement the IEnumerable and IEnumerable&amp;lt;’T&amp;gt; interface, so that the container/data structure could be treated as a sequence.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;These are kind of rules for implementing new containers in F#, e.g. F# Map.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Detail 1. The generic &lt;/h2&gt;&lt;br /&gt;&lt;p&gt;It is meaningless if the Set implementation only supports integers, or floats. We want it to be generic, i.e., support any type. Since the underlying implementation is based on trees, so the base type should also support comparison. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;The other part of generic, which is more subtle, is the comparer. There are situations, we have different string sets. However, the rules to order them are not the same, i.e., strings maybe compared using different rules. So we should add another parameter into consideration. (Refer to Functor in OCaml if you want to see a more elegant more to deal with this situation.)&lt;/p&gt;Although F# Set currently does not support different comparers. Its internal implementation keeps comparer in mind. (Because F# supports OOP, the different comparers problem could be partially solved by inherit the original element type. Anyway, this is non-elegant.)&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We know that once a Set has some concrete values in it, the type is clear.  Here are two ways to initialize a Set:&lt;/p&gt;&lt;span style="color:blue;"&gt;static let &lt;/span&gt;empty : Set&amp;lt;'T&amp;gt; =&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color:blue;"&gt;let &lt;/span&gt;comparer = LanguagePrimitives.FastGenericComparer&amp;lt;'T&amp;gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;new &lt;/span&gt;Set&amp;lt;'T&amp;gt;(comparer, SetEmpty)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;new &lt;/span&gt;(elements : seq&amp;lt;'T&amp;gt;) =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;comparer = LanguagePrimitives.FastGenericComparer&amp;lt;'T&amp;gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;new &lt;/span&gt;Set&amp;lt;_&amp;gt;(comparer,SetTree.ofSeq comparer elements)&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;We can see that they get the comparer for type ‘T and use that comparer to pass into the constructor of type Set&amp;lt;’T&amp;gt;. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Detail 2. The comparer&lt;/h2&gt;The above code get the comparer for type ‘T using FastGenericComparer, which is in a big file prim-types.fs. The primitive type module should be discussed separately and thoroughly. There are quite a few ad hoc tricks in the implementation.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here we just need to know that it looks up a table to find a comparer and if the type is in the primitive types, then there are optimizations for them. &lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Detail 3. From the empty to concrete&lt;/h2&gt;F# allows you to write [], Set.Empty. These empties seem to be dynamic. Actually no. You cannot write sth like:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;empties = Array.create 100 Set.empty&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;the compiler gives the following error:&lt;/p&gt;&lt;br /&gt;error FS0030: Value restriction. The value 'empties' has been inferred to have generic type&lt;br /&gt;&lt;pre class="code"&gt;    val empties : Set&amp;lt;'_a&amp;gt; [] when '_a : comparison  &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;Because the compiler cannot generalize these empties into a single type. It is safe to write&lt;/p&gt;Set.empty + Set([1;2;3])&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Because the types of the two sets could be generalized. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Same situation applies to the &lt;strong&gt;zeros&lt;/strong&gt; in different number types.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Detail 4. The Iterator &lt;/h2&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;interface &lt;/span&gt;IEnumerable&amp;lt;'T&amp;gt; &lt;span style="color:blue;"&gt;with&lt;br /&gt;   member &lt;/span&gt;s.GetEnumerator() = SetTree.mkIEnumerator s.Tree&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;interface &lt;/span&gt;IEnumerable &lt;span style="color:blue;"&gt;with&lt;br /&gt;   override &lt;/span&gt;s.GetEnumerator() = (SetTree.mkIEnumerator s.Tree :&amp;gt; IEnumerator)&lt;/pre&gt;Both of the IEnumerable interfaces are implemented. Notice that difference between the two: there is a upcast in the type. Let’s see the actual implementation:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;mkIterator s = { stack = collapseLHS [s]; started = &lt;span style="color:blue;"&gt;false &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;mkIEnumerator s =&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color:blue;"&gt;let &lt;/span&gt;i = ref (mkIterator s)&lt;br /&gt;   { &lt;span style="color:blue;"&gt;new &lt;/span&gt;IEnumerator&amp;lt;_&amp;gt; &lt;span style="color:blue;"&gt;with&lt;br /&gt;         member &lt;/span&gt;x.Current = current !i&lt;br /&gt;     &lt;span style="color:blue;"&gt;interface &lt;/span&gt;IEnumerator &lt;span style="color:blue;"&gt;with&lt;br /&gt;         member &lt;/span&gt;x.Current = box (current !i)&lt;br /&gt;         &lt;span style="color:blue;"&gt;member &lt;/span&gt;x.MoveNext() = moveNext !i&lt;br /&gt;         &lt;span style="color:blue;"&gt;member &lt;/span&gt;x.Reset() = i :=  mkIterator s&lt;br /&gt;     &lt;span style="color:blue;"&gt;interface &lt;/span&gt;System.IDisposable &lt;span style="color:blue;"&gt;with&lt;br /&gt;         member &lt;/span&gt;x.Dispose() = () }&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Exercise &lt;/h2&gt;&lt;br /&gt;&lt;p&gt;An exercise is to read map.fs in F# core. The implementation of F# Map is very similar to F# Set. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-8756714001535533192?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/8756714001535533192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/08/reading-f-projects-part-ii-f-set.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8756714001535533192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8756714001535533192'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/08/reading-f-projects-part-ii-f-set.html' title='Reading F# Projects, Part II: F# Set'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-5534508373294399179</id><published>2010-08-14T20:48:00.001+08:00</published><updated>2010-08-14T20:51:35.955+08:00</updated><title type='text'>Reading F# Projects, Part I: The Common Knowledge</title><content type='html'>&lt;p&gt; &lt;/p&gt;  &lt;p&gt;During the programming of my data mining library, I occasionally refer to the design of other libraries. E.g. data mining toolkits WEKA and MALLET are well designed. I also refer to the design of F# libraries. The examples on F# books mainly tell us how to program on small level, i.e. how to manipulate list or sequence and some language features. However, designing in F# is often covered in a general way. &lt;/p&gt;  &lt;p&gt;Thus, it is beneficial for new F# users to read well designed F# libraries. In this series,  the design of the following source code will be presented: (the list is tentative to change.)  &lt;/p&gt;  &lt;p&gt;* some parts of F# Core and PowerPack. &lt;/p&gt;  &lt;p&gt;* F# math providers  -- service design pattern and PInvoke in F#. &lt;/p&gt;  &lt;p&gt;* TrueSkill – how a specific data mining model is implemented. &lt;/p&gt;  &lt;p&gt;* FParsec  -- the best example of computation expression/monad. &lt;/p&gt;  &lt;p&gt;Before going to any concrete projects, we review the common issues/tips/concepts that may be involved in the process of the project design in F#. &lt;/p&gt;  &lt;p&gt;I will add more topics here as we go through different F# programs. &lt;/p&gt;  &lt;h2&gt;The interface/signature file .fsi&lt;/h2&gt;  &lt;p&gt;In Java or C#, the implementation and interface are put in one. In C/C++, the interface (.h) and the implementation (.c/c++) are separated. Either design is good and has advantages. &lt;/p&gt;  &lt;p&gt;For F#, I think the later is better. In .fsi, all the public functions/types are commented carefully. These commented can be used to generated documents for the program, or used by the IDE Intellisense. &lt;/p&gt;  &lt;p&gt;The actual implementation is in the .fs files. In this file, comments are in places where actually necessary for the program who implements it, e.g. a formula, or a refer to a page number in a book. Because the program who writes the function definitely know what every parameter mean. If this kind of comments are put into the implementation, then the programs look less succinct. &lt;/p&gt;  &lt;p&gt;So usually, the programmer doing the implementation write the implementation in .fs. When the implementation is stable or ready to ship out, the corresponding .fsi interface file is generated and well commented manually. &lt;/p&gt;  &lt;p&gt;Here is the &lt;a href="http://msdn.microsoft.com/en-us/library/dd233196.aspx"&gt;MSDN page on signature file&lt;/a&gt;. &lt;/p&gt;  &lt;h2&gt;Namespaces and Modules&lt;/h2&gt;  &lt;p&gt;The latest F# version requires that an F# source file (.fs) starts with a namespace, that means all the following code is under this namespace(An exception: there  could be multiple namespaces in a source file.) &lt;/p&gt;  &lt;p&gt;Modules are similar to namespaces. You can view that a group of functions are put into a namespace. &lt;/p&gt;  &lt;p&gt;The usage difference between namespaces and modules, in my option, is that namespace is broader concept. E.g. Microsoft.FSharp.Collections names contains a lot of standard data structures, where the manipulating functions for each data structure are put into a module. &lt;/p&gt;  &lt;p&gt;You can also view modules as a class containing only static members. &lt;/p&gt;  &lt;h2&gt;Classes and Interfaces&lt;/h2&gt;  &lt;p&gt;F# interfaces are just like interfaces in C#/Java. Interface is a quite universal concept occurring different programming paradigms. Even in the pure FP Haskell, type classes are similar to interfaces. &lt;/p&gt;  &lt;p&gt;F# classes are different from classes in OO languages. First, F# encourages an immutable programming fashion for classes: you have only one main &lt;strong&gt;constructor, &lt;/strong&gt;once the object is constructed the values remain unchanged. You can also have mutable member fields in a class. The constraints on F# classes are for safer programs, although sometimes causes some inconvenience. &lt;/p&gt;  &lt;p&gt;Besides classes, F# also has &lt;strong&gt;Record&lt;/strong&gt;s, &lt;strong&gt;Enum&lt;/strong&gt;s and &lt;strong&gt;Discrete Union&lt;/strong&gt;s. &lt;/p&gt;  &lt;h2&gt;Extension to existing classes and modules&lt;/h2&gt;  &lt;p&gt;In F#, you can extend an existing .net class by using “type .. with ..” construct:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;System.Net.WebRequest &lt;span style="color:blue;"&gt;with&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:blue;"&gt;member &lt;/span&gt;this.AsyncGetResponse() : Async&amp;lt;System.Net.WebResponse&amp;gt;=&lt;br /&gt;       async {… the implementation }&lt;/pre&gt;(from F# source code: control.fs)&lt;br /&gt;&lt;p&gt;In this piece of code, System.Net.WebRequest is an existing class in .Net. We add a member function AsyncGetResponse into this class. This is like lightweight inheritance by saving from creating a new class.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;The attributes&lt;/h2&gt;In .Net, attributes associate declarative information with the code. Usually they occur in production/formal code. &lt;a href="http://msdn.microsoft.com/en-us/library/aa288454%28VS.71%29.aspx"&gt;Here&lt;/a&gt; is a tutorial.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here I list some commonly used attributes in F#: &lt;/p&gt;[&amp;lt;CompiledName("xxx")&amp;gt;]&lt;br /&gt;[&amp;lt;AutoOpen&amp;gt;]&lt;br /&gt;[&amp;lt;RequireQualifiedAccess&amp;gt;]&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To see a full list, and the exact meaning. No resource is better than the &lt;a href="http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec.pdf"&gt;F# Specification&lt;/a&gt;, Chapter 16. Special Attributes and Types.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-5534508373294399179?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/5534508373294399179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/08/reading-f-projects-part-i-common.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5534508373294399179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5534508373294399179'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/08/reading-f-projects-part-i-common.html' title='Reading F# Projects, Part I: The Common Knowledge'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-8110061152115401405</id><published>2010-07-10T23:58:00.001+08:00</published><updated>2010-07-11T00:02:37.908+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crawler'/><category scheme='http://www.blogger.com/atom/ns#' term='async'/><category scheme='http://www.blogger.com/atom/ns#' term='Flickr'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>F# Async workflow application: a Flickr crawler</title><content type='html'>&lt;p&gt;In data mining, much time is spent on collecting raw data and preprocessing data. Quite a few data mining research tasks require to download data from Internet, e.g. Wikipedia articles from Wikipedia, photos from Flickr, Google search results, reviews from Amazon, etc. &lt;/p&gt;  &lt;p&gt;Usually general purpose crawlers, such as wget, are not sufficiently powerful and specialized in downloading data from Internet. Writing a crawler on our own is often required. &lt;/p&gt;  &lt;p&gt;Recently I am doing an image related research project, in which I need to download a lot of tagged images from Flickr. I am aware that there is a &lt;a href="http://flickrdownloadr.codeplex.com/"&gt;Flickr downloadr&lt;/a&gt;, which uses Flickr API to download images. However 1) it only downloads licensed photos and 2) it cannot download the tags of a photo. Thus I decided to write one myself. &lt;/p&gt;  &lt;p&gt;The input of the is &lt;strong&gt;a tag query&lt;/strong&gt;, e.g. “dog”, &lt;strong&gt;# of photos to download&lt;/strong&gt; and &lt;strong&gt;the disk folder&lt;/strong&gt; to store the downloaded images. &lt;/p&gt;  &lt;p&gt;Because the number of photos is quite big, so downloading them in parallel is critical. In .Net, there are several ways to do parallel computing. For IO intensive tasks, Async workflow is the best. &lt;/p&gt;  &lt;h2&gt;Tutorials for Async workflow&lt;/h2&gt;  &lt;p&gt;As Async workflow is one of the key features of F#, there are quite a few tutorials online for F# Async programming. Providing one more in this blog would be repetious. &lt;/p&gt;  &lt;p&gt;Luke Hoban’s PDC 2009 talk, &lt;a href="http://microsoftpdc.com/Sessions/FT20"&gt;F# for Parallel and Asynchronous Programming&lt;/a&gt;, is very good for beginners. &lt;/p&gt;  &lt;p&gt;Don Syme wrote 3 articles in &lt;a href="http://blogs.msdn.com/b/dsyme/archive/2010/01/10/async-and-parallel-design-patterns-in-f-reporting-progress-with-events-plus-twitter-sample.aspx"&gt;a series&lt;/a&gt;. Highly recommended for experienced F# users!&lt;/p&gt;  &lt;h2&gt;The Flickr crawler&lt;/h2&gt;  &lt;p&gt;To write a crawler for a web site like Flickr, we need to 1) design the downloading strategy and 2) analyze the structures of Flickr. &lt;/p&gt;  &lt;p&gt;My strategy is to use the search query to search images with some specific tags and from the result page(as shown below), the url of each image is extracted, from which the image and its tags are then crawled. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TDiYh4_RNMI/AAAAAAAABAs/2IOuyqeMB_k/s1600-h/flickr-1%5B7%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="flickr-1" border="0" alt="flickr-1" src="http://lh6.ggpht.com/_4bFBK2BKrQI/TDiYiicPc7I/AAAAAAAABAw/Ozc7U9r3x8Y/flickr-1_thumb%5B5%5D.png?imgmax=800" width="473" height="317" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Figure 1. Flickr search result page. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_4bFBK2BKrQI/TDiYjqMaL5I/AAAAAAAABA0/yrSNr74wDdQ/s1600-h/flickr-2%5B9%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="flickr-2" border="0" alt="flickr-2" src="http://lh5.ggpht.com/_4bFBK2BKrQI/TDiYkjH6MPI/AAAAAAAABA4/5H_QkoDsCnM/flickr-2_thumb%5B5%5D.png?imgmax=800" width="474" height="337" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Figure 2. Flickr image page with an image and its tags. &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;So first we need a function to download a web page asynchronously :&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:0ace0f00-7aab-4b82-b687-9dad40934e62" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; fetchUrl (url:string) = &lt;/li&gt; &lt;li&gt;    async {&lt;/li&gt; &lt;li&gt;        &lt;span style="color:#0000ff"&gt;try&lt;/span&gt; &lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; req = WebRequest.Create(url) :?&amp;gt; HttpWebRequest&lt;/li&gt; &lt;li&gt;            req.UserAgent &amp;lt;- &lt;span style="color:#800000"&gt;&amp;quot;Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)&amp;quot;&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;            req.Method &amp;lt;- &lt;span style="color:#800000"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;            req.AllowAutoRedirect &amp;lt;- &lt;span style="color:#0000ff"&gt;true&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;            req.MaximumAutomaticRedirections &amp;lt;- 4;&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let!&lt;/span&gt; response1 = req.AsyncGetResponse()&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; response = response1 :?&amp;gt; HttpWebResponse&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;use&lt;/span&gt; stream = response.GetResponseStream()&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;use&lt;/span&gt; streamreader = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; System.IO.StreamReader(stream)&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;return!&lt;/span&gt; streamreader.AsyncReadToEnd() &lt;span style="color:#008000"&gt;// .ReadToEnd()&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color:#0000ff"&gt;with&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            _ &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#0000ff"&gt;return&lt;/span&gt; &lt;span style="color:#800000"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span style="color:#008000"&gt;// if there&amp;#39;s any exception, just return an empty string&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    }&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;fetchUrl pretends to be a Mozilla browser and can do some redirections if the url is slightly invalid. The current exception handling is very easy – just return the empty string for the web page. Notice that the return type of the function is Async&amp;lt;string&amp;gt;, thus it cannot be used to download images as images are of binary format, not text. &lt;/p&gt;  &lt;p&gt;So the next task is to write a function to download images:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:c1aeaea4-5a91-4c31-aea6-5793e2e8bb79" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; getImage (imageUrl:string) =&lt;/li&gt; &lt;li&gt;    async {&lt;/li&gt; &lt;li&gt;        &lt;span style="color:#0000ff"&gt;try&lt;/span&gt; &lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; req = WebRequest.Create(imageUrl) :?&amp;gt; HttpWebRequest&lt;/li&gt; &lt;li&gt;            req.UserAgent &amp;lt;- &lt;span style="color:#800000"&gt;&amp;quot;Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)&amp;quot;&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;            req.Method &amp;lt;- &lt;span style="color:#800000"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;            req.AllowAutoRedirect &amp;lt;- &lt;span style="color:#0000ff"&gt;true&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;            req.MaximumAutomaticRedirections &amp;lt;- 4;&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let!&lt;/span&gt; response1 = req.AsyncGetResponse()&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; response = response1 :?&amp;gt; HttpWebResponse&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;use&lt;/span&gt; stream = response.GetResponseStream()&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; ms = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; MemoryStream()&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; bytesRead = ref 1&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; buffer = Array.create 0x1000 0uy&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;while&lt;/span&gt; !bytesRead &amp;gt; 0 &lt;span style="color:#0000ff"&gt;do&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                bytesRead := stream.Read(buffer, 0, buffer.Length)&lt;/li&gt; &lt;li&gt;                ms.Write(buffer, 0, !bytesRead)&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;return&lt;/span&gt; ms.ToArray();&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;        &lt;span style="color:#0000ff"&gt;with&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            _ &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#0000ff"&gt;return&lt;/span&gt; Array.create 0 0uy &lt;span style="color:#008000"&gt;// if there&amp;#39;s any exception, just return an empty image&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    }&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Next we write the code to get the url of an image and its tags from an image page(see Figure 2):&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:b4ad7106-0157-42c2-b73a-fce1da392919" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; getBetween (page:string) (head:string) = &lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; len = head.Length&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; idx = page.IndexOf(head)&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; idx2 = page.IndexOf(&lt;span style="color:#800000"&gt;&amp;#39;&amp;quot;&amp;#39;&lt;/span&gt;, idx+len)&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; between = page.Substring(idx+len, idx2 - idx - len)&lt;/li&gt; &lt;li&gt;    between&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; getImageUrlAndTags (page:string) = &lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; header = &lt;span style="color:#800000"&gt;&amp;quot;class=&amp;#92;&amp;quot;photoImgDiv&amp;#92;&amp;quot;&amp;gt;&amp;quot;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; idx = page.IndexOf(header)&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; url = getBetween (page.Substring(idx)) &lt;span style="color:#800000"&gt;&amp;quot;&amp;lt;img src=&amp;#92;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; header2 = &lt;span style="color:#800000"&gt;&amp;quot;&amp;lt;meta name=&amp;#92;&amp;quot;keywords&amp;#92;&amp;quot; content=&amp;#92;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; tagStr = getBetween page header2&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; s = tagStr.Split([|&lt;span style="color:#800000"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;|], System.StringSplitOptions.RemoveEmptyEntries)&lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; tags = s |&amp;gt; Array.map (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; t &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; t.Trim())&lt;/li&gt; &lt;li&gt;    url, tags&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Finally, write a function to work through every search result page, parse the result page and download the images in that result page:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:224a8a9d-ba91-4077-9de7-ece91e8d140d" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px;"&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; getImagesWithTag (tag:string) (pages:int) = &lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; rooturl = &lt;span style="color:#800000"&gt;@&amp;quot;http://www.flickr.com/search/?q=&amp;quot;&lt;/span&gt;+tag+&lt;span style="color:#800000"&gt;&amp;quot;&amp;amp;m=tags&amp;amp;s=int&amp;quot;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    seq {&lt;/li&gt; &lt;li&gt;        &lt;span style="color:#0000ff"&gt;for&lt;/span&gt; i=1 &lt;span style="color:#0000ff"&gt;to&lt;/span&gt; pages &lt;span style="color:#0000ff"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; url = rooturl + &lt;span style="color:#800000"&gt;&amp;quot;&amp;amp;page=&amp;quot;&lt;/span&gt; + i.ToString() &lt;/li&gt; &lt;li&gt;            printfn &lt;span style="color:#800000"&gt;&amp;quot;url = %s&amp;quot;&lt;/span&gt; url&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; page = fetchUrl url |&amp;gt; Async.RunSynchronously&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; imageUrls = getImageUrls page&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; getName (iurl:string) = &lt;/li&gt; &lt;li&gt;                &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; s = iurl.Split &lt;span style="color:#800000"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                s.[s.Length-1]&lt;/li&gt; &lt;li&gt;            &lt;/li&gt; &lt;li&gt;            &lt;span style="color:#008000"&gt;(* images in every search page *)&lt;/span&gt; &lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; images = &lt;/li&gt; &lt;li&gt;                imageUrls &lt;/li&gt; &lt;li&gt;                |&amp;gt; Seq.map (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; url &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; fetchUrl url) &lt;/li&gt; &lt;li&gt;                |&amp;gt; Async.Parallel&lt;/li&gt; &lt;li&gt;                |&amp;gt; Async.RunSynchronously&lt;/li&gt; &lt;li&gt;                |&amp;gt; Seq.map (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; page &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; &lt;/li&gt; &lt;li&gt;                    async {&lt;/li&gt; &lt;li&gt;                        &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; iurl, tags = getImageUrlAndTags page&lt;/li&gt; &lt;li&gt;                        &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; icontent = getImage iurl |&amp;gt; Async.RunSynchronously&lt;/li&gt; &lt;li&gt;                        &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; iname = getName iurl&lt;/li&gt; &lt;li&gt;                        &lt;span style="color:#0000ff"&gt;return&lt;/span&gt; iname, icontent, tags&lt;/li&gt; &lt;li&gt;                    })&lt;/li&gt; &lt;li&gt;                |&amp;gt; Async.Parallel&lt;/li&gt; &lt;li&gt;                |&amp;gt; Async.RunSynchronously&lt;/li&gt; &lt;li&gt;            &lt;span style="color:#0000ff"&gt;yield!&lt;/span&gt; images&lt;/li&gt; &lt;li&gt;        }&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;with a driver function to write all the images into hard disk:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:314aecd5-abeb-4c7b-9fee-a3d83c16e158" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2em; padding: 0 0 0 5px;"&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;let&lt;/span&gt; downloadImagesWithTag (tag:string) (pages:int) (folder:string) = &lt;/li&gt; &lt;li&gt;    &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; images = getImagesWithTag tag pages&lt;/li&gt; &lt;li&gt;    images&lt;/li&gt; &lt;li&gt;    |&amp;gt; Seq.iter (&lt;span style="color:#0000ff"&gt;fun&lt;/span&gt; (name, content, tags) &lt;span style="color:#0000ff"&gt;-&amp;gt;&lt;/span&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color:#0000ff"&gt;let&lt;/span&gt; fname = folder + name&lt;/li&gt; &lt;li&gt;        File.WriteAllBytes(fname, content)&lt;/li&gt; &lt;li&gt;        File.WriteAllLines(fname + &lt;span style="color:#800000"&gt;&amp;quot;.tag&amp;quot;&lt;/span&gt;, tags)&lt;/li&gt; &lt;li&gt;        )&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;We’ve done! A Flickr image crawler in only about 120 lines of code. Let’s download some images!&lt;/p&gt;  &lt;p&gt;downloadImagesWithTag &amp;quot;sheep&amp;quot; 5 @&amp;quot;D:\WORK\ImageData\flickr\sheep\&amp;quot;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4bFBK2BKrQI/TDiYlWJVV-I/AAAAAAAABA8/-agNPrssJXE/s1600-h/flickr-3%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="flickr-3" border="0" alt="flickr-3" src="http://lh3.ggpht.com/_4bFBK2BKrQI/TDiYmJIyrjI/AAAAAAAABBA/NpirJUB8T6I/flickr-3_thumb%5B2%5D.png?imgmax=800" width="436" height="380" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;It only costs less than 5 minutes to download 300 sheep pictures from Flickr. &lt;/p&gt;  &lt;h2&gt;Discussions&lt;/h2&gt;  &lt;p&gt;1. One of the strengths of F# Async programming is its ease for exception handling. In this example, the exception is handled immediately, we can also propagate the exception into an upper level function and handle it there. However, that would require more thinking….&lt;/p&gt;  &lt;p&gt;2. I only parallelly download images in one search page. The program could be modified to parallelly process multiple search result pages, which is done sequentially now. If done in this way, we can see that, we can build a hierarchical parallel program: 1) at the first level, multiple search result pages are processed parallelly and 2) at the second level, images in a research result pages are downloaded parallelly. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-8110061152115401405?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/8110061152115401405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/07/f-async-workflow-application-flickr.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8110061152115401405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8110061152115401405'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/07/f-async-workflow-application-flickr.html' title='F# Async workflow application: a Flickr crawler'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_4bFBK2BKrQI/TDiYiicPc7I/AAAAAAAABAw/Ozc7U9r3x8Y/s72-c/flickr-1_thumb%5B5%5D.png?imgmax=800' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-2499200263712973352</id><published>2010-06-04T22:42:00.001+08:00</published><updated>2010-06-04T22:56:32.751+08:00</updated><title type='text'>A note on efficiency in F#, part I: small things first</title><content type='html'>&lt;p&gt; &lt;/p&gt;  &lt;p&gt;In this blog, I often talk about efficiency aside data mining as efficiency really matters in this field. Previously, we’ve talked about how to use PInvoke and C++/CLI to use C++ code to boost the performance. In this post, I’d like to start a series on F# itself. As a functional programming language, F# programs are kind of high-level, thus harder to reason its performance than imperative programs. Not the big-O thing, it is the constant. This series contains little tips about performance. &lt;/p&gt;  &lt;p&gt;Note that these tips are just facts to let you know. There is no performance guidance that one should follow. There is always a treadoff, high level functional constructs usually are slower than their pure imperative equivalents. However, they are safer and have better abstraction. It usually depends when you really need to do some optimization. E.g. if you could guarantee that your program runs less than 0.5 second, then optimizing it would usually become less meaningful, unless you have another program calling this program a million times. &lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;Also remember that &lt;/p&gt;  &lt;p&gt;"&lt;em&gt;We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil&lt;/em&gt;" (by &lt;a href="http://en.wikipedia.org/wiki/Donald_Knuth"&gt;Donald Knuth&lt;/a&gt;)&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt; &lt;strong&gt;1. Stream IO and Seq module &lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Think about that we need to process a large text file, say, 1TB. Then it is infeasible to load the file into memory. The approach is to treat the text file as a stream and scan this stream to get useful information. &lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:d940adbc-42ee-4c6b-87ea-0d557716bc99" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;open&lt;/span&gt; System.IO&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; readLinesSeq = File.ReadLines&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;.Net 4.0 provides ReadLines, the type of its return value is seq&amp;lt;string&amp;gt;. &lt;/p&gt;  &lt;p&gt;In lower versions of .Net, one needs to implement this function:&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:74125093-6888-4f1b-a0c6-e351606bb331" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; readLines filePath = seq { &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;use&lt;/span&gt; sr = &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; StreamReader (filePath) &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;while&lt;/span&gt; not sr.EndOfStream &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;yield&lt;/span&gt; sr.ReadLine () &lt;/li&gt; &lt;li&gt;}&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;Once we have this function, we can process it as as a sequence.&lt;/p&gt;  &lt;p&gt;While prototyping a model, we may make mistakes, and we also know that scanning the whole file would cost a lot of time. If we find an error after scanning the whole scanning, that would waste a lot of time. So we would like to take the first few lines of the file as a small test set for the debugging purpose. By treating a file as a sequence, we could simply use&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:e26da00c-dec5-45d1-a491-2d048f79ff9e" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; lines100 = &lt;span style="color: rgb(128, 0, 0);"&gt;"filename"&lt;/span&gt; |&amp;gt; readLines |&amp;gt; Seq.take 100&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;to get a small test set. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.technet.com/b/apg/archive/2006/11/04/dealing-with-terabytes-with-f.aspx"&gt;Here&lt;/a&gt; is an quite old post in 2006. At that time, there seemed to be no Seq module. The idea is the same. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;2. Loop, Seq module and the tail-recursive version of “while”&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;One of my first F# programs is a prime number test function. I first used an ugly for loop with mutable variables:&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:b5ac7c00-3b41-4aad-adb5-a557e2cb2903" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; isPrime1 n = &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; m = int(sqrt (float n))&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;mutable&lt;/span&gt; flag = &lt;span style="color: rgb(0, 0, 255);"&gt;true&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; i=2 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; m &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; n % i = 0 &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt; &lt;/li&gt; &lt;li&gt;            flag &amp;lt;- &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    flag&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;You can see that I clearly didn’t know how to early stop/break the loop when flag is assigned to false. Because I could not find break, so I crazily use exception to break out the loop:-)&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:8155d702-1abc-4f7f-8608-429b51a55663" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2.5em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;exception&lt;/span&gt; Break &lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; isPrime1' n = &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; m = int(sqrt (float n))&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;mutable&lt;/span&gt; flag = &lt;span style="color: rgb(0, 0, 255);"&gt;true&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;try&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;for&lt;/span&gt; i=2 &lt;span style="color: rgb(0, 0, 255);"&gt;to&lt;/span&gt; m &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; n % i = 0 &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt; &lt;/li&gt; &lt;li&gt;                flag &amp;lt;- &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                raise Break&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;with&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        _ &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; ()&lt;/li&gt; &lt;li&gt;    flag&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;And based on my FP experience on Scheme, I immediately wrote a tail-recursive function, which behaves like a “while” loop:&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:4c79394a-70fd-4726-8700-dd0cf0f24b85" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; isPrime2 n = &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; m = int(sqrt (float n))&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;rec&lt;/span&gt; loop div = &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; div &amp;gt; m &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;true&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;elif&lt;/span&gt; n % div = 0 &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;else&lt;/span&gt; loop (div+1)&lt;/li&gt; &lt;li&gt;    loop 2&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;I can also explicitly use a while loop with a mutable flag:&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:9059711a-ff9b-4de5-9f93-1d172f5420d0" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; isPrime3 n = &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; m = int(sqrt (float n))&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;mutable&lt;/span&gt; flag = &lt;span style="color: rgb(0, 0, 255);"&gt;true&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;mutable&lt;/span&gt; div = 2&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;while&lt;/span&gt; flag &amp;amp;&amp;amp; div &amp;lt;= m &lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; n % div = 0 &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            flag &amp;lt;- &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        div &amp;lt;- div + 1&lt;/li&gt; &lt;li&gt;    flag&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;And I finally find the elegant way using Seq:&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:abfc0ff5-9956-411a-a34a-857cec3cf562" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; isPrime n = {2..int(sqrt (float(n)))} |&amp;gt; Seq.forall (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; i&lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt;n%i&amp;lt;&amp;gt;0)&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;However, the last version would be slower than others except for the first one. Because a sequence is actually an object implements IEnumerable interface. If the object currently points to 10, to get 11, it needs to call a Next function to get it. There is definitely some overhead here. &lt;/p&gt;  &lt;p&gt;So pay attention when you use Seq to do the job of a for loop or while loop when the loop is executed for a huge number of times.  &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;3. List and Array&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;F# lists are single linked lists. Thus, Lists use more memory as it needs to keep a next pointer in every node. Lists are also slow. Let’s do a test:&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:1da0fb2e-f56d-40a7-9669-e8d40b3ee219" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; n = 10000000&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; a = List.init n (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; i&lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; i)&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; b = Array.init n (&lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; i&lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; i)&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;creating list a uses about 2.2 seconds and about 200MB memory, while creating array b only uses 0.12 second and about 40MB memory, which equals our estimate n * 4 bytes/int ~= 40MB. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;4. Access lists, arrays and sequences &lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;We know that the three modules &lt;a href="http://msdn.microsoft.com/en-us/library/ee353738%28v=VS.100%29.aspx"&gt;List&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ee370273.aspx"&gt;Array&lt;/a&gt; and &lt;a href="http://msdn.microsoft.com/en-us/library/ee353635%28v=VS.100%29.aspx"&gt;Seq&lt;/a&gt; have similar functions. E.g. all of them have the function &lt;strong&gt;map&lt;/strong&gt;, which applies a function to the existing list, array or sequence to generate a new one. However, the three maps are implemented differently, with array most efficiently, list second, the most general seq the last:&lt;/p&gt;  &lt;div style="padding: 0px; margin: 0px; display: inline; float: none;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:faff392c-3085-4820-89bd-d2aa59017556" class="wlWriterEditableSmartContent"&gt; &lt;div   style="border: 1px solid rgb(0, 0, 128); color: rgb(0, 0, 0);font-family:'Courier New',Courier,Monospace;font-size:10pt;"&gt; &lt;div style="background: none repeat scroll 0% 0% rgb(221, 221, 221); max-height: 300px; overflow: auto;"&gt; &lt;ol style="background: none repeat scroll 0% 0% rgb(255, 255, 255); margin: 0pt 0pt 0pt 2.5em; padding: 0pt 0pt 0pt 5px;"&gt; &lt;li&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;// form F# source code: local.fs &lt;/span&gt;&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;// note: the actuall implementation for list is in local.fs, not in list.fs&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; map f x = &lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;match&lt;/span&gt; x &lt;span style="color: rgb(0, 0, 255);"&gt;with&lt;/span&gt;&lt;/li&gt; &lt;li&gt;    | [] &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; []&lt;/li&gt; &lt;li&gt;    | [h] &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; [f h]&lt;/li&gt; &lt;li&gt;    | (h::t) &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; &lt;/li&gt; &lt;li&gt;        &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; cons = freshConsNoTail (f h)&lt;/li&gt; &lt;li&gt;        mapToFreshConsTail cons f t&lt;/li&gt; &lt;li&gt;        cons&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;// form F# source code: array.fs &lt;/span&gt;&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; map (f: 'T &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt; 'U) (array : 'T[]) : 'U[]=&lt;/li&gt; &lt;li&gt;    checkNonNull &lt;span style="color: rgb(128, 0, 0);"&gt;"array"&lt;/span&gt; array&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; inputLength = array.Length&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked inputLength&lt;/li&gt; &lt;li&gt;    Parallel.For(0, inputLength, &lt;span style="color: rgb(0, 0, 255);"&gt;fun&lt;/span&gt; i &lt;span style="color: rgb(0, 0, 255);"&gt;-&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;        result.[i] &amp;lt;- f array.[i]) |&amp;gt; ignore&lt;/li&gt; &lt;li&gt;    result&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;// form F# source code: seq.fs &lt;/span&gt;&lt;/li&gt; &lt;li&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;let&lt;/span&gt; map f (e : IEnumerator&amp;lt;_&amp;gt;) : IEnumerator&amp;lt;_&amp;gt;=&lt;/li&gt; &lt;li&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;upcast&lt;/span&gt; &lt;/li&gt; &lt;li&gt;        { &lt;span style="color: rgb(0, 0, 255);"&gt;new&lt;/span&gt; MapEnumerator&amp;lt;_&amp;gt;() &lt;span style="color: rgb(0, 0, 255);"&gt;with&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;member&lt;/span&gt; this.DoMoveNext (curr : byref&amp;lt;_&amp;gt;) =&lt;/li&gt; &lt;li&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt; e.MoveNext() &lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                    curr &amp;lt;- (f e.Current)&lt;/li&gt; &lt;li&gt;                    &lt;span style="color: rgb(0, 0, 255);"&gt;true&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                &lt;span style="color: rgb(0, 0, 255);"&gt;else&lt;/span&gt;&lt;/li&gt; &lt;li&gt;                    &lt;span style="color: rgb(0, 0, 255);"&gt;false&lt;/span&gt;&lt;/li&gt; &lt;li&gt;            &lt;span style="color: rgb(0, 0, 255);"&gt;member&lt;/span&gt; this.Dispose() = e.Dispose()&lt;/li&gt; &lt;li&gt;        }&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;We can see that the Array.map is actually implemented using ParallelFor. The most general implementation is Seq.map based one the IEnumerable interface in .Net, which is relatively inefficient compared to a for loop. &lt;/p&gt;  &lt;p&gt;However, by using Array.map or List.map, we create another array or list. While Seq.map only creates an auxiliary sequence, which costs nearly no memory!&lt;/p&gt;  &lt;p&gt;So usually, I use Seq.map to map arrays or lists to keep memory usage small. When performance really matters, I use &lt;strong&gt;array&lt;/strong&gt; and &lt;strong&gt;for&lt;/strong&gt; loop, i.e. I write imperative code. In my experience, writing imperative code in F# sometimes still introduces some runtime errors, while this is rare in pure functional code.&lt;/p&gt;  &lt;p&gt;When I only need to iterate an array, I will directly use Array.iter since it is efficient, and use no extra memory.&lt;/p&gt;  &lt;p&gt;All in all: performance costs. Do some quick estimation before you write imperative code. Also know the low-level implementation of your daily-use libraries is critical for you to estimate the performance.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;5. Access F# matrices and vectors&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The Vector&amp;lt;’T&amp;gt; type is implemented using 1-D array and the Matrix&amp;lt;’T&amp;gt; type is implemented using 2-D array for dense case and row-sparse for sparse case. Details are in &lt;a href="http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-v.html"&gt;a matrix series post&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;Let m be a matrix, we could also use m.[i,j] to access the element, which is actually a function call to the the internal array. Thus there is some overhead. Some profiling is reported in &lt;a href="http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iv.html"&gt;a previous post&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;The way to get rid of this kind of overhead is to program on the internal arrays directly. As in &lt;a href="http://stackoverflow.com/questions/2970731/parallelizing-the-element-wise-multiplication-of-two-matrices-in-f/2970811#2970811"&gt;this answer in Stackoverflow&lt;/a&gt;. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-2499200263712973352?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/2499200263712973352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/06/note-on-efficiency-in-f-part-i-small.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/2499200263712973352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/2499200263712973352'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/06/note-on-efficiency-in-f-part-i-small.html' title='A note on efficiency in F#, part I: small things first'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-7606967637915492015</id><published>2010-05-04T22:51:00.001+08:00</published><updated>2010-05-14T08:46:05.870+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='data mining'/><title type='text'>Why F# is the language for data mining</title><content type='html'>&lt;p&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/fsharp/default.aspx"&gt;F#&lt;/a&gt; is a newly released language in Visual Studio 2010, as the 4-th language in .Net platform officially supported by Microsoft, aside with C#, VB.Net and C++/CLI. It has been a research project within Microsoft Research Cambridge for more than 5 years. Its origin could date back to the &lt;a href="http://en.wikipedia.org/wiki/ML_%28programming_language%29"&gt;ML language&lt;/a&gt; designed by Turning Award winner &lt;a href="http://en.wikipedia.org/wiki/Robin_Milner"&gt;Robin Milner&lt;/a&gt; in 1970s.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;As a new yet old static-typed functional language designed for .Net platform, it will spread quickly in the coming years. It is such enjoyment to explore its usage in data mining, or more broadly, statistical computing area that I now share some my personal experience using it by trying to answer one question:&lt;/p&gt;&lt;p style="font-style: italic;"&gt;&lt;span style="font-size:130%;"&gt;Why F# is the language for data mining.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This article is divided into 3 sections:&lt;/p&gt;&lt;p&gt;1. a review of data mining environments and a feature summary from them.&lt;/p&gt;&lt;p&gt;2. the answer. I list 7 reasons with 2 short examples.&lt;/p&gt;&lt;p&gt;3. a preview of a data mining library in F#, which will be open sourced this summer.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Before we go to the answer of this question, let’s see some existing data mining environments.&lt;/p&gt;  &lt;h2&gt;Data mining environments&lt;/h2&gt;  &lt;p&gt;Commercial:&lt;/p&gt;  &lt;p&gt;SAS and SPSS are the main commercial data mining software. SAS has a mature macro language, also a reasonable GUI support. SPSS has a good GUI support. Their data mining algorithms are written in C/C++. The user doesn’t need to know about the underlying languages (C/C++), the data mining API is provided by a macro language (SAS) or GUI (SPSS). Using a macro language or GUI greatly eases users to perform data mining tasks. &lt;/p&gt;  &lt;p&gt;There are also other products, e.g. the decision tree family developed by Salford. &lt;/p&gt;  &lt;p&gt;Matlab is perhaps the most popular software in data mining community, especially in machine learning community. Although Matlab is not free, it is easily available in schools and research institutions. There are a lot of free programs in Matlab to perform data mining tasks. Matlab is so popular that putting an data mining algorithm name + "matlab" into Google gets what you need. The other reason is that the matrix operations in Matlab are efficient and convenient considering that the data tables in data mining are either dense or sparse matrices.&lt;/p&gt;  &lt;p&gt;Free/Open source:&lt;/p&gt;  &lt;p&gt;There are quite a lot of open source software of different quality. Here’s a list I am interested:&lt;/p&gt;  &lt;p&gt;1. &lt;a href="http://www.r-project.org/"&gt;R&lt;/a&gt;. R is for statistics. The core part of R contains the R languages and the stable statistical procedures. The &lt;a href="http://cran.r-project.org/web/views/MachineLearning.html"&gt;data mining part&lt;/a&gt; is a set of external packages. Most of the data mining models are available, e.g. Decision Trees, Neural Network, SVM, etc. R is a very capable language, however, it is not popular in data mining community. For data mining and machine learning, Matlab is more popular than R. R core does not support sparse data, thus a lot of extensions do not consider this support, which is extremely important. As in data mining, a lot of datasets are sparse, a tool that has sparse and large scale design in the beginning is very critical for performing data mining. &lt;/p&gt;  &lt;p&gt;2. &lt;a href="http://www.cs.waikato.ac.nz/ml/weka/"&gt;Weka&lt;/a&gt;. Weka is the most famous open source software in data mining and machine learning field. If data mining has three perspectives: database, machine learning and statistics[2]. Weka is from the second. Weka is  widely used in data mining courses, in which instructors are able to show a lot of data mining algorithms to the students, turning the formulas in the book into several mouse clicks and the resulting figures and graphs. Who use Weka for serious data analysis? As I know, most people only use Weka’s decision tree implementation J48, because  decision tree is really useful sometimes and Weka is handy to run to get the tree and its results are close to the standard C4.5's.  But its usage is often on dense data set, as building a tree on sparse data sets costs a huge amount of memory in Weka. Weka does support sparse dataset, however, the performance is very bad, the speed is slow, the memory usage is unacceptable. The design in Weka is too general, thus sacrifices the speed.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Weka is written in Java. So the extensions to Weka, e.g. a different boosting scheme for trees, are usually written in Java. This kind of task is better done in a language like R or Matlab, i.e. a interpreted script language, not in Java. There are interpreted languages in JVM, most notably, Scala and Clojure. Calling Weka from Scala or Clojure will be a good idea. &lt;/p&gt;  &lt;p&gt;3. &lt;a href="http://www.ailab.si/orange/"&gt;Orange&lt;/a&gt;. Orange is written in Python and C++. All data mining algorithms are coded in C++ for performance’s sake. Python is used as the script language to call the algorithms and the language to build the GUI. Using Python in data mining is a great idea. It is interpreted, an concise language to read and write, easy binding to C/C++. Orange only supports dense data set. &lt;/p&gt;  &lt;p&gt;4. &lt;a href="http://lush.sourceforge.net/"&gt;Lush&lt;/a&gt;. Designed by Prof. &lt;a href="http://yann.lecun.com/"&gt;Yann LeCun&lt;/a&gt;. He is a big figure in machine learning, a definite expert. There are not so many researchers, who write great programs, and write great papers at the same time. Prof. LeCun is one of the few. Lush is a Lisp environment with bindings to algorithms written in C/C++. The neural network in it is mature. Lisp is good, very expressive. However, nowadays, not many people write it.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;after we have seen a set of data mining software, we can draw a conclusion on&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;the most favored features of a data mining environment&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;1. Data mining algorithms are built in. The implementation should reliable and efficient. It should supports dense and sparse datasets equally well. It should also be robust, e.g. effectively handle missing values, large datasets, etc.&lt;br /&gt;&lt;br /&gt;2. script language and iterative shell. The script language could be used to do &lt;span style="font-style: italic;"&gt;data preprocessing&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;calling data mining procedures&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;visualization&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;report &lt;/span&gt;easily.&lt;br /&gt;&lt;br /&gt;3. Math: Matrix and vector (both sparse and dense), linear algebra, common numerical optimizations. Sometimes, we need to design new algorithms or modified existing algorithms to suite one specific data mining application. In this case, these math tools are really important, e.g. a simple logistic regression could be done in several lines of code if these math modules are available.&lt;br /&gt;&lt;br /&gt;4. GUI is preferred, but not a must. GUI is good for data exploration and visualization, e.g. Excel is used a front end for a lot of Business Intelligence solutions for its great GUI and stability. When performing data mining, most of the time, we don't need GUI (just as when we use Matlab, we seldom use its START menu and prefer command line). &lt;/p&gt;&lt;p&gt;5. A integrated system. You use tool A do download the data, use tool B to do clean the data, use tool C to do some matrix operations on the data, finally you get a dataset and put it into an SVM command line tool. Not the end, you still need to parse the ouput of the SVM to get the result. And think about you need to repeat this process for several times. What's wrong here? Tools are scattered. The Unix philosophy "do one thing and do it well" does not apply here because the data structure from one thing to the other is very complex. This is why we need a integrated system, at least data processing and model building are connected seamlessly, like in Matlab.&lt;br /&gt;&lt;br /&gt;Weka has all the 5 points. However, none of them are very good. E.g. Java is hard to be used as a script language as you need to write a lot of lines of code and compile them to perform an easy task, and using Java also requires you to be very familiar with Weka's java classes, which is just huge. Java could also be used to do data preprocessing, however, few use it for this purpose. Python, Perl or F# are more appropriate.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;F#&lt;/h2&gt;  &lt;p&gt;Following lists the notable features, which I think are helpful for data mining. &lt;/p&gt;  &lt;p&gt;1. Succinct, static typing at the same time. These two features are combined together. When we write programs, we need to think about the programming logic and the programs, e.g. write the loop, define a variable, &lt;em&gt;write the type for it, &lt;/em&gt;etc. Programming logic is the core thing, but every language has a syntax to obey, so we need to write a lot of other things, which could be distractive during programming because you need to think about more things at the same time. More succinct means you don’t need to write so many `declarations’ for your programs. Python is also succinct, however it is not static typing, which means typing checking is done during runtime. Dynamic typing is slow. In a lot of cases, F# does not need to write types, its other syntax is also succinct, it is also static typing at the same time. Combining these two together means we can focus on the programming logic and at the same time keep the performance. &lt;/p&gt;  &lt;p&gt;2. Functional and programming style. F# is functional, thus the A, B, C of a functional programming also applies to F#. Here A could be ‘function as the first class or high order functions’, which means you could write:&lt;/p&gt;  &lt;p&gt;a |&amp;gt; split |&amp;gt; Seq.filter notStopWord |&amp;gt; Seq.map stemmer&lt;/p&gt;  &lt;p&gt;to perform string split, filtering using a self defined filter and doing word stemming in a single line!&lt;/p&gt;  &lt;p&gt;In an OOP language, this would require to use a pipe line design pattern, which is far more complex than high order functions here. &lt;/p&gt;  &lt;p&gt;3. Functional and parallel. A function is pure if it does not change anything outside the function, and every time you pass the same parameter into it, it gets the same result (this is a weaker version of purity). Based on this, we can easily parallel pure functions. In data mining, most of cases, we can simply split the data into several parts to get parallel execution. &lt;/p&gt;  &lt;p&gt;As an example, following is a piece of code to perform multiple k-means clusterings at the same time:&lt;/p&gt;  &lt;p&gt;if isParallel then&lt;br /&gt;let dorepeats =&lt;br /&gt;{0..restart-1} |&amp;gt; PSeq.map (fun _ -&amp;gt; doClustering(ds, k, maxiter, init, algo) :?&amp;gt; kmeansResult)&lt;br /&gt;dorepeats |&amp;gt; PSeq.toList |&amp;gt; List.min :&amp;gt; IClusterResult&lt;br /&gt;(* use async&lt;br /&gt;let dore =&lt;br /&gt;{0..restart-1} |&amp;gt; Seq.map (fun _ -&amp;gt; async { return doClustering(ds, k, maxiter, init, algo) :?&amp;gt; kmeansResult })&lt;br /&gt;dore |&amp;gt; Async.Parallel |&amp;gt; Async.RunSynchronously |&amp;gt; Array.min :&amp;gt; IClusterResult&lt;br /&gt;*)&lt;br /&gt;else&lt;br /&gt;//printfn "no parallel"&lt;br /&gt;let dorepeats =&lt;br /&gt;{0..restart-1} |&amp;gt; Seq.map (fun _ -&amp;gt; doClustering(ds, k, maxiter, init, algo) :?&amp;gt; kmeansResult)&lt;br /&gt;dorepeats |&amp;gt; Seq.min :&amp;gt; IClusterResult&lt;/p&gt;  &lt;p&gt;Note that the difference between sequential code and parallel code is minor.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4. OOP. At a high level of designing an application, the design patterns come from the application itself. A data mining application has its own working patterns. Design patterns are what OOP is good at. F# provides limited, but concise support for OOP. E.g. the following is the interface for a learner and predictor:&lt;/p&gt;&lt;p&gt;type IPredictor =&lt;br /&gt;abstract member Predict: gvector -&gt; int&lt;br /&gt;abstract member Predict: datavecs -&gt; int array&lt;br /&gt;abstract member Predict: dataset2 -&gt; float * (int array)&lt;/p&gt;&lt;p&gt;type IProbPredictor =&lt;br /&gt;abstract member Predict: gvector -&gt; int * (float array)&lt;br /&gt;abstract member Predict: datavecs -&gt; (int array) * (float array array)&lt;br /&gt;abstract member Predict: dataset2 -&gt; float * (int array) * (float array array)&lt;br /&gt;&lt;/p&gt;type ILearner =&lt;br /&gt;abstract member Learn: dataset2 * parameters -&gt; IPredictor&lt;p&gt;A classification algorithm has two components, a learner and predictor. All learners implement ILearner interface. All predictors implement the IPredictor interface,some predictors also implement the IProbPredictor  interface if it supports probability output. Because the code is so concise, I can change this interface more easily than in C#.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;5. Interpret language. This is simply a must! Everyone wants to write a single line to perform data loading and model building together and see the result immediately.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;6. .Net! If F# has the above 5, but without this one, it will not be successful. Tied with .Net has a lot of advantages, most notably a) a stable environment and a lot of stable libraries, b) writing components in C# when needed, c) the components in F# could be used by other .Net languages with no effort d) Visual Studio 2008 and 2010 are very good IDEs and finally e) Microsoft supports it, the `evil company’ is more responsible for its products than others and at last f) the performance of .Net is very good.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-size:130%;"&gt;7. &lt;strong&gt;IO and preprocessing&lt;/strong&gt;! &lt;/span&gt;This point is subsumed in previous points. However it is so &lt;span style="font-weight: bold;"&gt;important&lt;/span&gt; that I separate it out. A lot of software simply assume that data mining is the processing applying an algorithm to a dataset. This is WRONG! It is said that &lt;span style="font-weight: bold;"&gt;80% of the data mining work is to preparing the data to get a dataset&lt;/span&gt;[1]. To prepare the data, we need to do a lot of IOs, from Internet, databases, xml files etc. .Net provides the most stable yet simple way to do these tasks. F# language is also well suited to perform data preprocessing. As an interpreted language, we can test and see the result immediately.&lt;/p&gt;&lt;p&gt;I use two examples from the code in my data mining library as examples to show F#'s strength.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Example 1. &lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;Wrod Tokenizer. &lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The following code is the Tokenizer module for text mining. It is so short, yet can perform various tokenizing tasks, removing stop words, doing stemming, bi-gram etc.. The conciseness comes from F# as a functional language and .Net library.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;module Tokenizer =&lt;br /&gt; let sep = [|' '; '\r'; '\n'; '-'; '.'; ',' ; '\t'; '!'; '?'; '\'' |]&lt;br /&gt;&lt;br /&gt; let unigramToken (s:string) =&lt;br /&gt;         s.Split(sep, StringSplitOptions.RemoveEmptyEntries) |&gt; Seq.filter (fun w -&gt; not (isStopWord w)) |&gt; Seq.map (fun s -&gt; s.ToLower())&lt;br /&gt;&lt;br /&gt; let unigramTokenWithStem s = s |&gt; unigramToken |&gt; Seq.map stemmer&lt;br /&gt;&lt;br /&gt; let bigramToken (s:string) =&lt;br /&gt;     let uni = s.Split(sep, StringSplitOptions.RemoveEmptyEntries)&lt;br /&gt;         let bi = seq { for i=0 to uni.Length-2 do yield (uni.[i]+uni.[i+1]) }&lt;br /&gt;         bi |&gt; Seq.filter (fun w -&gt; not (isStopWord w)) |&gt; Seq.map (fun s -&gt; s.ToLower())&lt;br /&gt;&lt;br /&gt; let bigramTokenWithStem s = s |&gt; bigramToken |&gt; Seq.map stemmer&lt;br /&gt;&lt;br /&gt; let defaultToken = unigramTokenWithStem&lt;br /&gt;&lt;/p&gt;&lt;p style="font-weight: bold;"&gt;&lt;span style="font-size:130%;"&gt;Example 2. Parallel Tasks&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;You probably see similar code before as an example of the &lt;a href="http://msdn.microsoft.com/en-us/library/dd233250.aspx"&gt;F# asynchronous programming&lt;/a&gt;. Here I changed the 'example' code to let it disguises as a Mozilla browser(this is useful when you try to crawling Google search results), also it does some retries and support exception handling. The code is so short!! Using it downloading 10000 posts (I need some special posts from a question answer forum for my research) &lt;span style="font-weight: bold;"&gt;in parallel&lt;/span&gt; costs only half an hour.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;let fetchUrl (url:string) =&lt;br /&gt;async {&lt;br /&gt;       try&lt;br /&gt;              let req = WebRequest.Create(url) :?&gt; HttpWebRequest&lt;br /&gt;              req.UserAgent &lt;- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)";             req.Method &lt;- "GET";             req.AllowAutoRedirect &lt;- true;&lt;/p&gt;&lt;p&gt;         req.MaximumAutomaticRedirections &lt;- 4; &lt;/p&gt;&lt;p&gt;         let! response1 = req.AsyncGetResponse()             let response = response1 :?&gt; HttpWebResponse&lt;br /&gt;              use stream = response.GetResponseStream()&lt;br /&gt;              use streamreader = new System.IO.StreamReader(stream)&lt;br /&gt;              return! streamreader.AsyncReadToEnd() // .ReadToEnd()&lt;br /&gt;        with&lt;br /&gt;              _ -&gt; return "" // if there's any exception, just return an empty string&lt;br /&gt;                               // checked and found that pages that cannot be retrieved mainly because the pages are already deleted&lt;br /&gt;                               // not because of the downloader&lt;br /&gt;}&lt;br /&gt;&lt;/p&gt;&lt;p&gt;let getPages pageIds rooturl =&lt;br /&gt;pageIds |&gt; Seq.map (fun id -&gt; let name = rooturl + id.ToString() in fetchUrl name) |&gt; Async.Parallel |&gt; Async.RunSynchronously&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;A data mining library in F#&lt;/h2&gt;  &lt;p&gt;In the past month, I have written a prototype of an incomplete data mining system in F#. The more I write, the more I feel that it is the language for data mining. The library will be &lt;span style="font-weight: bold;"&gt;open sourced&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;this summer&lt;/span&gt; after functionality completion and testing. I’d like to pronounce some highlights here:&lt;/p&gt;  &lt;p&gt;&lt;span style="font-weight: bold;"&gt;1. Datasets and IO.&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Dense and Sparse datasets are equally support! Various data formats are supported, csv files, weka's ARFF format, libsvm/svm light data format, Excel table, for both read and save. The library does not have its own data format. Processed data are stored in different formats for different needs. Using a built-in database will be considered in the future, e.g. the light-weight &lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;There are a set of preprocessing functions for datasets, e.g.,  filling missing values, feature value normalization, etc.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="font-weight: bold;"&gt;2. Data mining algorithms.&lt;/p&gt;&lt;p&gt;A few State-of-the-art data mining algorithms are implemented:&lt;/p&gt;&lt;p&gt;classification: decision tree family, logistic regression, support vector machines (by compiling &lt;a href="http://www.csie.ntu.edu.tw/%7Ecjlin/libsvm/"&gt;libsvm &lt;/a&gt;to .Net using C++/CLI), neural networks, Naive Bayes, KNN.&lt;/p&gt;&lt;p&gt;regression: simple least square linear regression, neural networks and SVM(using libsvm).&lt;br /&gt;&lt;/p&gt;&lt;p&gt;clustering: k-means and its variants, EM on Gaussian Mixture Model, spectral clustering&lt;br /&gt;&lt;/p&gt;&lt;p&gt;dimension reduction: SOM, PCA, LDA.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;feature selection: Mutual Information, decision trees, logistic regression with Wald test.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;MISC: Probability Latent Semantic Analysis(PLSA), co-clustering  &lt;/p&gt;&lt;p&gt;association rule and sequence mining: I have a plan in mind, but the actual implementation may delay.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The library is mainly for data mining, not for machine learning research. So only the most useful models are implemented. I hope after several years, the they will be very stable with few bugs. Trying to make it big would be dangerous. E.g. Weka is big in the sense of its algorithms, however, the standard statistical and data mining workhouse, logistic regression, is badly implemented there. If even logistic regression cannot apply to a modest text classification problem, what shall we expect Weka's other algorithms?&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Also, implement a lot of immature models is less meaningful for practical data mining. E.g. the &lt;a href="http://msdn.microsoft.com/en-us/library/ms175595.aspx"&gt;data  mining module&lt;/a&gt; in SQL Server 2008 only implements a few  state-of-the-art models. The novel algorithms are suitable to be implemented on demand for a specific application or dataset.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-weight: bold;"&gt;3. Parallel.&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Some algorithms have parallel implementations in algorithm level, e.g. k-means clustering. At a higher level, I define a set of tasks using the type systems in F#, e.g. the classification task is defined as&lt;/p&gt;&lt;p&gt;    type classificationEvaluationTask =&lt;br /&gt;  | CrossValidation of int * dataset2 * ILearner * parameters&lt;br /&gt;  | RandomSplit of float * dataset2 * ILearner * parameters&lt;br /&gt;  | TrainTest of dataset2 * dataset2 * ILearner * parameters&lt;br /&gt;  | LeaveOneOut of dataset2 * ILearner * parameters&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Data mining tasks in the library run in parallel on default, e.g. performing a classification task and a clustering task at the same time, or, doing cross validation on different parameters to choose the best parameter of a model at the same time. A major technique to boost the performance of classifiers is &lt;a href="http://en.wikipedia.org/wiki/Ensemble_learning"&gt;ensemble learning&lt;/a&gt;, which bags a lot of models together. Parallel really helps a lot in this kind of learning task. Of course, parallel programming could be done in C++/Java using threads too. The advantage of using F# is that the interface is very easy to use, parallel executing various tasks at the same time needs only a single line. &lt;/p&gt;  &lt;p&gt;&lt;span style="font-weight: bold;"&gt;4. A service provider design pattern for data mining algorithms.&lt;/span&gt; An algorithm, e.g. KNN will have several implementations, F# implementation  is called on default, the user could also start a service provider and call the implementation of that provider. A candidate for such a wrapper is &lt;a href="http://opencv.willowgarage.com/wiki/"&gt;OpenCV&lt;/a&gt;’s machine learning component for its robustness, speed and friendly license.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ML library in OpenCV only supports dense dataset as machine learning tasks in CV usually use dense data format. I haven't found a good  service provider for sparse data sets yet.  But for large scale sparse data sets, the common practice is to use SVM, Logistic regression and tree family, which I already have code in C/C++.&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight: bold;"&gt;5. Visualization.&lt;/span&gt; A limited support is already done by extending&lt;span style="text-decoration: underline;"&gt; &lt;/span&gt;&lt;a href="http://blogs.msdn.com/lucabol/default.aspx"&gt;Luca Bolognese' code snippet&lt;/a&gt;. The visualization is mainly for understanding the data and diagnosing the algorithms, e.g. plotting the scatter of two columns of a data table, drawing the objective values through iterations. For high quality visualization, choose Mathematica, Matlab, R or  Excel:)&lt;br /&gt;&lt;/p&gt;&lt;span style="font-weight: bold;"&gt;6. Math library.&lt;/span&gt; It is a pity that there's no stable, yet free math library in .Net. For data mining, a math library is really important. I use the matrix and vector implementation in &lt;a href="http://fsharppowerpack.codeplex.com/"&gt;F#'s PowerPack&lt;/a&gt;. The matrix type in PowerPack supports both dense and sparse matrix, however sparse vector is not implemented. So I implemented the sparse vector myself. &lt;a href="http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-v.html"&gt;PowerPack now deprecates its math provider, which I pick up and use as the linear algebra module&lt;/a&gt;. So actually, I made little effort to get matrix supported.&lt;br /&gt;&lt;br /&gt;Other math functions are implemented on demand. Some gradient based optimization solvers are implemented to solve linear regression and logistic regression, e.g. conjugate gradient decedent and LBFGS, which are two state-of-the-art gradient based solvers. Currently I implemented them in F#, the performance is modest compared to my C++ implementation. Code tuning is yet to be done. Some probability distributions are implemented for EM and Naive Bayes. Some important statistical algorithms, e.g., various tests, are also considered to be implemented in the future.&lt;br /&gt;&lt;div align="justify"&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Acknowledgment&lt;/span&gt;&lt;br /&gt;I'd like to thank Haipeng Zhang for reviewing this article for me.&lt;br /&gt;&lt;br /&gt;[1] S. Zhang, C.  Zhang, and Q. Yang. &lt;/span&gt;&lt;a href="http://cs.nju.edu.cn/zhouzh/zhouzh.files/course/dm/reading/reading03/zhang_aai03.pdf"&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;Data preparation for data mining&lt;/span&gt;&lt;/a&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;. &lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;&lt;i&gt;Applied      Artificial Intelligence&lt;/i&gt;&lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;,      2003, 17(5-6): 375-381.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="justify"&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;[2] Z.-H. Zhou. &lt;/span&gt;&lt;a href="http://cs.nju.edu.cn/zhouzh/zhouzh.files/course/dm/reading/reading01/zhou_aij03.pdf"&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;Three perspectives of data mining&lt;/span&gt;&lt;/a&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;. &lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;&lt;i&gt;Artificial      Intelligence&lt;/i&gt;&lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;, 2003,  143(1):      139-146.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-7606967637915492015?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/7606967637915492015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/05/why-f-is-language-for-data-mining.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7606967637915492015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7606967637915492015'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/05/why-f-is-language-for-data-mining.html' title='Why F# is the language for data mining'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-1283127462835779761</id><published>2010-04-10T14:34:00.001+08:00</published><updated>2010-04-10T15:31:26.090+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='sparse matrix'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='svd'/><category scheme='http://www.blogger.com/atom/ns#' term='pinvoke'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Matrix and linear algebra in F#, Part V: Sparse matrix implementation in PowerPack, and PInvoke a large scale SVD library as an application.</title><content type='html'>&lt;p&gt;In the previous posts of this series. We mainly talked about dense matrices in F#:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-i-f.html"&gt;Part I: a tutorial on using matrix type in F#, mainly focused on dense matrix.&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-ii.html"&gt;Part II: doing dense matrix linear algebra using math providers.&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iii.html"&gt;Part III: eigenface application&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iv.html"&gt;Part IV: performance issues &lt;/a&gt;&lt;/p&gt;  &lt;p&gt;In this post, we move to sparse matrices. In many applications in data mining, the data table is stored as a sparse matrix. For example, in text mining, a document can be represented by a row-vector(or column-vector), the value at index i means that how many times word i occurs in this document. While the vocabulary could be quite large (tens of thousands), thus the document vector is highly sparse. If we store the documents in a matrix, then the matrix is also highly sparse. In DNA sequence analysis applications, the same situation occurs, that is we need to deal with sparse matrices! &lt;/p&gt;  &lt;p&gt;We first &lt;strong&gt;dig into the implementation of F# sparse matrix in PowerPack&lt;/strong&gt;. This is important, as we need to know how the sparse matrices are represented in F#, what algorithms are supported, etc. Based on the F# implementation, we can build our own routines, e.g. change the row based sparse representation to column based. At last, we PInvoke a SVD library in C as a example to show how to link existing stable linear algebra routines for sparse matrices.  &lt;/p&gt;  &lt;h2&gt;The sparse matrix implementation in PowerPack&lt;/h2&gt;  &lt;p&gt;Sparse matrices have several different representation, which also have different strength. A good introduction is &lt;a href="http://en.wikipedia.org/wiki/Sparse_matrix"&gt;this Wikipedia page&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;F# uses &lt;em&gt;Yale Sparse Matrix Format&lt;/em&gt;, or another name, &lt;em&gt;Compressed Sparse Row&lt;/em&gt;. (ref to the above Wiki article.) &lt;/p&gt;  &lt;p&gt;Let’s check this out:&lt;/p&gt;  &lt;pre class="code"&gt;&amp;gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;B = Matrix.initSparse 4 3 [0,0,2.3; 2,0,3.8; 1,1,1.3; 0,2,4.2; 1,2,2.2; 2,2,0.5]&lt;br /&gt;&lt;span style="color:blue;"&gt;val &lt;/span&gt;B : matrix = matrix [[2.3; 0.0; 4.2]&lt;br /&gt;                     [0.0; 1.3; 2.2]&lt;br /&gt;                     [3.8; 0.0; 0.5]&lt;br /&gt;                     [0.0; 0.0; 0.0]]&lt;br /&gt;&amp;gt; B.InternalSparseValues&lt;br /&gt;&lt;span style="color:blue;"&gt;val &lt;/span&gt;it : float [] = [|2.3; 4.2; 1.3; 2.2; 3.8; 0.5|]&lt;br /&gt;&amp;gt; B.InternalSparseColumnValues&lt;br /&gt;&lt;span style="color:blue;"&gt;val &lt;/span&gt;it : int [] = [|0; 2; 1; 2; 0; 2|]&lt;br /&gt;&amp;gt; B.InternalSparseRowOffsets&lt;br /&gt;&lt;span style="color:blue;"&gt;val &lt;/span&gt;it : int [] = [|0; 2; 4; 6; 6|]&lt;/pre&gt;&lt;br /&gt;Non-zero entries are sequentially in B.InternalSparseValues in a row-by-row fashion. B.InternalSparseColumnValues stores their column indices. B.InternalSparseRowOffsets stores the row offsets, for row i, it starts at B.InternalSparseRowOffsets[i] and ends at B.InternalSparseRowOffsets[i+1]-1 in the value array(B.InternalSparseValues).&lt;br /&gt;&lt;p&gt;This representation allows to take out one row of a sparse matrix very efficiently. In data mining, data instances/samples are usually stored as a row vector in a matrix, so it supports efficient handling on the instance level. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;However, this representation does not support efficient column operations. In numerical computing, taking columns is more often (following the old Fortran tradition), thus a lot of libraries use &lt;em&gt;Compressed Sparse Column &lt;/em&gt;representation for sparse matrices. It is also called &lt;a href="http://netlib.org/linalg/html_templates/node92.html#SECTION00931200000000000000"&gt;&lt;em&gt;Harwell-Boeing sparse matrix format&lt;/em&gt;&lt;/a&gt; (&lt;strong&gt;HB format&lt;/strong&gt; for short)  in the numerical computing community. An example is &lt;a href="http://tedlab.mit.edu/%7Edr/SVDLIBC/SVD_F_ST.html"&gt;here&lt;/a&gt;. &lt;/p&gt;PowerPack’s aim for providing the Internal* member functions of matrix type is to let users to interpolate with other libraries. However, it does not provide a function to convert the row representation to the column representation. Thus, this could be our first exercise:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;toColumnSparse (A:matrix) =&lt;br /&gt;&lt;span style="color:blue;"&gt;if &lt;/span&gt;A.IsDense &lt;span style="color:blue;"&gt;then &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"only for sparse matrix!"&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = A.NumRows&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = A.NumCols&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;cidx = A.InternalSparseColumnValues&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;roffset = A.InternalSparseRowOffsets&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;vals = A.InternalSparseValues&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nval = vals.Length&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// 1. scan to get column offset&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;colCnt = Array.create ncol 0&lt;br /&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;nrow-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;    for &lt;/span&gt;idx=roffset.[i] &lt;span style="color:blue;"&gt;to &lt;/span&gt;roffset.[i+1]-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;        &lt;/span&gt;colCnt.[cidx.[idx]] &amp;lt;- colCnt.[cidx.[idx]] + 1&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;colOffset = Array.zeroCreate (ncol+1)&lt;br /&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;i=1 &lt;span style="color:blue;"&gt;to &lt;/span&gt;ncol &lt;span style="color:blue;"&gt;do&lt;br /&gt;    &lt;/span&gt;colOffset.[i] &amp;lt;- colOffset.[i-1] + colCnt.[i-1]&lt;br /&gt;Array.Clear(colCnt, 0, ncol) &lt;span style="color:green;"&gt;// clear cnt array&lt;br /&gt;&lt;br /&gt;// 2. score the value&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;rowIdx = Array.create nval 0&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;vals2 = Array.create nval 0.0&lt;br /&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;nrow-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;    for &lt;/span&gt;idx=roffset.[i] &lt;span style="color:blue;"&gt;to &lt;/span&gt;roffset.[i+1]-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;        let &lt;/span&gt;j = cidx.[idx]&lt;br /&gt;        vals2.[colOffset.[j] + colCnt.[j]] &amp;lt;- vals.[idx]&lt;br /&gt;        rowIdx.[colOffset.[j] + colCnt.[j]] &amp;lt;- i&lt;br /&gt;        colCnt.[j] &amp;lt;- colCnt.[j] + 1&lt;br /&gt;    &lt;br /&gt;vals2, rowIdx, colOffset&lt;/pre&gt;This implementation is quite efficient. It does two scans and returns three arrays for the HB format. This implementation currently only supports sparse matrices, it &lt;strong&gt;should&lt;/strong&gt; also support dense matrices as we often need to transform a dense matrix into a sparse one. We can add the support in this function, or we can first transfer a dense matrix into a sparse one and use the above function. Both are OK. However, PowerPack currently does not support dense to sparse operation (it supports sparse to dense, which is used more often). Let this dense-to-sparse operation be our second exercise:&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;let &lt;/span&gt;toSparse (A:matrix) =&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color:blue;"&gt;if &lt;/span&gt;A.IsSparse &lt;span style="color:blue;"&gt;then &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"A should be a desne matrix"&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;l =&lt;br /&gt;    seq {&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;B = A.InternalDenseValues &lt;span style="color:green;"&gt;// use the internal array&lt;br /&gt;        &lt;/span&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;(Array2D.length1 B)-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;            for &lt;/span&gt;j=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;(Array2D.length2 B)-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;                if &lt;/span&gt;B.[i,j] &amp;gt; 1e-30 &lt;span style="color:blue;"&gt;then&lt;br /&gt;                    yield &lt;/span&gt;(i,j,B.[i,j])&lt;br /&gt;    }&lt;br /&gt;Matrix.initSparse A.NumRows A.NumCols l&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To get the HB format of a dense matrix A, we simply use:&lt;/p&gt;let vals, rowIdx, colOffset = A |&amp;gt; toSparse |&amp;gt; toColumnSparse&lt;br /&gt;&lt;p&gt;At last of this section, let’s have a look at the &lt;strong&gt;annotated&lt;/strong&gt; implementation for Matrix.initSparse in matrix.fs to have a better understanding of the F# sparse matrix type:&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;/// Create a matrix from a sparse sequence&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;initSparseMatrixGU maxi maxj ops s =&lt;br /&gt;&lt;span style="color:green;"&gt;(* 1.  the matrix is an array of dictionarys&lt;br /&gt;       tab[i] is the dictionary for row i *)&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;tab = Array.create maxi &lt;span style="color:blue;"&gt;null&lt;br /&gt;let &lt;/span&gt;count = ref 0&lt;br /&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;(i,j,v) &lt;span style="color:blue;"&gt;in &lt;/span&gt;s &lt;span style="color:blue;"&gt;do&lt;br /&gt;    if &lt;/span&gt;i &amp;lt; 0 || i &amp;gt;= maxi || j &amp;lt;0 || j &amp;gt;= maxj &lt;span style="color:blue;"&gt;then &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"initial value out of range"&lt;/span&gt;;&lt;br /&gt;    count := !count + 1;&lt;br /&gt;    &lt;span style="color:blue;"&gt;let &lt;/span&gt;tab2 =&lt;br /&gt;        &lt;span style="color:blue;"&gt;match &lt;/span&gt;tab.[i] &lt;span style="color:blue;"&gt;with&lt;br /&gt;        &lt;/span&gt;| &lt;span style="color:blue;"&gt;null -&amp;gt;&lt;br /&gt;            let &lt;/span&gt;tab2 = &lt;span style="color:blue;"&gt;new &lt;/span&gt;Dictionary&amp;lt;_,_&amp;gt;(3)&lt;br /&gt;            tab.[i] &amp;lt;- tab2;&lt;br /&gt;            tab2&lt;br /&gt;        | tab2 &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;tab2&lt;br /&gt;    tab2.[j] &amp;lt;- v&lt;br /&gt;&lt;span style="color:green;"&gt;// 2. calcuate the offset for each row&lt;br /&gt;// need to be optimized!&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;offsA =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;rowsAcc = Array.zeroCreate (maxi + 1)&lt;br /&gt;   &lt;span style="color:blue;"&gt;let mutable &lt;/span&gt;acc = 0&lt;br /&gt;   &lt;span style="color:green;"&gt;// 3. this loop could be optimized using&lt;br /&gt;   // sorted map for tab&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;i = 0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;maxi-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;      &lt;/span&gt;rowsAcc.[i] &amp;lt;- acc;&lt;br /&gt;      acc &amp;lt;- &lt;span style="color:blue;"&gt;match &lt;/span&gt;tab.[i] &lt;span style="color:blue;"&gt;with&lt;br /&gt;              &lt;/span&gt;| &lt;span style="color:blue;"&gt;null -&amp;gt; &lt;/span&gt;acc&lt;br /&gt;              | tab2 &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;acc+tab2.Count&lt;br /&gt;   rowsAcc.[maxi] &amp;lt;- acc;&lt;br /&gt;   rowsAcc&lt;br /&gt;&lt;span style="color:green;"&gt;// 4. get the column indices and values&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;colsA,valsA =&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;colsAcc = &lt;span style="color:blue;"&gt;new &lt;/span&gt;ResizeArray&amp;lt;_&amp;gt;(!count)&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;valsAcc = &lt;span style="color:blue;"&gt;new &lt;/span&gt;ResizeArray&amp;lt;_&amp;gt;(!count)&lt;br /&gt;   &lt;span style="color:blue;"&gt;for &lt;/span&gt;i = 0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;maxi-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;      match &lt;/span&gt;tab.[i] &lt;span style="color:blue;"&gt;with&lt;br /&gt;      &lt;/span&gt;| &lt;span style="color:blue;"&gt;null -&amp;gt; &lt;/span&gt;()&lt;br /&gt;      | tab2 &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;tab2 |&amp;gt; Seq.toArray |&amp;gt; Array.sortBy (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;kvp &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;kvp.Key) |&amp;gt; Array.iter (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;kvp &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;colsAcc.Add(kvp.Key); valsAcc.Add(kvp.Value));&lt;br /&gt;   colsAcc.ToArray(), valsAcc.ToArray()&lt;br /&gt;&lt;span style="color:green;"&gt;// 5. call the SparseMatrix constructor&lt;br /&gt;&lt;/span&gt;SparseMatrix(opsData=ops, sparseValues=valsA, sparseRowOffsets=offsA, ncols=maxj, columnValues=colsA)&lt;br /&gt;    &lt;/pre&gt;As noted in the code, one possible optimization is to use SortedMap for table, rather than an array. But this SortedMap has more overhead, the current implementation is already good. The other possible way is to sort the (i,j,val) sequence, which avoids the overhead in using a Dictionary structure.&lt;br /&gt;&lt;p&gt;Only a few matrix operations are implemented for sparse matrices, e.g. +, – and * are supported. However, map, columns and rows are not supported. This does not quite matter as when we need sparse matrices, we will be usually dealing with large datasets. For large datasets, calling a specialized library or writing the code ourselves is a better solution, as we will see the SVD example blow:&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;PInvoke SVDLIBC&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;In &lt;a href="http://fdatamining.blogspot.com/2010/04/pinvoke-native-cc-libraries-in-f.html"&gt;a previous post&lt;/a&gt;, We already know how to write&lt;strong&gt; &lt;/strong&gt;a simple matrix multiplication in C, and call it from F# using P/Invoke. Here we move to a more useful one, &lt;a href="http://tedlab.mit.edu/%7Edr/SVDLIBC/"&gt;a large scale SVD library&lt;/a&gt;, SVDLIBC. For a small dense SVD, using lapack’s svd is just fine. However, for a 10000-by-10000 sparse matrix, we need a more powerful one. (&lt;a href="http://www.caam.rice.edu/software/ARPACK/"&gt;ARPACK project&lt;/a&gt; is dedicated to this kind of decompositions. SVDLIBC is a C translation of a small part ARPACK.) &lt;/p&gt;&lt;br /&gt;&lt;p&gt;The SVDLIBC is a very good svd solver. It also provides a command line tool to do SVD for sparse or dense matrices. However, it uses some non-standard headers for I/O. To make it compile, we need to delete some code for IO. The main svd solver (in las2.c) is:&lt;/p&gt;SVDRec svdLAS2A(SMat A, &lt;span style="color:blue;"&gt;long&lt;/span&gt;dimensions)&lt;br /&gt;&lt;p&gt;A wrapper with a clear interface is need for this solver:&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;#define &lt;/span&gt;CHECK(ptr)  &lt;span style="color:blue;"&gt;if &lt;/span&gt;(!(ptr)) &lt;span style="color:blue;"&gt;return &lt;/span&gt;0;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;__declspec&lt;/span&gt;(&lt;span style="color:blue;"&gt;dllexport&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;int &lt;/span&gt;svds(&lt;span style="color:blue;"&gt;int &lt;/span&gt;nrow, &lt;span style="color:blue;"&gt;int &lt;/span&gt;ncol, &lt;span style="color:blue;"&gt;int &lt;/span&gt;nval, &lt;span style="color:blue;"&gt;double &lt;/span&gt;*val, &lt;span style="color:blue;"&gt;int &lt;/span&gt;*row, &lt;span style="color:blue;"&gt;int &lt;/span&gt;*offset,&lt;br /&gt;     &lt;span style="color:blue;"&gt;int &lt;/span&gt;dim,&lt;br /&gt;     &lt;span style="color:blue;"&gt;double &lt;/span&gt;*Uval,&lt;br /&gt;     &lt;span style="color:blue;"&gt;double &lt;/span&gt;*Sval,&lt;br /&gt;     &lt;span style="color:blue;"&gt;double &lt;/span&gt;*Vval)&lt;br /&gt;{&lt;br /&gt;SMat A;&lt;br /&gt;SVDRec res;&lt;br /&gt;DMat U, V; &lt;span style="color:blue;"&gt;double &lt;/span&gt;*S;&lt;br /&gt;FILE *fp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A = svdNewSMat(nrow, ncol, nval);&lt;br /&gt;CHECK(A);&lt;br /&gt;memcpy(A-&amp;gt;value, val, &lt;span style="color:blue;"&gt;sizeof&lt;/span&gt;(&lt;span style="color:blue;"&gt;double&lt;/span&gt;) * nval);&lt;br /&gt;memcpy(A-&amp;gt;rowind, row, &lt;span style="color:blue;"&gt;sizeof&lt;/span&gt;(&lt;span style="color:blue;"&gt;int&lt;/span&gt;) * nval);&lt;br /&gt;memcpy(A-&amp;gt;pointr, offset, &lt;span style="color:blue;"&gt;sizeof&lt;/span&gt;(&lt;span style="color:blue;"&gt;int&lt;/span&gt;) * (ncol+1));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;res = svdLAS2A(A, dim);&lt;br /&gt;CHECK(res);&lt;br /&gt;CHECK(res-&amp;gt;d == dim); &lt;span style="color:green;"&gt;// the dimension passed in must be correct!&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;S = res-&amp;gt;S;&lt;br /&gt;memcpy(Sval, S, &lt;span style="color:blue;"&gt;sizeof&lt;/span&gt;(&lt;span style="color:blue;"&gt;double&lt;/span&gt;) * dim);&lt;br /&gt;&lt;br /&gt;U = svdTransposeD(res-&amp;gt;Ut);    CHECK(U);&lt;br /&gt;V = svdTransposeD(res-&amp;gt;Vt);    CHECK(V);&lt;br /&gt;memcpy(Uval, &amp;amp;U-&amp;gt;value[0][0], &lt;span style="color:blue;"&gt;sizeof&lt;/span&gt;(&lt;span style="color:blue;"&gt;double&lt;/span&gt;) * (U-&amp;gt;rows * U-&amp;gt;cols));&lt;br /&gt;memcpy(Vval, &amp;amp;V-&amp;gt;value[0][0], &lt;span style="color:blue;"&gt;sizeof&lt;/span&gt;(&lt;span style="color:blue;"&gt;double&lt;/span&gt;) * (V-&amp;gt;rows * U-&amp;gt;cols));&lt;br /&gt;svdFreeDMat(U);&lt;br /&gt;svdFreeDMat(V);&lt;br /&gt;svdFreeSMat(A);&lt;br /&gt;svdFreeSVDRec(res);&lt;br /&gt;&lt;span style="color:blue;"&gt;return &lt;/span&gt;1; &lt;span style="color:green;"&gt;// successful&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;and in F#:&lt;/pre&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;module &lt;/span&gt;Native =&lt;br /&gt;[&amp;lt;System.Runtime.InteropServices.DllImport(&lt;span style="color:maroon;"&gt;@"svdlibc.dll"&lt;/span&gt;,EntryPoint=&lt;span style="color:maroon;"&gt;"svds"&lt;/span&gt;)&amp;gt;]&lt;br /&gt;&lt;span style="color:blue;"&gt;extern &lt;/span&gt;int svds(int nrow, int ncol, int nval, double *vals, int *row, int *offset, int dim, double *Uval, double *Sval, double *Vval);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;module &lt;/span&gt;LinearAlgebra =&lt;br /&gt;&lt;span style="color:green;"&gt;// Sparse SVD&lt;br /&gt;// A: F# sparse matrix&lt;br /&gt;// di: the dimension&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;svds (A:matrix) (di:int)=&lt;br /&gt;    &lt;span style="color:green;"&gt;/// A =  U * S * Vt&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:blue;"&gt;if &lt;/span&gt;A.IsDense &lt;span style="color:blue;"&gt;then &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"only for sparse matrix!"&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:blue;"&gt;else&lt;br /&gt;        let &lt;/span&gt;nrow = A.NumRows&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = A.NumCols&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;nval = A.InternalSparseValues.Length&lt;br /&gt;        &lt;span style="color:green;"&gt;// let dim = min nrow ncol&lt;br /&gt;        &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;dim = max 1 (min (min nrow ncol) di) &lt;span style="color:green;"&gt;// choose a valid value&lt;br /&gt;        &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;U = Matrix.zero nrow dim&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;S = Vector.zero dim&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;V = Matrix.zero ncol dim&lt;br /&gt;    &lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;colVals, rowIdx, colOffset = MatrixUtility.toColumnSparse A&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;valsP = NativeUtilities.pinA colVals&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;rowP = NativeUtilities.pinA rowIdx&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;offsetP = NativeUtilities.pinA colOffset&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;Up, Vp = NativeUtilities.pinMM U V&lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;Sp = NativeUtilities.pinV S&lt;br /&gt;    &lt;br /&gt;        &lt;span style="color:blue;"&gt;let &lt;/span&gt;ret = Native.svds(nrow, ncol, nval, valsP.Ptr, rowP.Ptr, offsetP.Ptr, dim, Up.Ptr, Sp.Ptr, Vp.Ptr)&lt;br /&gt;        valsP.Free()&lt;br /&gt;        rowP.Free()&lt;br /&gt;        offsetP.Free()&lt;br /&gt;        Up.Free()&lt;br /&gt;        Vp.Free()&lt;br /&gt;        Sp.Free()&lt;br /&gt;        &lt;span style="color:blue;"&gt;if &lt;/span&gt;ret = 0 &lt;span style="color:blue;"&gt;then&lt;br /&gt;            &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"error in pinvoke svds"&lt;br /&gt;        &lt;/span&gt;U, S, V&lt;br /&gt;&lt;span style="color:green;"&gt;// default Sparse SVD     &lt;br /&gt;// A: F# sparse matrix&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;svds0 A =&lt;br /&gt;    svds A (min A.NumCols A.NumRows)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="code"&gt;test it:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;test2() =&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;r = &lt;span style="color:blue;"&gt;new &lt;/span&gt;System.Random()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = 1000&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = 1000&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nval = 100000&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;l =&lt;br /&gt;    seq {&lt;br /&gt;        &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=1 &lt;span style="color:blue;"&gt;to &lt;/span&gt;nval &lt;span style="color:blue;"&gt;do&lt;br /&gt;            yield &lt;/span&gt;(r.Next()%nrow, r.Next()%ncol, r.NextDouble())&lt;br /&gt;    }&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A = Matrix.initSparse nrow ncol l&lt;br /&gt;tic()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;U, S, V = svds A 100&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"svds"&lt;/span&gt;)&lt;br /&gt;()&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We will in later posts to show the data mining applications of SVD.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;NativeUtilities module&lt;/h2&gt;I used utility functions in NativeUtilities to convert the F# array/matrix/vector and their native array representations. Here’s its implementation from F# math-provider source code:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;System&lt;br /&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;System.Runtime.InteropServices&lt;br /&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.NativeInterop&lt;br /&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.Math&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// from math-provider source code&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;module &lt;/span&gt;NativeUtilities = &lt;span style="color:blue;"&gt;begin&lt;br /&gt;let &lt;/span&gt;nativeArray_as_CMatrix_colvec (arr: 'T NativeArray) = &lt;span style="color:blue;"&gt;new &lt;/span&gt;CMatrix&amp;lt;_&amp;gt;(arr.Ptr,arr.Length,1)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nativeArray_as_FortranMatrix_colvec (arr: 'T NativeArray) = &lt;span style="color:blue;"&gt;new &lt;/span&gt;FortranMatrix&amp;lt;_&amp;gt;(arr.Ptr,arr.Length,1)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinM m = PinnedArray2.of_matrix(m)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinV v = PinnedArray.of_vector(v)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinA arr = PinnedArray.of_array(arr)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinA2 arr = PinnedArray2.of_array2D(arr)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinMV m1 v2 = pinM m1,pinV v2&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinVV v1 v2 = pinV v1,pinV v2&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinAA v1 v2 = pinA v1,pinA v2&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinMVV m1 v2 m3 = pinM m1,pinV v2,pinV m3&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinMM m1 m2  = pinM m1,pinM m2&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pinMMM m1 m2 m3 = pinM m1,pinM m2,pinM m3&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeM (pA: PinnedArray2&amp;lt;'T&amp;gt;) = pA.Free()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeV (pA: PinnedArray&amp;lt;'T&amp;gt;) = pA.Free()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeA (pA: PinnedArray&amp;lt;'T&amp;gt;) = pA.Free()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeA2 a = freeM a&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeMV (pA: PinnedArray2&amp;lt;'T&amp;gt;,pB : PinnedArray&amp;lt;'T&amp;gt;) = pA.Free(); pB.Free()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeVV (pA: PinnedArray&amp;lt;'T&amp;gt;,pB : PinnedArray&amp;lt;'T&amp;gt;) = pA.Free(); pB.Free()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeAA (pA: PinnedArray&amp;lt;'T&amp;gt;,pB : PinnedArray&amp;lt;'T&amp;gt;) = pA.Free(); pB.Free()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeMM (pA: PinnedArray2&amp;lt;'T&amp;gt;,(pB: PinnedArray2&amp;lt;'T&amp;gt;)) = pA.Free();pB.Free()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeMMM (pA: PinnedArray2&amp;lt;'T&amp;gt;,(pB: PinnedArray2&amp;lt;'T&amp;gt;),(pC: PinnedArray2&amp;lt;'T&amp;gt;)) = pA.Free();pB.Free();pC.Free()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;freeMVV (pA: PinnedArray2&amp;lt;'T&amp;gt;,(pB: PinnedArray&amp;lt;'T&amp;gt;),(pC: PinnedArray&amp;lt;'T&amp;gt;)) = pA.Free();pB.Free();pC.Free()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;matrixDims (m:Matrix&amp;lt;_&amp;gt;) = m.NumRows, m.NumCols&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;matrixDim1 (m:Matrix&amp;lt;_&amp;gt;) = m.NumRows&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;matrixDim2 (m:Matrix&amp;lt;_&amp;gt;) = m.NumCols&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;vectorDim  (v:Vector&amp;lt;_&amp;gt;) = v.Length&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;assertDimensions functionName (aName,bName) (a,b) =&lt;br /&gt;  &lt;span style="color:blue;"&gt;if &lt;/span&gt;a=b &lt;span style="color:blue;"&gt;then &lt;/span&gt;() &lt;span style="color:blue;"&gt;else&lt;br /&gt;  &lt;/span&gt;failwith (sprintf &lt;span style="color:maroon;"&gt;"Require %s = %s, but %s = %d and %s = %d in %s" &lt;/span&gt;aName bName aName a bName b functionName)&lt;br /&gt;&lt;span style="color:blue;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;A Note on &lt;em&gt;Compressed Sparse Row&lt;/em&gt; and &lt;em&gt;Compressed Sparse Column(HB format)&lt;/em&gt;&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;We can see both representations have deficiency, is there a magic structure or an engraining trick taking the advantage of both? Let’s check the standard software Matlab. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;In Matlab, we usually write A(:,j) to take the j-th column of sparse matrix A or A(i,:) to take the i-th row. Does Matlab have a magic to let both operations run efficiently. Then answer is &lt;strong&gt;NO&lt;/strong&gt;. The following script is used to test this:&lt;/p&gt;function [ ret ] = testOctaveSvds ()&lt;br /&gt;&lt;p&gt;      nrow = 10000;&lt;br /&gt; ncol = 10000;&lt;br /&gt; nval = 100000;&lt;br /&gt; rows = floor(rand(1, nval)*nrow)+1;&lt;br /&gt; cols = floor(rand(1, nval)*ncol)+1;&lt;br /&gt; vals = rand(1, nval);&lt;br /&gt; m = sparse(rows, cols, vals, nrow, ncol);&lt;br /&gt; tic;&lt;br /&gt; s = 0;&lt;br /&gt; for i=1:10000,&lt;br /&gt;     c = floor(rand(1) * ncol) + 1;&lt;br /&gt;     s = s + sum(m(:,c));&lt;br /&gt; end&lt;br /&gt; fprintf('cols: ')&lt;br /&gt; toc;&lt;/p&gt;&lt;p&gt;    tic;&lt;br /&gt; s = 0;&lt;br /&gt; for i=1:10000,&lt;br /&gt;     r = floor(rand(1) * nrow) + 1;&lt;br /&gt;     s = s + sum(m(r,:));&lt;br /&gt; end&lt;br /&gt; fprintf('rows: ')&lt;br /&gt; toc;&lt;/p&gt;&lt;p&gt;endfunction&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In Matlab 2009a, the output is:&lt;/p&gt;cols: Elapsed time is 0.204693 seconds.&lt;br /&gt;&lt;p&gt;rows: Elapsed time is 16.058717 seconds.&lt;/p&gt;&lt;br /&gt;Octave also has similar result. From the result, we can deduce that Matlab/Octave use HB format for sparse matrix and it does not do heavy optimization for the operation for taking the rows. This is reasonable, as mentioned before, that when using sparse matrices, the user/programmer has the obligation to optimize the program, rather than the matrix library.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-1283127462835779761?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/1283127462835779761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-v.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/1283127462835779761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/1283127462835779761'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-v.html' title='Matrix and linear algebra in F#, Part V: Sparse matrix implementation in PowerPack, and PInvoke a large scale SVD library as an application.'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-5415103158408624933</id><published>2010-04-05T01:00:00.001+08:00</published><updated>2010-06-04T20:15:47.913+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='c/c++'/><category scheme='http://www.blogger.com/atom/ns#' term='pinvoke'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>PInvoke native C/C++ libraries in F#</title><content type='html'>&lt;p&gt;In this blog post, we explore on PInvoke in F#. Before going into PInvoke, an matrix  multiplication example is also given to show how to build a DLL for PInvoke, which is important as in most of the cases we don't have pre-built DLLs.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;PInvoke stands for Platform Invoke, meaning calling naive libraries. The two major reasons to use PInvoke is 1) &lt;strong&gt;native performance&lt;/strong&gt;, native code is usually faster than .Net managed code, especially for numerical computing routines, and 2) existing &lt;strong&gt;stable(well tested) libraries&lt;/strong&gt; can be used on the fly on .Net. &lt;/p&gt;  &lt;p&gt;Of course, it also has some disadvantages, e.g. 1) extra instructions are used for each PInvoke call, this is not important as long as P/Invoke is not used too frequently, 2) calling native code means not safe anymore, e.g. no exception, no memory access checking. &lt;/p&gt;  &lt;p&gt;There are several tutorials online for C#: &lt;a href="http://msdn.microsoft.com/en-us/library/aa288468%28VS.71%29.aspx"&gt;here&lt;/a&gt;, &lt;a href="http://csharpcomputing.com/Tutorials/Lesson18.htm"&gt;here&lt;/a&gt; and &lt;a href="http://forum.codecall.net/csharp-tutorials/20420-tutorial-playing-mp3-files-c.html"&gt;here&lt;/a&gt;. However, these are more focused on existing windows dlls, their motivation for PInvoke is that some functions are not available on .Net, thus calling an existing native DLL, e.g. Win32 API. However, we are talking about performance in this series,  PInvoke means that we rewrite the performance critical part in C/C++, or compile an existing library (maybe we also need to make a C wrapper of it in the meantime) and call it using PInvoke. So we should start with the native code part as we don’t have the native dll yet. &lt;/p&gt;    &lt;h2&gt;Matrix multiplication&lt;/h2&gt;  &lt;p&gt;Because C and C++ are slightly different, and C++ usually compiles C code well. So we’ll deal with C++ mode in VC++ compiler, i.e. all files have &lt;strong&gt;.cpp&lt;/strong&gt; extensions. &lt;/p&gt;  &lt;p&gt;First we write mattrixproduct.cpp:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;__declspec&lt;/span&gt;(&lt;span style="color:blue;"&gt;dllexport&lt;/span&gt;) &lt;span style="color:blue;"&gt;int &lt;/span&gt;minus(&lt;span style="color:blue;"&gt;int &lt;/span&gt;a, &lt;span style="color:blue;"&gt;int &lt;/span&gt;b)&lt;br /&gt;{&lt;br /&gt;&lt;span style="color:blue;"&gt;return &lt;/span&gt;a - b;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;extern &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;"C" &lt;/span&gt;&lt;span style="color:blue;"&gt;__declspec&lt;/span&gt;(&lt;span style="color:blue;"&gt;dllexport&lt;/span&gt;) &lt;span style="color:blue;"&gt;int &lt;/span&gt;add(&lt;span style="color:blue;"&gt;int &lt;/span&gt;a, &lt;span style="color:blue;"&gt;int &lt;/span&gt;b)&lt;br /&gt;{&lt;br /&gt;&lt;span style="color:blue;"&gt;return &lt;/span&gt;a + b;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;extern &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;"C" &lt;/span&gt;&lt;span style="color:blue;"&gt;__declspec&lt;/span&gt;(&lt;span style="color:blue;"&gt;dllexport&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;void &lt;/span&gt;matmul(&lt;span style="color:blue;"&gt;double &lt;/span&gt;*a, &lt;span style="color:blue;"&gt;int &lt;/span&gt;an, &lt;span style="color:blue;"&gt;int &lt;/span&gt;am,&lt;br /&gt;        &lt;span style="color:blue;"&gt;double &lt;/span&gt;*b, &lt;span style="color:blue;"&gt;int &lt;/span&gt;bn, &lt;span style="color:blue;"&gt;int &lt;/span&gt;bm,&lt;br /&gt;        &lt;span style="color:blue;"&gt;double &lt;/span&gt;*c)&lt;br /&gt;{&lt;br /&gt;&lt;span style="color:blue;"&gt;int &lt;/span&gt;i, j, k;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;(i=0; i&amp;lt;an; i++)&lt;br /&gt;    &lt;span style="color:blue;"&gt;for &lt;/span&gt;(j=0; j&amp;lt;bm; j++) {&lt;br /&gt;        &lt;span style="color:blue;"&gt;double &lt;/span&gt;s = 0;&lt;br /&gt;        &lt;span style="color:blue;"&gt;for &lt;/span&gt;(k=0; k&amp;lt;am; k++)&lt;br /&gt;            s += a[i*am + k] * b[k*bm + j];&lt;br /&gt;        c[i*bm+j] = s;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;span style="color:blue;"&gt;__declspec&lt;/span&gt;(&lt;span style="color:blue;"&gt;dllexport&lt;/span&gt;) is the header we must add to each function to tell the compiler to generate this function in the DLL. &lt;span style="color:blue;"&gt;extern &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;"C" &lt;/span&gt;is to tell the compiler that this is a C code. We use command line: &lt;em&gt;&lt;br /&gt;cl.exe /LD mattrixproduct.cpp &lt;/em&gt;&lt;br /&gt;to get mattrixproduct.dll. We can also create a new Visual C++ project and set the target to .dll.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here’s the information for this dll: (use command line: &lt;em&gt;link /DUMP /EXPORTS mattrixproduct.dll)&lt;/em&gt;&lt;/p&gt;    ordinal hint RVA      name&lt;br /&gt;       1    0 00001000 &lt;strong&gt;?minus@@YAHHH@Z&lt;/strong&gt; = ?minus@@YAHHH@Z (int __cdecl minus(int,int))     &lt;br /&gt;       2    1 00001010 &lt;strong&gt;add&lt;/strong&gt; = _add&lt;br /&gt;       3    2 00001020 &lt;strong&gt;matmul&lt;/strong&gt; = _matmul&lt;br /&gt;&lt;br /&gt;We can see that minus function (without extern “C”) has a non-readable name, while the other two have correct names.&lt;br /&gt;&lt;br /&gt;Now, we have the DLL, let’s move on to the PInvoke in F#. Here is the code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;System&lt;br /&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;System.Runtime.InteropServices&lt;br /&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.NativeInterop&lt;br /&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.Math&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;module &lt;/span&gt;Native =&lt;br /&gt;[&amp;lt;System.Runtime.InteropServices.DllImport(&lt;span style="color:maroon;"&gt;@"mattrixproduct.dll"&lt;/span&gt;,&lt;br /&gt;EntryPoint=&lt;span style="color:maroon;"&gt;"add"&lt;/span&gt;)&amp;gt;]&lt;br /&gt;&lt;span style="color:blue;"&gt;extern &lt;/span&gt;int add(int a, int b);&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// notice the wired name for minus&lt;br /&gt;&lt;/span&gt;[&amp;lt;System.Runtime.InteropServices.DllImport(&lt;span style="color:maroon;"&gt;@"mattrixproduct.dll"&lt;/span&gt;,&lt;br /&gt;EntryPoint=&lt;span style="color:maroon;"&gt;"?minus@@YAHHH@Z"&lt;/span&gt;)&amp;gt;]&lt;br /&gt;&lt;span style="color:blue;"&gt;extern &lt;/span&gt;int minus(int a, int b);&lt;br /&gt;&lt;br /&gt;[&amp;lt;System.Runtime.InteropServices.DllImport(&lt;span style="color:maroon;"&gt;@"mattrixproduct.dll"&lt;/span&gt;,&lt;br /&gt;EntryPoint=&lt;span style="color:maroon;"&gt;"matmul"&lt;/span&gt;)&amp;gt;]&lt;br /&gt;&lt;span style="color:blue;"&gt;extern void &lt;/span&gt;matmul(double *a, int an, int am, double *b, int bn,&lt;br /&gt;int bm, double *c);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;a = Native.add(10,20)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;b = Native.minus(10,20)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A = matrix [[1.0;2.0;]; [3.0;4.0;]]&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;B = matrix [[3.0;2.0;1.0]; [1.0;1.0;1.0]]&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C = Matrix.zero 2 3&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Ap = PinnedArray2.of_matrix(A)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Bp = PinnedArray2.of_matrix(B)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Cp = PinnedArray2.of_matrix(C)&lt;br /&gt;Native.matmul(Ap.Ptr, 2, 2, Bp.Ptr, 2, 3, Cp.Ptr)&lt;br /&gt;Ap.Free()&lt;br /&gt;Bp.Free()&lt;br /&gt;Cp.Free()&lt;br /&gt;&lt;br /&gt;printfn &lt;span style="color:maroon;"&gt;"a = %A\nb = %A\nC = %A" &lt;/span&gt;a b C&lt;/pre&gt;There are several things to mention:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;1. For basic type, like int, double, as in the add and minus, they don’t need any special treatment. &lt;/p&gt;2. The hard part is the array. This introduces concept called data marshaling. Because .Net arrays and native arrays are different, so they cannot be directly mapped. This is why we open namespaces for Interop. Another thing is that data marshaling &lt;strong&gt;&lt;a href="http://www.eggheadcafe.com/software/aspnet/31311381/passing-dynamic-2d-array.aspx"&gt;does not&lt;/a&gt; support 2 dimensional arrays &lt;/strong&gt;directly! This is why I used 1-D array in the C implementation. High performance code usually only use 1-D arrays, so this inconvenience does not quite matter to us. To marshal the array, we get a one dimensional pointer for a matrix using &lt;strong&gt;PinnedArray2.of_matrix,&lt;/strong&gt; which is defined in F# PowerPack (in NativeArrayExtensions.fs), and then pass this pointer in the function call.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The performance&lt;/h2&gt;Let's now test the performance of the wrapped native matrix multiplication. First write some test on both small matrices and big matrices:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;mm A B =&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Ap = PinnedArray2.of_matrix(A)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Bp = PinnedArray2.of_matrix(B)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C = Matrix.zero (A.NumRows) (B.NumCols)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Cp = PinnedArray2.of_matrix(C)&lt;br /&gt;Native.matmul(Ap.Ptr, A.NumRows, A.NumCols, Bp.Ptr, B.NumRows, B.NumCols, Cp.Ptr)&lt;br /&gt;Ap.Free()&lt;br /&gt;Bp.Free()&lt;br /&gt;Cp.Free()&lt;br /&gt;C&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;test() =&lt;br /&gt;&lt;span style="color:green;"&gt;// use small matrices&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A = matrix [[1.0;2.0;]; [3.0;4.0;]]&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;B = matrix [[3.0;2.0;1.0]; [1.0;1.0;1.0]]&lt;br /&gt;tic()&lt;br /&gt;mm A B |&amp;gt; ignore&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"my native matrix *"&lt;/span&gt;)&lt;br /&gt;A * B  |&amp;gt; ignore&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"F# *"&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;// use big matrices&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;r = &lt;span style="color:blue;"&gt;new &lt;/span&gt;System.Random()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A1 = Matrix.init (92*112) 280 (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;r.NextDouble())&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A' = A1.Transpose&lt;br /&gt;tic()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C1 = mm A' A1&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"my native matrix *"&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C2 = A' * A1&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"F# *"&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;if &lt;/span&gt;C1.Equals(C2) &lt;span style="color:blue;"&gt;then true&lt;br /&gt;else false&lt;/span&gt;&lt;/pre&gt;The timing functions tic() and toc() are defined in &lt;a href="http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iv.html"&gt;Part IV&lt;/a&gt; of the matrix and linear algebra series. Run it in F# interactive:&lt;br /&gt;&lt;br /&gt;&amp;gt; test();;&lt;br /&gt;my native matrix * 1.758400 ms&lt;br /&gt;F# * 0.005100 ms&lt;br /&gt;my native matrix * 7207.073800 ms&lt;br /&gt;F# * 16524.670200 ms&lt;br /&gt;val it : bool = true&lt;br /&gt;&lt;br /&gt;First the return value is true, indicating that my C implementation is correct. In the small matrix case, the overhead of PInvoke is obvious. In the big matrix case, the performance boost using native code is also obvious.&lt;br /&gt;&lt;h2&gt;Note 1: C++ class&lt;/h2&gt;So far so good. Because we didn’t touch C++ yet! Classes, templates are more complex.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To work with these. One method is to &lt;strong&gt;write C wrappers for C++ classes&lt;/strong&gt;. This method is commonly used &lt;strong&gt;in practice&lt;/strong&gt;. &lt;/p&gt;We can also directly add &lt;span style="color:blue;"&gt;__declspec&lt;/span&gt;(&lt;span style="color:blue;"&gt;dllexport&lt;/span&gt;) to the class definition. However, creating objects via constructors is not supported anymore. One pattern is to write two static methods: one for creating an object and the other for freeing an object. Member functions are used by providing &lt;strong&gt;this&lt;/strong&gt; pointer explicitly. If you know how to enable object style programming in C, this patter would be familiar to you. Anyway, in this style, the accessing ability is equivalent to C’s. As this pattern requires modifying existing class definitions(although only a little), it is less commonly used, thus I don’t give detailed example here.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Note 2: Pure C&lt;/h2&gt;The above example is given in C++ as in most of the cases we are dealing with C++ files, at least C files could be compiled in C++.&lt;br /&gt;&lt;br /&gt;For Pure C files (with .c extensions), things are actually easier. Just removing &lt;span style="color:blue;"&gt;extern &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;"C" &lt;/span&gt;would be OK.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Using existing C/C++ code without PInvoke&lt;/h2&gt;We can use C++/CLI to compile a C/C++ library without modifying anything into a &lt;strong&gt;unsafe&lt;/strong&gt; &lt;strong&gt;managed&lt;/strong&gt; dll. I will write a separate blog for this later.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Remark&lt;/h2&gt;In this tutorial, we know the basics of P/Invoke in F#. We don’t touch two things:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;1) &lt;strong&gt;Marshaling&lt;/strong&gt; complex parameters, e.g. different format of strings, non-array pointers, C++ classes, etc. &lt;/p&gt;&lt;p&gt;2) &lt;strong&gt;MEMORY&lt;/strong&gt;! MEMORY! MEMORY! The hard and dangerous part of P/Invoke is memory management. I don’t have enough experience of this part yet. Currently I just follow the style in &lt;a href="http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-ii.html"&gt;Math-Provider&lt;/a&gt;. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-5415103158408624933?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/5415103158408624933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/04/pinvoke-native-cc-libraries-in-f.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5415103158408624933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5415103158408624933'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/04/pinvoke-native-cc-libraries-in-f.html' title='PInvoke native C/C++ libraries in F#'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-4727783890163649403</id><published>2010-04-03T21:04:00.001+08:00</published><updated>2010-04-03T23:07:27.766+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Matrix and linear algebra in F#, Part IV: profile your program, find the bottleneck and speed it up: using matrix multiplication as an example</title><content type='html'>&lt;p&gt;We will use the &lt;a href="http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iii.html"&gt;face recognition program we developed in Part III&lt;/a&gt; to illustrate how to profile programs.&lt;/p&gt;  &lt;p&gt;The performance problem of the face recognition program is that it takes about 25 seconds to process 280 faces (each face has 10304=112*92 pixels). I want to find which part takes most of the time. Is it due to the eigen decomposition?&lt;/p&gt;  &lt;h2&gt;Profile&lt;/h2&gt;  &lt;p&gt;Profiling means knowing how much time and memory every part of your program take. Good profiling tools could give a detail report on the running behavior of a program. In .Net, there are &lt;a href="http://stackoverflow.com/questions/1077014/what-is-the-best-way-to-debug-performance-problems"&gt;several good tools&lt;/a&gt; for this purpose. Some of them are free. &lt;/p&gt;  &lt;p&gt;However, these tools are too big to use in a explorative/interactive programming style. Following Matlab’s tic and toc, I have written them for F#:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;stopWatch = &lt;span style="color:blue;"&gt;new &lt;/span&gt;System.Diagnostics.Stopwatch()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;tic() =&lt;br /&gt;stopWatch.Reset()&lt;br /&gt;stopWatch.Start()&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;toc(msg:string) =&lt;br /&gt;stopWatch.Stop()&lt;br /&gt;printfn &lt;span style="color:maroon;"&gt;"%s %f ms" &lt;/span&gt;msg stopWatch.Elapsed.TotalMilliseconds&lt;br /&gt;stopWatch.Reset()&lt;br /&gt;stopWatch.Start()&lt;/pre&gt;I also use the same style for my C programs:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;#include &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;&amp;lt;time.h&amp;gt;&lt;br /&gt;&lt;/span&gt;time_t stopWatch;&lt;br /&gt;&lt;span style="color:blue;"&gt;void &lt;/span&gt;tic()&lt;br /&gt;{&lt;br /&gt;stopWatch = clock();&lt;br /&gt;}&lt;br /&gt;&lt;span style="color:blue;"&gt;void &lt;/span&gt;toc(&lt;span style="color:blue;"&gt;const char &lt;/span&gt;*msg)&lt;br /&gt;{&lt;br /&gt;&lt;span style="color:blue;"&gt;double &lt;/span&gt;s = (clock() - stopWatch) * 1000.0 / CLOCKS_PER_SEC;&lt;br /&gt;printf(&lt;span style="color: rgb(163, 21, 21);"&gt;"%s: %.2lf ms\n"&lt;/span&gt;, msg, s);&lt;br /&gt;stopWatch = clock();&lt;br /&gt;}&lt;/pre&gt;This implementation enables us to use a sequence of &lt;strong&gt;toc&lt;/strong&gt;s to &lt;em&gt;profile a code block line by line&lt;/em&gt; without the need to write &lt;strong&gt;tic&lt;/strong&gt; every time. However this simple profiler can not be nested as there is only a global timer.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;After putting several &lt;strong&gt;toc&lt;/strong&gt;s into my eigCov:&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;tic()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;colm = colMean faces&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"mean face"&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A = Matrix.init nrow ncol (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;faces.[i,j] - colm.[i])&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"minus mean face"&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;L = A.Transpose * A&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"get L"&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;val1, vec1 = eig L &lt;span style="color:green;"&gt;&lt;br /&gt;&lt;/span&gt;toc(&lt;span style="color:maroon;"&gt;"lapack eig"&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;v = val1 * (1.0 / float (ncol - 1))&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;u = A * vec1.Transpose&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"get u"&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:green;"&gt;// normalize eigen vectors&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let mutable &lt;/span&gt;cnt = 0&lt;br /&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;ncol-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;if &lt;/span&gt;abs v.[i] &amp;lt; 1e-8 &lt;span style="color:blue;"&gt;then&lt;br /&gt;   &lt;/span&gt;u.[0..nrow-1,i..i] &amp;lt;- Matrix.create nrow 1 0.0&lt;br /&gt;&lt;span style="color:blue;"&gt;else&lt;br /&gt;   &lt;/span&gt;cnt &amp;lt;- cnt + 1&lt;br /&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;norm = Vector.norm (u.[0..nrow-1,i..i].ToVector())&lt;br /&gt;   u.[0..nrow-1,i..i] &amp;lt;- u.[0..nrow-1,i..i] * ( 1.0 / float norm )&lt;br /&gt;toc(&lt;span style="color:maroon;"&gt;"normolize u"&lt;/span&gt;)&lt;/pre&gt;It gets the following output:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:Terminal;"&gt;mean face 639.891700 ms&lt;br /&gt;minus mean face 195.484600 ms&lt;br /&gt;get L 12885.751500 ms&lt;br /&gt;lapack eig 247.015700 ms&lt;br /&gt;get u 7169.749100 ms&lt;br /&gt;normolize u 623.919700 ms&lt;/span&gt;&lt;/p&gt;The bottle neck is in "Get L” and “get u”, both of which are matrix multiplication operations. The main computation part “lapack eig” actually costs very little time (as the 280-by-280 matrix is not big).&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The matrix multiplication in “get L” is 280x10304 multiplies 10304x280, which is quite large!&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Matrix Multiplication&lt;/h2&gt;Matrix multiplication is a great example to show that why we need well tuned libraries to perform numerical computations.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Table: the time cost for self-made(not optimized) implementations&lt;/p&gt;&lt;table width="400" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;implementations&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;operator * for matrix type&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;3 F# loops(use matrix)&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;3 F# loops(use float Array2D)&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;3 C loops (native)&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;time(seconds)&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;13.07&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;38.81&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;13.52&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="80"&gt;8.10&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;I used the following 3 loops:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;(i=0; i&amp;lt;N; i++)&lt;br /&gt;&lt;span style="color:blue;"&gt;for &lt;/span&gt;(j=0; j&amp;lt;N; j++) {&lt;br /&gt;   &lt;span style="color:blue;"&gt;double &lt;/span&gt;s = 0;&lt;br /&gt;   &lt;span style="color:blue;"&gt;for &lt;/span&gt;(k=0; k&amp;lt;M; k++)&lt;br /&gt;       s += a[i][k] * b[k][j];&lt;br /&gt;   c[i][j] = s;&lt;br /&gt;}&lt;/pre&gt;The two F# programs are similar to the above one in C. We can find that if we use a lot of F# matrix element access operator [,], the performance is very poor. The * operator of matrix type does not have any optimization in it, so it costs the same amount of time as 3-loops (Array2D) does. Native code is faster than the managed ones as it does not do boundary checking for index.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Matlab, R and Numpy/Python all wrap optimized matrix implementations. Let us next see how they perform:&lt;/p&gt;Table: the time cost for optimized implementations&lt;br /&gt;&lt;br /&gt;&lt;table width="400" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;Software&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;Matlab 2009a&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;R 2.10.1&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;Numpy 1.3.0&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;time&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;0.25&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;1.98&lt;/td&gt;&lt;br /&gt;&lt;br /&gt; &lt;td valign="top" width="100"&gt;0.35&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Matlab is the fastest, but it uses 200% CPU. While R and Numpy are single threaded. R takes 2 seconds. Matlab uses Intel MKL, Numpy uses Lapack(maybe optimized version), I don’t know what algebra routines R uses.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We can see the difference between optimized versions and non-optimized ones is very large. A similar report from a .Net math software vender is &lt;a href="http://www.centerspace.net/doc/NMath/whitepapers/NMath.Benchmarks.pdf"&gt;here&lt;/a&gt;. The conclusion is that &lt;strong&gt;we should use optimized matrix multiplication procedure when matrices are large&lt;/strong&gt;.  &lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Add optimized matrix multiplication support to math-provider&lt;/h2&gt;The math-provider has made an wrapper for &lt;a href="http://docs.sun.com/source/819-3691/dgemm.html"&gt;dgemm_&lt;/a&gt;, which is a BLAS procedure for computing:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;C  :=&lt;br /&gt;alpha*op( A )*op( B ) + beta*C&lt;br /&gt;where op (A) = A or A’. &lt;/pre&gt;This is general case of matrix multiplication. The math-provider also has an wrapper for &lt;a href="http://docs.sun.com/source/819-0497/dgemv.html"&gt;dgemv_&lt;/a&gt;, matrix-vector multiplication.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;However, they are not directly callable from Lapack service provider. &lt;/p&gt;add the following code to linear_algebra_service.fs:&lt;span style="color:blue;"&gt;&lt;br /&gt;let &lt;/span&gt;MM a b =&lt;br /&gt;&lt;pre class="code"&gt;  Service().dgemm_(a,b)&lt;/pre&gt;&lt;br /&gt;and add the following to linear_algebra.fs: &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;MM A B =&lt;br /&gt;&lt;span style="color:blue;"&gt;if &lt;/span&gt;HaveService() &lt;span style="color:blue;"&gt;then&lt;br /&gt;  &lt;/span&gt;LinearAlgebraService.MM A B&lt;br /&gt;&lt;span style="color:blue;"&gt;else&lt;br /&gt;  &lt;/span&gt;A * B&lt;br /&gt;&lt;/pre&gt;and add the following to linear_algebra.fsi:&lt;br /&gt;/// BLAS matrix multiplication&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;val &lt;/span&gt;MM: A:matrix &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;B:matrix &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;matrix&lt;/pre&gt;Now we define a local name for it:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;mm = LinearAlgebra.MM &lt;/pre&gt;Using BLAS matrix multiplication routine costs about &lt;strong&gt;4 seconds&lt;/strong&gt;, which is faster than native C’s performance(8 seconds), but worse than Matlab’s or Numpy’s. The reason is that the BLAS(Netlib BLAS 3.1.1) is not optimized for my platform. If I use ATLAS for BLAS and use an Intel Fortran Compiler, the performance will be close to Numpy’s or Matlab’s.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;By using BLAS’s matrix multiplication routine for the face recognition, the total running reduces from 25 seconds to 9 seconds.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Attachment&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;wait.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-4727783890163649403?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/4727783890163649403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iv.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/4727783890163649403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/4727783890163649403'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iv.html' title='Matrix and linear algebra in F#, Part IV: profile your program, find the bottleneck and speed it up: using matrix multiplication as an example'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-7863461705557710233</id><published>2010-04-03T17:55:00.001+08:00</published><updated>2010-04-03T23:17:18.722+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eigenface'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='face recognition'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Matrix and linear algebra in F#, Part III: Eigen decomposition and face recognition</title><content type='html'>&lt;h2&gt;The theory&lt;/h2&gt;  &lt;p&gt;Face recognition is one of the most import research problems in computer vision. It is also an important application for everyday use. A lot of security system has a face recognition component, as well as other parts, e.g., finger print recognition. &lt;/p&gt;  &lt;p&gt;While this application has a lot of algorithms, the most famous one must be Eigen face proposed in &lt;/p&gt;  &lt;p&gt;M. Turk and A. Pentland (1991). &lt;a href="http://www.cs.ucsb.edu/%7Emturk/Papers/jcn.pdf"&gt;"Eigenfaces for recognition"&lt;/a&gt;. &lt;i&gt;Journal of Cognitive Neuroscience&lt;/i&gt; &lt;b&gt;3&lt;/b&gt; (1): 71–86. &lt;/p&gt;  &lt;p&gt;The theory of Eigen face is very simple if one has learned the concept of Principal Component Analysis (PCA). Given a set of faces, each face is a n-by-m matrix, if we reshape the matrix into a vector, then each face can be viewed as a point in  the (n*m) dimensional space. The idea of Eigen face is to find some directions, which are orthogonal, in this space such that the points(faces) have a minimal representation, or have maximum variance along these directions. As you can see, this is an optimization problem. The magic part is that this optimization is solved by Singular Value Decomposition (SVD). &lt;/p&gt;  &lt;p&gt;The following is a summary of the procedures to get the eigen faces:&lt;/p&gt;  &lt;p&gt;1. for a set of faces, F = {f1,…,fn}, calculate its mean face m. F is a d-by-n matrix, where d is the number of pixels of an face and n is the number of faces in the dataset. fi is a column vector. &lt;/p&gt;  &lt;p&gt;2. subtract the mean face from F, get A = {f1-m, … fn-m}. We want to get the eigenvalues and eigenvectors of Cov(A) = A * A’. The eigenvectors associated with big eigenvalues are eigenfaces. (In the program part, we will visualize these eigenfaces). &lt;/p&gt;  &lt;p&gt;However, Cov(A) is usually too big. (If there are 10K pixels in each face, then the size of Cov(A) would be 10K-by-10K). A trick is to calculate the eigenvalues and eigenvectors of A’ * A (which is of small size if the number of faces n is small.) and calculate Cov(A)’s based on them. This trick is well documented in the program section. &lt;/p&gt;  &lt;p&gt;Once we get the Eigen faces, we can do face recognition. As said before, each eigen face is actually an direction, we can map our new face and the face in the database into the space defined by the set of Eigen faces. For example, if we have 10 Eigen faces, and we know that they are orthogonal to each other, thus a 10-D real space is defined by the 10 Eigen faces. Once real faces are mapped into this 10-D space, we can measure the distance between the new face and the existing faces, and find the closet one as the label face for the new face.  &lt;/p&gt;  &lt;p&gt;Here are some reference for interested readers:&lt;/p&gt;  &lt;p&gt;1. &lt;a href="http://en.wikipedia.org/wiki/Eigenface"&gt;Eigenface wiki page.&lt;/a&gt; It has the mathematics and links to programs and papers. &lt;/p&gt;  &lt;p&gt;2. &lt;a href="http://www.pages.drexel.edu/%7Esis26/Eigenface%20Tutorial.htm"&gt;A Matab Tutorial&lt;/a&gt;. and &lt;a href="http://www.cs.ait.ac.th/%7Emdailey/matlab/"&gt;this one&lt;/a&gt;. These two are very good tutorials using Matlab. &lt;/p&gt;  &lt;p&gt;3. &lt;a href="http://en.wikipedia.org/wiki/Principal_component_analysis"&gt;Principal Component Analysis (PCA)&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Singular_value_decomposition"&gt;Singular Value Decomposition&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix"&gt;Eigendecompsition&lt;/a&gt;.  If you want to know more theory of it, you can move on to have a good understanding of PCA. &lt;/p&gt;  &lt;h2&gt;&lt;/h2&gt;  &lt;h2&gt;The ORL face dataset&lt;/h2&gt;  &lt;p&gt;There are more than 10 face datasets available online, which range from different complexity. I use an quite old one &lt;a href="http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html"&gt;the ORL face dataset&lt;/a&gt;. This one is well pre-processed, e.g. under the same light condition, of the same size, etc. &lt;/p&gt;  &lt;p&gt;Using this dataset greatly eases our work in processing the images and enables us to focus on the Eigen face algorithm in F#. &lt;/p&gt;  &lt;p&gt;This dataset contains 400 faces from 40 subjects, with each having 10 faces. The images are of .PGM format. For the I/O of the images, I wrote a separate blog post on reading, writing and visualizing this image format. That post also features on how to write a simple GUI in F#. &lt;/p&gt;  &lt;p&gt;The data structures for the dataset:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;pgm = matrix&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;person = matrix array&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;dataset = person array&lt;/pre&gt;Each PGM image is an F# matrix, each person is an array of matrices, and the dataset is an array of persons.&lt;br /&gt;&lt;br /&gt;Then we define functions to read the dataset for a given folder:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;readPerson folder =&lt;br /&gt;  seq {&lt;br /&gt;      &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=1 &lt;span style="color:blue;"&gt;to &lt;/span&gt;10 &lt;span style="color:blue;"&gt;do  &lt;br /&gt;          let &lt;/span&gt;name = folder + string(i) + &lt;span style="color:maroon;"&gt;".pgm"&lt;br /&gt;          &lt;/span&gt;&lt;span style="color:blue;"&gt;yield &lt;/span&gt;readPgm name&lt;br /&gt;  } |&amp;gt; Seq.toArray&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;readDataset folder =&lt;br /&gt;  seq {&lt;br /&gt;      &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=1 &lt;span style="color:blue;"&gt;to &lt;/span&gt;40 &lt;span style="color:blue;"&gt;do&lt;br /&gt;          let &lt;/span&gt;name = folder + &lt;span style="color:maroon;"&gt;"s" &lt;/span&gt;+ string(i) + &lt;span style="color:maroon;"&gt;"\\"&lt;br /&gt;          &lt;/span&gt;&lt;span style="color:blue;"&gt;yield &lt;/span&gt;readPerson name&lt;br /&gt;  } |&amp;gt; Seq.toArray&lt;/pre&gt;We also define functions to split the dataset into training data and testing data. Training data are used to get the eigenfaces, and then faces in the testing data are recognized using the eigenfaces. For each person, the first 7 faces are used for training, and the 3 left ones are used for testing.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;splitDataSet (ds:dataset) =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;train = seq {&lt;br /&gt;      &lt;span style="color:blue;"&gt;for &lt;/span&gt;p &lt;span style="color:blue;"&gt;in &lt;/span&gt;ds &lt;span style="color:blue;"&gt;do&lt;br /&gt;          yield &lt;/span&gt;(&lt;br /&gt;              seq {&lt;br /&gt;              &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;9 &lt;span style="color:blue;"&gt;do&lt;br /&gt;                  if &lt;/span&gt;i&amp;lt;7 &lt;span style="color:blue;"&gt;then yield &lt;/span&gt;p.[i]&lt;br /&gt;              } |&amp;gt; Seq.toArray )&lt;br /&gt;      }&lt;br /&gt;    &lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;test = seq {&lt;br /&gt;      &lt;span style="color:blue;"&gt;for &lt;/span&gt;p &lt;span style="color:blue;"&gt;in &lt;/span&gt;ds &lt;span style="color:blue;"&gt;do&lt;br /&gt;          yield &lt;/span&gt;(&lt;br /&gt;              seq {&lt;br /&gt;              &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;9 &lt;span style="color:blue;"&gt;do&lt;br /&gt;                  if &lt;/span&gt;i&amp;gt;=7 &lt;span style="color:blue;"&gt;then yield &lt;/span&gt;p.[i]&lt;br /&gt;              } |&amp;gt; Seq.toArray )&lt;br /&gt;      }&lt;br /&gt;  train |&amp;gt; Seq.toArray, test |&amp;gt; Seq.toArray&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;strip (data: 'a array array) =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;dat = seq {&lt;br /&gt;      &lt;span style="color:blue;"&gt;for &lt;/span&gt;x &lt;span style="color:blue;"&gt;in &lt;/span&gt;data &lt;span style="color:blue;"&gt;do&lt;br /&gt;          for &lt;/span&gt;y &lt;span style="color:blue;"&gt;in &lt;/span&gt;x &lt;span style="color:blue;"&gt;do&lt;br /&gt;              yield &lt;/span&gt;y&lt;br /&gt;      }&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;label = seq {&lt;br /&gt;      &lt;span style="color:blue;"&gt;let &lt;/span&gt;i = ref 0&lt;br /&gt;      &lt;span style="color:blue;"&gt;for &lt;/span&gt;x &lt;span style="color:blue;"&gt;in &lt;/span&gt;data &lt;span style="color:blue;"&gt;do&lt;br /&gt;          for &lt;/span&gt;y &lt;span style="color:blue;"&gt;in &lt;/span&gt;x &lt;span style="color:blue;"&gt;do&lt;br /&gt;              yield &lt;/span&gt;!i&lt;br /&gt;          i := !i + 1&lt;br /&gt;      }&lt;br /&gt;  dat |&amp;gt; Seq.toArray, label |&amp;gt; Seq.toArray&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Function strip is used to transform a data set (matrix array array) into a flat array (matrix array) and remember their labels.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;The program&lt;/h2&gt;First we write some help functions:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;mat2vec (A:matrix) =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;n = A.NumRows&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;m = A.NumCols&lt;br /&gt;  Vector.init (n*m) (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i&lt;span style="color:blue;"&gt;-&amp;gt;&lt;/span&gt;A.[i%n, i/n])&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;vec2mat (vec:vector) nrow ncol =&lt;br /&gt;  &lt;span style="color:blue;"&gt;if &lt;/span&gt;vec.Length &amp;lt;&amp;gt; nrow * ncol &lt;span style="color:blue;"&gt;then &lt;/span&gt;failwith &lt;span style="color:maroon;"&gt;"vec length != nrow * ncol "&lt;br /&gt;  &lt;/span&gt;Matrix.init nrow ncol (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;vec.[j*nrow+i])&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;colSum (m:matrix) =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = m.NumRows&lt;br /&gt;  Matrix.foldByRow (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;a b &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;a+b) (Vector.create nrow 0.0) m&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;colMean (m:matrix) =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = m.NumCols&lt;br /&gt;  colSum m * (1.0 / float ncol)&lt;/pre&gt;mat2vec is concate the columns of a matrix into a long column vector, while vec2mat does the inverse. Their behaviors are similar to the reshape function in Matlab. colSum and colMean are similar to sum and mean in Matlab.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To use the eigen decomposition for a symmetric matrix, we call lapack via math-provider. Read &lt;a href="http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-ii.html"&gt;Part II of this F# matrix series&lt;/a&gt; to know to how use them. &lt;/p&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.Math&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.Math.Experimental&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;isSucc = Experimental.LinearAlgebra.Lapack.Start()&lt;br /&gt;printfn &lt;span style="color:maroon;"&gt;"service = %A" &lt;/span&gt;(&lt;span style="color:blue;"&gt;if &lt;/span&gt;isSucc &lt;span style="color:blue;"&gt;then &lt;/span&gt;&lt;span style="color:maroon;"&gt;"Netlib lapack" &lt;/span&gt;&lt;span style="color:blue;"&gt;else &lt;/span&gt;&lt;span style="color:maroon;"&gt;"Managed code"&lt;/span&gt;)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;eig = LinearAlgebra.EigenSpectrumWhenSymmetric&lt;/pre&gt;eig is a shorthand for EigenSpectrumWhenSymmetric. Notice that eig gets the eigenvectors in a matrix, &lt;strong&gt;each row of which is an eigenvector&lt;/strong&gt;. This may be a bug in the math-provider wrapper, but since it only deals with square and symmetric matrices, this incontinence does not hurt too much.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The main function:&lt;/p&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;eigCov (faces:matrix) numVecs =&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color:green;"&gt;// each column is a face&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = faces.NumRows&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = faces.NumCols&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;colm = colMean faces&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;A = Matrix.init nrow ncol (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;faces.[i,j] - colm.[i])&lt;br /&gt;  &lt;span style="color:green;"&gt;// A - each column is a difference face&lt;br /&gt;  // Cov(A) = A * A', a big matrix&lt;br /&gt;  // trick: A'*A*v(i) = lambda(i)*v(i)&lt;br /&gt;  //      A*A'*A*u(i) = lambda(i)*A*u(i)&lt;br /&gt;  //      (A*u(i)) is an eignvector of Cov(A)&lt;br /&gt;  // L(A) = A' * A&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;L = A.Transpose * A&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;val1, vec1 = eig L &lt;span style="color:green;"&gt;// each row of vec1 is an eigenvector&lt;br /&gt;  // get the eigen values and eigen vectors for Cov(A)&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;v = val1 * (1.0 / float (ncol - 1))&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;u = A * vec1.Transpose&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:green;"&gt;// normalize eigen vectors&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;let mutable &lt;/span&gt;cnt = 0&lt;br /&gt;  &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;ncol-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;      if &lt;/span&gt;abs v.[i] &amp;lt; 1e-8 &lt;span style="color:blue;"&gt;then&lt;br /&gt;          &lt;/span&gt;u.[0..nrow-1,i..i] &amp;lt;- Matrix.create nrow 1 0.0&lt;br /&gt;      &lt;span style="color:blue;"&gt;else&lt;br /&gt;          &lt;/span&gt;cnt &amp;lt;- cnt + 1&lt;br /&gt;          &lt;span style="color:blue;"&gt;let &lt;/span&gt;norm = Vector.norm (u.[0..nrow-1,i..i].ToVector())&lt;br /&gt;          u.[0..nrow-1,i..i] &amp;lt;- u.[0..nrow-1,i..i] * ( 1.0 / float norm )&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:green;"&gt;// sort&lt;br /&gt;  // let u' = u.PermuteColumns (fun j -&amp;gt; j ) //u.NumCols - j - 1)&lt;br /&gt;  // the PermuteColumns function is buggy&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;u' = Matrix.init u.NumRows u.NumCols (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;u.[i, u.NumCols - j - 1])&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;v' = v.Permute (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;v.NumRows - j - 1)&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;select = min cnt numVecs&lt;br /&gt;  &lt;span style="color:green;"&gt;//printfn "%A" select&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;if &lt;/span&gt;cnt &amp;lt; numVecs &lt;span style="color:blue;"&gt;then&lt;br /&gt;      &lt;/span&gt;printfn &lt;span style="color:maroon;"&gt;"warning: numvec is %d; only %d exist." &lt;/span&gt;numVecs cnt&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;v1 = v'.[0..(select-1)]&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;u1 = u'.Columns(0, select)&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;transformed = (u1.Transpose) * A&lt;br /&gt;  v1, u1, colm, transformed&lt;/pre&gt;and:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;eigenface (p:matrix array) numVecs =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;B = p |&amp;gt; Array.map mat2vec&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = B.[0].Length &lt;span style="color:green;"&gt;// nrow is big  == # of elements in a face&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = B.Length  &lt;span style="color:green;"&gt;// # of faces for a person&lt;br /&gt;  // put a 2d face into a vector as a column in matrix A&lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A = Matrix.init nrow ncol (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;B.[j].[i])&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;v, u, mface, transformed = eigCov A numVecs&lt;br /&gt;  &lt;span style="color:green;"&gt;(*&lt;br /&gt;  (new PGMViewer(vec2mat mface 112 92, "mean face")).Show()&lt;br /&gt;  for i=0 to 2 do&lt;br /&gt;      let eface = scale (u.Column(i))&lt;br /&gt;      let f = vec2mat eface 112 92&lt;br /&gt;      (new PGMViewer(f, "eigenface " + i.ToString())).Show()&lt;br /&gt;  *)&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;v, u, mface, transformed&lt;/pre&gt;This function transform an image matrix into a vector and call the eigCov  to get the eigenfaces. The commented lines in this function is used to output the mean face and the first 3 eigenfaces:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_4bFBK2BKrQI/S7cQce236kI/AAAAAAAAA_E/3pdNvya-szU/s1600-h/eigenfaces2.png"&gt;&lt;img style="border-width: 0px; display: inline;" title="eigenfaces" alt="eigenfaces" src="http://lh4.ggpht.com/_4bFBK2BKrQI/S7cQdREnaLI/AAAAAAAAA_I/CS5hYlEOqKM/eigenfaces_thumb.png?imgmax=800" width="244" border="0" height="219" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Use eigenfaces to do recognition for a new face:&lt;/p&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;recognition (u:matrix) (mface:vector) (transformed:matrix) (face:vector) =&lt;br /&gt;&lt;pre class="code"&gt;    &lt;span style="color:blue;"&gt;let &lt;/span&gt;d = face - mface&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;t = u.Transpose * d&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;distance a b = Vector.norm (a - b)&lt;br /&gt;  {0..transformed.NumCols-1}&lt;br /&gt;  |&amp;gt; Seq.map (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;distance t (transformed.Column(j)), j)&lt;br /&gt;  |&amp;gt; Seq.min&lt;br /&gt;  |&amp;gt; snd&lt;/pre&gt;To put the recognition into a learning and testing framework:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;learning data =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;train, test = splitDataSet data&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;dat, lab = strip train&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;dat0, lab0 = strip test&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;v, d, mface, tran = eigenface dat 10&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;correctCnt =&lt;br /&gt;      dat0&lt;br /&gt;      |&amp;gt; Seq.mapi (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i f &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;              let &lt;/span&gt;idx = recognition d mface tran (mat2vec f)&lt;br /&gt;              &lt;span style="color:blue;"&gt;let &lt;/span&gt;label = lab.[idx]&lt;br /&gt;              &lt;span style="color:blue;"&gt;if &lt;/span&gt;label = lab0.[i] &lt;span style="color:blue;"&gt;then &lt;/span&gt;1 &lt;span style="color:blue;"&gt;else &lt;/span&gt;0&lt;br /&gt;          )&lt;br /&gt;      |&amp;gt; Seq.sum&lt;br /&gt;  float correctCnt / (float dat0.Length)&lt;/pre&gt;Using 10 most significant eigen faces from the 280(7 * 40) faces, we recognize the remaining 120(3 * 40) faces, and get 113 faces recognized to the correct person, thus a 94.17% accuracy, which is reasonable good considering that we have done any normalization on the faces, and the classifier is only 1 nearest neighbor.&lt;br /&gt;&lt;h2&gt;Remark&lt;/h2&gt;In this post, we have seen that how F# could be used as an prototype tool like Matlab. In the matrix computation part, we need to write some utility functions ourselves, while in Matlab they are built in. However, the strength of F# comes from the language part, we could control the training and testing more freely than in Matlab. Moreover, once we have developed this face recognition component, it is just straightforward to use C# or VB.Net to call it.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Although we have a high accuracy in the final test, the result is only for a well prepared dataset. In real situations, things would be very hard. Maybe over 90% time is spent on tuning best parameters and preprocessing the face images. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Attachment&lt;/h2&gt;wait.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-7863461705557710233?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/7863461705557710233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iii.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7863461705557710233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/7863461705557710233'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/04/matrix-and-linear-algebra-in-f-part-iii.html' title='Matrix and linear algebra in F#, Part III: Eigen decomposition and face recognition'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_4bFBK2BKrQI/S7cQdREnaLI/AAAAAAAAA_I/CS5hYlEOqKM/s72-c/eigenfaces_thumb.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-3148644091308857799</id><published>2010-04-03T17:49:00.001+08:00</published><updated>2010-04-03T23:18:10.833+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><category scheme='http://www.blogger.com/atom/ns#' term='PGM'/><title type='text'>A PGM Image Viewer in F#, with PGM writer and reader</title><content type='html'>&lt;p&gt;This post is to serve the face recognition project. &lt;/p&gt;  &lt;p&gt;In that project, we need a little tool to visualize/display Eigen faces. It would be very helpful during debugging/testing to display the Eigen faces and mean faces visually after they are calculated. We would also like to display an image to see if our IO part is correct or not. &lt;/p&gt;  &lt;p&gt;T&lt;a href="http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html"&gt;he ORL face data set&lt;/a&gt; I use is of PGM format, so I need to load these images and display them. &lt;/p&gt;  &lt;p&gt;First let’s get familiar with the PGM image format. PGM stands for Net&lt;strong&gt;p&lt;/strong&gt;bm &lt;strong&gt;g&lt;/strong&gt;rayscale i&lt;strong&gt;m&lt;/strong&gt;age. Its format is every easy:&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;P5 or P2&lt;br /&gt;# 0 or more lines for comments&lt;br /&gt;nrows ncolumns&lt;br /&gt;max-value&lt;br /&gt;image content &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The format of image content part could be either text(P2) or binary(P5). More detail is &lt;a href="http://netpbm.sourceforge.net/doc/pgm.html"&gt;here&lt;/a&gt; and &lt;a href="http://meru.cecs.missouri.edu/people/hai/research/pgm4.c"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Before reading the file, let’s define the data structure for a PGM image. Because it is grey scale, thus it is actually an F# matrix:&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;type &lt;/span&gt;pgm = matrix&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;We would use two readers: a BinaryReader for binary image content and a StreamReader for text image content. (I think we could also use StreamReader for both, But I started using BinaryReader for binary image content and added the text image content later, so two readers are used.)&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;exception &lt;/span&gt;PgmFormatError &lt;span style="color:blue;"&gt;of &lt;/span&gt;string&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;readPgm path =&lt;br /&gt;&lt;pre class="code"&gt;   &lt;span style="color:blue;"&gt;let rec &lt;/span&gt;skipCommentLine br =&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;l = getline br&lt;br /&gt;     &lt;span style="color:blue;"&gt;if &lt;/span&gt;l.StartsWith(&lt;span style="color:maroon;"&gt;"#"&lt;/span&gt;) &lt;span style="color:blue;"&gt;then&lt;br /&gt;         &lt;/span&gt;skipCommentLine br&lt;br /&gt;     &lt;span style="color:blue;"&gt;else&lt;br /&gt;         &lt;/span&gt;l&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;br = &lt;span style="color:blue;"&gt;new &lt;/span&gt;BinaryReader(File.Open(path, FileMode.Open))&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;pgmType = getline br&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;sizeStr = skipCommentLine br&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;s = sizeStr.Split &lt;span style="color:maroon;"&gt;' '&lt;br /&gt; &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = int (s.[0])&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = int (s.[1])&lt;br /&gt; printfn &lt;span style="color:maroon;"&gt;"nrow = %d ncol = %d" &lt;/span&gt;nrow ncol&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;maxStr = getline br&lt;br /&gt;&lt;br /&gt; &lt;span style="color:blue;"&gt;if &lt;/span&gt;(pgmType.StartsWith(&lt;span style="color:maroon;"&gt;"P5"&lt;/span&gt;)) &lt;span style="color:blue;"&gt;then&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:green;"&gt;// binary format&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:blue;"&gt;let mutable &lt;/span&gt;b = Array.create (nrow * ncol) 0uy&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;nread = br.Read(b, 0, (nrow*ncol))&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;bb = b&lt;br /&gt;     Matrix.init nrow ncol (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;float (bb.[i*ncol+j]))&lt;br /&gt; &lt;span style="color:blue;"&gt;elif &lt;/span&gt;(pgmType.StartsWith(&lt;span style="color:maroon;"&gt;"P2"&lt;/span&gt;)) &lt;span style="color:blue;"&gt;then&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:green;"&gt;// text format&lt;br /&gt;     &lt;/span&gt;br.Close() &lt;span style="color:green;"&gt;// close the file and re-open&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:blue;"&gt;use &lt;/span&gt;sr = &lt;span style="color:blue;"&gt;new &lt;/span&gt;StreamReader(path)&lt;br /&gt;     sr.ReadLine() |&amp;gt; ignore&lt;br /&gt;     &lt;span style="color:blue;"&gt;let mutable &lt;/span&gt;line = sr.ReadLine()&lt;br /&gt;     &lt;span style="color:blue;"&gt;while &lt;/span&gt;line.StartsWith(&lt;span style="color:maroon;"&gt;"#"&lt;/span&gt;) &lt;span style="color:blue;"&gt;do&lt;br /&gt;         &lt;/span&gt;line &amp;lt;- sr.ReadLine()&lt;br /&gt;     sr.ReadLine() |&amp;gt; ignore&lt;br /&gt;&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;str =&lt;br /&gt;         seq {&lt;br /&gt;             &lt;span style="color:blue;"&gt;while &lt;/span&gt;not sr.EndOfStream &lt;span style="color:blue;"&gt;do&lt;br /&gt;                 yield &lt;/span&gt;sr.ReadLine()&lt;br /&gt;         }&lt;br /&gt;         |&amp;gt; Seq.fold (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;a b &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;a + b) &lt;span style="color:maroon;"&gt;""&lt;br /&gt;      &lt;br /&gt;     &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pic = Matrix.create nrow ncol 0.0&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;vals = str.Split([|&lt;span style="color:maroon;"&gt;' '&lt;/span&gt;;&lt;span style="color:maroon;"&gt;'\t'&lt;/span&gt;;&lt;span style="color:maroon;"&gt;'\r'&lt;/span&gt;;&lt;span style="color:maroon;"&gt;'\n'&lt;/span&gt;|], StringSplitOptions.RemoveEmptyEntries)&lt;br /&gt;     printfn &lt;span style="color:maroon;"&gt;"%A" &lt;/span&gt;vals&lt;br /&gt;     Matrix.init nrow ncol (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;float (vals.[i*ncol+j]))&lt;br /&gt; &lt;span style="color:blue;"&gt;else&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:green;"&gt;// unknown format&lt;br /&gt;     &lt;/span&gt;raise (PgmFormatError(path))&lt;/pre&gt;&lt;br /&gt;The getline function for BinaryReader:&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;let &lt;/span&gt;getline (br:BinaryReader) =&lt;br /&gt;&lt;pre class="code"&gt;   &lt;span style="color:blue;"&gt;let &lt;/span&gt;chSeq = seq {&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;ch = ref (br.ReadChar())&lt;br /&gt;     &lt;span style="color:blue;"&gt;while &lt;/span&gt;!ch &amp;lt;&amp;gt; &lt;span style="color:maroon;"&gt;'\n' &lt;/span&gt;&lt;span style="color:blue;"&gt;do&lt;br /&gt;         yield &lt;/span&gt;!ch&lt;br /&gt;         ch :=  br.ReadChar()&lt;br /&gt;     }&lt;br /&gt;      &lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;chArr = chSeq |&amp;gt; Seq.toArray&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;line = &lt;span style="color:blue;"&gt;new &lt;/span&gt;String(chArr)&lt;br /&gt;&lt;br /&gt; line&lt;/pre&gt;Next we write the writer for a pgm image, I only implemented the writer for the text content case as this is mainly used for outputting some intermediate results, thus need to be human-readable.&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;writePgm (path:string) (p:pgm) =&lt;br /&gt;     &lt;span style="color:blue;"&gt;use &lt;/span&gt;fs = &lt;span style="color:blue;"&gt;new &lt;/span&gt;FileStream(path, FileMode.Create)&lt;br /&gt;&lt;pre class="code"&gt;   &lt;span style="color:blue;"&gt;use &lt;/span&gt;sw = &lt;span style="color:blue;"&gt;new &lt;/span&gt;StreamWriter(fs, Encoding.ASCII)&lt;br /&gt; sw.Write(&lt;span style="color:maroon;"&gt;"P2\n{0} {1}\n255\n"&lt;/span&gt;, p.NumCols, p.NumRows)&lt;br /&gt;&lt;br /&gt; &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;(p.NumRows - 1) &lt;span style="color:blue;"&gt;do&lt;br /&gt;     for &lt;/span&gt;j=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;(p.NumCols - 1) &lt;span style="color:blue;"&gt;do&lt;br /&gt;         &lt;/span&gt;sw.Write(&lt;span style="color:maroon;"&gt;" " &lt;/span&gt;+ string (p.[i,j]))&lt;br /&gt;     sw.WriteLine()&lt;/pre&gt;&lt;br /&gt;Finally, the viewer. F# actually is quite good at doing small GUI programs. First inherits from a form:&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;type &lt;/span&gt;PGMViewer(pic:matrix, name:string) &lt;span style="color:blue;"&gt;as &lt;/span&gt;this =&lt;br /&gt;&lt;pre class="code"&gt;   &lt;span style="color:blue;"&gt;inherit &lt;/span&gt;Form(Width = 300, Height = 300)&lt;/pre&gt;&lt;br /&gt;then create a main menu, and menu items in it, a PictureBox to display bitmap images and add a SaveFileDialog:&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;let &lt;/span&gt;mainmenu = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MainMenu()&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;mFile = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MenuItem()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;miFileSave = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MenuItem()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;miExit = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MenuItem()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;pb = &lt;span style="color:blue;"&gt;new &lt;/span&gt;PictureBox()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;filesave = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SaveFileDialog()&lt;/pre&gt;Set necessary properties of these objects (put these code in a &lt;strong&gt;&lt;span style="color: rgb(0, 128, 255);"&gt;do&lt;/span&gt;&lt;/strong&gt; block)&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;do&lt;br /&gt; &lt;/span&gt;&lt;span style="color:green;"&gt;// set the property of picture box&lt;br /&gt; &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = pic.NumRows&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = pic.NumCols&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;image = &lt;span style="color:blue;"&gt;new &lt;/span&gt;Bitmap(ncol, nrow)&lt;br /&gt; printfn &lt;span style="color:maroon;"&gt;"%A %A" &lt;/span&gt;nrow ncol&lt;br /&gt; &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;nrow-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;     for &lt;/span&gt;j=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;ncol-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;         &lt;/span&gt;image.SetPixel(j, i, Color.FromArgb(int pic.[i,j], int pic.[i,j], int pic.[i,j]))&lt;br /&gt; pb.Image &amp;lt;- image&lt;br /&gt; pb.Height &amp;lt;- nrow&lt;br /&gt; pb.Width &amp;lt;- ncol&lt;br /&gt;&lt;br /&gt; &lt;span style="color:green;"&gt;// add picture box&lt;br /&gt; &lt;/span&gt;this.Controls.Add(pb)&lt;br /&gt;&lt;br /&gt; &lt;span style="color:green;"&gt;// set memu property&lt;br /&gt; &lt;/span&gt;miFileSave.Index &amp;lt;- 0&lt;br /&gt; miFileSave.Text &amp;lt;- &lt;span style="color:maroon;"&gt;"&amp;amp;Save"&lt;br /&gt; &lt;/span&gt;miExit.Index &amp;lt;- 1&lt;br /&gt; miExit.Text &amp;lt;- &lt;span style="color:maroon;"&gt;"&amp;amp;Exit"&lt;br /&gt; &lt;/span&gt;mFile.Index &amp;lt;- 0&lt;br /&gt; mFile.Text &amp;lt;- &lt;span style="color:maroon;"&gt;"&amp;amp;File"&lt;br /&gt; &lt;/span&gt;mFile.MenuItems.Add(miFileSave) |&amp;gt; ignore&lt;br /&gt; mFile.MenuItems.Add(miExit) |&amp;gt; ignore&lt;br /&gt; mainmenu.MenuItems.Add(mFile) |&amp;gt; ignore&lt;br /&gt; &lt;span style="color:green;"&gt;// add menu&lt;br /&gt; &lt;/span&gt;this.Menu &amp;lt;- mainmenu&lt;br /&gt;&lt;span style="font-family:tahoma;"&gt;And add event handler for click event of the save menu item:&lt;/span&gt;&lt;/pre&gt;// savefile dialog&lt;br /&gt;&lt;pre class="code"&gt;filesave.DefaultExt &amp;lt;- &lt;span style="color:maroon;"&gt;"pgm"&lt;br /&gt;&lt;/span&gt;filesave.Filter &amp;lt;- &lt;span style="color:maroon;"&gt;"PGM Files|*.pgm"&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;filesaveClick e =&lt;br /&gt; &lt;span style="color:blue;"&gt;if &lt;/span&gt;pb.Image = &lt;span style="color:blue;"&gt;null then&lt;br /&gt;     &lt;/span&gt;()&lt;br /&gt; &lt;span style="color:blue;"&gt;else&lt;br /&gt;     if &lt;/span&gt;filesave.ShowDialog() = DialogResult.OK &lt;span style="color:blue;"&gt;then&lt;br /&gt;         let &lt;/span&gt;bm = pb.Image :?&amp;gt; Bitmap&lt;br /&gt;         &lt;span style="color:blue;"&gt;let &lt;/span&gt;pic = Matrix.init (bm.Height) (bm.Width) (&lt;br /&gt;             &lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;                 let &lt;/span&gt;c = bm.GetPixel(j,i)&lt;br /&gt;                 float c.R * 0.3 + float c.G * 0.59 + float c.B * 0.11&lt;br /&gt;             )&lt;br /&gt;              &lt;br /&gt;         writePgm filesave.FileName pic&lt;br /&gt;miFileSave.Click.Add(filesaveClick)&lt;/pre&gt;Put them together:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;System.Windows.Forms&lt;br /&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;System.Drawing&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;PGMViewer(pic:matrix, name:string) &lt;span style="color:blue;"&gt;as &lt;/span&gt;this =&lt;br /&gt; &lt;span style="color:blue;"&gt;inherit &lt;/span&gt;Form(Width = 300, Height = 300)&lt;br /&gt;&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;mainmenu = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MainMenu()&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;mFile = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MenuItem()&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;miFileSave = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MenuItem()&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;miExit = &lt;span style="color:blue;"&gt;new &lt;/span&gt;MenuItem()&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;pb = &lt;span style="color:blue;"&gt;new &lt;/span&gt;PictureBox()&lt;br /&gt; &lt;span style="color:blue;"&gt;let &lt;/span&gt;filesave = &lt;span style="color:blue;"&gt;new &lt;/span&gt;SaveFileDialog()&lt;br /&gt;&lt;br /&gt; &lt;span style="color:blue;"&gt;do&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:green;"&gt;// set the property of picture box&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = pic.NumRows&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = pic.NumCols&lt;br /&gt;     &lt;span style="color:blue;"&gt;let &lt;/span&gt;image = &lt;span style="color:blue;"&gt;new &lt;/span&gt;Bitmap(ncol, nrow)&lt;br /&gt;     printfn &lt;span style="color:maroon;"&gt;"%A %A" &lt;/span&gt;nrow ncol&lt;br /&gt;     &lt;span style="color:blue;"&gt;for &lt;/span&gt;i=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;nrow-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;         for &lt;/span&gt;j=0 &lt;span style="color:blue;"&gt;to &lt;/span&gt;ncol-1 &lt;span style="color:blue;"&gt;do&lt;br /&gt;             &lt;/span&gt;image.SetPixel(j, i, Color.FromArgb(int pic.[i,j], int pic.[i,j], int pic.[i,j]))&lt;br /&gt;     pb.Image &amp;lt;- image&lt;br /&gt;     pb.Height &amp;lt;- nrow&lt;br /&gt;     pb.Width &amp;lt;- ncol&lt;br /&gt;  &lt;br /&gt;     &lt;span style="color:green;"&gt;// add picture box&lt;br /&gt;     &lt;/span&gt;this.Controls.Add(pb)&lt;br /&gt;  &lt;br /&gt;     &lt;span style="color:green;"&gt;// set memu property&lt;br /&gt;     &lt;/span&gt;miFileSave.Index &amp;lt;- 0&lt;br /&gt;     miFileSave.Text &amp;lt;- &lt;span style="color:maroon;"&gt;"&amp;amp;Save"&lt;br /&gt;     &lt;/span&gt;miExit.Index &amp;lt;- 1&lt;br /&gt;     miExit.Text &amp;lt;- &lt;span style="color:maroon;"&gt;"&amp;amp;Exit"&lt;br /&gt;     &lt;/span&gt;mFile.Index &amp;lt;- 0&lt;br /&gt;     mFile.Text &amp;lt;- &lt;span style="color:maroon;"&gt;"&amp;amp;File"&lt;br /&gt;     &lt;/span&gt;mFile.MenuItems.Add(miFileSave) |&amp;gt; ignore&lt;br /&gt;     mFile.MenuItems.Add(miExit) |&amp;gt; ignore&lt;br /&gt;     mainmenu.MenuItems.Add(mFile) |&amp;gt; ignore&lt;br /&gt;     &lt;span style="color:green;"&gt;// add menu&lt;br /&gt;     &lt;/span&gt;this.Menu &amp;lt;- mainmenu&lt;br /&gt;  &lt;br /&gt;     &lt;span style="color:green;"&gt;// savefile dialog&lt;br /&gt;     &lt;/span&gt;filesave.DefaultExt &amp;lt;- &lt;span style="color:maroon;"&gt;"pgm"&lt;br /&gt;     &lt;/span&gt;filesave.Filter &amp;lt;- &lt;span style="color:maroon;"&gt;"PGM Files|*.pgm"&lt;br /&gt;     &lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;filesaveClick e =&lt;br /&gt;         &lt;span style="color:blue;"&gt;if &lt;/span&gt;pb.Image = &lt;span style="color:blue;"&gt;null then&lt;br /&gt;             &lt;/span&gt;()&lt;br /&gt;         &lt;span style="color:blue;"&gt;else&lt;br /&gt;             if &lt;/span&gt;filesave.ShowDialog() = DialogResult.OK &lt;span style="color:blue;"&gt;then&lt;br /&gt;                 let &lt;/span&gt;bm = pb.Image :?&amp;gt; Bitmap&lt;br /&gt;                 &lt;span style="color:blue;"&gt;let &lt;/span&gt;pic = Matrix.init (bm.Height) (bm.Width) ( &lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;                         let &lt;/span&gt;c = bm.GetPixel(j,i)&lt;br /&gt;                         float c.R * 0.3 + float c.G * 0.59 + float c.B * 0.11&lt;br /&gt;                     )&lt;br /&gt;                      &lt;br /&gt;                 writePgm filesave.FileName pic&lt;br /&gt;     miFileSave.Click.Add(filesaveClick)&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;     &lt;span style="color:green;"&gt;// set title of the dialog&lt;br /&gt;     &lt;/span&gt;this.Text &amp;lt;- &lt;span style="color:maroon;"&gt;"PGMViewer" &lt;/span&gt;+ &lt;span style="color:maroon;"&gt;" - " &lt;/span&gt;+ name&lt;/pre&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="font-family:tahoma;"&gt;And let’s test the viewer now:&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="code"&gt;(&lt;span style="color:blue;"&gt;new &lt;/span&gt;PGMViewer(readPgm &lt;span style="color:maroon;"&gt;@"C:\temp\temp4.pgm"&lt;/span&gt;)).Show()&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_4bFBK2BKrQI/S7cPKnE4fiI/AAAAAAAAA-0/1RITnNUhv84/s1600-h/pgmviewer2.png"&gt;&lt;img style="border-width: 0px; display: inline;" title="pgmviewer" alt="pgmviewer" src="http://lh5.ggpht.com/_4bFBK2BKrQI/S7cPLZEERFI/AAAAAAAAA-8/U03YartWT6c/pgmviewer_thumb.png?imgmax=800" width="244" border="0" height="244" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(Copyright: this face is from the ORL face database.)&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Attachment&lt;/h2&gt;wait.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-3148644091308857799?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/3148644091308857799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/04/pgm-image-viewer-in-f-with-pgm-writer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3148644091308857799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3148644091308857799'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/04/pgm-image-viewer-in-f-with-pgm-writer.html' title='A PGM Image Viewer in F#, with PGM writer and reader'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_4bFBK2BKrQI/S7cPLZEERFI/AAAAAAAAA-8/U03YartWT6c/s72-c/pgmviewer_thumb.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-6493222714507877548</id><published>2010-03-28T23:51:00.001+08:00</published><updated>2010-04-04T23:55:36.732+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='lapack'/><category scheme='http://www.blogger.com/atom/ns#' term='math-provider'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Matrix and linear algebra in F#, Part II: doing linear algebra via math providers</title><content type='html'>&lt;h2&gt; &lt;/h2&gt;  &lt;h2&gt;Long story short&lt;/h2&gt;  &lt;p&gt;Suppose we are using the latest release, 1.9.9.9, which does not support linear algebra anymore in its PowerPack. But linear algebra is supported before in 1.9.7.8. Under its bin\ folder, there is a DLL named FSharp.PowerPack.Math.Providers.dll. “Math providers” means F# calls other math libraries, to be specific, an incomplete managed implementation in F#, Netlib’s Lapack+Blas and Intel MKL. The F# one is immature, while the last two are really really good! Plus Netlib’s implementation is free. &lt;/p&gt;  &lt;p&gt;However, we cannot just use that provider dll because it has conflicts with latest PowerPack. We need to build a new one! Here are the steps:&lt;/p&gt;  &lt;p&gt;1. Compile Netlib’s Lapack to get &lt;strong&gt;lapack.dll&lt;/strong&gt; and &lt;strong&gt;blas.dll&lt;/strong&gt;. Refer to &lt;a href="http://fdatamining.blogspot.com/2010/03/compiling-lapack-for-net-usage.html"&gt;this post&lt;/a&gt; of mine. You can also search online to download these two dlls, e.g. http://www.stanford.edu/~vkl/code/libs.html (I haven't tested this). &lt;/p&gt;  &lt;p&gt;2. Get the source code of &lt;a href="http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/release.aspx"&gt;1.9.7.8.&lt;/a&gt; Go to folder FSharp-1.9.7.8\source\fsppack\FSharp.PowerPack. Remove the old reference to FSharp.PowerPack and add two 1.9.9.9 DLLs to the reference:&lt;/p&gt;  &lt;p&gt;FSharp.PowerPack.dll and FSharp.PowerPack.Compatibility (Because in 1.9.9.9, Compatibility module is separated out.)&lt;/p&gt;  &lt;p&gt;Compile and you get only a few errors regarding “permutation”:&lt;/p&gt;  &lt;p&gt;you could just define it explicitly: type permutation = int –&amp;gt; int&lt;/p&gt;  &lt;p&gt;Done! Now we move on to&lt;/p&gt;  &lt;h2&gt;Use Lapack library&lt;/h2&gt;  &lt;p&gt;First make sure your project &lt;/p&gt;  &lt;p&gt;1. references to &lt;/p&gt;  &lt;p&gt;FSharp.PowerPack.Compatibility, FSharp.PowerPack and FSharp.PowerPack.Math.Providers.dll. &lt;/p&gt;  &lt;p&gt;2. has lapack.dll and blas.dll in the binary output folder. E.g. Debug\ or Release\. (Technically we only need to put them under a folder .Net platform is searchable, similar to Java’s CLASSPATH.)&lt;/p&gt;  &lt;p&gt;Finally, let’s write a simple program:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;open &lt;/span&gt;Microsoft.FSharp.Math&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;r = &lt;span style="color:blue;"&gt;new &lt;/span&gt;System.Random()&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A = Matrix.init 4 4 (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;r.NextDouble() * 1000.0)&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Ainv = Experimental.LinearAlgebra.Inverse A&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;eigen = Experimental.LinearAlgebra.EigenValues A&lt;/pre&gt;Although this program compiles, it gives a Not-Implemented exception on EigenValues function. (It can compute Inverse of A correctly.) This is because functions in LinearAlgebra module use managed implementation (see linear_algebra_managed.fs in lapack folder) on default. However, the managed implementation is incomplete, and not thoroughly tested.&lt;br /&gt;&lt;br /&gt;We need to &lt;strong&gt;first open the lapack service&lt;/strong&gt;:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;isSucc = Experimental.LinearAlgebra.Lapack.Start()&lt;br /&gt;&lt;/pre&gt;Put this before any computation. (Because there’s no documentation at all, I’ve read all the code in lapack folder to find this line…)&lt;br /&gt;&lt;br /&gt;The returned bool value isSucc indicates whether the Lapack service providers successfully loads dlls for Netlib or not.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;If you have Intel MKL&lt;br /&gt;&lt;/h2&gt;Put &lt;strong&gt;mkl_def.dll&lt;/strong&gt; and &lt;strong&gt;mkl_lapack.dll&lt;/strong&gt; under a .Net searchable folder. Notice that the wrapper is for MKL 9.1 not for 10 series.&lt;br /&gt;&lt;p&gt;Btw, the Lapack service provider will choose MKL if both MKL and Netlib are presented.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;The Long story&lt;br /&gt;&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;.Net do have some good math libraries. They are efficient (native performance), easy to use. However, they are not free. For the free ones, there’s a Math.Net open source project, which is still under its alpha-testing phase. These open source projects seem to tend to implement all algorithms from starch in C#, rather than link to some mature ones, like Netlib’s lapack. This certainly has some advantages, like more safe code, better memory management in a .Net sense and exception handling.  However, this strategy would have a long development time.&lt;br /&gt;&lt;/p&gt;I also suspect their efficiency. I have a L-BFGS based logistic regression solver written C++, it runs 3~4 times slower if compiled into .Net using C++/CLI. Drawing a conclusion from this single case is too assertive. (I used STL and STL is slow in C++/CLI, in C++/CLI we’d better use CLI STL.) However, I still trust native libraries to perform basic linear algebra for the performance's seek.&lt;br /&gt;&lt;p&gt;I started to use F# from its 1.9.7.8 (The October CTP, 2009). In that release, the math provider library is already spitted out from the main PowerPack as a standalone DLL. But there’s no documentation on it, except the source code. Other math types, e.g. matrix, vector, are documented. So I didn’t know there exists such a library at first. By occasion, I found the lapack folder in the F# source. Today, I finally have some time to dig into it, get it compiled and know how to start the linear algebra service provider. Fortunately, once started, the service provider is able to automatically find the DLLs of Netlib or MKL, which is really nice.&lt;br /&gt;&lt;/p&gt;The code is still compliable using 1.9.9.9, but I am not sure it will in the future as there are a lot of warnings mentioning deprecated language usages, e.g. use OCaml-style ‘^’to connect two strings.&lt;br /&gt;&lt;p&gt;As this code is under MS open source license, there would be license problems if we maintain and distribute it. So a possible solution is to rewrite the service provider and make it open source (e.g. MIT or BSD license), or, persuade MSR Cambridge or MS VS Team to continue the development. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-6493222714507877548?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/6493222714507877548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-ii.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/6493222714507877548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/6493222714507877548'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-ii.html' title='Matrix and linear algebra in F#, Part II: doing linear algebra via math providers'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-8954569697686626155</id><published>2010-03-28T22:28:00.001+08:00</published><updated>2010-04-03T23:20:30.245+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blas'/><category scheme='http://www.blogger.com/atom/ns#' term='fortran'/><category scheme='http://www.blogger.com/atom/ns#' term='lapack'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Compiling Lapack for .Net usage</title><content type='html'>&lt;h2&gt;The aim: we need lapack.dll and blas.dll&lt;/h2&gt;  &lt;p&gt;A stable and efficient implementation of the common linear algebra routines is essential to engineering. Lapack is the one. Lapack are blas are kind of standard, there are different vendors implementing them, e.g. Intel and AMD both have tuned versions targeted for their hardware. &lt;/p&gt;  &lt;p&gt;There are two major implementations available on Windows-Intel platform: Netlib and Intel Math Kernel Library (MKL). Although MKL is very good (Matlab uses it for its basic linear algebra computation), it costs money.Today I’d like to write a post on how to build Netlib’s implementation for .Net usage.&lt;/p&gt;  &lt;p&gt;There are two tutorials online for the same purpose:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://www.codingday.com/compile-lapack-and-blas-as-dll-on-windows" href="http://www.codingday.com/compile-lapack-and-blas-as-dll-on-windows"&gt;http://www.codingday.com/compile-lapack-and-blas-as-dll-on-windows&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="http://icl.cs.utk.edu/lapack-for-windows/lapack/index.html" href="http://icl.cs.utk.edu/lapack-for-windows/lapack/index.html"&gt;http://icl.cs.utk.edu/lapack-for-windows/lapack/index.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The first one is quite good. we may encounter some minor problems if we directly follow that guide. I’d like to give a new summary here:&lt;/p&gt;  &lt;p&gt;1. Download Lapack+Blas(a single package) from &lt;a href="http://www.netlib.org/lapack/"&gt;Netlib&lt;/a&gt;. Use &lt;a href="http://www.netlib.org/lapack/lapack-3.1.1.tgz"&gt;lapack-3.1.1.tgz&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;2. Install &lt;a href="http://www.mingw.org/"&gt;MinGw&lt;/a&gt;, we only need to install the basic tools + g77 compiler. Because this free compiler only supports Fortran 77 and the latest Lapack is using Fortran 90 now, so in the first step, we use an old version (2007) of Lapack. &lt;/p&gt;  &lt;p&gt;Set the PATH for MinGw. And make sure to successfully compile a hello world Fortran program. &lt;/p&gt;  &lt;p&gt;3. Copy dlamch.f and slamch.f from INSTALL directory SRC directory &lt;/p&gt;  &lt;p&gt;4. Compile BLAS:&lt;/p&gt;  &lt;p&gt;g77 --shared -o blas.dll BLAS\SRC\*.f  -O3&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;5. Compile Lapack:&lt;/p&gt;  &lt;p&gt;cd SRC  &lt;br /&gt;g77 -c *.f -O3   &lt;br /&gt;cd ..   &lt;br /&gt;g77 -shared -o lapack.dll SRC/*.o blas.dll -O3&lt;/p&gt;  &lt;p&gt;If you see some “collect2.exe” error, then change TMP environment variable(originally %USERPROFILE%\AppData\Local\Temp) to a short one, e.g. “c:\temp”&lt;/p&gt;  &lt;p&gt;We should have lapack.dll and blas.dll in the Lapack folder now.&lt;/p&gt;  &lt;p&gt;Once we have the two native DLLs, we can use P/Invoke in .Net to call linear algebra operations in Lapack. If you use F#, then we don’t even need to know P/Invoke because the F# team already did that for us.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;A Note: P/Invoke in Mono platform is also quite convenient, compile lapack and blas into .so files. http://www.mono-project.com/Interop_with_Native_Libraries.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-8954569697686626155?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/8954569697686626155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/03/compiling-lapack-for-net-usage.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8954569697686626155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/8954569697686626155'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/03/compiling-lapack-for-net-usage.html' title='Compiling Lapack for .Net usage'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-3609190231729185648</id><published>2010-03-28T22:08:00.001+08:00</published><updated>2010-04-03T23:19:57.664+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorials'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><title type='text'>Matrix and linear algebra in F#, Part I: the F# Matrix type</title><content type='html'>&lt;p&gt;Every language has libraries, besides the big .Net libraries, F# has two own: the Core, which is shipped with Visual Studio 2010, and the PowerPack, which is an external library developed by MSR Cambridge and Visual Studio Team. Notice that the code quality in PowerPack is actually quite high, it is put outside the Core library because it is evolving fast. Once stable, they may be put into the Core. &lt;/p&gt;  &lt;p&gt;Our concern is matrix and linear algebra operations. There is a matrix class in F# PowerPack. However, Microsoft didn’t officially put the documentation online. For F# 1.9.6, there is a &lt;a href="http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/namespaces.html"&gt;outdated page&lt;/a&gt; on MSR’s website. But it doe not matter we use the old documentation, since the interface for Math haven’t change much since then. &lt;/p&gt;  &lt;p&gt;The namespace for Math is: &lt;/p&gt;  &lt;p&gt;Namespace Microsoft.FSharp.Math &lt;/p&gt;  &lt;p&gt;In this namespace, we have: &lt;/p&gt;  &lt;p&gt;1. complex numbers&lt;/p&gt;  &lt;p&gt;2. Big rational numbers&lt;/p&gt;  &lt;p&gt;3. vector and row-vector&lt;/p&gt;  &lt;p&gt;4. matrix &lt;/p&gt;  &lt;p&gt;In this post, I focus on matrix. &lt;/p&gt;  &lt;h2&gt;The real matrix:&lt;/h2&gt;  &lt;p&gt;First, Names! There is a &lt;u&gt;type&lt;/u&gt; called &lt;strong&gt;matrix&lt;/strong&gt;, which is a matrix holding double or 32-bit long float values. &lt;/p&gt;  &lt;p&gt;There is a &lt;u&gt;module&lt;/u&gt; called &lt;strong&gt;Matrix&lt;/strong&gt;,  inside which there are lots of functions to operate on an F# matrix. &lt;/p&gt;  &lt;p&gt;There is also a&lt;u&gt; function/val &lt;/u&gt;called &lt;strong&gt;matrix&lt;/strong&gt;, which is used like a constructor to construct a new matrix from lists or arrays. &lt;/p&gt;  &lt;p&gt;We can easily create two 3-by-3 matrices using the &lt;strong&gt;matrix&lt;/strong&gt; function:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A = matrix [ [ 1.0; 7.0; 2.0 ];&lt;br /&gt;               [ 1.0; 3.0; 1.0 ];&lt;br /&gt;               [ 2.0; 9.0; 1.0 ]; ]&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;B = matrix [ [ 10.0; 70.0; 20.0 ];&lt;br /&gt;               [ 10.0; 30.0; 10.0 ];&lt;br /&gt;               [ 20.0; 90.0; 10.0 ]; ]&lt;/pre&gt;All the member functions and operators associated with matrix type are &lt;a href="http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/FSharp.PowerPack/Microsoft.FSharp.Math.type_Matrix-1.html"&gt;documented here&lt;/a&gt;. Here are some examples:&lt;br /&gt;&lt;pre class="code"&gt;A+B&lt;br /&gt;A-B&lt;br /&gt;A*B &lt;span style="color:green;"&gt;// matrix product&lt;br /&gt;&lt;/span&gt;A.*B  &lt;span style="color:green;"&gt;// element-wise product&lt;br /&gt;&lt;/span&gt;A * 2.0 &lt;span style="color:green;"&gt;// scalar product&lt;br /&gt;&lt;/span&gt;2.0 * A &lt;span style="color:green;"&gt;// this is also ok&lt;br /&gt;&lt;/span&gt;-A &lt;span style="color:green;"&gt;// negation of a matrix&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;b = vector [5.;8.;9.]; &lt;span style="color:green;"&gt;// defines a vector&lt;br /&gt;&lt;/span&gt;A*b &lt;span style="color:green;"&gt;// matrix-vector product&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;You can get the properties using member functions:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;dim = A.Dimensions&lt;br /&gt;&lt;span style="color:green;"&gt;// val dim : int * int = (3, 3), the dimension is a tuple&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;A' = A.Transpose &lt;span style="color:green;"&gt;// you can use ' in a variable name!&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;nrow = A.NumRows&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;ncol = A.NumCols&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Anew = A.Copy() &lt;span style="color:green;"&gt;// get a new matrix&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Aarr = A.ToArray2D() &lt;span style="color:green;"&gt;// convert to a Array2D type&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Avec = A.ToVector() &lt;span style="color:green;"&gt;// take the first column of A&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Arvec = A.ToRowVector() &lt;span style="color:green;"&gt;// take the fisrt row of A&lt;br /&gt;// ToVector and ToRowVector is usually used&lt;br /&gt;// when you know your matrix is actually a vector&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Accessing a matrix&lt;br /&gt;&lt;/h2&gt;We can have Matlab like access to an F# matrix. One different thing is that the index starts from 0, not 1. Mathematicians like the index to start with 1, e.g. in R and Matlab. While programs like 0-based index, e.g. Numpy for Python.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// notice that the index starts at 0&lt;br /&gt;&lt;/span&gt;A.[2,2] &lt;span style="color:green;"&gt;//The operator [,] allows access a specific&lt;br /&gt;      //element in the matrix, shorthand for A.Item&lt;br /&gt;&lt;/span&gt;A.[2,2] &amp;lt;- 100.0 &lt;span style="color:green;"&gt;// change a value&lt;br /&gt;&lt;/span&gt;A.[1..2,1..2] &lt;span style="color:green;"&gt;// get a sub matrix, shorthand for A.GetSlice&lt;br /&gt;&lt;/span&gt;A.[1..2,1..2] &amp;lt;- matrix [[2.;3.]; [8.;9.;]] &lt;span style="color:green;"&gt;// set a sub matrix, shorthand for A.SetSlice&lt;/span&gt;&lt;/pre&gt;We also have 4 member functions: Column, Columns, Row and Rows:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;A.Column 2 &lt;span style="color:green;"&gt;// Vector&amp;lt;float&amp;gt; = vector [|2.0; 1.0; 1.0|]&lt;br /&gt;&lt;/span&gt;A.Row 2 &lt;span style="color:green;"&gt;// RowVector&amp;lt;float&amp;gt; = rowvec [|2.0; 9.0; 1.0|]&lt;br /&gt;&lt;/span&gt;A.Columns (1,2) &lt;span style="color:green;"&gt;// starts at column 1, take 2 columns&lt;br /&gt;//val it : Matrix&amp;lt;float&amp;gt; = matrix [[7.0; 2.0]&lt;br /&gt;//                                 [3.0; 1.0]&lt;br /&gt;//                                 [9.0; 1.0]]&lt;br /&gt;&lt;/span&gt;A.Rows (1,2) &lt;span style="color:green;"&gt;// starts at row 1, take 2 columns&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;The Matrix module&lt;br /&gt;&lt;/h2&gt;Similar to that F# list type has a List module containing handy functions like map, fold and etc, the real matrix type &lt;strong&gt;matrix&lt;/strong&gt; also has a module.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Asum = Matrix.sum A &lt;span style="color:green;"&gt;// sum of all elements in A&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Aprod = Matrix.prod A &lt;span style="color:green;"&gt;// product of all elements in A&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C = Matrix.create 10 10 1.0 &lt;span style="color:green;"&gt;// create a matrix with 1s&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;table = Matrix.init 9 9 (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;i j &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;(float i + 1.) * (float j + 1.))&lt;br /&gt;&lt;span style="color:green;"&gt;// create a matrix with a function&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;I10 = Matrix.identity 10 &lt;span style="color:green;"&gt;// 10 1s one diagnal&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Atrace = Matrix.trace A &lt;span style="color:green;"&gt;// trace sum&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Asqr = Matrix.map (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;x &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;x*x) A &lt;span style="color:green;"&gt;// A^2&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;And there are some repetitions on the member function/operators of matrix type. E.g. Matrix.add, Matrix.set, Matrix.get, Matrix.toVector, Matrix.toRowVector, Matrix.transpose, etc.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Sparse matrix&lt;br /&gt;&lt;/h2&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;D = Matrix.initSparse 3 3 [ (0,0,1.0); (1,1,2.0); (2,2,3.0); ]&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// init a sparse 3-by-3 matrix&lt;br /&gt;//val it : matrix = matrix [[1.0; 0.0; 0.0]&lt;br /&gt;//                          [0.0; 2.0; 0.0]&lt;br /&gt;//                          [0.0; 0.0; 3.0]]&lt;br /&gt;&lt;/span&gt;D.IsSparse &lt;span style="color:green;"&gt;// sparse test&lt;/span&gt;&lt;/pre&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;E = Matrix.initSparse 100000 100000 [ (0,0,1.0); (1,1,2.0); (2,2,3.0); ]&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Esum = Matrix.sum E&lt;/pre&gt;&lt;br /&gt;However, map, fold, exists, .. are not supported on sparse matrix.&lt;br /&gt;&lt;h2&gt;Int Matrix, BigNum Matrix, and others&lt;/h2&gt;To know about Generic matrix, you may want to read another post of mine:&lt;br /&gt;&lt;p&gt;&lt;a title="http://fdatamining.blogspot.com/2010/03/f-inumerics-interface-and-matrix-class.html" href="http://fdatamining.blogspot.com/2010/03/f-inumerics-interface-and-matrix-class.html"&gt;http://fdatamining.blogspot.com/2010/03/f-inumerics-interface-and-matrix-class.html&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;which also discusses some implementation details of the Matrix class, and how to define your own matrix, e.g. a Pixel matrix.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-3609190231729185648?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/3609190231729185648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-i-f.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3609190231729185648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3609190231729185648'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/03/matrix-and-linear-algebra-in-f-part-i-f.html' title='Matrix and linear algebra in F#, Part I: the F# Matrix type'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-5452874099099706864</id><published>2010-03-27T22:31:00.001+08:00</published><updated>2010-04-03T23:19:41.003+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='matrix'/><category scheme='http://www.blogger.com/atom/ns#' term='generic type'/><title type='text'>F# INumerics Interface, and Matrix class</title><content type='html'>&lt;p&gt;The blog post is inspired by a &lt;a href="http://stackoverflow.com/questions/2518000/f-how-to-create-matrix-of-elements-of-any-other-type-except-double"&gt;question&lt;/a&gt; on StackOverflow. It asks whether the Matrix class in F# PowerPack supports a user defined type. &lt;/p&gt; &lt;p&gt;First the answer is: YES. &lt;/p&gt; &lt;p&gt;But supporting that is non-trivial. I found very few material online on this subject, so I decided to to write a blog post on it. Since matrix is the very core concept in data mining and machine learning, our digging into the F# Matrix and relevant interfaces would also be valuable.  &lt;/p&gt; &lt;p&gt;In this post, I will lead to read a part of the F# PowerPack source code, which is in source\fsppack\FSharp.PowerPack\math folder. &lt;/p&gt; &lt;p&gt;Although the latest version is 1.9.9.9, I would suggest to use the source code of 1.9.7.8, which contains a lapack subfolder in the math folder. What’s this? The researchers in MSR did a great wrapper to enable F# to call common linear algebra libraries, e.g. the free lapack and the commercial IMK (Intel Math Kernel Library). However, this part of code is removed from the 1.9.9.9 release. These code will be merged into Math.Net project, which is still under development. This is just a kind notice, we don’t need to know lapack wrapper today. &lt;/p&gt; &lt;p&gt;Let’s get back to that question. In F#, we can use matrices similar to Matlab:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;B = matrix [ [ 1.0; 7.0 ];&lt;br /&gt;               [ 1.0; 3.0 ] ]&lt;/pre&gt;where matrix is a actually a function, its declaration/signature is &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;val &lt;/span&gt;matrix : seq&amp;lt;#seq&amp;lt;float&amp;gt;&amp;gt; –&lt;span style="color:blue;"&gt;&amp;gt; &lt;/span&gt;matrix&lt;/pre&gt;&lt;pre class="code"&gt;and the implementation is:&lt;/pre&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;matrix ll = Matrix.ofSeq ll&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;As a list implements the IENumerable interface, so we can use a list to initialize a matrix, so is array.   &lt;p&gt;Let see the type of B:&lt;/p&gt;&lt;pre class="code"&gt;val B : matrix = matrix [[1.0; 7.0]&lt;br /&gt;                       [1.0; 3.0]]&lt;/pre&gt;So we can see that matrix is also a type name, it is ok to have the same name for a type and a function. In the source file Matrix.fs, we can find the definition of type matrix:&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;matrix = Matrix&amp;lt;float&amp;gt;&lt;/pre&gt;It is defined as Matrix&amp;lt;float&amp;gt;, where Matrix&amp;lt;_&amp;gt; is called a meta type or polymorphic type. Meta types are used to create other type by putting a element type in between &amp;lt;&amp;gt;. You can think it as an equivalent to C++ templates, or Java/.Net Generics. (There are differences, however that would be a long story.)&lt;br /&gt;&lt;p&gt;Also notice that Matrix in Matrix&amp;lt;_&amp;gt; is a meta type, while Matrix in Matrix.ofSeq is a module name. So they also can have the same name as they are different things. &lt;/p&gt;One thing we can come to mind is that, we can define our integer matrix type as&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;intmatrix = Matrix&amp;lt;int&amp;gt;&lt;/pre&gt;and we also need a constructor for it&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;intmatrix ll = Matrix.Generic.ofSeq  ll&lt;/pre&gt;Matrix.Generic module contains functions for generic type matrices, while the functions in Matrix module are only for float matrices.&lt;br /&gt;&lt;p&gt;We can happily do some operations on integer matrices:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C = intmatrix [ [1;2]; [3;4] ];&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;D = intmatrix [ [1;1]; [1;1] ];&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;E = Matrix.Generic.inplaceAdd C D&lt;br /&gt;C+D&lt;br /&gt;C * 10&lt;/pre&gt;So far so good. So we can put float, int, etc into Matrix&amp;lt;_&amp;gt;, can we put: &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;Pixel = &lt;br /&gt;  | P &lt;span style="color:blue;"&gt;of &lt;/span&gt;int*int*int&lt;/pre&gt;into it? Kind of yes:&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C = pmatrix [ [P(2,2,2);P(2,2,2)]; [P(2,2,2);P(2,2,2)] ];&lt;/pre&gt;But when adding two such matrices, of course error occurs!&lt;br /&gt;&lt;p&gt;We must lack something, e.g. at least we need to define the behavior of the add operator for Pixel type.&lt;/p&gt;The idea is that &lt;strong&gt;every type that could put into Matrix&amp;lt;_&amp;gt; is associated with an instance of INumeric interface&lt;/strong&gt;. Notice that I use the word ‘associate’. It means that for each number type T, we associate an object with it, which tells the matrix class how to add, subtract, multiply, etc. on T. E.g. we have Int32Numerics defined in INumeric.fs, which is associated with int type:&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;Int32Numerics =&lt;br /&gt;{ &lt;span style="color:blue;"&gt;new &lt;/span&gt;IIntegral&amp;lt;int32&amp;gt; &lt;span style="color:blue;"&gt;with&lt;br /&gt;     member &lt;/span&gt;__.Zero = 0&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.One = 1&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Add(a,b) = a + b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Subtract(a,b) = a - b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Multiply(a,b) = a * b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Equals(a,b) = (a = b)&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Compare(a,b) = compare a b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Negate(a) = - a&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Abs(a) = a&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.ToBigInt(a) = &lt;span style="color:blue;"&gt;new &lt;/span&gt;BigInteger(a)&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.OfBigInt(a) = int32 a&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Sign(a) = Math.Sign(a)&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Modulus(a,b) = a % b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Divide(a,b) = a / b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.DivRem(a,b) = (a / b, a % b)&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.ToString((x:int32),fmt,fmtprovider) =&lt;br /&gt;            x.ToString(fmt,fmtprovider)&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Parse(s,numstyle,fmtprovider) =&lt;br /&gt;            System.Int32.Parse(s,numstyle,fmtprovider)&lt;br /&gt;  &lt;span style="color:blue;"&gt;interface &lt;/span&gt;INormFloat&amp;lt;int32&amp;gt; &lt;span style="color:blue;"&gt;with&lt;br /&gt;     member &lt;/span&gt;__.Norm(x) = float (abs x)&lt;br /&gt;}&lt;/pre&gt;&lt;pre class="code"&gt;where IIntegral&amp;lt;&amp;gt; is an interface inherited from INumeric&amp;lt;&amp;gt;. &lt;/pre&gt;Let’s move on to user defined types. We know there is a Big Rational type defined in F# PowerPack. We can easily use it as:&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;rmatrix ll = Matrix.Generic.ofSeq  ll&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;C = rmatrix [ [1N;2N]; [3N;4N] ];&lt;br /&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;D = rmatrix [ [1N;1N]; [1N;1N] ];&lt;br /&gt;C+D&lt;/pre&gt;The rational number is defined in q.fs. However, the code to enable it to be used in Matrix&amp;lt;_&amp;gt; are in other places. First, In INumerics.fs, we find:&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;BigNumNumerics =&lt;br /&gt;{ &lt;span style="color:blue;"&gt;new &lt;/span&gt;IFractional&amp;lt;bignum&amp;gt; &lt;span style="color:blue;"&gt;with&lt;br /&gt;     member &lt;/span&gt;__.Zero = BigNum.Zero&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.One = BigNum.One&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Add(a,b)      = a + b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Subtract(a,b) = a - b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Multiply(a,b) = a * b&lt;br /&gt;     &lt;span style="color:blue;"&gt;member &lt;/span&gt;__.Equals(a,b) = (a = b)&lt;/pre&gt;How will Matrix know a number type is associated with its numeric? The code is in associations.fs:&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;ht =&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;ht = &lt;span style="color:blue;"&gt;new &lt;/span&gt;System.Collections.Generic.Dictionary&amp;lt;Type,obj&amp;gt;()&lt;br /&gt;  &lt;span style="color:blue;"&gt;let &lt;/span&gt;optab =&lt;br /&gt;      [ typeof&amp;lt;float&amp;gt;,   (Some(FloatNumerics    :&amp;gt; INumeric&amp;lt;float&amp;gt;) :&amp;gt; obj);&lt;br /&gt;        typeof&amp;lt;int32&amp;gt;,   (Some(Int32Numerics    :&amp;gt; INumeric&amp;lt;int32&amp;gt;) :&amp;gt; obj);&lt;br /&gt;        typeof&amp;lt;int64&amp;gt;,   (Some(Int64Numerics    :&amp;gt; INumeric&amp;lt;int64&amp;gt;) :&amp;gt; obj);&lt;br /&gt;        typeof&amp;lt;BigInteger&amp;gt;,  (Some(BigIntNumerics   :&amp;gt; INumeric&amp;lt;BigInteger&amp;gt;) :&amp;gt; obj);&lt;br /&gt;        typeof&amp;lt;float32&amp;gt;, (Some(Float32Numerics  :&amp;gt; INumeric&amp;lt;float32&amp;gt;) :&amp;gt; obj);&lt;br /&gt;        typeof&amp;lt;Microsoft.FSharp.Math.Complex&amp;gt;, (Some(ComplexNumerics :&amp;gt; INumeric&amp;lt;Microsoft.FSharp.Math.Complex&amp;gt;) :&amp;gt; obj);&lt;br /&gt;        typeof&amp;lt;bignum&amp;gt;,  (Some(BigNumNumerics   :&amp;gt; INumeric&amp;lt;bignum&amp;gt;) :&amp;gt; obj); ]&lt;br /&gt;   &lt;br /&gt;  List.iter (&lt;span style="color:blue;"&gt;fun &lt;/span&gt;(ty,ops) &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;ht.Add(ty,ops)) optab;&lt;br /&gt;  ht&lt;/pre&gt;The types above are automatically supported. If we define a new type, we need to add the association of the type and its numeric into the variable ht.&lt;br /&gt;&lt;p&gt;By now, we know all the secrets. We can code our Pixel type now:&lt;/p&gt;&lt;span style="color:blue;"&gt;type &lt;/span&gt;Pixel = &lt;br /&gt;&lt;pre class="code"&gt;    | P &lt;span style="color:blue;"&gt;of &lt;/span&gt;int*int*int&lt;br /&gt;  &lt;span style="color:blue;"&gt;static member &lt;/span&gt;makePixel (a,b,c) = P (a,b,c)&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;override &lt;/span&gt;p.ToString() =&lt;br /&gt;      &lt;span style="color:blue;"&gt;let &lt;/span&gt;(P(x,y,z)) = p&lt;br /&gt;      &lt;span style="color:maroon;"&gt;"("&lt;/span&gt;+x.ToString()+&lt;span style="color:maroon;"&gt;", "&lt;/span&gt;+y.ToString()+&lt;span style="color:maroon;"&gt;", "&lt;/span&gt;+z.ToString()+&lt;span style="color:maroon;"&gt;")"&lt;br /&gt;    &lt;br /&gt;  &lt;/span&gt;&lt;span style="color:blue;"&gt;static member &lt;/span&gt;(+) (P(a,b,c), P(x,y,z)) = P (a+x, b+y, c+z)&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;static member &lt;/span&gt;(-) (P(a,b,c), P(x,y,z)) = P (a-x, b-y, c-z)&lt;br /&gt;  &lt;span style="color:blue;"&gt;static member &lt;/span&gt;(*) (P(a,b,c), P(x,y,z)) = P (0, 0, 0)&lt;br /&gt;  &lt;span style="color:blue;"&gt;static member &lt;/span&gt;(/) (P(a,b,c), P(x,y,z)) = P (0, 0, 0)&lt;br /&gt;  &lt;span style="color:blue;"&gt;static member &lt;/span&gt;(~+) (P(a,b,c), P(x,y,z)) = P (0, 0, 0)&lt;br /&gt;  &lt;span style="color:blue;"&gt;static member &lt;/span&gt;(~-) (P(a,b,c), P(x,y,z)) = P (0, 0, 0)&lt;/pre&gt;Its numerics:&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;let &lt;/span&gt;PixelNumerics =&lt;br /&gt;  { &lt;span style="color:blue;"&gt;new &lt;/span&gt;INumeric&amp;lt;Pixel&amp;gt; &lt;span style="color:blue;"&gt;with&lt;br /&gt;      member &lt;/span&gt;op.Zero = P(0,0,0)&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;op.One = P(1,1,1)&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;op.Add(a,b) = a + b&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;op.Subtract(a,b) = a - b&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;op.Multiply(a,b) = a * b&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;ops.Negate(a) = a&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;ops.Abs(a) = a&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;ops.Equals(a, b) = &lt;span style="color:blue;"&gt;false&lt;br /&gt;      member &lt;/span&gt;ops.Compare(a, b) = 0&lt;br /&gt;      &lt;span style="color:blue;"&gt;member &lt;/span&gt;ops.Sign(a) = failwith &lt;span style="color:maroon;"&gt;"not defined."&lt;br /&gt;      &lt;/span&gt;&lt;span style="color:blue;"&gt;member &lt;/span&gt;ops.ToString(x,fmt,fmtprovider) =  failwith &lt;span style="color:maroon;"&gt;"not implemented"&lt;br /&gt;      &lt;/span&gt;&lt;span style="color:blue;"&gt;member &lt;/span&gt;ops.Parse(s,numstyle,fmtprovider) = failwith &lt;span style="color:maroon;"&gt;"not implemented"&lt;br /&gt;  &lt;/span&gt;}&lt;/pre&gt;add the association:&lt;pre class="code"&gt;GlobalAssociations.RegisterNumericAssociation(PixelNumerics)&lt;/pre&gt;&lt;pre class="code"&gt; &lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-5452874099099706864?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/5452874099099706864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/03/f-inumerics-interface-and-matrix-class.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5452874099099706864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/5452874099099706864'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/03/f-inumerics-interface-and-matrix-class.html' title='F# INumerics Interface, and Matrix class'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-3935899378170438493</id><published>2010-03-27T00:51:00.001+08:00</published><updated>2010-04-03T23:19:15.284+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='sort'/><title type='text'>Some Sort Algorithms in F#</title><content type='html'>&lt;p&gt; &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// insertSort&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;insert x lst =&lt;br /&gt;   &lt;span style="color:blue;"&gt;match &lt;/span&gt;lst &lt;span style="color:blue;"&gt;with&lt;br /&gt;   &lt;/span&gt;| [] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;[x]&lt;br /&gt;   | y::rest &lt;span style="color:blue;"&gt;-&amp;gt; if &lt;/span&gt;x &amp;lt; y &lt;span style="color:blue;"&gt;then &lt;/span&gt;x::y::rest &lt;span style="color:blue;"&gt;else &lt;/span&gt;y::(insert x rest)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;insertSort = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| [] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;[]&lt;br /&gt;   | x::rest &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;insert x (insertSort rest)&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;&lt;span style="color:green;"&gt;// mergeSort&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;split = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| [] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;([], [])&lt;br /&gt;   | a::[] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;([a], [])&lt;br /&gt;   | a::b::rest &lt;span style="color:blue;"&gt;-&amp;gt; let &lt;/span&gt;p, q = split rest &lt;span style="color:blue;"&gt;in &lt;/span&gt;(a::p, b::q)&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;merge p q =&lt;br /&gt;   &lt;span style="color:blue;"&gt;match &lt;/span&gt;p, q &lt;span style="color:blue;"&gt;with&lt;br /&gt;   &lt;/span&gt;| [], [] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;[]&lt;br /&gt;   | a, [] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;a&lt;br /&gt;   | [], a &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;a&lt;br /&gt;   | x::xs, y::ys &lt;span style="color:blue;"&gt;when &lt;/span&gt;x&amp;lt;=y &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;x::merge xs q&lt;br /&gt;   | x::xs, y::ys &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;y::merge p ys&lt;br /&gt;  &lt;br /&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;mergeSort = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| [] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;[]&lt;br /&gt;   | a::[] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;[a]&lt;br /&gt;   | lst &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       let &lt;/span&gt;p, q = split lst&lt;br /&gt;       &lt;span style="color:blue;"&gt;let &lt;/span&gt;ps = mergeSort p&lt;br /&gt;       &lt;span style="color:blue;"&gt;let &lt;/span&gt;qs = mergeSort q&lt;br /&gt;       merge ps qs&lt;br /&gt;      &lt;br /&gt;&lt;span style="color:green;"&gt;// quickSort&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;let rec &lt;/span&gt;quickSort = &lt;span style="color:blue;"&gt;function&lt;br /&gt;   &lt;/span&gt;| [] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;[]&lt;br /&gt;   | [a] &lt;span style="color:blue;"&gt;-&amp;gt; &lt;/span&gt;[a]&lt;br /&gt;   | x::xs &lt;span style="color:blue;"&gt;-&amp;gt;&lt;br /&gt;       &lt;/span&gt;quickSort [&lt;span style="color:blue;"&gt;for &lt;/span&gt;y &lt;span style="color:blue;"&gt;in &lt;/span&gt;xs &lt;span style="color:blue;"&gt;do if &lt;/span&gt;y&amp;lt;=x &lt;span style="color:blue;"&gt;then yield &lt;/span&gt;y]&lt;br /&gt;       @ [x]&lt;br /&gt;       @ quickSort [&lt;span style="color:blue;"&gt;for &lt;/span&gt;y &lt;span style="color:blue;"&gt;in &lt;/span&gt;xs &lt;span style="color:blue;"&gt;do if &lt;/span&gt;y&amp;gt;x &lt;span style="color:blue;"&gt;then yield &lt;/span&gt;y]&lt;br /&gt;       &lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-3935899378170438493?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/3935899378170438493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/03/test.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3935899378170438493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/3935899378170438493'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/03/test.html' title='Some Sort Algorithms in F#'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-923653725573087643</id><published>2010-03-17T14:39:00.001+08:00</published><updated>2010-03-17T14:39:29.557+08:00</updated><title type='text'>test from live writer</title><content type='html'>&lt;p&gt;seems to be good!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-923653725573087643?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/923653725573087643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2010/03/test-from-live-writer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/923653725573087643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/923653725573087643'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2010/03/test-from-live-writer.html' title='test from live writer'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3332270588565635319.post-698118330634680453</id><published>2009-12-13T15:06:00.000+08:00</published><updated>2010-05-23T10:12:45.464+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='videos'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorials'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>F# online videos</title><content type='html'>I found the following videos are very good tutorials for F#.&lt;br /&gt;&lt;br /&gt;Luke Hoban&lt;br /&gt;&lt;a target="_blank" href="http://microsoftpdc.com/Sessions/FT20"&gt;http://microsoftpdc.com/Sessions/FT20&lt;/a&gt;&lt;br /&gt;http://blogs.msdn.com/lukeh/&lt;br /&gt;&lt;br /&gt;Luca Bolognese&lt;br /&gt;&lt;a target="_blank" href="http://channel9.msdn.com/pdc2008/TL11/"&gt;http://channel9.msdn.com/pdc2008/TL11/&lt;/a&gt;&lt;br /&gt;http://blogs.msdn.com/lucabol/&lt;br /&gt;&lt;br /&gt;Don Syme&lt;br /&gt;&lt;a href="http://blogs.msdn.com/dsyme/"&gt;http://blogs.msdn.com/dsyme/&lt;/a&gt;&lt;br /&gt;Don, the creator of F#, has some videos too:&lt;br /&gt;&lt;a href="http://channel9.msdn.com/posts/martinesmann/Don-Syme-FSharp-and-functional-programming-in-NET/"&gt;http://channel9.msdn.com/posts/martinesmann/Don-Syme-FSharp-and-functional-programming-in-NET/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-- some special videos--&lt;br /&gt;Big Algorithms Made Easy with Microsoft's F# by Joel Pobar&lt;br /&gt;&lt;a href="http://www.slideshare.net/chlong/dev450-pobar-2884153"&gt;http://www.slideshare.net/chlong/dev450-pobar-2884153&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.msteched.com/online/view.aspx?tid=3cd60332-dfcb-4b53-b8c5-8aee09648139"&gt;http://www.msteched.com/online/view.aspx?tid=3cd60332-dfcb-4b53-b8c5-8aee09648139&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;InfoQ, Don Syme interview:&lt;br /&gt;&lt;a href="http://www.infoq.com/interviews/don-syme-fsharp-2_0"&gt;http://www.infoq.com/interviews/don-syme-fsharp-2_0&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;http://www.infoq.com/presentations/Pragmatic-F-Sharp-in-Action&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3332270588565635319-698118330634680453?l=fdatamining.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fdatamining.blogspot.com/feeds/698118330634680453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fdatamining.blogspot.com/2009/12/f-online-videos.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/698118330634680453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3332270588565635319/posts/default/698118330634680453'/><link rel='alternate' type='text/html' href='http://fdatamining.blogspot.com/2009/12/f-online-videos.html' title='F# online videos'/><author><name>Yin Zhu</name><uri>http://www.blogger.com/profile/14892386755350244000</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
