Thanks for helping me /r/compsci. I used your suggestions to design an optimal keyboard layout for 1-finger use

First of all, congratulations on sitting down and solving a problem. Always fun, that is.

But you mentioned searching around a bit for information about the problem, and it appears you never hit on a popular formalism for it, and that formalism can help you figure out how to use a GA for it as well. I'd actually advise you to code one up if you have time. It's fun. But I'll warn you up front, GAs suck at this problem.

Anyway, you can formulate this as something called the Quadratic Assignment Problem (QAP). Originally, QAP was formulated as a facilities layout problem (in 1957 if memory serves me). The basic idea is that you're building a university. You have N plots of land, and N buildings to put on them. You know the distance between plots obviously, but you're also given a matrix of numbers called the "flow" matrix. The flow matrix (also called a cost matrix) is defined as follows: C_ij = number of people who will walk between building i and building j during some unit of time. So the flow between the math and physics buildings is very high. The flow between engineering and nursing is very low. Your goal is to assign each building to exactly one location such that the overall product of distance*flow is minimized. That is, the total number of miles walked per unit time is minimized.

What does this mean? Well, first, note that if you number each building and each location, all solutions are simply permutations of the numbers 1:n (or 0:n-1 if you prefer).

4 7 0 2 3 6 1 5

denotes that building 4 goes in location 0, building 7 in location 1, building 0 in location 2, and so on.

Now for the evaluation function. We need to sum up the product of distance times flow between all pairs of buildings in their assigned locations. In LaTeX notation, given a distance matrix D and a flow matrix C, the overall cost of a permutation X is given by

f(X) = \sum_{i=1}^n\sum_{j=1}^n{D_{i,j}*C_{X_i,X_j}}.

It is this function you need to minimize. OK, but what about keyboard layout? Well, view the problem as assigning symbols to key locations. You want to minimize the total "effort", for lack of a better word, needed to type typical English prose. Let the distance matrix be the "closeness" of two keys (not necessarily physically, but in terms of how easily you can type the two keys consecutively). In the case of a one finger keyboard, physical distance might work OK. But in general, you can assign lower distances for alternating hands, higher distances for moving the same finger up and down rows, etc. The flow is just the frequency of typing two characters consecutively, which you can compute from the Twitter data the same as you did before.

OK, what's needed for a GA now are really two operators: mutation and crossover. In both cases, what you want is to take permutations and produce permutations. Simple one-point crossover won't work, because you won't get valid permutations back out. However, there are several permutation crossover operators: Order Crossover (OX), Cycle Crossover (CX), and Edge Assembly Crossover (EAX) are three examples that differ in which characteristics of the parent strings they attempt to pass down to the offspring. For QAP, you want cycle crossover, as what is important is the absolute location of each building, and CX tries to pass that down where possible.

Mutation is easy: just pick two points in the permutation and swap them.

With that, you can build a GA. Again, it's worth a warning. They suck. You can download several papers from Google Scholar on genetic algorithms for QAP (I've actually written a couple myself that weren't specifically about GAs, but included some relevant information. But there are better sources than me for this problem.)

The state of the art for QAP solvers depends on a couple of things. If you have small enough problems (and keyboard layout is at the upper end of "small enough") you can solve them exactly with branch and bound techniques. It's not fast, but with N=35 or so, and a big enough machine, you can crank through the space if you're clever enough with the cuts. If you want to stick with something easier, Tabu Search algorithms are to the best of my knowledge the best heuristic search methods for QAP. Eric Taillard's Robust Taboo Search (that's the way he spells it) is actually pretty simple to code and works pretty damn well on QAP problems.

I was too lazy to Google you some citations, but if you're interested and can't find them yourself, hit me up and I'll send you some more information.

Edit: I should also add, QAP is (probably obviously) NP-hard. But more importantly, unlike say the Traveling Salesrep Problem, it's also brutally hard in practice. There is no polynomial time bounded approximation scheme for QAP, and there are no heuristics anywhere near as good as say Lin-Kernighan is for TSP. It's just a nasty, nasty problem. That's why it's fun. :)

Oh man where were you last week! QAP is exactly the problem I was trying to solve. I even defined the score calculating formula in the same exact way and calculated the distance and weight just like you described. My mutation was very similar too - swap two points in the permutation. I allowed for swapping more than two points to speed up mutation. In my Python source, I stored the layout in a 1D array like the permutation of numbers that you mentioned. So far I'm with you. What I don't understand is how [CX](http://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)) could help? I can't just take the first 13 keys from QWERTY and the last 13 keys from DVORAK to get a better keyboard - I'd have missing and repeating keys.

I'm just reading Taillard's Robust Taboo Search (PDF) and I realized I'm doing something similar to to the Taboo list with my reject variable (rej = {}) i.e. I ignore keyboards previously computed, though I don't break it down by specific moves like him.

CX is unfortunately rather complicated to describe. The basic idea is not so bad, but there's a lot of bookkeeping.

Assume the following two permutations:

p1 = 0 1 2 3 4 5 6 7
p2 = 4 5 7 3 1 0 6 2

What CX tries to do is find subsets of positions such that a "cycle" is formed, where a cycle is defined as a closure on the contained values. OK, that doesn't help at all. Back to the example. Let's take only the first three positions (just put 'x' for the others to indicate we don't care).

p1' = 0 1 2 x x x x
p2' = 4 5 7 x x x x

If I just swap those two pieces, I get nonsensical results, right? But the reason I get bad results is because the subset of values I chose from each parent contains values not contained in the other, which means that those values must be in the other part of the parents, and will be repeated when I swap the pieces out. For example, if I swap them, the child 2 is going to get "0 1 2" as its first three numbers, but because those weren't in the first three numbers of parent 2, they must be somewhere else, and since child 2 gets the rest of the values from parent two, you repeat them, which is bad. What if instead, I choose indexes 0, 1, 4, and 5. Now I have

p1' = 0 1 x x 4 5 x x
p2' = 4 5 x x 1 0 x x

If I swap those values to create two partial offspring, I get

c1 = 4 5 x x 1 0 x x
c2 = 0 1 x x 4 5 x x

If I replace the 'x' symbols with the numbers from the original parents (c1 from p1, c2 from p2), I get

c1 = 4 5 2 3 1 0 6 7
c2 = 0 1 7 3 4 5 6 2

which works nicely. What CX does is define the algorithm by which you can repeatedly generate these subsets of indices in such a way as to allow you go do a crossover operation without mucking up your permutations.

I assume that the matter of interest in this post is the process that was used to identify an optimal keyboard layout, and I've up-voted it because it's a very interesting problem.

If anyone is actually interested in the subject of one-finger keyboards, of course you have to take into account a lot of practical trade-offs that are already exemplified by the QWERTY vs. DVORAK vs. (etc.) competition: What is already out there? Are there already existing solutions in use that have the practical advantage of established usage and general availability on common platforms, even if the layout itself might not be as optimal as another new layout?

In the case of one-finger keyboards, FITALY has been around for quite a while. Its first market was on the Palm family of devices, which is where I first discovered it. That market is just about done. They have since expanded to Tablet PC (oops -- evaporating too), and Pocket PC (uh, this isn't going very well.) Oh well, platforms move on.

Despite the unfortunate platform choices (I suppose Palm was a good horse to ride for a decade or so), FITALY is a very good one-finger keyboard. Over the years, as I've dabbled with platforms that FITALY hasn't officially supported, I have always used virtual keyboard development tools to create my own knock-off for personal use.

I can't say I understand why FITALY hasn't already made an appearance in the Android or iPad/iPhone markets. You can find internet posts with guidance on how to create your own virtual FITALY keyboards for Android.

Now that Swype exists, I can't say I would ever go back to any peck-peck-peck single-finger keyboard again, no matter how perfect the layout. The combination of Swype and FITALY would be very nice, but the way that the Swype AI decides the most likely word choice is probably strongly tied to the keyboard layout.

A 6x5 layout like Fitaly is indeed a much better layout for one-finger use because it is closer to a square than 7x4 or 10x3 layout but I had to make my own for a number of reasons.

Fitaly is not freely available and I wanted to release Optimus free (as in beer and speech) for anyone to adopt for their own. This alone was reason enough for me to go through this the entire experiment.

While Fitaly was made with the intention of reducing typing distance, I don't think it is the most optimal layout for my users. Fitaly uses letter-pair frequency based on the Brown Corpus and put the most frequently used characters in the middle. I used letter-pair frequency, compiled from 9 million Tweets because I feel my users will most likely type informally like in a chat instead of formally like in newspaper articles.

I realize that putting multiple space bars in the middle of the keyboard is a very useful thing but I assume my users will make a lot of typing errors due to unstable movements and lack of control. As a result, I want them to avoid hitting the space and back-space keys as much as possible. I can account for unnecessary letters in code but unintentional space characters would be significantly more complex to deal with.

I just ran Fitaly through my code to see how well it compares against Optimus. It does a very good job though it is not the most optimal layout I could find:

Optimized Fixed Space Bars 6x5 is better than Fitaly though not significantly. The different is most probably because the dataset Fitaly used to build its letter-pair frequency table is different from KType's Twitter data.

I will try to get in touch with Fitaly's designers to see if they will allow me to provide a Fitaly layout option in KType. Swype is indeed a whole different ball-game and I highly doubt my users will have the dexterity to swipe correctly from one key to another, regardless of the layout.

Swype does take a lot of dexterity. On the other hand, it is remarkably forgiving of errors. It doesn't really care what you are typing at all -- you are just making a drawing for it that it matches to drawn patterns of words that it knows. You can completely miss letters, or just kind of scribble the approximate shape of the word, and it amazingly gets it right anyway. I actually think that might be an advantage in the context of your target users. But Swype is neither libre nor gratis -- there is big money at stake.

This is amazing. You've found a real-world problem and developed a very interesting solution for it. I've been thinking for several months on how to design an optimal keyboard for one- or two-finger use. However, your keyboard does not help eliminate common misspellings between words. I use an iPhone, and obviously it doesn't know whether I mean "bet" or "get", since both are actual words. Another common misspelling is "good" vs. "food". In my opinion, an optimal keyboard would be one that spaces the commonly-used letters as far apart as possible so that a good autocorrector could figure out what you really mean more easily. Despite my critiques, please don't think I'm putting your project down in any way. I still think it's awesome!

The keyboard layout won't fix this problem but my prediction engine hopefully will help address the issue. I doubt I'll be able to predict if you mean "bet" or "get" after "I would..." because both words work well there. But I can predict that "Make me some..." is followed by "food" more often than "good".

What I could do is allow some typed letters to be ignored if another word without those letters is found with a substantially high probability. I don't know the computing cost for this but I'll give it a shot.

Just in case you missed his point - if 'g' is not in the vicinity of 'b', then there's no ambiguoty between 'bet' and 'get' when you press 'bet' because you'll never hit 'g'. In other words, the more a given pair of similar words occur in natural texts, the more likely are at least one of the letters to not be in the vicinity of each other. It's similar to what you've already done, just with a much larger matrix (let n be the amount of words you want to consider - then it's n² entries).

This is interesting work, but do you ever prove optimality? You claim that you have found an optimal keyboard layout, but using an SA algorithm is not guaranteed to find an optimal layout. It will probably find a damn good one, and what you have may indeed be optimal, but just because an SA found it doesn't mean it is.

I wholeheartedly agree. I attempted to find THE optimal layout but could not prove it in mathematical terms because the problem is in NP.

So I tried my best to get as close to the optimal solution as possible. I still can't say if I found it or not but the fact that my fixed-vowel-row algorithm kept coming up with the same exact layout hundreds and thousands of times despite starting from completely different random layouts gives me some evidence that Optimus hit a very stable local minima, if not the global. Sorry if I gave any indication that I came up with a perfectly optimal solution for general NP problems.

No need to be sorry, it's just a small mistake in terms of terminology in what is rather interesting work! Good job :)

By the way, it is actually possible to prove optimality even for NP problems. Techniques like Constraint Programming and Mixed Integer Programming can prove optimality, although I'm not so sure either would be easily applicable to your problem.

It isn't a trivial rise in score so I'm inclined to reject it. But isn't a substantial rise either and comes with the benefit that TH is typed a lot more often than HT. This makes me wonder if I should have calculated score any differently. Maybe Left-to-Right = 1, Right-to-Left = 1.25?

When I get started with user testing, I think I will let them try both out and see if they prefer one over the other. The best part of KType is that you can just drag-drop keys on the iPad itself to re-arrange them.

Also thanks for the comment about the writeup. I have been blogging personally for a decade now but haven't written any tech-heavy articles. KType is my first attempt at writing something beyond how my day was and I've been pretty nervous about coming off like a dolt. So reading your comment certainly helped.

That's a problem I'm currently trying to figure out. What I did was to calculate weights (between 0 and 1) for each letter given its position and many other factors, and then calculate a probability that the letter will survive to the next generation. The ones that don't survive are replaced with the corresponding letters from the other parent. I don't think this is the best way to do it, but I have been getting decent results so far.

Do you mean, you couldn't figure out a way to encode the DNA? Personally I'd assign numbers to each letter, so that when sorted show up as either qwerty or DVORAK. You could then cross them over, resort them and be able to compare them.

I think I can encode DNA by just assigning each letter a numeric value and using the 5-bit binary representation. What I can't figure out is the cross over step. How can I pick the first 13 keys from QWERTY and merge them with the last 13 keys from DVORAK? There will be keys missing and repeating if I do that. The obvious solution seems to ignore low-scoring repeating keys and fill in the missing keys in those positions but that really messes with the 2D layout. Each key can be close to 8 other keys and repositioning them will drastically affect the score.

Computer Science Theory and Application. We share and discuss any content that computer scientists find interesting. People from all walks of life welcome, including hackers, hobbyists, professionals, and academics.