mathematician, batteries included.

Yet Another Fizz-Buzz in Common Lisp Description of the problem Finally, I’ve finished a pure math project and two computational humanities projects, and I have some free time on my hand. So, I thought it is time that I wrote my own version of fizz-buzz in Common Lisp. The code One can find many different solutions for the problem at Rosetta Code. The solution I have is slightly different. I always hated the straightforward solution that checks divisibility by 15 first then by 5 then by 3 because the last two precludes the first. Anyway, here is yet another solution for fizz-buzz: First, I need a function that does function composition: (defun compose (&rest fs) (lambda (x) (let ((res x)) (dolist (f (reverse fs) res) (setf res (funcall f res)))))) COMPOSE Next, I need functions that do the right thing for a given number: (defun do-the-right-thing (k response) (lambda (x) (if (atom x) (if (zerop (mod x k)) (cons x response) x) (if (zerop (mod (car x) k)) (cons (car x) (cons response (cdr x))) x)))) DO-THE-RIGHT-THING And the final code: (let ((fizz (do-the-right-thing 3 :fizz)) (buzz (do-the-right-thing 5 :buzz)) (extract (lambda (x) (if (atom x) x (cdr x))))) (mapcar (compose extract fizz buzz) (loop for i from 0 to 50 collect i))) ((FIZZ . BUZZ) 1 2 FIZZ 4 BUZZ FIZZ 7 8 FIZZ BUZZ 11 FIZZ 13 14 (FIZZ . BUZZ) 16 17 FIZZ 19 BUZZ FIZZ 22 23 FIZZ BUZZ 26 FIZZ 28 29 (FIZZ . BUZZ) 31 32 FIZZ 34 BUZZ FIZZ 37 38 FIZZ BUZZ 41 FIZZ 43 44 (FIZZ . BUZZ) 46 47 FIZZ 49 BUZZ)

Next Permutation in the Lexicographical Ordering Description of the problem Listing permutations of a list of objects is a useful thing. However, the space complexity is $n!\approx n^n$ which is worse than exponential. One way getting around this problem is to find a way of generating the list in a sequential manner by using a total order on all permutations. Today, I am going to write a common lisp program that gives the list of all permutations of a list using such a total order. Some theory One can order all permutations using the lexicographical ordering. If we use this order, then given a permutation we know what the next one is going to be. So, here is the algorithm in pseudo-code Algorithm NextPermutation Input: A permutation sigma = a(1), ..., a(n) Output: The next permutation sigma' = b(1), ..., b(n) in the lexicographical order Begin i <- max { k | a(k-1) <= a(k) } If i > 0 j <- max { k | a(k+i) > a(i-1) } Swap a(i-1) and a(i+j-1) Return a(1), ..., a(i-1), a(n), a(n-1), ..., a(i) Else Return NIL End If End The algorithm and the pseudo-code I use here is a modified version I saw in here. The code First, I need two utility functions: (defun take-until (pred xs &optional (c 0)) (if (funcall pred xs) c (take-until pred (cdr xs) (incf c)))) TAKE-UNTIL This function processes a list of objects until a predicate is satisfied. It returns the index at which the predicate is satisfied. Next, we have: (defun iterate-until (pred fn val &optional carry) (let ((next (funcall fn val))) (if (funcall pred next) (reverse (cons val carry)) (iterate-until pred fn next (cons val carry))))) ITERATE-UNTIL This function iterates a function until a predicate is satisfied. It returns the list of values in the iteration as a list. Now, the main function: (defun next-permutation (xs) (let ((i (- (length xs) 1 (take-until (lambda (ys) (or (null ys) (null (cdr ys)) (> (car ys) (cadr ys)))) (reverse xs))))) (if (> i 0) (let* ((x (elt xs (1- i))) (j (take-until (lambda (ys) (or (null ys) (<= (car ys) x))) (subseq xs i))) (zs (copy-list xs))) (setf (elt zs (1- i)) (elt xs (+ i j -1)) (elt zs (+ i j -1)) (elt xs (1- i))) (append (subseq zs 0 i) (reverse (subseq zs i))))))) NEXT-PERMUTATION This function takes a permutation of elements and then returns the next permutation in the lexicographical ordering. Let us check: (iterate-until #'null #'next-permutation '(0 1 1 2 2 2)) ((0 1 1 2 2 2) (0 1 2 1 2 2) (0 1 2 2 1 2) (0 1 2 2 2 1) (0 2 1 1 2 2) (0 2 1 2 1 2) (0 2 1 2 2 1) (0 2 2 1 1 2) (0 2 2 1 2 1) (0 2 2 2 1 1) (1 0 1 2 2 2) (1 0 2 1 2 2) (1 0 2 2 1 2) (1 0 2 2 2 1) (1 1 0 2 2 2) (1 1 2 0 2 2) (1 1 2 2 0 2) (1 1 2 2 2 0) (1 2 0 1 2 2) (1 2 0 2 1 2) (1 2 0 2 2 1) (1 2 1 0 2 2) (1 2 1 2 0 2) (1 2 1 2 2 0) (1 2 2 0 1 2) (1 2 2 0 2 1) (1 2 2 1 0 2) (1 2 2 1 2 0) (1 2 2 2 0 1) (1 2 2 2 1 0) (2 0 1 1 2 2) (2 0 1 2 1 2) (2 0 1 2 2 1) (2 0 2 1 1 2) (2 0 2 1 2 1) (2 0 2 2 1 1) (2 1 0 1 2 2) (2 1 0 2 1 2) (2 1 0 2 2 1) (2 1 1 0 2 2) (2 1 1 2 0 2) (2 1 1 2 2 0) (2 1 2 0 1 2) (2 1 2 0 2 1) (2 1 2 1 0 2) (2 1 2 1 2 0) (2 1 2 2 0 1) (2 1 2 2 1 0) (2 2 0 1 1 2) (2 2 0 1 2 1) (2 2 0 2 1 1) (2 2 1 0 1 2) (2 2 1 0 2 1) (2 2 1 1 0 2) (2 2 1 1 2 0) (2 2 1 2 0 1) (2 2 1 2 1 0) (2 2 2 0 1 1) (2 2 2 1 0 1) (2 2 2 1 1 0))

Turkish Hyphenation in Common Lisp Description of the problem Unlike English, Turkish has a very regular orthography especially when it comes to hyphenation rules. Today, I am going to write a common lisp program that gives you the proper Turkish hyphenation of a Turkish word. Back in the day, I wrote the original in C for ISO8859-9 encoding. I should extend it for unicode, but dealing with unicode in C feels like cleaning hair from shower drain. The code First, I need a function which would tell us whether a given character is a vowel or a consonant: (let ((vowels '(#\a #\â #\e #\ı #\i #\o #\ö #\u #\ü #\û))) (defun vowelp (ch) (member ch vowels))) VOWELP Next, the function that hypenates a given word: (defun hyphen (raw) (let ((w (format nil "~a " raw))

res flag dash)

(dotimes (i (length raw))

(if (vowelp (elt w i))

(setf flag nil

dash (some #'vowelp (subseq w (1+ i) (+ i 3))))

(when (not (or (setf flag (not flag))

(setf dash (not (vowelp (elt w (1+ i)))))))

(push #\- res)))

(push (elt w i) res)

(when dash (push #\- res))

(setf dash nil))

(ppcre:split #\- (concatenate 'string (reverse res))))) HYPHEN And few tests: (mapcar #'hyphen '("işkillendim" "ağrılarımsa" "erkekle" "tarımsal" "yap" "üre" "tank" "ionya" "çekoslavakyalılaştıramadıklarımızdanmışcasınaymışsa")) ((iş kil len dim) (ağ rı la rım sa) (er kek le) (ta rım sal) (yap) (ü re) (tank) (i on ya) (çe kos la vak ya lı laş tı ra ma dık la rı mız dan mış ca sı nay mış sa))

Using JavaPlex with Clojure Java isn’t great when it comes to heavy numerical computations unless it gets aid from native libraries. But, nevertheless, I am going to show you an example of using the topological data analysis java library JavaPlex developed by the applied topology group at Stanford University. My example code below is written in Clojure. Topological Data Analysis Topologists have known a very important fact since the inception of topology back in the beginning of the 20th Century: one can effectively approximate continuous structures, such as manifolds (surfaces and embedded bodies in the 3d space if you are so-inclined), using discrete structures and calculate their continuous invariants (such as dimension, number of holes etc.) using the said discrete approximations. So, why not do the same thing with collection data points? Here is a mathematician’s way of describing the fundamental problematic of topological data analysis: Assume we have an embedded manifold $M$ in $\mathbb{R}^n$, and a finite sample of points $X$ taken from from $M$. Can one calculate topological invariants of $M$ from $X$. The topological invariants we are after are called Betti numbers. For example, the 0-th betti number calculates the number of connected components, 1-st betti number calculates the number of circle-like features, 2-nd betti number calculates the number of sphere-like features, and so on… No Free Lunch In theory, topology, unlike geometry, is insensitive to small changes to the underlying metric (distance function). However, how these finite cloud of points are chosen (sampling) does effect the results. This means, if sampled carefully (dare I say correctly), calculations would give almost correct answers. So far so good. But, unfortunately, there is no such a thing as a free lunch. In order to calculate the betti numbers we need to construct a simplicial complex and then construct its singular complex. The size of the simplicial complex on $n$ points (in the worst case scenario) is $n!\approx n^n$ (worse than exponential!) which makes calculations tricky to say the least. Barcodes From our cloud of points we first construct a gradually growing network of points depending on their proximity. One key point here is that even though we need a notion of proximity of points in constructing the network, the topological invariants we are going to calculate at the end, are independent of any choices we make in the process. In the process of enlarging the network, some topological features might appear (new polygonal features) and some might disappear (some polygons features might fill-up and disappear). These features are encoded with things called homology classes, and betti numbers count how many homology classes of a certain degree there are. Barcodes are records of homology classes appearing (their birth) and disappearing (their death) as a pair of time-stamps. [Source] If you’d like to learn more, I would recommend the lectures by Frédéric Chazal and Julien Tierny at INRIA. JavaPlex and Clojure There is an excellent tutorial on how to use JavaPlex, you should read it. What I do down here is somewhat different, but you can adjust the examples there for the code below. Biggest difference is that I did write my own sampling code. Let’s do some coding! Unfortunately, the jar files of the JavaPlex library are not on Maven. So, you would have to download it. You have two options: You can declare it a local resource in your project.clj file, You can add it to your local maven repository. I went with the second option. Let me start with the namespace stuff: (ns tda.core (:import edu.stanford.math.plex4.api.Plex4) (:import java.util.Random) (:gen-class)) In order to test the code, I need sample points from a nice topological object. In my case this is the n-sphere (defn sample-from-Sn [dim n r epsilon] (let [rng (Random.) rs (take n (repeatedly (fn [] (+ r (* epsilon (.nextGaussian rng))))))] (loop [m dim points (take n (repeatedly (fn [] [(rand-nth [-1 1])])))] (if (zero? m) (map (fn [xs r] (map (fn [x] (* x r)) xs)) points rs) (let [thetas (take n (repeatedly (fn [] (* Math/PI (.nextDouble rng)))))] (recur (dec m) (map (fn [point theta] (conj (map (fn [x] (* x (Math/cos theta))) point) (Math/sin theta))) points thetas))))))) #'tda.core/sample-from-Sn Parameter dim controls the dimension of the sphere, n tells the size of the sample, r controls the radius of the sphere and epsilon controls the variance of the error I added to the radius. I must warn: my samples are not uniformly distributed. If you plot this for the 2-dimensional sphere, you will see accumulation on the poles. But, it will do here. Okay, let me sample some points from $S^2$ embedded in $\mathbb{R}^3$: (def S2-clojure (sample-from-Sn 2 60 1.0 0.05)) (into [] S2-clojure) #'tda.core/S2-clojure [(0.9694257845977597 0.2565225846251115 0.149693530313045) (0.3160506015340551 -0.08420911142786046 -0.9730145781728613) (0.2441359514617179 -0.6387846652366139 -0.7411718962603825) (0.17159371543720592 -0.9472605873221599 -0.5206384637048138) (0.8191247892843844 -0.5051582258691645 -0.15355993023272338) (0.08761606749846149 -1.0593979203996742 -0.18669956905276947) (1.0351699217914776 -0.16973081443032012 -0.05548596986325099) (0.6321192170240967 -0.4463243263048794 -0.6331108910818303) (0.44110576960908393 0.7901333222386744 -0.17699391506183906) (0.6664362117540702 -5.182362640554818E-4 -0.7878044433470869) (0.23099494868770934 1.0035143441460654 0.15219310831575988) (0.3793772572961613 0.9211308061484103 0.3368188034751347) (0.9391765946251789 -0.5149998723423344 0.00951410804395951) (0.5850653567514654 -0.1440059217893346 0.7914547828690756) (0.3508641770888623 -0.8216806863532357 0.3210694225742778) (0.365271058828488 0.467372201564975 -0.7477195994475816) (0.9998784904968095 -0.2504306059505103 0.17057029208945632) (0.37153553168598225 -0.8371234147021953 -0.4039289385911284) (0.1880595946327987 0.872119289752966 -0.25457609711238316) (0.9078318321103805 0.11443429289075147 0.30985628091744377) (0.2753178550631587 0.5522157044155734 0.724835568584973) (0.7276416387195462 0.43091836672710043 -0.5955978441028754) (0.928458266429133 0.34186663945326023 0.05966688766915473) (0.9483252756222065 0.03997832071469546 -0.03415491558412182) (1.039819688540467 -0.0029375076770020523 -0.05596330134040296) (0.018988746613726758 -0.8139128755155158 0.5399992866645357) (0.75171740780237 -0.38526794255364827 0.17028688698667463) (0.7046546908705352 0.45716437242694546 -0.38071293516071086) (0.9672486476322995 0.04443394765956402 0.13088126355371188) (0.814551798395074 -0.4884786291897059 -0.41141139951264294) (0.8059618509573785 0.2578346913295543 0.4784906568264699) (0.5851445796338018 0.17210563813682658 -0.729820796773424) (1.022865088634098 -0.06132159806564196 -0.22144052975566123) (0.8019568682509614 0.058094639441568514 -0.6154093307097448) (0.8749755458384921 0.1779446617970637 -0.5069180924762302) (0.7561408596423005 0.6209311431238985 0.3056639783082412) (0.020146319348906944 -0.620716247392654 0.8449959844580252) (0.7308601008956529 0.08219526175260423 -0.790211858586775) (0.43137452712078234 -0.37506259931597696 -0.8054673920079827) (0.8410469276246595 -0.4552426709132372 -0.014705503002436397) (1.0196327878770284 0.017668451272742396 0.13452291759828935) (0.7881072022617578 -0.40181778621391795 -0.23440165612318994) (0.9407412796388347 -0.015940593773706064 0.030361770156930493) (0.958641455522085 -0.04760662629066117 0.037604490626258216) (0.7975644193823939 0.07790949438593872 -0.4407502726054776) (0.8723001721910031 -0.18197209769171202 0.3734974136910884) (1.0075286251968685 -0.07024816367980981 0.0012243200449112126) (0.8914068849160154 -0.16636570799712203 0.09751276054119445) (0.7248253421185545 -0.7441517660417161 -0.009518393915065062) (0.46741625025217876 0.5982647783111011 -0.4012345471412837) (0.9395881922522764 -0.28054294602773683 -0.13171366106619273) (0.6550895775349393 -0.6990611847238194 0.467956330584316) (0.48736324839252215 0.09402215688413616 -0.9338551699907922) (0.9589695669528585 0.16568955031112448 -0.09542644569897873) (0.8726612994318831 -0.010564243542695102 0.6340352123016327) (1.0292743257857333 -0.18881987244771503 0.11154210093830234) (0.7993825384808707 0.3512141469954968 -0.5124549756863521) (0.47595730072279946 0.9016441364280734 -0.18816975248168527) (0.6220383817799092 -0.29058614371905445 0.6718158125861152) (0.5954384246793762 -0.4642050574372491 -0.6583431440198491)] Since this is a java library, we need to play nice and convert the data point cloud into a java array: (defn into-java [xs] (into-array (map double-array xs))) (def S2 (into-java S2-clojure)) #'tda.core/into-java #'tda.core/S2 Next, we need to construct a simplicial complex. JavaPlex has a built-in functions for that. Below, I chose to construct a Rips complex which is computationally cheaper to other alternatives such a Čech complex. (def rips (Plex4/createVietorisRipsStream S2 3 2.25 100)) #'tda.core/rips The second parameter controls the maximum dimension of the simplicial complex, and the last parameter controls the upper bound for the proximity parameters in the filtration. Next, we have a java-ism: we need to instantiate a class for the algorithm that calculates the barcodes. (def algo (Plex4/getRationalSimplicialAlgorithm 3)) #'tda.core/algo The parameter we pass to the function tells us the upper bound for the dimension it needs to check. Now, moment of truth. Calculate the barcodes. Recall that these are collections of time-stamps of births and deaths of homology classes along with the dimension of the homology class. (.computeIntervals algo rips) Dimension: 0 [0.0, 0.0225) [0.0, 0.045) [0.0, 0.045) [0.0, 0.045) [0.0, 0.0675) [0.0, 0.09) [0.0, 0.09) [0.0, 0.09) [0.0, 0.11249999999999999) [0.0, 0.11249999999999999) [0.0, 0.11249999999999999) [0.0, 0.11249999999999999) [0.0, 0.11249999999999999) [0.0, 0.11249999999999999) [0.0, 0.135) [0.0, 0.135) [0.0, 0.135) [0.0, 0.135) [0.0, 0.135) [0.0, 0.135) [0.0, 0.1575) [0.0, 0.1575) [0.0, 0.1575) [0.0, 0.18) [0.0, 0.18) [0.0, 0.18) [0.0, 0.18) [0.0, 0.18) [0.0, 0.18) [0.0, 0.18) [0.0, 0.20249999999999999) [0.0, 0.20249999999999999) [0.0, 0.20249999999999999) [0.0, 0.22499999999999998) [0.0, 0.22499999999999998) [0.0, 0.22499999999999998) [0.0, 0.2475) [0.0, 0.2475) [0.0, 0.2475) [0.0, 0.2475) [0.0, 0.27) [0.0, 0.27) [0.0, 0.27) [0.0, 0.2925) [0.0, 0.2925) [0.0, 0.2925) [0.0, 0.315) [0.0, 0.33749999999999997) [0.0, 0.33749999999999997) [0.0, 0.33749999999999997) [0.0, 0.36) [0.0, 0.36) [0.0, 0.36) [0.0, 0.3825) [0.0, 0.3825) [0.0, 0.40499999999999997) [0.0, 0.4275) [0.0, 0.4275) [0.0, 0.54) [0.0, infinity) Dimension: 1 [0.09, 0.11249999999999999) [0.22499999999999998, 0.2475) [0.2475, 0.2925) [0.2925, 0.33749999999999997) [0.315, 0.33749999999999997) [0.315, 0.33749999999999997) [0.40499999999999997, 0.4275) [0.44999999999999996, 0.5175) [0.33749999999999997, 0.5625) [0.5175, 0.5625) [0.4725, 0.6975) [0.6074999999999999, 0.72) Analysis As the proximity parameter grows, eventually we get one large blob. So, there is always a 0-class with infinite life span. The life spans of higher classes are always finite. Thus one has to pay attention to how long these classes survive, and make a judgement call as to which of those are real-features and which are ghost-artifacts. The topological space 2-sphere has a very specific homology: one class at degree 0, and one class at degree 2. The calculation above, depending on the sampling, most often cannot capture the top degree class. Bummer! If I increase the number of points, my laptop becomes a lap-heating-device without much gain. Yet another bummer!

Constricted Arithmetic Progressions Description of the problem A stricly increasing sequence of integers $0 = a_0< a_1 < \cdots < a_n$ is said to be $m$-constricted if $1\leq a_{i+1}-a_i\leq m$ for every $i=0,\ldots,n-1$. Today, I am going to count all $m$-constricted sequences of integers within the interval $[1,N]$ for a fixed pair $(N,m)$. A recursive solution Let me use $CSL(m,b,k)$ to denote the number of $m$-constricted sequences that start at 0 and end at $b$ of length $k$. I will always assume $m\geq 1$. The problem is equivalent to counting positive integer solutions of the equation $$ x_1 + \cdots + x_k = b $$ with the constraint that $1\leq x_i\leq m$. We can immediately see that $CSL(m,b,k)=0$ if $bm$ because one cannot reach at $b$ in 1 step of size less than or equal to $m$ if $b$ is greater than $m$. Similarly, $CSL(m,b,1)=1$ if $b\leq m$. Finally, we also have a recursion relation: $$ CSL(m,b,k) = \sum_{i=1}^m CSL(m,b-i,k-1) $$ Now, if we need to count all $m$-constricted sequences of length $k$ inside $[1,N]$ we need $$ CS(m,N,k) = CSL(m,N,k+1) $$ As for all $m$-constricted sequences in $[1,N]$ we get $$ CS(m,N,k) = \sum_{k=1}^{N-1} CSL(m,N,k+1) $$ Notice that I only count the sequences of length greater than 1. An implementation Let me start by $CSL$: (let ((csl-table (make-hash-table :test #'equal))) (defun csl (m b k) (cond ((< b k) 0) ((= b k) 1) ((= k 1) (if (> b m) 0 1)) (t (or (gethash (list m b k) csl-table) (setf (gethash (list m b k) csl-table) (loop for i from 1 to (min m (1- b)) sum (csl m (- b i) (1- k))))))))) CSL Then the number of all constricted sequences is given by (defun cs (m N) (loop for k from 2 to N sum (csl m N k))) CS Let us test for a small case: (loop for i from 2 to 6 collect (csl 3 i 2)) (cs 3 6) (1 2 3 2 1) 24 and a large one: (cs 10 20) 521472

The Number of Arithmetic Progressions of Integers Description of the problem Let me start by defining what I mean by an arithmetic progression: an arithmetic progression of integers is a sequence of the form $$ (a + bi) = (a, a+b, a+2b, \ldots, a+kb) $$ for a fixed pair of integers $(a,b)$. For simplicity, let me use $[1,N]$ to denote the interval (that contains integers only) between 1 and N. Today’s question is How many arithmetic progressions of integers can we fit into the interval $[1,N]$? Mathematical solution Let us start simple: if we let $a=1$ and $b=1$, and fix the length as $k$ then the first sequence we have simply starts at 1 and ends at k $$ 1,2,\ldots,k $$ and the last sequence that would fit in the interval ends with $N$. So, $$ 1 + (N-k), 2 + (N-k), \ldots, N$$ If we let $LP(N,k,b)$ as the number of arithmetic progressions in $[1,N]$ of length $k$ that go by gaps of size $b$, we see that $$ LP(N,k,1) = N-k+1 $$ a similar argument for $b>1$ yields $$ LP(N,k,b) = N-b(k-1) $$ as long as $1\leq b\leq \lfloor N/(k-1)\rfloor$. For the number $LP(N,k)$ of all arithmetic progressions of length $k$ regarless of the parameter $b$ we must calculate the sum $$ LP(N,k) = \sum_{b=1}^{\left\lfloor\frac{N}{k-1}\right\rfloor} LP(N,k,b) = \sum_{b=1}^{\lfloor\frac{N}{k-1}\rfloor} N - b(k-1) = N\left\lfloor\frac{N}{k-1}\right\rfloor - \frac{k-1}{2}\left\lfloor\frac{N}{k-1}\right\rfloor\left(\left\lfloor\frac{N}{k-1}\right\rfloor + 1\right) $$ And if we need the number $LP(N)$ of all arithmetic progressions in the interval $[1,N]$ we would have to calculate $$ LP(N) = \sum_{k=2}^N LP(N,k) $$ An implementation in common lisp The implementation is straight-forward: (defun linear-progressions (n k) (let ((a (truncate n (1- k))) (b (/ (1- k) 2))) (- (* n a) (* b a (1+ a))))) (defun all-linear-progressions (n) (loop for i from 2 to n sum (linear-progressions n i))) LINEAR-PROGRESSIONS ALL-LINEAR-PROGRESSIONS Let us test: (linear-progressions 3 2) (all-linear-progressions 3) (loop for i from 2 to 20 collect (linear-progressions 20 i)) (loop for i from 2 to 100 collect (all-linear-progressions i)) 3 4 (190 90 57 40 30 24 19 16 13 10 9 8 7 6 5 4 3 2 1) (1 4 9 17 27 41 57 77 100 127 156 191 228 269 314 364 416 474 534 600 670 744 820 904 991 1082 1177 1278 1381 1492 1605 1724 1847 1974 2105 2245 2387 2533 2683 2841 3001 3169 3339 3515 3697 3883 4071 4269 4470 4677 4888 5105 5324 5551 5782 6021 6264 6511 6760 7021 7284 7551 7824 8104 8388 8680 8974 9274 9578 9890 10204 10530 10858 11190 11528 11872 12220 12576 12934 13302 13675 14052 14431 14822 15217 15616 16019 16430 16843 17268 17697 18132 18571 19014 19461 19920 20381 20848 21321) The last sequence is A078567 at OEIS.

Bron-Kerbosch Algorithm in Clojure Description of the problem Assume we have a simple undirected graph $G=(V,E)$. Then a subset $C$ of the set of vertices $V$ is called a clique if for every pair of distinct elements $a,b\in C$ the pair $\{a,b\}$ is an edge in $G$. The set of all cliques of a graph is partially ordered under set inclusion. A clique is called maximal if it is a maximal element in this poset. Today, I am going to implement a version of the Bron-Kerbosch Algorithm which finds the set of all maximal cliques in a graph. I wrote about it earlier but that version was in common lisp, and today I am going to write an implementation from scratch in clojure. An implementation in clojure Let us start with a clean namespace with the necessary libraries loaded: (ns bron-kerbosch (:use clojure.set)) I am going to represent a graph as a set of edges where each edge is a pair of distinct elements chosen from the underlying set of vertices. I am going to need two utility functions for later. The first returns the set of vertices for a given graph, while the second returns the set of neighbors of a given vertex in a given graph. (defn vertices [graph] (reduce union graph)) (defn neighbors [v graph] (difference (reduce union (filter #(contains? % v) graph)) #{v})) #'bron-kerbosch/vertices #'bron-kerbosch/neighbors Finally, the main function, which is implemented as a recursive function: (defn bron-kerbosch ([graph] (bron-kerbosch graph (vertices graph) #{} #{} #{})) ([graph p x r res] (if (and (empty? x) (empty? p)) (union #{r} res) (loop [ps p xs x cs res] (if (empty? ps) cs (let [v (first ps) nv (neighbors v graph)] (recur (into #{} (rest ps)) (union #{v} xs) (bron-kerbosch graph (intersection ps nv) (intersection xs nv) (union #{v} r) cs)))))))) #'bron-kerbosch/bron-kerbosch In order to test the code, I am going to need a function that generates a random simple graph: (defn get-random-graph [n m k] (->> (range n) (mapcat (fn [i] (repeatedly m (fn [] #{i (+ 1 i (rand-int k))})))) distinct (into #{}))) #'bron-kerbosch/get-random-graph OK. Here is a random graph: (def random-graph (get-random-graph 12 5 9)) random-graph #'bron-kerbosch/random-graph #{#{13 5} #{19 11} #{3 11} #{6 3} #{0 1} #{14 10} #{15 11} #{13 9} #{7 16} #{7 6} #{2 10} #{3 5} #{5 10} #{12 10} #{0 4} #{3 10} #{3 12} #{11 16} #{17 11} #{0 7} #{4 8} #{0 3} #{5 14} #{7 13} #{10 8} #{6 11} #{6 8} #{17 10} #{12 5} #{4 10} #{7 2} #{13 11} #{15 8} #{1 6} #{9 10} #{4 2} #{7 9} #{1 3} #{15 9} #{1 10} #{4 9} #{1 9} #{13 10} #{14 8} #{17 8} #{0 5} #{10 18}} And now, let us test our main function on this random graph: (bron-kerbosch random-graph) #{#{3 12 5 10} #{19 11} #{15 11} #{7 16} #{7 6} #{0 4} #{11 16} #{17 11} #{0 7} #{14 10 8} #{5 14 10} #{7 13 9} #{1 6 3} #{1 9 10} #{6 8} #{7 2} #{13 9 10} #{1 3 10} #{13 11} #{15 8} #{0 1 3} #{0 3 5} #{6 3 11} #{15 9} #{17 10 8} #{4 9 10} #{4 10 8} #{4 2 10} #{13 5 10} #{10 18}}

An Implementation of Ford-Fulkerson Algorithm in Clojure Description of the problem Today, I am going to write about Ford-Fulkerson Algorithm. I wrote about this earlier, but that post was in Common Lisp. Plus, I am going to go over the theory slightly deeper than my original post, and write a new implemention in Clojure. I also posted the text and the code for this post on my github.

Weighted Simple Directed Graphs We have a weighted simple directed graph $G = (V,E,w)$ where $V$ is a finite set of vertices, $E\subseteq V\times V$ is the set of edges and $w\colon E\to [0,\infty)$ is the weight function. Here is an example: Augmenting Subgraphs I will call a weighted simple directed graph $G’=(V’,E’,w’)$ an augmenting subgraph of $G$ if $G’=(V’,E’)$ is a subgraph of $G$, $w’(a,b)\leq w(a,b)$ for every $(a,b)\in E’$, and $\sum_{(c,a)\in E’} w’(c,a) = \sum_{(a,b)\in E’} w’(a,b)$ for every $a\in V’$ which is neither a source nor a sink. The last condition says in the subgraph, the total weights of incoming edges at a vertex is equal to the total weight of the outgoing edges on the same vertex. With these conditions at hand, notice that a weighted simple directed graph need not be an augmented subgraph of itself, as in our example above. Here is an example of an augmenting subgraph: Notice that any path in $G$ gives us an augmenting subgraph if we set the weights of each of the edges to the minimal weight along the path. For example the path $acef$ with all weights equal to $3$ is an augmenting path. Residual Subgraph If $G=(V,E,w)$ is a weighted simple directed graph and if $G’=(V’,E’,w’)$ is an augmented graph, the residual graph (which we denote by $G\setminus G’$ is the graph $(V,E, w’’)$ where the weight function $w’’$ is defined as $$ w’’(a,b) = \begin{cases} w(a,b) - w’(a,b) & \text{ if } (a,b)\in E’\\ w(b,a) + w’(a,b) & \text{ if } (a,b)\in E’ \text{ and } (b,a)\in E\\ w’(b,a) & \text{ if } (a,b)\in E’ \text{ but } (b,a)

otin E \end{cases} $$ In order to simplify the computation, let us set the weight of all nonexistent edges to 0. Then the new weight matrix is going to be $$ w’’(a,b) = \begin{cases} w(a,b) - w’(a,b) & \text{ if } (a,b)\in E’\\ w(a,b) + w’(b,a) & \text{ if } (b,a)\in E’ \end{cases} $$ With this definition at hand, for the graph and the augmenting subgraph we gave above the residual graph is going to be The Poset of Augmenting Subgraphs There is a partial order on the set of all augmenting subgraphs of a given weighted simple directed graph. So, if $G’=(V’,E’,w’)$ and $G’’=(V’’,E’’,w’’)$ are two such graphs then we say $G’\leq G’’$ if $G’$ is a subgraph of $G’’$ and we have $w’(a,b)\leq w’’(a,b)$ for every $(a,b)\in E’$. With this definition at hand, now we can talk about maximal augmented subgraphs. Here are two such maximal augmented subgraphs which (necessarily) are incomparible: Flow Along an Augmenting Subgraph Given an augmenting subgraph $G’=(V’,E’,w’)$ we define the total flow as $$ Flow(G’) = \sum_{s\in Sink(G’)} \sum_{(a,s)\in E’} w’(a,s) = \sum_{s\in Source(G’)} \sum_{(s,a)\in E’} w’(s,a) $$ Notice that even though both augmenting subgraphs example above are maximal with respect to the partial order we defined above, the total flow from the source to sink are different. Maximal Augmenting Subgraphs with Maximal Flow So, we can now talk about a maximal augmenting subgraph with maximal flow. If $F$ is the weight function of the maximal augmenting subgraph with maximal flow then one can define it recursively as $$ F(G) = F(G\setminus S) + w_S $$ for an augmenting subgraph $S$ of $G$ where $w_S$ is the weight function of the augmenting subgraph $S$. The base case is that $F(G)$ is uniform weight function 0 if $G$ has no augmenting subgraphs. The Ford-Fulkerson Algorithm finds the weight function of a maximal augmenting subgraph with maximal flow using this recursive formula with augmenting paths for $S$. An Implementation in Clojure #‘user/dot-file I will represent a weighted simple graph as a hashmap of pairs of vertices: (def G {[:a :b] 4 [:a :c] 6 [:b :d] 2 [:d :c] 1 [:d :f] 2 [:c :e] 3 [:e :f] 3}) #'user/G Here is an augmenting subgraph: (def S {[:a :b] 2 [:b :d] 2 [:a :c] 3 [:d :f] 2 [:c :e] 3 [:e :f] 3}) #'user/S Next, we need a function that returns a residual graph for a weighted simple directed graph and an augmenting subgraph: (defn residual-graph [G S] (->> (mapcat (fn [[k v]] {k (- v) (reverse k) v}) S) (into {}) (merge-with + G) (filter (fn [[k v]] (> v 0))) (into {}))) #'user/residual-graph Let us test (def RG (residual-graph G S)) RG #'user/RG {(:b :a) 2, (:c :a) 3, (:f :e) 3, [:a :b] 2, (:f :d) 2, [:a :c] 3, [:d :c] 1, (:e :c) 3, (:d :b) 2} Next, a depth-first search function to find an augmenting path between two vertices: (defn find-a-path [G a b] (loop [H G x a P []] (let [ys (->> (keys H) (filter (fn [[u v]] (= u x))))] (cond (or (empty? H) (nil? x)) [] (contains? (into #{} ys) [x b]) (reverse (cons [x b] P)) (empty? ys) (recur (dissoc H (first P)) (-> P first first) (rest P)) :true (recur (dissoc H (first ys)) (-> ys first second) (cons (first ys) P)))))) #'user/find-a-path Let us test this function. First a random graph: (defn get-random-graph [n m k w] (->> (range n) (mapcat (fn [i] (repeatedly m (fn [] [i (+ 1 i (rand-int k))])))) distinct (mapcat (fn [x] {x (+ 1 (rand-int w))})) (into {}))) (def random-graph (get-random-graph 8 2 5 10)) random-graph #'user/get-random-graph #'user/random-graph {[7 12] 4, [7 11] 9, [2 3] 2, [2 5] 8, [0 5] 4, [4 7] 4, [1 4] 1, [5 7] 9, [1 3] 2, [6 8] 8, [3 6] 6, [4 5] 10, [0 4] 9, [6 10] 1} (into [] (find-a-path random-graph 0 9)) (find-a-path random-graph 8 0) [] [] Finally, our implementation of Ford-Fulkerson: (defn ford-fulkerson [G a b] (loop [H G S {}] (let [R (find-a-path H a b)] (if (empty? R) (into {} S) (let [v (reduce min (map H R)) P (zipmap R (repeat v))] (recur (residual-graph H P) (merge-with + S P))))))) #'user/ford-fulkerson Let us test this on the graph we defined above: (ford-fulkerson G :a :f) {[:a :b] 4, [:b :d] 2, [:d :f] 2, (:b :a) 2, [:a :c] 3, [:c :e] 3, [:e :f] 3} Let us look find an augmenting suggraph on a different large random-graph (def random-graph (get-random-graph 20 5 5 10)) random-graph #'user/random-graph {[8 11] 2, [16 19] 2, [10 14] 1, [18 23] 1, [13 15] 10, [7 11] 1, [12 17] 4, [10 15] 5, [14 17] 3, [2 3] 5, [2 5] 1, [10 13] 5, [15 17] 3, [6 7] 2, [12 14] 6, [5 10] 8, [0 5] 10, [17 18] 6, [11 14] 1, [8 10] 3, [14 19] 3, [4 7] 7, [4 9] 10, [15 20] 4, [14 15] 10, [1 4] 1, [5 7] 5, [1 3] 8, [4 8] 5, [10 11] 2, [1 5] 9, [9 14] 1, [15 18] 10, [5 8] 4, [8 13] 5, [6 8] 1, [9 11] 4, [7 9] 8, [2 7] 2, [13 17] 1, [2 4] 5, [3 6] 4, [7 10] 2, [0 2] 10, [6 9] 5, [11 15] 10, [19 21] 4, [0 4] 3, [14 18] 1, [9 13] 7, [13 16] 4, [13 18] 9, [3 8] 7, [17 19] 10, [3 7] 3, [16 20] 1, [18 21] 5, [8 12] 1, [12 16] 6, [1 2] 7, [17 22] 2, [19 20] 4, [11 16] 10, [17 21] 8}

(def augmenting-subgraph (ford-fulkerson random-graph 0 20)) augmenting-subgraph #'user/augmenting-subgraph {[8 11] 3, [10 14] 1, (10 5) 8, [13 15] 2, (15 11) 2, (11 9) 5, [7 11] 3, [10 15] 4, [14 17] 4, [2 3] 2, [15 17] 2, [5 10] 8, (6 3) 1, [0 5] 9, [11 14] 1, [8 10] 2, [14 19] 1, [15 20] 4, (11 8) 1, [5 7] 6, [9 14] 1, (15 10) 1, [5 8] 2, (14 11) 1, [6 8] 1, [9 11] 5, [7 9] 5, [13 17] 1, [3 6] 2, (19 14) 1, [7 10] 2, (17 14) 2, (11 7) 3, (9 7) 2, (17 15) 1, (15 13) 1, [0 2] 2, [11 15] 3, [9 13] 2, [3 8] 1, [17 19] 4, [16 20] 1, (7 5) 1, (5 0) 2, [19 20] 4, [11 16] 1} Let me simplify the output by removing spurious feedback loops: (defn clean [G] (let [ks (into #{} (keys G)) H (->> (map reverse ks) (filter ks) (mapcat (fn [k] (let [v0 (G k) v1 (G (reverse k))] (if (> v0 v1) {k (- v0 v1)} {(reverse k) (- v1 v0)})))) (into {}))] (as-> (map reverse (keys H)) $ (concat (keys H) $) (into [] $) (merge (apply dissoc G $) H) (filter (fn [[k v]] (> v 0)) $)))) (def cleaned (clean augmenting-subgraph)) #'user/clean #'user/cleaned So, here is the final version of my implementation of the Ford-Fulkerson algorithm to find a maximal augmenting subgraph with maximal flow: (defn ford-fulkerson [G a b] (loop [H G S {}] (let [R (find-a-path H a b)] (if (empty? R) (into {} (clean S)) (let [v (reduce min (map H R)) P (zipmap R (repeat v))] (recur (residual-graph H P) (merge-with + S P))))))) #'user/ford-fulkerson and a final test: (= (into {} cleaned) (ford-fulkerson random-graph 0 20)) true `

Document Summarization via Nonnegative Matrix Factorization Description of the problem Assume we have a matrix $A$ of size $n\times m$ which consists of nonnegative entries. We want to write $A$ as a product $A = BC$ where $B$ has size $n\times k$ and $C$ has size $k\times m$ where $k$ is much smaller than both $n$ and $m$. We also want $B$ and $C$ to consist nonnegative entries. This problem is called nonnegative matrix factorization. We can think of this problem as an optimization problem where the error function is $$ err(B,C) = \sum_{i,j,\ell} |a_{ij}-b_{i\ell}c_{\ell j}| $$ with a postivity constraint on the entries of $B$ and $C$. I wrote about this before here and here. Today, I am going to apply this to a problem coming from natural language processing. Nonnegative Matrix Decomposition and Document Summarization Assume we have a text, and we write a matrix $A$ whose rows are labeled with the sentences appearing in the text and whose columns are labeled with the words appearing in the text. For a (sentence,word)-pair the corresponding entry in the matrix is 1 if the word appears in the sentence, and the entry is 0 otherwise. Dividing each row by the sum of the terms in that row, we convert these 1’s and 0’s to a probability distribution: in the new matrix for a (sentence,word)-pair the corresponding entry is the probability that that word appears in that sentence. If we apply the nonnegative matrix factorization to this matrix we get two matrices $B$ and $C$ with nonnegative entries such that $A = BC$. Now, I will use the following hypothesis: a topic is a specific probability distribution over the set of all words appearing in a text. Thus such a decomposition tries to identify k-many topics that one can associate with the text at hand. While $B$ measures how much of each sentence belongs to a topic, $C$ measures the same thing for each word. An Implementation I am going to re-cycle the code I wrote in my earlier posts: one for Latent Semantic Analysis to get the relevant matrix from a document, and another for Nonnegative Matrix Decomposition in clojure: First, let us define our namespace with the necessary libraries: (ns summary (:import opennlp.tools.sentdetect.SentenceDetector opennlp.tools.sentdetect.SentenceDetectorME opennlp.tools.sentdetect.SentenceModel opennlp.tools.stemmer.PorterStemmer java.io.File) (:require [clojure.string :as st] [clojure.core.matrix :as cm] [clatrix.core :as cc]) (:gen-class))

Now, let us write the functions that create the matrix $A$ from a given document: (defn bag-of-words [sentence stemmer stop-words] {sentence (as-> sentence $ (st/lower-case $) (st/replace $ #"[^\s\p{Isletter}]" "") (st/split $ #"\s+") (filter #(not (stop-words %)) $) (map #(.stem stemmer %) $) (into #{} $))}) (defn get-matrix [sentences detector stemmer stop-words] (let [raw (into {} (mapcat #(bag-of-words % stemmer stop-words) sentences)) ws (->> (vals raw) (reduce concat) (into #{}) (into [])) n (count sentences) m (count ws) A (cc/zeros m n)] (doseq [i (range n)] (doseq [w (get raw (nth sentences i))] (cc/set A (.indexOf ws w) i 1))) A)) #'summary/bag-of-words #'summary/get-matrix For the matrix decomposition, first I need the error function and a random matrix function: (defn cost-fn [A B] (->> (cm/sub A B) (cm/to-vector) (map (fn [x] (* x x))) (reduce +))) (defn random-matrix [n m] (as-> (repeatedly rand) $ (take (* n m) $) (cm/reshape $ [n m]))) #'summary/cost-fn #'summary/random-matrix Now, the matrix decomposition code: (defn nnmd [D k cost-fn epocs tol rate] (let [n (cm/row-count D) m (cm/column-count D) s (* n m)] (loop [W (random-matrix n k) H (random-matrix k m) i epocs c tol] (if (or (= i 0) (< c tol)) [W H i c] (let [u (cm/reshape (take s (repeat 1)) [n m]) Wt (cm/transpose W) Ht (cm/transpose H) et (cm/mul rate (cm/div W (cm/mmul u Ht))) mu (cm/mul rate (cm/div H (cm/mmul Wt u))) temp (cm/sub (cm/div D (cm/mmul W H)) u)] (recur (cm/add W (cm/mul et (cm/mmul temp Ht))) (cm/add H (cm/mul mu (cm/mmul Wt temp))) (dec i) (/ (cost-fn D (cm/mmul W H)) s))))))) #'summary/nnmd So, let us test: (def summary (let [sd (SentenceDetectorME. (SentenceModel. (File. "resources/en-sent.bin"))) stemmer (PorterStemmer.) sentences (->> (slurp "data/textc") (.sentDetect sd) (into [])) stop-words (as-> (slurp "resources/remove-en") $ (st/replace $ #"\p{IsPunctuation}" "") (st/split $ #"\s+") (into #{} $)) matrix (get-matrix sentences sd stemmer stop-words) [W H i c] (cc/t (nnmd matrix 3 cost-fn 2000 1e-2 1e-2)) weights (cc/matrix W)] (map (fn [s w] {:sentence s :weight w}) sentences weights))) Topic 1 Topic 2 Topic 3 Sentence 0.00 0.18 0.36 The Obama administration has backed down in its bitter dispute with Silicon Valley over the encryption of data on iPhones and other digital devices, concluding that it is not possible to give American law enforcement and intelligence agencies access to that information without also creating an opening that China, Russia, cybercriminals and terrorists could exploit. 0.00 0.00 0.18 With its decision, which angered the FBI and other law enforcement agencies, the administration essentially agreed with Apple, Google, Microsoft and a group of the nation’s top cryptographers and computer scientists that millions of Americans would be vulnerable to hacking if technology firms and smartphone manufacturers were required to provide the government with “back doors,” or access to their source code and encryption keys. 0.00 0.18 0.00 Companies like Apple say they are protecting their customers’ information by resisting government demands for access to text messages. 0.00 0.18 0.00 A standoff has grown between the sides as the companies have embraced tougher encryption. 0.00 0.18 0.00 Peter G Neumann, a computer security pioneer, says “there are more vulnerabilities than ever. 0.00 0.00 0.18 Security experts like Richard A. Clarke, the former White House counterterrorism czar, also signed the letter to Obama. 0.00 0.00 0.18 That would enable the government to see messages, photographs and other data now routinely encrypted on smartphones. 0.00 0.00 0.18 Current technology puts the keys for access to the information in the hands of the individual user, not the companies. 0.00 0.75 0.87 The first indication of the retreat came on Thursday, when the FBI director, James B Comey, told the Senate Homeland Security and Governmental Affairs Committee that the administration would not seek legislation to compel the companies to create such a portal. 0.00 0.18 0.18 But the decision, made at the White House a week ago, goes considerably beyond that. 0.19 0.00 0.00 While the administration said it would continue to try to persuade companies like Apple and Google to assist in criminal and national security investigations, it determined that the government should not force them to breach the security of their products. 0.79 0.93 0.65 In essence, investigators will have to hope they find other ways to get what they need, from data stored in the cloud in unencrypted form or transmitted over phone lines, which are covered by a law that affects telecommunications providers but not the technology giants. 0.00 0.18 0.00 Mr Comey had expressed alarm a year ago after Apple introduced an operating system that encrypted virtually everything contained in an iPhone. 0.00 0.00 0.18 What frustrated him was that Apple had designed the system to ensure that the company never held on to the keys, putting them entirely in the hands of users through the codes or fingerprints they use to get into their phones. 0.38 0.00 0.00 As a result, if Apple is handed a court order for data — until recently, it received hundreds every year — it could not open the coded information. 0.19 0.00 0.18 Mr Comey compared that system to the creation of a door no law officers could enter, or a car trunk they could not unlock. 0.19 0.00 0.00 His concern about what the FBI calls the “going dark” problem received support from the director of the National Security Agency and other intelligence officials. 0.19 0.00 0.00 But after a year of study and extensive White House debate, President Obama and his advisers have reached a broad conclusion that an effort to compel the companies to give the government access would fail, both politically and technologically. 0.95 0.00 0.73 “This looks promising, but there’s still going to be tremendous pressure from law enforcement,” said Peter G Neumann, one of the nation’s leading computer scientists and a co-author of a paper that examined the government’s proposal for special access. 0.19 0.18 0.00 “The N.S.A. is capable of dealing with the cryptography for now, but law enforcement is going to have real difficulty with this. 0.19 0.00 0.00 This is never a done deal.” 0.19 0.00 0.00 In the paper, released in July, Mr Neumann and other top cryptographers and computer scientists argued that there was no way for the government to have a back door into encrypted communications without creating an opening that would be exploited by Chinese and Russian intelligence agents, cybercriminals and terrorist groups. 0.00 0.00 0.18 Inside the White House, the Office of Science and Technology Policy came largely to the same conclusion. 0.19 0.00 0.00 Those determinations surprised the FBI and local law enforcement officials, who had believed just months ago that the White House would ultimately embrace their efforts. 0.00 0.16 1.29 The intelligence agencies were less vocal, which may reflect their greater capability to search for and gather information. 0.00 0.18 0.00 The National Security Agency spends vast sums to get around digital encryption, and it has tools and resources that local law enforcement officials still do not have and most likely never will. 0.38 0.00 0.00 Disclosures by the former N.S.A. contractor Edward J. Snowden showed the extent of the agency’s focus on cracking and circumventing the encryption of digital communications, including those of Apple, Facebook, Google and Yahoo users. 0.00 0.00 0.18 There were other motivations for the administration’s decision. 0.19 0.00 0.00 Mr Obama and his aides had come to fear that the United States could set a precedent that China and other nations would emulate, requiring Apple, Google and the rest of America’s technology giants to provide them with the same access, officials said. 0.38 0.00 0.00 Timothy D Cook, the chief executive of Apple, sat at the head table with Mr Obama and Xi Jinping, the Chinese president, at a state dinner at the White House last month. 0.19 0.00 0.00 According to government officials and industry executives, Mr Cook told Mr Obama that the Chinese were waiting for an opportunity to seize on administration action to insist that Apple devices, which are also encrypted in China, be open to Beijing’s agents. 0.00 0.00 0.18 In January, three months after Mr Comey began pressing companies for special government access, Chinese officials had threatened to do just that: They considered submitting foreign companies to invasive audits and requiring them to build back doors into their hardware and software. 0.00 0.18 0.00 Those rules have not been put into effect. 0.00 0.18 0.00 The Obama administration’s position was also undercut by officials’ inability to keep their own data safe from Chinese hackers, as shown by the extensive cyberattack at the Office of Personnel Management discovered this year. 0.00 0.18 0.18 That breach, and its aftermath, called into question whether the government could keep the keys to the world’s communications safe from its adversaries in cyberspace. 0.15 0.46 0.29 White House officials said they would continue trying to persuade technology companies to help them in investigations, but they did not specify how. 0.00 0.18 0.00 “As the president has said, the United States will work to ensure that malicious actors can be held to account, without weakening our commitment to strong encryption,” said Mark Stroh, a spokesman for the National Security Council. 0.38 0.00 0.00 “As part of those efforts, we are actively engaged with private companies to ensure they understand the public safety and national security risks that result from malicious actors’ use of their encrypted products and services. 0.00 0.00 0.18 However, the administration is not seeking legislation at this time.” 0.00 1.05 0.74 But here in Silicon Valley, executives did not think the government’s announcement went far enough. 0.00 0.00 0.18 According to administration officials and technology executives, Mr Cook of Apple has pressed the White House for a clear statement that it will never seek a back door in any form, legislative or technical — a statement he hoped to take to Beijing, Moscow and even London. 0.00 0.18 0.00 Prime Minister David Cameron of Britain has threatened to ban encrypted devices and services, like the iPhone and Facebook’s popular WhatsApp messaging service, but has done nothing so far to make good on that threat. 0.19 0.00 0.00 Technology executives are determined to reassure customers abroad that American intelligence agencies are not reading their digital communications. 0.00 0.14 0.22 It is an effort driven by economics: 64 percent of Apple’s revenue originates overseas. 0.00 0.00 0.18 Apple, Google, Facebook and Microsoft argue that people put not only their conversations but their entire digital lives — medical records, tax returns, bank accounts — into a device that slips into their pocket. 0.00 0.00 0.37 While Mr Obama has repeatedly said he is sympathetic to the concerns of law enforcement officials, he made clear during a visit to Silicon Valley in February that he was also aware of privacy concerns and that he sought to balance both interests. 0.00 0.00 0.18 Technologists responded that, with regard to encryption, no such balance existed. 0.00 0.00 0.18 “The real problem is, I don’t see any middle ground for dumbing down everything to make special access possible and having the secure systems we need for commerce, government and everything else,” Mr Neumann said. In this example, the third topic seems promising. If we take only the sentences of weight 0.3 and higher we get The Obama administration has backed down in its bitter dispute with Silicon Valley over the encryption of data on iPhones and other digital devices, concluding that it is not possible to give American law enforcement and intelligence agencies access to that information without also creating an opening that China, Russia, cybercriminals and terrorists could exploit. The first indication of the retreat came on Thursday, when the FBI director, James B Comey, told the Senate Homeland Security and Governmental Affairs Committee that the administration would not seek legislation to compel the companies to create such a portal. In essence, investigators will have to hope they find other ways to get what they need, from data stored in the cloud in unencrypted form or transmitted over phone lines, which are covered by a law that affects telecommunications providers but not the technology giants. “This looks promising, but there’s still going to be tremendous pressure from law enforcement,” said Peter G Neumann, one of the nation’s leading computer scientists and a co-author of a paper that examined the government’s proposal for special access. The intelligence agencies were less vocal, which may reflect their greater capability to search for and gather information. But here in Silicon Valley, executives did not think the government’s announcement went far enough. While Mr Obama has repeatedly said he is sympathetic to the concerns of law enforcement officials, he made clear during a visit to Silicon Valley in February that he was also aware of privacy concerns and that he sought to balance both interests.